// ===== CIPHER SHARED UTILITY: INDEX SYNC GUARDIAN ===== // Location: cipher-engine-clean-v2/.vscode-extensions/cipher-autonomous-dev/src/shared/indexSyncUtils.ts // Purpose: Simple backup + index management utilities for Cipher handlers // Follows established Cipher shared utilities pattern import * as fs from "fs"; import * as path from "path"; import * as vscode from "vscode"; // ===== TYPES ===== interface FolderScanResult { path: string; fileCount: number; hasIndex: boolean; shouldHaveIndex: boolean; files: string[]; } interface BackupResult { success: boolean; backupPath?: string; error?: string; } // ===== SIMPLE BACKUP UTILITIES ===== /** * Create simple inline backup (PracticeGenerator.tsx → PracticeGenerator.tsx.backup) * Follows standard dev backup pattern */ export function createInlineBackup(filePath: string): BackupResult { try { if (!fs.existsSync(filePath)) { return { success: true }; // No file to backup } const backupPath = `${filePath}.backup`; // If backup exists, create numbered backup let finalBackupPath = backupPath; let counter = 1; while (fs.existsSync(finalBackupPath)) { finalBackupPath = `${filePath}.backup${counter}`; counter++; } fs.copyFileSync(filePath, finalBackupPath); console.log(`💾 Backup: ${path.basename(finalBackupPath)}`); return { success: true, backupPath: finalBackupPath, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`🚨 Backup failed for ${filePath}:`, errorMessage); return { success: false, error: errorMessage, }; } } /** * Backup multiple files at once */ export function createMultipleBackups(filePaths: string[]): boolean { let allSuccessful = true; for (const filePath of filePaths) { const result = createInlineBackup(filePath); if (!result.success) { allSuccessful = false; } } return allSuccessful; } // ===== INDEX.TS MANAGEMENT ===== /** * Check if folder should have index.ts based on decision matrix */ export function shouldFolderHaveIndex( folderPath: string, fileCount: number ): boolean { // Must have 2+ TypeScript files if (fileCount < 2) return false; const folderName = path.basename(folderPath).toLowerCase(); const fullPath = folderPath.toLowerCase(); // ❌ Skip utility/shared folders (Cipher pattern) const skipPatterns = [ "shared", "utils", "utilities", "helpers", "types", "interfaces", "constants", "test", "tests", "__tests__", "spec", "config", "configs", "settings", ]; if ( skipPatterns.some( (pattern) => folderName.includes(pattern) || fullPath.includes(`/${pattern}/`) ) ) { return false; } // ✅ Create index.ts for cohesive modules const modulePatterns = [ "components", "modules", "core", "integrations", "audio", "guitar", "vocal", "practice", "theory", "hooks", "pages", "features", ]; return modulePatterns.some( (pattern) => folderName.includes(pattern) || fullPath.includes(`/${pattern}/`) ); } /** * Scan folder and get index.ts status */ export function scanFolderForIndex(folderPath: string): FolderScanResult { if (!fs.existsSync(folderPath) || !fs.statSync(folderPath).isDirectory()) { return { path: folderPath, fileCount: 0, hasIndex: false, shouldHaveIndex: false, files: [] as string[], }; } const files = fs.readdirSync(folderPath); const tsFiles: string[] = files.filter( (file) => (file.endsWith(".ts") || file.endsWith(".tsx")) && file !== "index.ts" ); const hasIndex = files.includes("index.ts"); const shouldHaveIndex = shouldFolderHaveIndex(folderPath, tsFiles.length); return { path: folderPath, fileCount: tsFiles.length, hasIndex, shouldHaveIndex, files: tsFiles, }; } /** * Create index.ts file for a folder */ export function createIndexFile(folderPath: string, files: string[]): boolean { try { const indexPath = path.join(folderPath, "index.ts"); // Backup existing index.ts if it exists if (fs.existsSync(indexPath)) { createInlineBackup(indexPath); } const exports = files.map((file) => { const baseName = path.basename(file, path.extname(file)); return `export * from './${baseName}';`; }); const content = `// Auto-generated index file // Generated by Cipher IndexSync on ${new Date().toISOString()} // Folder: ${path.basename(folderPath)} ${exports.join("\n")} `; fs.writeFileSync(indexPath, content); console.log( `📄 Created index.ts in ${path.basename(folderPath)}/ (${files.length} exports)` ); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error( `🚨 Failed to create index.ts in ${folderPath}:`, errorMessage ); return false; } } /** * Manage index.ts files recursively from a starting path */ export function manageIndexFiles(startPath: string): number { if (!fs.existsSync(startPath)) { console.warn(`Path doesn't exist: ${startPath}`); return 0; } const foldersToProcess = findFoldersRecursively(startPath); let created = 0; let skipped = 0; for (const folderPath of foldersToProcess) { const result = scanFolderForIndex(folderPath); if (result.shouldHaveIndex && !result.hasIndex) { const success = createIndexFile(folderPath, result.files); if (success) created++; } else if (!result.shouldHaveIndex) { skipped++; console.log(`⏭️ Skipped ${path.basename(folderPath)}/ (utility folder)`); } else if (result.hasIndex) { console.log(`✅ ${path.basename(folderPath)}/ already has index.ts`); } } console.log(`📊 Index management: ${created} created, ${skipped} skipped`); return created; } // ===== MAIN PROTECTION FUNCTIONS ===== /** * Main protection for single file modification * Usage: Call before modifying any file */ export function protectFileModification(filePath: string): boolean { try { console.log(`🛡️ Protecting: ${path.basename(filePath)}`); // Step 1: Create backup const backupResult = createInlineBackup(filePath); if (!backupResult.success) { console.warn("⚠️ Backup failed, but continuing..."); reportToCipherBrain("backup-failure", "failure"); } else { reportToCipherBrain("backup-success", "success"); } // Step 2: Manage index.ts in the file's folder const folderPath = path.dirname(filePath); const indexResult = manageIndexFiles(folderPath); console.log(`✅ Protection complete: ${path.basename(filePath)}`); // Report to CipherBrain for learning reportToCipherBrain("file-protection", "success", { fileType: path.extname(filePath), hasBackup: backupResult.success, indexManaged: indexResult > 0, }); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`🚨 Protection failed for ${filePath}:`, errorMessage); reportToCipherBrain("file-protection", "failure"); return false; } } /** * Bulk protection for multiple files (zip installs, etc.) */ export function protectBulkModification( targetFolder: string, filePaths?: string[] ): boolean { try { console.log(`🛡️ Bulk protection for: ${path.basename(targetFolder)}`); let backupCount = 0; let indexCount = 0; // Step 1: Create backups for existing files if (filePaths && filePaths.length > 0) { const backupSuccess = createMultipleBackups(filePaths); if (!backupSuccess) { console.warn("⚠️ Some backups failed, but continuing..."); reportToCipherBrain("bulk-backup", "failure"); } else { backupCount = filePaths.length; reportToCipherBrain("bulk-backup", "success"); } } // Step 2: Manage index.ts files in target area indexCount = manageIndexFiles(targetFolder); console.log(`✅ Bulk protection complete`); // Report bulk protection analytics to CipherBrain reportToCipherBrain("bulk-protection", "success", { targetFolder: path.basename(targetFolder), backupsCreated: backupCount, indexFilesManaged: indexCount, totalFiles: filePaths?.length || 0, }); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`🚨 Bulk protection failed:`, errorMessage); reportToCipherBrain("bulk-protection", "failure"); return false; } } /** * 🔧 UPDATED: Protect Maestro.ai Guitar Practice multi-folder installation * Simple one-call protection for handlers that create files across multiple folders */ export function protectSongsterrInstallation(): boolean { try { const maestroRoot = findMaestroProjectRoot(); if (!maestroRoot) { console.warn("⚠️ Maestro project not found, skipping protection"); reportToCipherBrain("protection-failure", "failure"); return false; } // Protect all the common target folders for Maestro.ai guitar practice components const targetFolders = [ path.join(maestroRoot, "src", "components"), path.join(maestroRoot, "src", "hooks"), path.join(maestroRoot, "src", "utils"), path.join(maestroRoot, "src", "modules"), ]; let protectedCount = 0; targetFolders.forEach((folder) => { if (fs.existsSync(folder)) { protectBulkModification(folder); protectedCount++; } }); console.log( `🛡️ Protected ${protectedCount} folders for Maestro.ai guitar practice installation` ); // Report successful protection to CipherBrain for learning reportToCipherBrain("maestro-guitar-protection", "success", { foldersProtected: protectedCount, targetFolders: targetFolders.map((f) => path.basename(f)), }); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error( "🚨 Maestro.ai guitar practice protection failed:", errorMessage ); reportToCipherBrain("maestro-guitar-protection", "failure"); return false; } } // 🔧 ADDED: New function with Maestro.ai branding (keep old one for backward compatibility) export function protectMaestroGuitarInstallation(): boolean { return protectSongsterrInstallation(); } // ===== UTILITY FUNCTIONS ===== function findFoldersRecursively(startPath: string): string[] { const folders: string[] = []; const scanDir = (currentPath: string) => { if ( !fs.existsSync(currentPath) || !fs.statSync(currentPath).isDirectory() ) { return; } folders.push(currentPath); const items = fs.readdirSync(currentPath); const subdirs = items.filter((item) => { const fullPath = path.join(currentPath, item); return ( fs.statSync(fullPath).isDirectory() && !item.startsWith(".") && item !== "node_modules" ); }); subdirs.forEach((subdir) => { scanDir(path.join(currentPath, subdir)); }); }; scanDir(startPath); return folders; } /** * Get folder status for debugging */ export function getFolderStatus(folderPath: string): string { const result = scanFolderForIndex(folderPath); if (result.fileCount === 0) { return `📁 ${path.basename(folderPath)}/: Empty folder`; } if (!result.shouldHaveIndex) { return `⏭️ ${path.basename(folderPath)}/: ${result.fileCount} files (utility - no index)`; } if (result.hasIndex) { return `✅ ${path.basename(folderPath)}/: ${result.fileCount} files + index.ts`; } return `🔄 ${path.basename(folderPath)}/: ${result.fileCount} files (needs index.ts)`; } // ===== MAESTRO PROJECT DETECTION ===== /** * Find maestro-ai project root from current workspace */ export function findMaestroProjectRoot(): string | null { const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders) { return null; } // Look for maestro-ai folder in workspace for (const folder of workspaceFolders) { const folderPath = folder.uri.fsPath; // Check if this IS the maestro-ai folder if (path.basename(folderPath) === "maestro-ai") { return folderPath; } // Check if maestro-ai is a subfolder const maestroPath = path.join(folderPath, "maestro-ai"); if (fs.existsSync(maestroPath) && fs.statSync(maestroPath).isDirectory()) { return maestroPath; } } return null; } /** * Get relative path from maestro-ai root */ export function getMaestroRelativePath(filePath: string): string | null { const maestroRoot = findMaestroProjectRoot(); if (!maestroRoot) return null; return path.relative(maestroRoot, filePath); } // ===== CIPHER BRAIN INTEGRATION (SIMPLIFIED) ===== /** * Report protection activities to CipherBrain for learning and analytics * Simplified version with better error handling */ function reportToCipherBrain( action: string, result: "success" | "failure", context?: any ): void { try { // Simple console logging for now - can be enhanced later console.log( `🧠 CipherBrain Learning: ${action} -> ${result}`, context || "" ); // Future enhancement: Connect to actual CipherBrain when available // This is a placeholder for now to avoid TypeScript complexity } catch (error) { // Silently fail if brain reporting fails - don't break protection console.warn("Failed to report to CipherBrain:", error); } } /** * Get protection recommendations (simplified version) */ export function getProtectionRecommendations(handlerName: string): string[] { return [ `🛡️ Protection enabled for ${handlerName}`, "💾 Backup system active", "📄 Index file management enabled", ]; } /** * Get protection analytics (simplified version) */ export function getProtectionAnalytics(): any { return { message: "Protection system active", features: ["File backup", "Index management", "Smart folder detection"], status: "operational", }; }