// 🌳 ULTIMATE MULTI-ECOSYSTEM ROUTE TREE HANDLER WITH AI-PERFECT EXPORTS + ENHANCED WARNINGS // .vscode-extension/cipher-autonomous-dev/src/handlers/route-handlers/showRouteTreeHandler.ts import * as fs from "fs"; import * as path from "path"; import * as vscode from "vscode"; import { RouteInfo } from "../../shared/types"; import { ensureDirectoryExists } from "../../shared/utils"; /** * 🌳 ULTIMATE FEATURE: Multi-Ecosystem Interactive Route Tree + AI-Perfect Exports + Enhanced Warnings * Visualizes Maestro-AI, Ava, and Cipher-Engine in one beautiful tree! * Perfect for sharing with new Claude chats to show entire ecosystem * NOW WITH: ASCII Tree exports for AI consumption + Detailed Warning Analysis! * FIXED: Reset button and Display Mode switching + Consistent Deep Scanning + Clickable Warnings */ export async function showRouteTreeHandler(): Promise { try { vscode.window.showInformationMessage( "🌳 Generating ULTIMATE ecosystem route tree..." ); const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; if (!workspaceFolder) { vscode.window.showErrorMessage("No workspace folder found"); return; } // πŸš€ MULTI-ECOSYSTEM PROJECT DETECTION const ecosystemProjects = await detectEcosystemProjects( workspaceFolder.uri.fsPath ); // 🎯 DEFAULT DISPLAY MODE - Can be toggled in HTML const defaultDisplayMode = "file-types"; const treeData = await generateMultiEcosystemTreeData( ecosystemProjects, defaultDisplayMode ); const htmlTree = generateUltimateEcosystemHTML(treeData, ecosystemProjects); // Create reports directory const reportsDir = vscode.Uri.joinPath( workspaceFolder.uri, "..", "maestro-ai", "cipher-reports" ); await ensureDirectoryExists(reportsDir); // Save HTML tree with ecosystem timestamp const timestamp = Date.now(); const treeFile = vscode.Uri.joinPath( reportsDir, `maestro-ecosystem-tree-${timestamp}.html` ); await vscode.workspace.fs.writeFile(treeFile, Buffer.from(htmlTree)); // 🎯 NEW: Generate AI-Perfect Exports await generateAIExports(reportsDir, treeData, timestamp); // Open the ultimate tree in external browser (Safari/Chrome) vscode.env.openExternal(treeFile); // Show export options const result = await vscode.window.showInformationMessage( "🌳 ULTIMATE Maestro Ecosystem Tree generated! 🎸✨", "Open Tree", "Export for AI", "Export ASCII", "Export Markdown", "Export All" ); if (result === "Export for AI") { await exportForAI(reportsDir, treeData, timestamp); } else if (result === "Export ASCII") { await exportASCIITree(reportsDir, treeData, timestamp); } else if (result === "Export Markdown") { await exportMarkdownTree(reportsDir, treeData, timestamp); } else if (result === "Export All") { await generateAllExports(reportsDir, treeData, timestamp); } } catch (error) { vscode.window.showErrorMessage( `Ultimate route tree generation failed: ${error}` ); } } /** * πŸ” FIXED ENHANCED FILE ANALYSIS - More accurate health scoring */ /** * πŸ” ULTRA LENIENT HEALTH ANALYSIS - Only flags truly broken files * Philosophy: If the file exists and has code, it's probably working! */ async function analyzeFileWithDetailedWarnings(filePath: string): Promise<{ status: "working" | "warning" | "error"; warnings: string[]; severity: "low" | "medium" | "high"; }> { try { const content = await fs.promises.readFile(filePath, "utf8"); const warnings: string[] = []; let status: "working" | "warning" | "error" = "working"; let severity: "low" | "medium" | "high" = "low"; const ext = path.extname(filePath).toLowerCase(); const fileName = path.basename(filePath); // 🚨 ONLY TRULY CRITICAL ERRORS (Red status) - VERY RESTRICTIVE // 1. Empty files if (content.trim().length === 0) { warnings.push("File is completely empty"); status = "error"; severity = "high"; return { status, warnings, severity }; } // 2. Only flag TypeScript/JavaScript files with SEVERE issues if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) { // βœ… RECOGNIZE ALL LEGITIMATE PATTERNS const legitimatePatterns = [ // Index file patterns /export\s*\*\s*from/i, /export\s*{\s*[^}]+\s*}\s*from/i, /export\s*{\s*default\s*}/i, // Normal code patterns /function\s+\w+/i, /const\s+\w+\s*=/i, /let\s+\w+\s*=/i, /var\s+\w+\s*=/i, /class\s+\w+/i, /interface\s+\w+/i, /type\s+\w+\s*=/i, /import\s+/i, /require\s*\(/i, // Config patterns (for config files) /module\.exports\s*=/i, /export\s*=\s*/i, // Even basic JSON structure /{\s*['"]\w+['"]\s*:/i, ]; const hasLegitimateCode = legitimatePatterns.some((pattern) => pattern.test(content) ); // 🚨 ONLY FLAG AS CRITICAL IF TRULY PROBLEMATIC const actuallyBrokenPatterns = [ // Explicit throw errors for not implemented /throw\s+new\s+Error\(\s*['"`]Not\s+implemented['"`]/i, /throw\s+new\s+Error\(\s*['"`]TODO:/i, // Files that literally say they're stubs /\/\/\s*STUB:\s*This\s+entire\s+file\s+needs\s+implementation/i, /\/\*\s*TODO:\s*IMPLEMENT\s+EVERYTHING\s*\*\//i, ]; const isActuallyBroken = actuallyBrokenPatterns.some((pattern) => pattern.test(content) ); // Only flag as critical if the file is explicitly broken AND has no legitimate code if (isActuallyBroken && !hasLegitimateCode) { warnings.push("File explicitly marked as not implemented"); status = "error"; severity = "high"; return { status, warnings, severity }; } // 🎯 VERY LENIENT: Only flag if file is nearly empty AND suspicious const codeLines = content.split("\n").filter((line) => { const trimmed = line.trim(); return ( trimmed.length > 0 && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !trimmed.startsWith("*") && !trimmed.startsWith("*/") ); }); // Only flag if less than 2 lines of actual code AND contains concerning patterns if ( codeLines.length < 2 && content.includes("TODO: Implement everything") && !hasLegitimateCode ) { warnings.push("File appears to be an empty placeholder"); status = "error"; severity = "high"; return { status, warnings, severity }; } } // 🎯 ULTRA LENIENT WARNINGS - Most things are now just informational // We only add warnings for things that are genuinely concerning, not style issues if (status === "working") { // Only warn about critical TODOs, not regular ones if ( content.includes("TODO: CRITICAL") || content.includes("FIXME: URGENT") || content.includes("BROKEN:") ) { warnings.push("Contains critical TODO/FIXME markers"); status = "warning"; severity = "medium"; } // Only warn about obvious compilation errors if ( content.includes("cannot find module") || content.includes("SyntaxError:") || content.includes("TypeError:") || content.includes("ReferenceError:") ) { warnings.push("May contain compilation errors"); status = "warning"; severity = "medium"; } // 🎯 REMOVED ALL STYLE-BASED WARNINGS: // - No more warnings for console.log (common in scripts) // - No more warnings for TODO comments (normal development) // - No more warnings for "any" types (sometimes necessary) // - No more warnings for unused variables (handled by linters) // - No more warnings for deprecated comments (might be outdated) } // 🎯 DEFAULT: If we made it here, the file is working! // Philosophy: If it has content and no critical issues, it's working return { status: warnings.length === 0 ? "working" : status, warnings, severity, }; } catch (error) { return { status: "error", warnings: [`Cannot read file: ${error}`], severity: "high", }; } } /** * 🎯 ENHANCED HEALTH SCORE CALCULATION - More realistic for large codebases */ function calculateHealthScore(stats: { working: number; total: number; }): number { if (stats.total === 0) return 100; let baseScore = Math.round((stats.working / stats.total) * 100); // πŸš€ BONUSES for large, healthy codebases if (stats.total > 200) { // Large codebase bonus - if most files work, it's impressive! if (baseScore >= 85) baseScore = Math.min(95, baseScore + 3); if (baseScore >= 90) baseScore = Math.min(97, baseScore + 2); } if (stats.total > 300) { // Massive codebase bonus - even 80% is great for 300+ files if (baseScore >= 80) baseScore = Math.min(94, baseScore + 4); if (baseScore >= 85) baseScore = Math.min(96, baseScore + 2); } // 🎯 REALISTIC CAPS: Even perfect codebases have some issues return Math.min(97, baseScore); // Cap at 97% - perfection is rare! } /** * 🎯 UPDATE YOUR TREE STATS CALCULATION * Use this in your generateMultiEcosystemTreeData function */ function updateTreeDataWithRealisticHealth(treeData: any): any { if (treeData.stats) { treeData.stats.healthPercentage = calculateHealthScore({ working: treeData.stats.totalWorking, total: treeData.stats.totalRoutes, }); } // Also update individual project health scores if (treeData.children) { treeData.children.forEach((project: any) => { if (project.stats) { project.stats.healthPercentage = calculateHealthScore({ working: project.stats.working, total: project.stats.total, }); } }); } return treeData; } /** * πŸ” FIXED ECOSYSTEM PROJECT DETECTION - Enhanced project boundary isolation */ async function detectEcosystemProjects( currentPath: string ): Promise { const projects: EcosystemProject[] = []; // 🎯 SMART PROJECT DETECTION - Check multiple locations const maestroAiRoot = path.join(currentPath, ".."); const workspaceRoot = currentPath; // πŸ› οΈ PROJECT CONFIGURATIONS - Updated with proper exclusions const projectConfigs = [ { name: "Cipher Engine", symbol: "🟣", color: "cipher", paths: [ "cipher-engine-clean-v2", "cipher-engine", ".vscode-extensions/cipher-autonomous-dev", ], description: "AI Development Assistant", excludePaths: [], // Cipher has no exclusions }, { name: "Maestro AI", symbol: "🟦", color: "maestro", paths: ["maestro-ai", "maestro-brain", "../maestro-ai"], description: "Central AI Intelligence", excludePaths: ["cipher-engine-clean-v2", "ava"], // 🎯 EXCLUDE: Subprojects }, { name: "Ava", symbol: "πŸ’—", color: "ava", paths: ["ava", "../ava"], description: "AI Agent Interface", excludePaths: [], // Ava has no exclusions }, ]; // πŸ” SCAN FOR PROJECTS for (const config of projectConfigs) { for (const projectPath of config.paths) { let fullPath = path.join(maestroAiRoot, projectPath); let found = false; if (await pathExists(fullPath)) { found = true; } else { // Try as subdirectory of current workspace fullPath = path.join(workspaceRoot, projectPath); if (await pathExists(fullPath)) { found = true; } } if (found) { console.log(`🎯 Found project: ${config.name} at ${fullPath}`); // 🎯 FIXED: Pass exclusions to scanning const routeData = await scanProjectRoutes( fullPath, config.color, config.excludePaths ); projects.push({ name: config.name, symbol: config.symbol, color: config.color, path: fullPath, description: config.description, routes: routeData.routes, stats: routeData.stats, }); break; } } } console.log(`πŸ“Š Total projects found: ${projects.length}`); return projects; } /** * πŸ” FIXED PROJECT ROUTE SCANNING - Now with project boundary enforcement */ async function scanProjectRoutes( projectPath: string, projectColor: string, excludePaths: string[] = [] ): Promise { const routes: RouteInfo[] = []; const stats = { total: 0, working: 0, missing: 0, folders: 0, files: 0, }; try { await scanDirectory( projectPath, "", routes, stats, projectColor, 0, excludePaths ); } catch (error) { console.warn(`Failed to scan project ${projectPath}:`, error); } console.log( `πŸ“Š Project ${projectColor}: ${stats.folders} folders, ${stats.files} files, ${stats.working} working` ); return { routes, stats }; } /** * πŸ“ ENHANCED RECURSIVE DIRECTORY SCANNER - With detailed warning analysis * πŸš€ Enhanced for consistent 12-level deep scanning with proper project boundaries + file warnings * 🎯 FIXED: Now properly detects and includes empty folders */ async function scanDirectory( basePath: string, relativePath: string, routes: RouteInfo[], stats: any, projectColor: string, depth: number = 0, excludePaths: string[] = [] ): Promise { // 🎯 CONSISTENT DEPTH LIMIT: 12 levels for all projects if (depth > 12) return; const fullPath = path.join(basePath, relativePath); try { const items = await fs.promises.readdir(fullPath, { withFileTypes: true }); for (const item of items) { // 🎯 FIXED: Check project boundary exclusions first if (shouldExcludeFromProject(item.name, relativePath, excludePaths)) { console.log(`🚫 Excluding ${item.name} from project boundary`); continue; } if (shouldSkipItem(item.name, relativePath, depth)) { continue; } const itemPath = path.join(relativePath, item.name); const fullItemPath = path.join(fullPath, item.name); if (item.isDirectory()) { if (shouldIncludeDirectory(item.name, itemPath, depth)) { stats.folders++; // πŸš€ ENHANCED: Track routes before/after for empty folder detection const routeCountBefore = routes.length; await scanDirectory( basePath, itemPath, routes, stats, projectColor, depth + 1, excludePaths ); // 🎯 NEW: Check if directory is empty after scanning const routeCountAfter = routes.length; if (routeCountBefore === routeCountAfter) { // No routes were added - check if directory is actually empty try { const subItems = await fs.promises.readdir(fullItemPath); const hasOnlyHiddenFiles = subItems.every((name) => name.startsWith(".") ); // If directory is empty or has only hidden files, add as empty folder if (subItems.length === 0 || hasOnlyHiddenFiles) { const routeType = getRouteTypeFromFile(item.name, itemPath); routes.push({ path: itemPath, type: routeType, exists: true, status: "working", warnings: [], url: `file://${fullItemPath}`, component: `${item.name} (empty folder)`, projectColor, }); stats.total++; stats.working++; } } catch (error) { // Silently ignore read errors for empty folder detection } } } } else if (item.isFile()) { if (shouldIncludeFile(item.name, itemPath)) { stats.files++; const routeType = getRouteTypeFromFile(item.name, itemPath); // 🎯 ENHANCED: Use detailed analysis instead of simple check const fileAnalysis = await analyzeFileWithDetailedWarnings(fullItemPath); routes.push({ path: itemPath, type: routeType, exists: true, status: fileAnalysis.status === "error" ? "error" : fileAnalysis.status === "warning" ? "warning" : "working", warnings: fileAnalysis.warnings, // 🎯 NEW: Store detailed warnings url: `file://${fullItemPath}`, component: path.basename(item.name, path.extname(item.name)), projectColor, }); if (fileAnalysis.status === "working") { stats.working++; } else { // Count both warnings and errors as "missing" for overall health stats.missing++; } stats.total++; } } } } catch (error) { console.warn(`Failed to scan directory ${fullPath}:`, error); } } /** * 🚫 NEW: PROJECT BOUNDARY EXCLUSION CHECKER */ function shouldExcludeFromProject( itemName: string, relativePath: string, excludePaths: string[] ): boolean { // Only check at root level (depth 0) if (relativePath !== "") return false; return excludePaths.includes(itemName); } /** * πŸ›‘οΈ FIXED FILTERING FUNCTIONS - Consistent rules for all projects */ function shouldSkipItem( itemName: string, relativePath: string, depth: number ): boolean { const name = itemName.toLowerCase(); // 🎯 ENHANCED HIDDEN FILE HANDLING - Allow important dotfiles if (itemName.startsWith(".")) { const allowedDotFiles = [".vscode-extensions", ".vscode", ".env"]; if (allowedDotFiles.includes(itemName)) return false; if (relativePath.includes(".vscode-extensions")) return false; return true; } // 🚫 EXPANDED SKIP DIRECTORIES const skipDirs = [ "node_modules", "dist", "out", "build", "coverage", "tmp", "temp", ".git", ".svn", ".hg", "logs", "backups", "archive", "old", "deprecated", "backup", "bak", "reports", "exports", "cache", ]; if (skipDirs.includes(name)) return true; return false; } /** * πŸ“ FIXED DIRECTORY INCLUSION - Consistent 8-level deep scanning */ function shouldIncludeDirectory( itemName: string, itemPath: string, depth: number ): boolean { const name = itemName.toLowerCase(); const pathLower = itemPath.toLowerCase(); // πŸš€ ENHANCED IMPORTANT DIRECTORIES - Focus on code structures const importantDirs = [ // Core development folders "src", "components", "handlers", "brain", "pages", "routes", "api", "utils", "lib", "types", "interfaces", "modules", // Maestro AI specific "guitar", "vocal", "music", "ai", "engine", "scripts", "route-handlers", "shared", "core", "services", // Handler systems (enhanced support) "command-handlers", "file-handlers", "ui-handlers", "analysis-handlers", "export-handlers", "git-handlers", // VS Code extensions "vscode-extensions", "cipher-autonomous-dev", ]; // 🎯 CONSISTENT DEPTH POLICY: All projects get same treatment if (depth <= 5) return true; // Always include first 5 levels if (depth <= 12 && isImportantDir(name, pathLower, importantDirs)) return true; // Important dirs to level 12 // 🚫 SKIP DEEP TEST/DOC DIRECTORIES const skipDeepDirs = [ "tests", "test", "__tests__", "spec", "docs", "examples", "demo", ]; if (depth > 5 && skipDeepDirs.includes(name)) return false; return false; } /** * 🎯 HELPER: Enhanced important directory checker */ function isImportantDir( name: string, pathLower: string, importantDirs: string[] ): boolean { return importantDirs.some( (dir) => name.includes(dir) || pathLower.includes(dir) || name === dir ); } function shouldIncludeFile(fileName: string, filePath: string): boolean { const ext = path.extname(fileName).toLowerCase(); const name = fileName.toLowerCase(); const pathLower = filePath.toLowerCase(); // 🚫 PRIORITY EXCLUSIONS: These override everything else const excludeExtensions = [ ".html", ".htm", ".css", ".scss", ".sass", ".less", ".map", ".min.js", ".min.css", ".d.ts.map", ".log", ".tmp", ".cache", ".lock", ".swp", ".DS_Store", ".gitkeep", ".gitignore", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".mp3", ".wav", ".mp4", ".avi", ".mov", ".zip", ".tar", ".gz", ".rar", ".7z", ".md", ".txt", ".sh", ".json", // ⚠️ JSON files excluded by default ]; // 🎯 STRICT JSON FILTERING: Only allow specific config files if (ext === ".json") { // Only allow important config JSON files if ( name.includes("package") || name.includes("tsconfig") || name.includes("config") || name.includes("settings") ) { return true; } // 🚫 EXCLUDE ALL OTHER JSON FILES (reports, logs, learning data, etc.) return false; } // 🚫 EXCLUDE OTHER UNWANTED EXTENSIONS if (excludeExtensions.includes(ext)) { return false; } // 🚫 FILTER OUT BUILD/TEST/LOG FILES BY NAME const excludeNames = [ ".min.", ".bundle.", ".chunk.", ".test.", ".spec.", "test.", ".log", ".bak", "backup", ]; if (excludeNames.some((exclude) => name.includes(exclude))) { return false; } // 🚫 FILTER OUT REPORT/LEARNING FILES BY NAME OR PATH if ( name.includes("learning-") || name.includes("report-") || pathLower.includes("intelligence/") || pathLower.includes("reports/") || pathLower.includes("logs/") ) { return false; } // βœ… INCLUDE CORE DEVELOPMENT FILES const includeExtensions = [ ".ts", ".tsx", ".js", ".jsx", ".yaml", ".yml", ".toml", ".env", ".config.js", ".py", ".rs", ".go", ".java", ".cpp", ".c", ]; if (includeExtensions.includes(ext)) return true; // 🎯 CONTEXT-BASED INCLUSION (only for non-excluded files) const importantPaths = [ "src/", "components/", "handlers/", "brain/", "engine/", ]; if (importantPaths.some((p) => pathLower.includes(p))) { return true; } return false; } function getRouteTypeFromFile(fileName: string, filePath: string): string { const ext = path.extname(fileName).toLowerCase(); const name = fileName.toLowerCase(); const pathLower = filePath.toLowerCase(); if (pathLower.includes("guitar") || name.includes("guitar")) return "guitar"; if (pathLower.includes("vocal") || name.includes("vocal")) return "vocal"; if (pathLower.includes("practice") || name.includes("practice")) return "practice"; if (pathLower.includes("tuner") || name.includes("tuner")) return "tuner"; if (pathLower.includes("metronome")) return "metronome"; if (pathLower.includes("jam")) return "jam"; if (pathLower.includes("tabs")) return "tabs"; if (pathLower.includes("brain") || name.includes("brain")) return "brain"; if (pathLower.includes("ai") || name.includes("ai")) return "ai"; if (pathLower.includes("intelligence")) return "intelligence"; if (pathLower.includes("api")) return "api"; if (pathLower.includes("handler")) return "handler"; if (pathLower.includes("component")) return "component"; if (pathLower.includes("page")) return "page"; if ([".tsx", ".jsx"].includes(ext)) return "react-component"; if ([".ts", ".js"].includes(ext)) return "module"; if (ext === ".json") return "config"; if (ext === ".md") return "documentation"; return "file"; } /** * 🌳 MULTI-ECOSYSTEM TREE DATA GENERATOR */ async function generateMultiEcosystemTreeData( projects: EcosystemProject[], displayMode: string = "file-types" ): Promise { const treeData = { name: "Maestro-AI Ecosystem", type: "root", children: [] as any[], stats: { totalProjects: 0, totalRoutes: 0, totalWorking: 0, totalMissing: 0, healthPercentage: 0, }, }; let totalRoutes = 0; let totalWorking = 0; let totalMissing = 0; for (const project of projects) { totalRoutes += project.stats.total; totalWorking += project.stats.working; totalMissing += project.stats.missing; const projectNode = { name: `${project.symbol} ${project.name}`, type: "project", color: project.color, description: project.description, stats: project.stats, children: [] as any[], }; if (displayMode === "folder-paths") { const folderTree = buildFolderTree(project.routes); projectNode.children = convertFolderTreeToNodes( folderTree, project.color ); } else { // 🎯 FIXED FILE TYPES MODE: Categorize by ACTUAL LOCATION, not just content const categorizedRoutes = categorizeRoutesByActualLocation( project.routes, project.color ); projectNode.children = categorizedRoutes; } treeData.children.push(projectNode); } treeData.stats = { totalProjects: projects.length, totalRoutes, totalWorking, totalMissing, healthPercentage: totalRoutes > 0 ? Math.round((totalWorking / totalRoutes) * 100) : 0, }; return treeData; } /** * 🎯 FIXED CATEGORIZATION FUNCTION - Prevents artificial brain folders */ function categorizeRoutesByActualLocation( routes: RouteInfo[], projectColor: string ): any[] { const categories = new Map(); routes.forEach((route) => { const pathParts = route.path .split(path.sep) .filter((part) => part.length > 0); let category = "πŸ“¦ Other"; // 🎯 CRITICAL FIX: Categorize based on PRIMARY folder location first const primaryFolder = pathParts[0] || ""; if (primaryFolder === "brain" || pathParts.includes("brain")) { // This is actually a brain file - keep in brain categories if (pathParts.includes("guitar")) category = "🧠 Brain - Guitar"; else if (pathParts.includes("vocal")) category = "🧠 Brain - Vocal"; else if (pathParts.includes("learning") || pathParts.includes("practice")) category = "🧠 Brain - Learning"; else if (pathParts.includes("audio")) category = "🧠 Brain - Audio"; else if (pathParts.includes("composition")) category = "🧠 Brain - Composition"; else category = "🧠 Brain - Core"; } else if (primaryFolder === "src" || pathParts.includes("src")) { // This is a src file (UI components, etc.) - separate from brain if (pathParts.includes("components")) { if (pathParts.includes("guitar")) category = "🎸 UI - Guitar"; else if (pathParts.includes("vocal")) category = "🎀 UI - Vocal"; else if (pathParts.includes("practice")) category = "🎡 UI - Practice"; else category = "🧩 UI - Components"; } else if (pathParts.includes("hooks")) { category = "πŸ”— Hooks"; } else if (pathParts.includes("pages") || pathParts.includes("app")) { category = "πŸ“„ Pages"; } else if (pathParts.includes("modules")) { category = "πŸ“¦ Modules"; } else { category = "πŸ“¦ Source Files"; } } else if (primaryFolder === "handlers" || pathParts.includes("handlers")) { category = "⚑ Handlers"; } else if (primaryFolder === "scripts" || pathParts.includes("scripts")) { category = "πŸ“œ Scripts"; } else { // Fallback - use existing getTypeIcon function const routeType = route.type || "unknown"; category = `${getTypeIcon(routeType)} ${routeType.charAt(0).toUpperCase() + routeType.slice(1)}`; } if (!categories.has(category)) { categories.set(category, []); } categories.get(category)!.push(route); }); // Convert to tree structure const result: any[] = []; categories.forEach((routes, categoryName) => { if (routes.length > 0) { const maxItems = 100; const displayRoutes = routes.slice(0, maxItems); const hasMore = routes.length > maxItems; const categoryNode = { name: `${categoryName} (${routes.length}${hasMore ? ", showing " + maxItems : ""})`, type: "category", color: projectColor, children: [] as any[], }; // πŸ”§ BUILD HIERARCHICAL FOLDER STRUCTURE FOR THIS CATEGORY const categoryFolderTree = buildFolderTree(displayRoutes); categoryNode.children = convertFolderTreeToNodes( categoryFolderTree, projectColor ); if (categoryNode.children.length > 0) { result.push(categoryNode); } } }); return result; } function getTypeIcon(type: string): string { const icons: Record = { guitar: "🎸", vocal: "🎀", practice: "🎡", tuner: "🎚️", metronome: "⏱️", jam: "🎢", tabs: "🎼", brain: "🧠", ai: "πŸ€–", intelligence: "πŸ’‘", api: "πŸ”Œ", handler: "⚑", component: "🧩", page: "πŸ“„", "react-component": "βš›οΈ", module: "πŸ“¦", config: "βš™οΈ", documentation: "πŸ“š", file: "πŸ“„", }; return icons[type] || "πŸ“„"; } function buildFolderTree(routes: RouteInfo[]): FolderNode { const root: FolderNode = { name: "root", children: {}, files: [] }; routes.forEach((route) => { const parts = route.path.split("/").filter((part) => part.length > 0); let current = root; for (let i = 0; i < parts.length - 1; i++) { const part = parts[i]; if (!current.children[part]) { current.children[part] = { name: part, children: {}, files: [] }; } current = current.children[part]; } if (parts.length > 0) { current.files.push(route); } }); return root; } function convertFolderTreeToNodes( folderNode: FolderNode, projectColor: string ): any[] { const nodes: any[] = []; Object.entries(folderNode.children).forEach(([folderName, childNode]) => { const folderDisplayNode = { name: `πŸ“ ${folderName} (${getFolderStats(childNode)})`, type: "folder-node", color: projectColor, children: convertFolderTreeToNodes(childNode, projectColor), }; nodes.push(folderDisplayNode); }); if (folderNode.files.length > 0) { folderNode.files.forEach((file) => { nodes.push({ name: path.basename(file.path), fullPath: file.path, type: "route", status: file.status, exists: file.exists, url: file.url, color: projectColor, routeType: file.type, warnings: file.warnings, // 🎯 ENHANCED: Include warnings }); }); } return nodes; } function getFolderStats(folderNode: FolderNode): string { let totalFiles = folderNode.files.length; let totalFolders = Object.keys(folderNode.children).length; Object.values(folderNode.children).forEach((child) => { const childStats = countNestedItems(child); totalFiles += childStats.files; totalFolders += childStats.folders; }); // 🎯 ENHANCED: Explicitly handle empty folders if (totalFolders === 0 && totalFiles === 0) { return "0 files"; } else if (totalFolders > 0 && totalFiles > 0) { return `${totalFolders} folders, ${totalFiles} files`; } else if (totalFiles > 0) { return `${totalFiles} files`; } else if (totalFolders > 0) { return `${totalFolders} folders`; } else { return "0 files"; // Fallback for empty } } function countNestedItems(folderNode: FolderNode): { files: number; folders: number; } { let files = folderNode.files.length; let folders = Object.keys(folderNode.children).length; Object.values(folderNode.children).forEach((child) => { const childStats = countNestedItems(child); files += childStats.files; folders += childStats.folders; }); return { files, folders }; } interface FolderNode { name: string; children: Record; files: RouteInfo[]; } /** * πŸ€– AI EXPORT GENERATORS */ async function generateAIExports( reportsDir: vscode.Uri, treeData: any, timestamp: number ): Promise { const aiExportContent = generateAIExportContent(treeData); const asciiTreeContent = generateASCIITreeContent(treeData); const markdownContent = generateMarkdownTreeContent(treeData); const aiFile = vscode.Uri.joinPath( reportsDir, `maestro-ai-export-${timestamp}.md` ); const asciiFile = vscode.Uri.joinPath( reportsDir, `maestro-ascii-tree-${timestamp}.txt` ); const markdownFile = vscode.Uri.joinPath( reportsDir, `maestro-markdown-tree-${timestamp}.md` ); await vscode.workspace.fs.writeFile(aiFile, Buffer.from(aiExportContent)); await vscode.workspace.fs.writeFile(asciiFile, Buffer.from(asciiTreeContent)); await vscode.workspace.fs.writeFile( markdownFile, Buffer.from(markdownContent) ); console.log("πŸ€– AI exports generated successfully!"); } function generateAIExportContent(treeData: any): string { const date = new Date().toLocaleString(); return `# πŸ€– Maestro-AI Ecosystem - Complete AI Export **Generated for AI Consumption:** ${date} **Export Type:** Complete Ecosystem Overview **Perfect for:** Claude, GPT, Gemini, Developer Sharing --- ## πŸ“Š Ecosystem Overview ${ treeData.stats ? ` - **Total Projects:** ${treeData.stats.totalProjects} - **Total Routes:** ${treeData.stats.totalRoutes} - **Working Routes:** ${treeData.stats.totalWorking} - **Issues Found:** ${treeData.stats.totalMissing} - **Health Score:** ${treeData.stats.healthPercentage}% ` : "" } --- ## 🌳 ASCII Tree Structure \`\`\` ${generateASCIITree(treeData)} \`\`\` --- ## πŸ“‹ Detailed Project Breakdown ${generateDetailedProjectBreakdown(treeData)} --- **πŸš€ This export contains everything needed to understand the complete Maestro-AI ecosystem!** *Generated by Cipher Extension v9 - Ultimate Ecosystem Manager*`; } function generateASCIITree( node: any, prefix: string = "", isLast: boolean = true, depth: number = 0 ): string { if (depth > 12) return ""; let result = ""; const connector = isLast ? "└── " : "β”œβ”€β”€ "; const childPrefix = isLast ? " " : "β”‚ "; result += prefix + connector + getNodeDisplayName(node) + "\n"; if (node.children && node.children.length > 0) { node.children.forEach((child: any, index: number) => { const isLastChild = index === node.children.length - 1; result += generateASCIITree( child, prefix + childPrefix, isLastChild, depth + 1 ); }); } return result; } function getNodeDisplayName(node: any): string { let name = node.name || "Unknown"; // 🎯 NEW: Handle empty folders first if (node.component && node.component.includes("(empty folder)")) { return `πŸ“ ${name.replace(" (empty folder)", "")} (0 files)`; } if (node.type === "project" && node.stats) { name += ` (${node.stats.total} routes, ${node.stats.working} working)`; } else if (node.type === "category" && node.children) { name += ` [${node.children.length} items]`; } else if (node.type === "route") { const statusIcon = node.status === "working" ? "βœ…" : node.status === "warning" ? "⚠️" : "❌"; name += ` ${statusIcon}`; } return name; } function generateDetailedProjectBreakdown(treeData: any): string { let breakdown = ""; if (treeData.children) { treeData.children.forEach((project: any) => { breakdown += `### ${project.name}\n`; breakdown += `- **Description:** ${project.description}\n`; if (project.stats) { breakdown += `- **Statistics:**\n`; breakdown += ` - Total Files: ${project.stats.files}\n`; breakdown += ` - Folders: ${project.stats.folders}\n`; breakdown += ` - Working Routes: ${project.stats.working}\n`; breakdown += ` - Issues: ${project.stats.missing}\n`; } breakdown += "\n"; }); } return breakdown; } function generateASCIITreeContent(treeData: any): string { const date = new Date().toLocaleString(); return `# 🌲 Maestro-AI Ecosystem ASCII Tree Generated: ${date} ${generateASCIITree(treeData)} --- Export generated by Cipher Extension v9`; } function generateMarkdownTreeContent(treeData: any): string { const date = new Date().toLocaleString(); let content = `# 🌳 Maestro-AI Ecosystem Route Tree **Generated:** ${date} ${ treeData.stats ? `**Total Projects:** ${treeData.stats.totalProjects} **Total Routes:** ${treeData.stats.totalRoutes} **Health Score:** ${treeData.stats.healthPercentage}%` : "" } --- `; const generateMarkdownTree = (node: any, depth: number = 0): string => { if (depth > 12) return ""; let result = ""; const indent = " ".repeat(depth); const bullet = depth === 0 ? "# " : "- "; result += `${indent}${bullet}**${node.name}**`; if (node.type === "project" && node.stats) { result += ` (${node.stats.total} routes, ${node.stats.working} working, ${node.stats.missing} issues)`; } else if (node.type === "route") { const statusIcon = node.status === "working" ? "βœ…" : node.status === "warning" ? "⚠️" : "❌"; result += ` ${statusIcon}`; } result += "\n"; if (node.children && node.children.length > 0) { node.children.forEach((child: any) => { result += generateMarkdownTree(child, depth + 1); }); } return result; }; content += generateMarkdownTree(treeData); content += "\n---\n*Generated by Cipher Extension v9*"; return content; } async function exportForAI( reportsDir: vscode.Uri, treeData: any, timestamp: number ): Promise { const content = generateAIExportContent(treeData); const file = vscode.Uri.joinPath(reportsDir, `ai-export-${timestamp}.md`); await vscode.workspace.fs.writeFile(file, Buffer.from(content)); vscode.window.showInformationMessage("πŸ€– AI Export saved!"); } async function exportASCIITree( reportsDir: vscode.Uri, treeData: any, timestamp: number ): Promise { const content = generateASCIITreeContent(treeData); const file = vscode.Uri.joinPath(reportsDir, `ascii-tree-${timestamp}.txt`); await vscode.workspace.fs.writeFile(file, Buffer.from(content)); vscode.window.showInformationMessage("🌲 ASCII Tree exported!"); } async function exportMarkdownTree( reportsDir: vscode.Uri, treeData: any, timestamp: number ): Promise { const content = generateMarkdownTreeContent(treeData); const file = vscode.Uri.joinPath(reportsDir, `markdown-tree-${timestamp}.md`); await vscode.workspace.fs.writeFile(file, Buffer.from(content)); vscode.window.showInformationMessage("πŸ“ Markdown Tree exported!"); } async function generateAllExports( reportsDir: vscode.Uri, treeData: any, timestamp: number ): Promise { await generateAIExports(reportsDir, treeData, timestamp); vscode.window.showInformationMessage( "πŸš€ All exports generated successfully!" ); } /** * 🎨 ULTIMATE ECOSYSTEM HTML GENERATOR - ENHANCED WITH WARNING SYSTEM */ function generateUltimateEcosystemHTML( treeData: any, projects: EcosystemProject[] ): string { return ` 🌳 Maestro-AI Ultimate Ecosystem Tree

🌳 Maestro-AI Ultimate Ecosystem Tree

Complete multi-project route visualization | Generated: ${new Date().toLocaleString()}

${ treeData.stats ? `
${treeData.stats.totalProjects}
Projects
${treeData.stats.totalRoutes}
Total Routes
${treeData.stats.totalWorking}
Working
${treeData.stats.totalMissing}
Issues
${treeData.stats.healthPercentage}%
Health Score
` : "" }

πŸ“€ AI-Perfect Exports

🎯 Display Mode:

🎯 Ecosystem Legend

Projects

🟣 Cipher Engine
🟦 Maestro AI
πŸ’— Ava

Display Modes

🏷️ File Types
πŸ“ Folder Paths

Status

βœ… Working
⚠️ Warnings (clickable)
🚨 Errors (clickable)
`; } interface EcosystemProject { name: string; symbol: string; color: string; path: string; description: string; routes: RouteInfo[]; stats: ProjectStats; } interface ProjectRouteData { routes: RouteInfo[]; stats: ProjectStats; } interface ProjectStats { total: number; working: number; missing: number; folders: number; files: number; } async function pathExists(path: string): Promise { try { await fs.promises.access(path); return true; } catch { return false; } }