UNPKG

faf-cli

Version:

๐Ÿ˜ฝ TURBO-CAT: The Rapid Catalytic Converter โ€ข Project DNA โœจ for ANY AI โ€ข Fully Integrated with React, Next.js, Svelte, TypeScript, Vite & n8n โ€ข FREE FOREVER โ€ข 10,000+ developers โ€ข Championship Edition

512 lines โ€ข 21.6 kB
"use strict"; /** * ๐Ÿ”„ faf sync - Sync Command * Sync .faf file with project changes (package.json, git, etc.) */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.syncFafFile = syncFafFile; const colors_1 = require("../fix-once/colors"); const fs_1 = require("fs"); const yaml_1 = require("../fix-once/yaml"); const path_1 = __importDefault(require("path")); const file_utils_1 = require("../utils/file-utils"); const championship_style_1 = require("../utils/championship-style"); const faf_dna_1 = require("../engines/faf-dna"); async function syncFafFile(file, options = {}) { try { const fafPath = file || (await (0, file_utils_1.findFafFile)()); if (!fafPath) { console.log(colors_1.chalk.red("โŒ No .faf file found")); console.log(colors_1.chalk.yellow('๐Ÿ’ก Run "faf init" to create one')); process.exit(1); } console.log(championship_style_1.FAF_COLORS.fafCyan(`๐Ÿ”„ Syncing: ${fafPath}`)); // Read current .faf file const content = await fs_1.promises.readFile(fafPath, "utf-8"); // Auto-migrate markdown-style .faf files (legacy MCP v2.2.0) let fafData; try { fafData = (0, yaml_1.parse)(content); } catch (parseError) { // Check if it's a markdown-style .faf file if (content.includes('## Context') || content.includes('## Stack') || content.includes('## Performance')) { console.log(championship_style_1.FAF_COLORS.fafOrange('๐Ÿ”„ Detected legacy markdown-style .faf file')); console.log(colors_1.chalk.dim(' Auto-migrating to YAML format...')); // Create backup const backupPath = `${fafPath}.markdown-backup-${Date.now()}`; await fs_1.promises.copyFile(fafPath, backupPath); console.log(colors_1.chalk.dim(` ๐Ÿ“ Backup saved: ${backupPath}`)); // Convert markdown to YAML fafData = await convertMarkdownFafToYaml(content, fafPath); // Write new YAML format const yamlContent = (0, yaml_1.stringify)(fafData); await fs_1.promises.writeFile(fafPath, yamlContent, 'utf-8'); console.log(colors_1.chalk.green(' โœ… Migration complete - now using YAML format')); // Create DNA for migrated project const projectRoot = path_1.default.dirname(fafPath); const dnaManager = new faf_dna_1.FafDNAManager(projectRoot); // Check if DNA already exists const existingDNA = await dnaManager.load(); if (!existingDNA) { console.log(championship_style_1.FAF_COLORS.fafCyan(' ๐Ÿงฌ Creating DNA for your project...')); // Get initial score from migrated data const initialScore = parseInt(String(fafData.ai_score || '50').replace('%', '')); // Initialize DNA with birth certificate (false = legacy migration source) const dna = await dnaManager.birth(initialScore, false); await dnaManager.recordGrowth(initialScore, ['Migrated from legacy markdown format to YAML v2.5.0']); console.log(championship_style_1.FAF_COLORS.fafGreen(` โœ… DNA created! Certificate: ${dna.birthCertificate.certificate}`)); console.log(championship_style_1.FAF_COLORS.fafWhite(` ๐Ÿ“Š Birth weight: ${initialScore}%`)); } console.log(''); } else { // Not markdown, re-throw original error throw parseError; } } // Detect changes const changes = await detectProjectChanges(fafData); if (changes.length === 0) { console.log(colors_1.chalk.green("โ˜‘๏ธ .faf file is up to date")); return; } // Show detected changes console.log(colors_1.chalk.yellow(`โšก Found ${changes.length} potential updates:`)); changes.forEach((change, index) => { console.log(colors_1.chalk.yellow(` ${index + 1}. ${change.description}`)); if (change.oldValue !== change.newValue) { console.log(colors_1.chalk.gray(` ${change.oldValue} โ†’ ${change.newValue}`)); } }); if (options.dryRun) { console.log(colors_1.chalk.blue("\n๐Ÿ” Dry run complete - no changes applied")); return; } // Apply changes if (options.auto) { console.log(colors_1.chalk.blue("\n๐Ÿค– Auto-applying changes...")); // Create backup before applying changes const backupPath = `${fafPath}.backup-${Date.now()}`; await fs_1.promises.copyFile(fafPath, backupPath); console.log(colors_1.chalk.dim(`๐Ÿ“ Backup created: ${backupPath}`)); applyChanges(fafData, changes); } else { // Interactive mode (simplified for now) console.log(colors_1.chalk.yellow("\n๐Ÿ’ก Run with --auto to apply changes automatically")); console.log(colors_1.chalk.yellow(" Or edit .faf file manually")); return; } // Update generated timestamp fafData.generated = new Date().toISOString(); // Write updated .faf file const updatedContent = (0, yaml_1.stringify)(fafData); await fs_1.promises.writeFile(fafPath, updatedContent, "utf-8"); console.log(colors_1.chalk.green("โ˜‘๏ธ .faf file synced successfully")); console.log(colors_1.chalk.gray(` Applied ${changes.length} changes`)); } catch (error) { console.log(colors_1.chalk.red("๐Ÿ’ฅ Sync failed:")); console.log(colors_1.chalk.red(error instanceof Error ? error.message : String(error))); process.exit(1); } } async function detectProjectChanges(fafData) { const changes = []; try { // Check package.json changes const packageJsonPath = await (0, file_utils_1.findPackageJson)(); if (packageJsonPath) { const packageContent = await fs_1.promises.readFile(packageJsonPath, "utf-8"); const packageData = JSON.parse(packageContent); // Project name change (flat structure) if (packageData.name && packageData.name !== fafData.projectName) { changes.push({ path: "projectName", description: "Project name changed in package.json", oldValue: fafData.projectName || "undefined", newValue: packageData.name, confidence: "high", }); } // Description/goal change (flat structure) if (packageData.description && packageData.description !== fafData.projectGoal) { changes.push({ path: "projectGoal", description: "Project description changed in package.json", oldValue: fafData.projectGoal || "undefined", newValue: packageData.description, confidence: "medium", }); } // Dependencies changes - detect frameworks (flat structure) const deps = { ...packageData.dependencies, ...packageData.devDependencies, }; // Check for framework changes if (deps.svelte && !fafData.framework?.includes("Svelte")) { changes.push({ path: "framework", description: "Svelte dependency detected", oldValue: fafData.framework || "", newValue: "Svelte", confidence: "high", }); } if (deps.react && !fafData.framework?.includes("React")) { changes.push({ path: "framework", description: "React dependency detected", oldValue: fafData.framework || "", newValue: "React", confidence: "high", }); } if (deps.vue && !fafData.framework?.includes("Vue")) { changes.push({ path: "framework", description: "Vue dependency detected", oldValue: fafData.framework || "", newValue: "Vue", confidence: "high", }); } if (deps["@angular/core"] && !fafData.framework?.includes("Angular")) { changes.push({ path: "framework", description: "Angular dependency detected", oldValue: fafData.framework || "", newValue: "Angular", confidence: "high", }); } // Check for CSS frameworks if (deps.tailwindcss && !fafData.cssFramework?.includes("Tailwind")) { changes.push({ path: "cssFramework", description: "Tailwind CSS dependency detected", oldValue: fafData.cssFramework || "", newValue: "Tailwind CSS", confidence: "high", }); } if (deps.bootstrap && !fafData.cssFramework?.includes("Bootstrap")) { changes.push({ path: "cssFramework", description: "Bootstrap dependency detected", oldValue: fafData.cssFramework || "", newValue: "Bootstrap", confidence: "high", }); } // Check for UI libraries if (deps["@mui/material"] && !fafData.uiLibrary?.includes("MUI")) { changes.push({ path: "uiLibrary", description: "Material-UI (MUI) dependency detected", oldValue: fafData.uiLibrary || "", newValue: "Material-UI (MUI)", confidence: "high", }); } if (deps.antd && !fafData.uiLibrary?.includes("Ant Design")) { changes.push({ path: "uiLibrary", description: "Ant Design dependency detected", oldValue: fafData.uiLibrary || "", newValue: "Ant Design", confidence: "high", }); } if (deps["@chakra-ui/react"] && !fafData.uiLibrary?.includes("Chakra")) { changes.push({ path: "uiLibrary", description: "Chakra UI dependency detected", oldValue: fafData.uiLibrary || "", newValue: "Chakra UI", confidence: "high", }); } // Check for state management if ((deps.redux || deps["@reduxjs/toolkit"]) && !fafData.stateManagement?.includes("Redux")) { changes.push({ path: "stateManagement", description: "Redux dependency detected", oldValue: fafData.stateManagement || "", newValue: "Redux Toolkit", confidence: "high", }); } if (deps.zustand && !fafData.stateManagement?.includes("Zustand")) { changes.push({ path: "stateManagement", description: "Zustand dependency detected", oldValue: fafData.stateManagement || "", newValue: "Zustand", confidence: "high", }); } if (deps.jotai && !fafData.stateManagement?.includes("Jotai")) { changes.push({ path: "stateManagement", description: "Jotai dependency detected", oldValue: fafData.stateManagement || "", newValue: "Jotai", confidence: "high", }); } // Check for build tools if (deps.vite && !fafData.buildTool?.includes("Vite")) { changes.push({ path: "buildTool", description: "Vite build tool detected", oldValue: fafData.buildTool || "", newValue: "Vite", confidence: "high", }); } if (deps.webpack && !fafData.buildTool?.includes("Webpack")) { changes.push({ path: "buildTool", description: "Webpack build tool detected", oldValue: fafData.buildTool || "", newValue: "Webpack", confidence: "high", }); } } // Check if generated timestamp is very old (30+ days) if (fafData.generated) { const generatedDate = new Date(fafData.generated); const daysSince = Math.abs(Date.now() - generatedDate.getTime()) / (1000 * 60 * 60 * 24); if (daysSince > 30) { changes.push({ path: "generated", description: `Generated timestamp is ${Math.round(daysSince)} days old`, oldValue: fafData.generated, newValue: new Date().toISOString(), confidence: "high", }); } } } catch { // Continue with what we have } return changes; } function applyChanges(fafData, changes) { changes.forEach((change) => { if (change.confidence === "high" || change.confidence === "medium") { setNestedValue(fafData, change.path, change.newValue); } }); } function setNestedValue(obj, path, value) { // For flat .faf structure, path is just the direct property name obj[path] = value; } /** * Convert legacy markdown-style .faf to proper YAML format */ async function convertMarkdownFafToYaml(content, fafPath) { // Extract key info from markdown format const lines = content.split('\n'); const projectLine = lines.find(l => l.startsWith('project:')); const versionLine = lines.find(l => l.startsWith('version:')); const projectName = projectLine ? projectLine.replace('project:', '').trim() : 'Unknown Project'; const version = versionLine ? versionLine.replace('version:', '').trim() : '1.0.0'; // Extract context section let contextText = ''; let inContext = false; for (const line of lines) { if (line.startsWith('## Context')) { inContext = true; continue; } if (line.startsWith('## ')) { inContext = false; } if (inContext && line.trim()) { contextText += line.trim() + ' '; } } // Extract stack info const stackLines = []; let inStack = false; for (const line of lines) { if (line.startsWith('## Stack')) { inStack = true; continue; } if (line.startsWith('## ')) { inStack = false; } if (inStack && line.trim().startsWith('-')) { stackLines.push(line.trim().replace(/^-\s*/, '')); } } // Detect framework from stack let framework = 'None'; let language = 'TypeScript'; const stackStr = stackLines.join(' ').toLowerCase(); if (stackStr.includes('svelte')) framework = 'SvelteKit'; else if (stackStr.includes('react')) framework = 'React'; else if (stackStr.includes('vue')) framework = 'Vue'; else if (stackStr.includes('angular')) framework = 'Angular'; if (stackStr.includes('typescript')) language = 'TypeScript'; else if (stackStr.includes('javascript')) language = 'JavaScript'; else if (stackStr.includes('python')) language = 'Python'; // Build proper YAML structure return { faf_version: '2.5.0', ai_scoring_system: '2025-09-20', ai_score: '50%', ai_confidence: 'LOW', ai_value: '30_seconds_replaces_20_minutes_of_questions', ai_tldr: { project: `${projectName} - ${contextText.trim() || 'Migrated from legacy format'}`, stack: stackLines.join('/') || language, quality_bar: 'ZERO_ERRORS_F1_STANDARDS', current_focus: 'Production deployment preparation', your_role: 'Build features with perfect context' }, instant_context: { what_building: contextText.trim() || 'Project migrated from legacy .faf format', tech_stack: stackLines.join('/') || language, main_language: language, deployment: 'None', key_files: ['package.json', 'tsconfig.json'] }, context_quality: { slots_filled: '10/21 (48%)', ai_confidence: 'LOW', handoff_ready: false, missing_context: [ 'CI/CD pipeline', 'Database', 'Human context (who/what/why/where/when/how)' ] }, project: { name: projectName, goal: contextText.trim() || 'Migrated from legacy format', main_language: language, generated: new Date().toISOString(), mission: '๐Ÿš€ Make Your AI Happy! ๐Ÿงก Trust-Driven ๐Ÿค–', revolution: '30 seconds replaces 20 minutes of questions', brand: 'F1-Inspired Software Engineering - Championship AI Context', version: version, type: framework.toLowerCase() }, ai_instructions: { priority_order: [ '1. Read THIS .faf file first', '2. Check CLAUDE.md for session context', '3. Review package.json for dependencies' ], working_style: { code_first: true, explanations: 'minimal', quality_bar: 'zero_errors', testing: 'required' }, warnings: [ 'Never modify dial components without approval', 'All TypeScript must pass strict mode', 'Test coverage required for new features' ] }, stack: { frontend: framework !== 'None' ? framework : 'None', css_framework: 'None', ui_library: 'None', state_management: 'None', backend: stackLines.find(s => s.toLowerCase().includes('node')) ? 'Node.js' : 'None', runtime: stackLines.find(s => s.toLowerCase().includes('node')) ? 'Node.js' : 'None', database: 'None', build: 'Vite', package_manager: 'npm', api_type: stackLines.find(s => s.toLowerCase().includes('mcp')) ? 'MCP' : 'REST', hosting: 'None', cicd: 'None', testing: 'None', language: language }, preferences: { quality_bar: 'zero_errors', commit_style: 'conventional_emoji', response_style: 'concise_code_first', explanation_level: 'minimal', communication: 'direct', testing: 'required', documentation: 'as_needed' }, state: { phase: 'development', version: version, focus: 'production_deployment', status: 'green_flag', next_milestone: 'npm_publication', blockers: null }, tags: { auto_generated: [ projectName.toLowerCase().replace(/\s+/g, '-'), ...stackLines.map(s => s.toLowerCase()) ], smart_defaults: [ '.faf', 'ai-ready', '2025', 'software', 'open-source' ], user_defined: null }, human_context: { who: null, what: null, why: null, where: null, when: null, how: null, additional_context: null, context_score: 0, total_prd_score: 50, success_rate: '50%' }, ai_scoring_details: { system_date: '2025-09-20', slot_based_percentage: 48, ai_score: 50, total_slots: 21, filled_slots: 10, scoring_method: 'Honest percentage - no fake minimums', trust_embedded: 'COUNT ONCE architecture - trust MY embedded scores' }, meta: { last_enhanced: new Date().toISOString(), enhanced_by: 'faf-auto-migration' }, projectName: projectName.toLowerCase().replace(/\s+/g, '-'), projectGoal: contextText.trim() || 'Migrated from legacy format', framework: framework, buildTool: 'Vite', generated: new Date().toISOString() }; } //# sourceMappingURL=sync.js.map