#!/usr/bin/env node

/**
 * V0 项目预处理器（最小化处理模式）
 *
 * 只做 100% 有把握的操作：
 * 1. 完整复制项目
 * 2. 分析项目结构
 * 3. 生成任务文档
 *
 * 不做任何代码修改，全部留给 AI 处理
 */

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const TARGET_TYPE_TO_SRC_DIR = {
  prototypes: 'src/prototypes',
  components: 'src/components',
  themes: 'src/themes',
};

const THEME_SPLIT_SKILL_DOCS = [
  '/skills/axure-prototype-workflow/theme-generation.md',
  '/skills/axure-prototype-workflow/doc-generation.md',
  '/skills/axure-prototype-workflow/data-generation.md',
  '/skills/web-page-workflow/theme-generation.md',
  '/skills/web-page-workflow/doc-generation.md',
  '/skills/web-page-workflow/data-generation.md',
];

const CONFIG = {
  projectRoot: path.resolve(__dirname, '..'),
  tempDir: path.resolve(__dirname, '../temp'),
};

function log(message, type = 'info') {
  const prefix = { info: '✓', warn: '⚠', error: '✗', progress: '⏳' }[type] || 'ℹ';
  console.log(`${prefix} ${message}`);
}

function ensureDir(dirPath) {
  if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, { recursive: true });
  }
}

function sanitizeName(rawName) {
  return String(rawName || '')
    .replace(/[^a-z0-9-]/gi, '-')
    .replace(/-+/g, '-')
    .replace(/^-|-$/g, '')
    .toLowerCase();
}

function getTargetInfo(targetType, outputName) {
  const srcDir = TARGET_TYPE_TO_SRC_DIR[targetType];
  const outputBaseDir = path.resolve(CONFIG.projectRoot, srcDir);
  const outputDir = path.join(outputBaseDir, outputName);
  const relativeOutputDir = `${srcDir}/${outputName}`;

  if (targetType === 'themes') {
    return {
      targetType,
      srcDir,
      outputBaseDir,
      outputDir,
      relativeOutputDir,
      tasksFileName: '.v0-theme-tasks.md',
      analysisFileName: '.v0-theme-analysis.json',
      checkPath: `/themes/${outputName}`,
      label: '主题',
    };
  }

  return {
    targetType,
    srcDir,
    outputBaseDir,
    outputDir,
    relativeOutputDir,
    tasksFileName: '.v0-tasks.md',
    analysisFileName: '.v0-analysis.json',
    checkPath: `/${targetType}/${outputName}`,
    label: targetType === 'components' ? '组件' : '页面',
  };
}

// 递归查找所有 .tsx/.ts 文件
function findFiles(dir, extensions = ['.tsx', '.ts']) {
  const results = [];

  if (!fs.existsSync(dir)) return results;

  const entries = fs.readdirSync(dir, { withFileTypes: true });

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);

    if (entry.isDirectory()) {
      // 跳过 node_modules 和 .next
      if (entry.name === 'node_modules' || entry.name === '.next') continue;
      results.push(...findFiles(fullPath, extensions));
    } else {
      const ext = path.extname(entry.name);
      if (extensions.includes(ext)) {
        results.push(fullPath);
      }
    }
  }

  return results;
}

function copyDirectory(src, dest) {
  if (!fs.existsSync(src)) return 0;
  ensureDir(dest);
  const entries = fs.readdirSync(src, { withFileTypes: true });
  let count = 0;
  for (const entry of entries) {
    const srcPath = path.join(src, entry.name);
    const destPath = path.join(dest, entry.name);
    if (entry.isDirectory()) {
      if (entry.name === 'node_modules' || entry.name === '.next') continue;
      count += copyDirectory(srcPath, destPath);
    } else {
      fs.copyFileSync(srcPath, destPath);
      count++;
    }
  }
  return count;
}

console.log('V0 Converter - Minimal Processing Mode\n');

/**
 * 批量处理文件：删除 "use client" 和转换路径别名
 * 这些是 100% 确定需要做的转换
 */
function processFiles(targetDir) {
  const files = findFiles(targetDir, ['.tsx', '.ts', '.jsx', '.js']);
  let processedCount = 0;

  files.forEach(file => {
    let content = fs.readFileSync(file, 'utf8');
    let modified = false;

    // 1. 删除 "use client" 指令（100% 确定需要删除）
    const newContent1 = content.replace(/['"]use client['"]\s*;?\s*\n?/g, '');
    if (newContent1 !== content) {
      content = newContent1;
      modified = true;
    }

    // 2. 转换路径别名 @/ 为相对路径（100% 确定需要转换）
    if (content.includes('@/')) {
      const fileDir = path.dirname(file);
      const relativePath = path.relative(fileDir, targetDir);

      // 替换 from '@/...' 和 from "@/..."
      content = content.replace(
        /from\s+(['"])@\//g,
        `from $1${relativePath}/`
      );

      // 替换 import type ... from '@/...' 和 import type ... from "@/..."
      content = content.replace(
        /import\s+type\s+(.*from\s+)(['"])@\//g,
        `import type $1$2${relativePath}/`
      );

      modified = true;
    }

    if (modified) {
      fs.writeFileSync(file, content);
      processedCount++;
    }
  });

  return processedCount;
}

function analyzeProject(targetDir) {
  const analysis = { files: [], pathAliases: [], nextjsImports: [], dependencies: {}, structure: {} };

  const files = findFiles(targetDir, ['.tsx', '.ts']);

  files.forEach(file => {
    const relativePath = path.relative(targetDir, file);
    const content = fs.readFileSync(file, 'utf8');
    const fileInfo = {
      path: relativePath,
      hasUseClient: content.includes('"use client"') || content.includes("'use client'"),
      pathAliases: [],
      nextjsImports: []
    };

    const aliasMatches = content.matchAll(/from\s+['"]@\/([^'"]+)['"]/g);
    for (const match of aliasMatches) {
      fileInfo.pathAliases.push({
        original: `@/${match[1]}`,
        relative: path.relative(path.dirname(file), path.join(targetDir, match[1]))
      });
    }

    const nextImports = content.matchAll(/import\s+.*from\s+['"]next\/([^'"]+)['"]/g);
    for (const match of nextImports) {
      fileInfo.nextjsImports.push(`next/${match[1]}`);
    }

    const vercelImports = content.matchAll(/import\s+.*from\s+['"]@vercel\/([^'"]+)['"]/g);
    for (const match of vercelImports) {
      fileInfo.nextjsImports.push(`@vercel/${match[1]}`);
    }

    analysis.files.push(fileInfo);
    if (fileInfo.pathAliases.length > 0) {
      analysis.pathAliases.push(...fileInfo.pathAliases.map(a => ({ file: relativePath, ...a })));
    }
    if (fileInfo.nextjsImports.length > 0) {
      analysis.nextjsImports.push(...fileInfo.nextjsImports.map(imp => ({ file: relativePath, import: imp })));
    }
  });

  const packageJsonPath = path.join(targetDir, 'package.json');
  if (fs.existsSync(packageJsonPath)) {
    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
    const deps = packageJson.dependencies || {};
    analysis.dependencies = {
      all: deps,
      toInstall: Object.keys(deps).filter(dep => {
        if (dep === 'next' || dep.startsWith('next-')) return false;
        if (dep.startsWith('@vercel/')) return false;
        if (dep === 'react' || dep === 'react-dom') return false;
        return true;
      }),
      excluded: Object.keys(deps).filter(dep => {
        if (dep === 'next' || dep.startsWith('next-')) return true;
        if (dep.startsWith('@vercel/')) return true;
        if (dep === 'react' || dep === 'react-dom') return true;
        return false;
      })
    };
  }

  analysis.structure = {
    hasAppDir: fs.existsSync(path.join(targetDir, 'app')),
    hasPageTsx: fs.existsSync(path.join(targetDir, 'app/page.tsx')),
    hasLayoutTsx: fs.existsSync(path.join(targetDir, 'app/layout.tsx')),
    hasGlobalsCss: fs.existsSync(path.join(targetDir, 'app/globals.css')),
    hasComponentsDir: fs.existsSync(path.join(targetDir, 'components')),
    hasHooksDir: fs.existsSync(path.join(targetDir, 'hooks')),
    hasLibDir: fs.existsSync(path.join(targetDir, 'lib')),
    hasPublicDir: fs.existsSync(path.join(targetDir, 'public'))
  };

  return analysis;
}

function buildAnalysisReport(analysis) {
  return {
    summary: {
      totalFiles: analysis.files.length,
      filesWithUseClient: analysis.files.filter(f => f.hasUseClient).length,
      pathAliasCount: analysis.pathAliases.length,
      nextjsImportCount: analysis.nextjsImports.length,
      dependenciesToInstall: analysis.dependencies.toInstall?.length || 0
    },
    structure: analysis.structure,
    pathAliases: analysis.pathAliases,
    nextjsImports: analysis.nextjsImports,
    dependencies: analysis.dependencies,
    files: analysis.files
  };
}

function generateDefaultTasksDocument(report, targetInfo, outputName, tempDir) {
  const reportPath = path.join(targetInfo.outputDir, targetInfo.analysisFileName);
  fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));

  let markdown = `# V0 项目转换任务清单\n\n`;
  markdown += `> **重要**: 请先阅读 \`/skills/v0-project-converter/SKILL.md\` 了解转换规范\n\n`;
  markdown += `**名称**: ${outputName}\n`;
  markdown += `**项目位置**: \`${targetInfo.relativeOutputDir}/\`\n`;
  markdown += `**原始文件**: \`${tempDir}\` (仅供参考，不要修改)\n`;
  markdown += `**生成时间**: ${new Date().toLocaleString()}\n\n`;

  markdown += `## 📊 项目概况\n\n`;
  markdown += `- 总文件数: ${report.summary.totalFiles}\n`;
  markdown += `- ~~包含 'use client': ${report.summary.filesWithUseClient} 个文件~~ ✓ 已由脚本删除\n`;
  markdown += `- ~~路径别名 (@/): ${report.summary.pathAliasCount} 处~~ ✓ 已由脚本转换\n`;
  markdown += `- Next.js imports: ${report.summary.nextjsImportCount} 处（需要处理）\n`;
  markdown += `- 需要安装的依赖: ${report.summary.dependenciesToInstall} 个\n\n`;

  markdown += `## ✅ 转换任务\n\n`;

  markdown += `### 任务 1: 创建 index.tsx\n\n`;
  markdown += `**目标**: 将 \`app/page.tsx\` 包装为本项目组件\n\n`;
  markdown += `**参考文件**: \`${targetInfo.relativeOutputDir}/app/page.tsx\`\n\n`;
  markdown += `**操作**: 按照 \`/skills/v0-project-converter/SKILL.md\` 中的本项目组件规范创建 \`index.tsx\`\n\n`;

  markdown += `### 任务 2: 创建 style.css\n\n`;
  markdown += `**目标**: 基于 \`app/globals.css\` 创建样式文件\n\n`;
  if (report.structure.hasGlobalsCss) {
    markdown += `**参考文件**: \`${targetInfo.relativeOutputDir}/app/globals.css\`\n\n`;
    markdown += `**操作**: 复制内容，确保开头有 \`@import "tailwindcss";\`\n\n`;
  } else {
    markdown += `**操作**: 创建基础样式文件，内容为 \`@import "tailwindcss";\`\n\n`;
  }

  markdown += `### 任务 3: 清理 Next.js 代码\n\n`;
  markdown += `**目标**: 移除 Next.js 特定的 imports 和组件\n\n`;
  markdown += `**需要处理**:\n`;
  markdown += `- ~~删除 \`"use client"\` 指令~~ ✓ 已由脚本处理\n`;
  markdown += `- ~~转换路径别名 \`@/\`~~ ✓ 已由脚本处理\n`;
  markdown += `- 移除 Next.js imports (${report.nextjsImports.length} 处)\n`;
  markdown += `- 替换组件: \`<Image>\` → \`<img>\`, \`<Link>\` → \`<a>\`\n`;
  markdown += `- 删除 \`Metadata\` 类型声明\n\n`;

  if (report.nextjsImports.length > 0) {
    markdown += `**Next.js imports 需要移除**:\n`;
    const grouped = {};
    report.nextjsImports.forEach(item => {
      if (!grouped[item.import]) grouped[item.import] = [];
      grouped[item.import].push(item.file);
    });
    Object.entries(grouped).slice(0, 5).forEach(([imp, files]) => {
      markdown += `- \`${imp}\` (${files.length} 个文件)\n`;
    });
    if (Object.keys(grouped).length > 5) {
      markdown += `- *...还有 ${Object.keys(grouped).length - 5} 种 imports*\n`;
    }
    markdown += `\n`;
  }

  markdown += `### 任务 4: 安装依赖\n\n`;
  if (report.dependencies.toInstall && report.dependencies.toInstall.length > 0) {
    markdown += `**执行命令**:\n`;
    markdown += `\`\`\`bash\n`;
    markdown += `pnpm add ${report.dependencies.toInstall.join(' ')}\n`;
    markdown += `\`\`\`\n\n`;
  } else {
    markdown += `✓ 无需安装额外依赖\n\n`;
  }

  markdown += `### 任务 5: 验收测试\n\n`;
  markdown += `**执行命令**:\n`;
  markdown += `\`\`\`bash\n`;
  markdown += `node scripts/check-app-ready.mjs ${targetInfo.checkPath}\n`;
  markdown += `\`\`\`\n\n`;
  markdown += `**验收标准**: 状态为 READY，页面正常渲染，无控制台错误\n\n`;

  markdown += `## 📚 参考资料\n\n`;
  markdown += `- **转换规范**: \`/skills/v0-project-converter/SKILL.md\`\n`;
  markdown += `- **原始项目**: \`${tempDir}\` (仅供参考)\n`;
  markdown += `- **详细数据**: \`${targetInfo.analysisFileName}\`\n\n`;

  markdown += `## 💡 注意事项\n\n`;
  markdown += `1. ~~**"use client"**: Next.js 指令，必须删除~~ ✓ 已由脚本处理\n`;
  markdown += `2. ~~**路径别名**: \`@/\` 需转换为相对路径~~ ✓ 已由脚本处理\n`;
  markdown += `3. **原始文件**: \`${tempDir}\` 目录保留作为参考，不要修改\n`;
  markdown += `4. **验证**: 完成后务必运行验收脚本确认\n`;

  const mdPath = path.join(targetInfo.outputDir, targetInfo.tasksFileName);
  fs.writeFileSync(mdPath, markdown);

  return { reportPath, mdPath };
}

function generateThemeTasksDocument(report, targetInfo, outputName, tempDir) {
  const reportPath = path.join(targetInfo.outputDir, targetInfo.analysisFileName);
  fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));

  let markdown = `# V0 主题导入任务清单\n\n`;
  markdown += `> **重要**: 请先阅读 \`/skills/v0-project-converter/SKILL.md\` 与主题拆分技能文档，按任务顺序执行\n\n`;
  markdown += `**主题 key**: ${outputName}\n`;
  markdown += `**主题目录**: \`${targetInfo.relativeOutputDir}/\`\n`;
  markdown += `**原始文件**: \`${tempDir}\` (仅供参考，不要修改)\n`;
  markdown += `**生成时间**: ${new Date().toLocaleString()}\n\n`;

  markdown += `## 📊 输入概况\n\n`;
  markdown += `- 总文件数: ${report.summary.totalFiles}\n`;
  markdown += `- 路径别名 (@/): ${report.summary.pathAliasCount} 处（已转换）\n`;
  markdown += `- Next.js imports: ${report.summary.nextjsImportCount} 处\n`;
  markdown += `- 依赖待评估: ${report.summary.dependenciesToInstall} 个\n\n`;

  markdown += `## 📚 参考文档（必须阅读）\n\n`;
  markdown += `- \`/skills/v0-project-converter/SKILL.md\`\n`;
  THEME_SPLIT_SKILL_DOCS.forEach((docPath) => {
    markdown += `- \`${docPath}\`\n`;
  });
  markdown += `\n`;

  markdown += `## ✅ 主题导入任务（共 5 个）\n\n`;

  markdown += `### 任务 1：生成主题 token\n\n`;
  markdown += `**目标**：在 \`${targetInfo.relativeOutputDir}/\` 下生成 \`globals.css\` 或 \`designToken.json\`（二选一）\n\n`;
  markdown += `**要求**：\n`;
  markdown += `- 若输出 \`designToken.json\`，必须包含 \`name\` 字段\n`;
  markdown += `- 覆盖颜色、字体、间距、圆角、阴影等核心 token\n`;
  markdown += `- 与输入项目视觉风格一致\n\n`;

  markdown += `### 任务 2：生成 DESIGN-SPEC.md\n\n`;
  markdown += `**目标**：输出 \`${targetInfo.relativeOutputDir}/DESIGN-SPEC.md\`\n\n`;
  markdown += `**要求**：说明设计语言、组件风格、排版层级、状态规范与使用建议\n\n`;

  markdown += `### 任务 3：按需生成项目文档\n\n`;
  markdown += `**目标**：在 \`src/docs/\` 下补充主题相关项目文档（如页面地图/项目概览）\n\n`;
  markdown += `**要求**：文档需与主题风格、信息结构和业务语境保持一致\n\n`;

  markdown += `### 任务 4：按需生成数据模型\n\n`;
  markdown += `**目标**：在 \`src/database/\` 下补充或更新数据模型\n\n`;
  markdown += `**要求**：\n`;
  markdown += `- 文件名英文、\`tableName\` 中文\n`;
  markdown += `- 每个表包含 \`records\` 数组，记录 id 唯一\n\n`;

  markdown += `### 任务 5：生成/更新主题演示入口\n\n`;
  markdown += `**目标**：生成或更新 \`${targetInfo.relativeOutputDir}/index.tsx\`\n\n`;
  markdown += `**要求**：\n`;
  markdown += `- 演示主题 token 的核心效果\n`;
  markdown += `- 若使用 \`designToken.json\`，演示中需体现 token 注入方式\n\n`;

  markdown += `## 🔍 验收建议\n\n`;
  markdown += `- 目录检查：\`${targetInfo.relativeOutputDir}/\` 是否包含 token 文件、\`DESIGN-SPEC.md\` 与 \`index.tsx\`\n`;
  markdown += `- 文档检查：\`src/docs/\` 是否按需补充\n`;
  markdown += `- 数据检查：\`src/database/\` JSON 结构是否满足约束\n\n`;

  markdown += `## 📎 产物索引\n\n`;
  markdown += `- 任务清单：\`${targetInfo.tasksFileName}\`\n`;
  markdown += `- 分析报告：\`${targetInfo.analysisFileName}\`\n`;

  const mdPath = path.join(targetInfo.outputDir, targetInfo.tasksFileName);
  fs.writeFileSync(mdPath, markdown);

  return { reportPath, mdPath };
}

function parseArgs(rawArgs) {
  const args = [...rawArgs];
  const help = args.length === 0 || args.includes('--help') || args.includes('-h');

  if (help) {
    return { help: true };
  }

  let projectDirArg = '';
  let outputNameArg = '';
  let targetType = 'prototypes';

  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index];
    if (arg === '--target-type') {
      const nextValue = args[index + 1];
      if (!nextValue) {
        throw new Error('参数 --target-type 缺少值');
      }
      targetType = String(nextValue).trim();
      index += 1;
      continue;
    }

    if (!projectDirArg) {
      projectDirArg = arg;
      continue;
    }

    if (!outputNameArg) {
      outputNameArg = arg;
      continue;
    }
  }

  if (!projectDirArg) {
    throw new Error('缺少 <v0-project-dir> 参数');
  }

  if (!Object.prototype.hasOwnProperty.call(TARGET_TYPE_TO_SRC_DIR, targetType)) {
    throw new Error(`不支持的 targetType: ${targetType}。可选值: ${Object.keys(TARGET_TYPE_TO_SRC_DIR).join(', ')}`);
  }

  const outputName = sanitizeName(outputNameArg || path.basename(projectDirArg));
  if (!outputName) {
    throw new Error('无法生成有效的输出名称，请显式传入 [output-name]');
  }

  return {
    help: false,
    projectDirArg,
    outputName,
    targetType,
  };
}

function printHelp() {
  console.log(`
V0 项目预处理器

使用方法:
  node scripts/v0-converter.mjs <v0-project-dir> [output-name] [--target-type <prototypes|components|themes>]

示例:
  node scripts/v0-converter.mjs "temp/my-v0-project" my-page
  node scripts/v0-converter.mjs "temp/my-v0-project" brand-theme --target-type themes

功能:
  - 完整复制 V0 项目（不修改代码）
  - 生成 AI 工作文档（默认 .v0-tasks.md；主题模式 .v0-theme-tasks.md）
  - 生成分析报告（默认 .v0-analysis.json；主题模式 .v0-theme-analysis.json）
  `);
}

async function main() {
  let parsed;
  try {
    parsed = parseArgs(process.argv.slice(2));
  } catch (error) {
    log(`参数错误: ${error.message}`, 'error');
    printHelp();
    process.exit(1);
  }

  if (parsed.help) {
    printHelp();
    process.exit(0);
  }

  const v0Dir = path.resolve(CONFIG.projectRoot, parsed.projectDirArg);
  const targetInfo = getTargetInfo(parsed.targetType, parsed.outputName);
  const outputDir = targetInfo.outputDir;

  if (!fs.existsSync(v0Dir)) {
    log(`错误: 找不到目录 ${v0Dir}`, 'error');
    process.exit(1);
  }

  const appDir = path.join(v0Dir, 'app');
  if (!fs.existsSync(appDir)) {
    log('错误: 这不是一个有效的 V0 项目（缺少 app/ 目录）', 'error');
    process.exit(1);
  }

  try {
    ensureDir(targetInfo.outputBaseDir);

    log(`开始预处理 V0 项目（targetType=${parsed.targetType}）...`, 'info');

    log('步骤 1/4: 复制项目文件...', 'progress');
    const fileCount = copyDirectory(v0Dir, outputDir);
    log(`已复制 ${fileCount} 个文件`, 'info');

    log('步骤 2/4: 复制 public/images 到目标目录...', 'progress');
    const publicImagesDir = path.join(v0Dir, 'public/images');
    const outputImagesDir = path.join(outputDir, 'images');
    let imageCount = 0;
    if (fs.existsSync(publicImagesDir)) {
      imageCount = copyDirectory(publicImagesDir, outputImagesDir);
      log(`已复制 ${imageCount} 个图片文件到 ${targetInfo.relativeOutputDir}/images/`, 'info');
    } else {
      log('未找到 public/images 目录，跳过', 'info');
    }

    log('步骤 3/4: 处理确定性转换（删除 "use client"，转换路径别名）...', 'progress');
    const processedCount = processFiles(outputDir);
    log(`已处理 ${processedCount} 个文件`, 'info');

    log('步骤 4/4: 分析项目并生成任务文档...', 'progress');
    const analysis = analyzeProject(outputDir);
    const report = buildAnalysisReport(analysis);
    const { reportPath, mdPath } = parsed.targetType === 'themes'
      ? generateThemeTasksDocument(report, targetInfo, parsed.outputName, `temp/${path.basename(v0Dir)}`)
      : generateDefaultTasksDocument(report, targetInfo, parsed.outputName, `temp/${path.basename(v0Dir)}`);

    log('✅ 预处理完成！', 'info');
    log('', 'info');
    log(`📁 ${targetInfo.label}位置: ${targetInfo.relativeOutputDir}/`, 'info');
    log(`📋 AI 工作文档: ${path.relative(CONFIG.projectRoot, mdPath)}`, 'info');
    log(`📊 详细数据: ${path.relative(CONFIG.projectRoot, reportPath)}`, 'info');
    log('', 'info');
    log('📈 统计:', 'info');
    log(`  - 文件数: ${analysis.files.length}`, 'info');
    log(`  - 路径别名: ${analysis.pathAliases.length} 处`, 'info');
    log(`  - Next.js imports: ${analysis.nextjsImports.length} 处`, 'info');
    log(`  - 依赖: ${analysis.dependencies.toInstall?.length || 0} 个`, 'info');
    log('', 'info');
    log('🎯 下一步:', 'info');
    log(`1. 查看任务文档: cat ${path.relative(CONFIG.projectRoot, mdPath)}`, 'info');
    log(parsed.targetType === 'themes'
      ? '2. 让 AI 按任务单完成主题/文档/数据生成'
      : '2. 让 AI 根据任务清单完成转换', 'info');
  } catch (error) {
    log(`预处理失败: ${error.message}`, 'error');
    console.error(error);
    process.exit(1);
  }
}

main();
