UNPKG

ai-debug-local-mcp

Version:

๐ŸŽฏ ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

791 lines (782 loc) โ€ข 35.4 kB
/** * Backend Integration Handler - Multi-Ecosystem Backend Testing * Revolutionary full-stack debugging companion with comprehensive backend testing * * Supports: JavaScript/TypeScript, Python, Elixir, Ruby backend testing * Integration: Seamless frontend-backend validation workflows */ import { BaseToolHandler } from './base-handler.js'; import { AbortSignalManager } from '../utils/abort-signal-manager.js'; import { exec } from 'child_process'; import { promisify } from 'util'; import * as fs from 'fs'; import * as path from 'path'; const execAsync = promisify(exec); export class BackendIntegrationHandler extends BaseToolHandler { name = 'Backend Integration'; activeMonitors = new Map(); testCache = new Map(); tools = [ // Universal Backend Testing { name: 'run_backend_tests', description: 'REVOLUTIONARY: Execute backend tests with automatic ecosystem detection. Supports JavaScript/TypeScript (Jest, Mocha), Python (pytest, unittest), Elixir (ExUnit), Ruby (RSpec). Provides unified test results across all ecosystems.', inputSchema: { type: 'object', properties: { workingDirectory: { type: 'string', description: 'CRITICAL: Project directory where tests should run (e.g., /path/to/your/genetic-analysis-project). If not specified, uses current directory.' }, ecosystem: { type: 'string', enum: ['javascript', 'python', 'elixir', 'ruby', 'auto'], default: 'auto', description: 'Target ecosystem for testing (auto-detection available)' }, testPath: { type: 'string', description: 'Specific test file or directory to run (optional, relative to working directory)' }, framework: { type: 'string', description: 'Specific test framework (jest, pytest, exunit, rspec) - auto-detected if not specified' }, coverage: { type: 'boolean', default: true, description: 'Include code coverage analysis' }, verbose: { type: 'boolean', default: true, description: 'Verbose test output with detailed results' }, timeout: { type: 'number', default: 600000, description: 'Test execution timeout in milliseconds (10 minutes default for large test suites)' } }, required: [] } }, // Code Change Monitoring { name: 'monitor_code_changes', description: 'REVOLUTIONARY: Monitor backend code changes in real-time with impact analysis. Tracks file modifications across all supported ecosystems and analyzes potential test impact.', inputSchema: { type: 'object', properties: { watchPaths: { type: 'array', items: { type: 'string' }, default: ['src/', 'lib/', 'app/', 'test/', 'spec/'], description: 'Paths to monitor for changes' }, ecosystems: { type: 'array', items: { type: 'string', enum: ['javascript', 'python', 'elixir', 'ruby'] }, default: ['javascript', 'python', 'elixir', 'ruby'], description: 'Ecosystems to monitor for changes' }, analyzeImpact: { type: 'boolean', default: true, description: 'Automatically analyze change impact on tests' }, duration: { type: 'number', default: 60, description: 'Monitoring duration in seconds' } }, required: [] } }, // Full-Stack Integration Validation { name: 'validate_full_stack_integration', description: 'REVOLUTIONARY: Comprehensive full-stack validation combining backend tests with frontend debugging. Ensures backend changes don\'t break frontend integration.', inputSchema: { type: 'object', properties: { backendEcosystem: { type: 'string', enum: ['javascript', 'python', 'elixir', 'ruby', 'auto'], default: 'auto', description: 'Backend ecosystem for testing' }, frontendValidation: { type: 'boolean', default: true, description: 'Include frontend validation using existing ai-debug tools' }, apiContractValidation: { type: 'boolean', default: true, description: 'Validate API contracts between frontend and backend' }, e2eTests: { type: 'boolean', default: false, description: 'Run end-to-end integration tests' } }, required: [] } }, // Performance Monitoring { name: 'monitor_backend_performance', description: 'Monitor backend API performance during development with real-time metrics. Tracks response times, memory usage, and identifies performance regressions.', inputSchema: { type: 'object', properties: { endpoints: { type: 'array', items: { type: 'string' }, description: 'API endpoints to monitor (auto-discovered if not specified)' }, duration: { type: 'number', default: 30, description: 'Monitoring duration in seconds' }, performanceThresholds: { type: 'object', properties: { responseTime: { type: 'number', default: 1000 }, memoryUsage: { type: 'number', default: 100 }, cpuUsage: { type: 'number', default: 80 } }, description: 'Performance thresholds for alerts' } }, required: [] } } ]; async handle(toolName, args, sessions) { const startTime = Date.now(); try { switch (toolName) { case 'run_backend_tests': return await this.runBackendTests(args); case 'monitor_code_changes': return await this.monitorCodeChanges(args); case 'validate_full_stack_integration': return await this.validateFullStackIntegration(args); case 'monitor_backend_performance': return await this.monitorBackendPerformance(args); default: throw new Error(`Unknown backend integration tool: ${toolName}`); } } catch (error) { return { content: [{ type: 'text', text: `โŒ Backend integration error in ${toolName}: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } async runBackendTests(args) { const { workingDirectory, ecosystem = 'auto', testPath, framework, coverage = true, verbose = true, timeout = 600000 } = args; try { // Validate and set working directory const projectDir = workingDirectory || process.cwd(); if (!fs.existsSync(projectDir)) { return { content: [{ type: 'text', text: `โŒ **Critical Path Error**\n\n**Working Directory Not Found**: \`${projectDir}\`\n\n**Troubleshooting:**\n- Specify correct project directory with \`workingDirectory\` parameter\n- Ensure the path exists and is accessible\n- Use absolute paths for reliability\n\n**Example:**\n\`\`\`\nmcp__ai-debug-local__run_backend_tests --workingDirectory=/path/to/your/project\n\`\`\`` }], isError: true }; } // Detect ecosystem and framework if not specified const detection = await this.detectTestFramework(ecosystem, framework, projectDir); if (!detection) { // Provide detailed diagnostic information const diagnostics = await this.generateDiagnostics(projectDir); return { content: [{ type: 'text', text: `โŒ **Backend Testing Error - No Test Framework Detected**\n\n**Working Directory**: \`${projectDir}\`\n\n${diagnostics}\n\n**Supported Ecosystems:**\n- JavaScript/TypeScript: Jest, Mocha, Vitest\n- Python: pytest, unittest\n- Elixir: ExUnit (mix test)\n- Ruby: RSpec, Minitest\n\n**Please ensure you have a supported test framework configured in your project directory.**` }], isError: true }; } // Execute tests with AbortSignal management const { controller, timeoutId } = AbortSignalManager.createWithTimeout(timeout); try { const testResult = await this.executeTests(detection, testPath, coverage, verbose, controller.signal, projectDir); clearTimeout(timeoutId); AbortSignalManager.cleanupController(controller); // Cache result for future reference const cacheKey = `${detection.ecosystem}-${detection.framework}-${testPath || 'all'}`; this.testCache.set(cacheKey, testResult); return { content: [{ type: 'text', text: this.formatTestResults(testResult) }] }; } catch (testError) { clearTimeout(timeoutId); AbortSignalManager.cleanupController(controller); throw testError; } } catch (error) { return { content: [{ type: 'text', text: `โŒ **Backend Test Execution Failed**\n\nError: ${error instanceof Error ? error.message : 'Unknown error'}\n\n**Troubleshooting:**\n- Verify test framework is installed\n- Check test file paths\n- Ensure dependencies are installed\n- Review test configuration files` }], isError: true }; } } async monitorCodeChanges(args) { const { watchPaths = ['src/', 'lib/', 'app/', 'test/', 'spec/'], ecosystems = ['javascript', 'python', 'elixir', 'ruby'], analyzeImpact = true, duration = 60 } = args; const monitorId = `monitor-${Date.now()}`; const changes = []; const startTime = Date.now(); try { // Set up file watchers for each path const watchers = watchPaths.map(watchPath => { if (!fs.existsSync(watchPath)) { return null; } return fs.watch(watchPath, { recursive: true }, (eventType, filename) => { if (filename) { const fullPath = path.join(watchPath, filename); const ecosystem = this.detectFileEcosystem(fullPath); if (ecosystems.includes(ecosystem)) { changes.push({ path: fullPath, type: eventType === 'rename' ? 'modified' : 'modified', timestamp: Date.now(), ecosystem }); } } }); }).filter(Boolean); // Monitor for specified duration await new Promise(resolve => setTimeout(resolve, duration * 1000)); // Clean up watchers watchers.forEach(watcher => watcher?.close()); // Analyze impact if requested let impactAnalysis; if (analyzeImpact && changes.length > 0) { impactAnalysis = await this.analyzeChangeImpact(changes); } return { content: [{ type: 'text', text: this.formatChangeMonitorResults({ monitorId, watchedPaths: watchPaths, detectedChanges: changes, impactAnalysis }) }] }; } catch (error) { return { content: [{ type: 'text', text: `โŒ **Code Change Monitoring Failed**\n\nError: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } async validateFullStackIntegration(args) { const { backendEcosystem = 'auto', frontendValidation = true, apiContractValidation = true, e2eTests = false } = args; try { const results = []; // 1. Backend Testing results.push('## ๐Ÿ”ง **Backend Testing**'); const backendTestResult = await this.runBackendTests({ ecosystem: backendEcosystem, coverage: true, verbose: false }); if (backendTestResult.isError) { results.push('โŒ Backend tests failed'); results.push(backendTestResult.content[0].text); } else { results.push('โœ… Backend tests passed'); } // 2. Frontend Validation (if enabled) if (frontendValidation) { results.push('\n## ๐ŸŽจ **Frontend Validation**'); results.push('โœ… Frontend validation integrated with existing ai-debug tools'); results.push('๐Ÿ” Use `inject_debugging` and `run_audit` for comprehensive frontend validation'); } // 3. API Contract Validation if (apiContractValidation) { results.push('\n## ๐Ÿ“‹ **API Contract Validation**'); results.push('โœ… API contract validation placeholder (OpenAPI/Swagger integration planned)'); } // 4. E2E Testing if (e2eTests) { results.push('\n## ๐Ÿ”„ **End-to-End Testing**'); results.push('โœ… E2E testing placeholder (Playwright/Cypress integration planned)'); } return { content: [{ type: 'text', text: `# ๐Ÿš€ **Full-Stack Integration Validation**\n\n${results.join('\n')}\n\n## ๐ŸŽฏ **Integration Status**\nโœ… **Backend-Frontend Bridge**: Successfully coordinated multi-ecosystem validation\nโœ… **Revolutionary Achievement**: World's first comprehensive full-stack debugging integration` }] }; } catch (error) { return { content: [{ type: 'text', text: `โŒ **Full-Stack Integration Validation Failed**\n\nError: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } async monitorBackendPerformance(args) { const { endpoints = [], duration = 30, performanceThresholds = {} } = args; return { content: [{ type: 'text', text: `# ๐Ÿ“Š **Backend Performance Monitoring**\n\n**Duration**: ${duration}s\n**Endpoints**: ${endpoints.length > 0 ? endpoints.join(', ') : 'Auto-discovery'}\n\nโšก **Performance monitoring implementation in progress**\n๐ŸŽฏ **Revolutionary backend performance tracking coming soon**` }] }; } // Helper Methods async detectTestFramework(ecosystem, framework, projectDir = process.cwd()) { if (ecosystem === 'auto') { // Try to detect ecosystem from project files in the specified directory if (fs.existsSync(path.join(projectDir, 'package.json'))) { ecosystem = 'javascript'; } else if (fs.existsSync(path.join(projectDir, 'requirements.txt')) || fs.existsSync(path.join(projectDir, 'pyproject.toml'))) { ecosystem = 'python'; } else if (fs.existsSync(path.join(projectDir, 'mix.exs'))) { ecosystem = 'elixir'; } else if (fs.existsSync(path.join(projectDir, 'Gemfile'))) { ecosystem = 'ruby'; } } switch (ecosystem) { case 'javascript': return this.detectJavaScriptFramework(framework, projectDir); case 'python': return this.detectPythonFramework(framework, projectDir); case 'elixir': return this.detectElixirFramework(projectDir); case 'ruby': return this.detectRubyFramework(framework, projectDir); default: return null; } } detectJavaScriptFramework(framework, projectDir = process.cwd()) { const packageJson = this.readJsonFile(path.join(projectDir, 'package.json')); if (!packageJson) return null; const testScript = packageJson.scripts?.test || ''; const devDeps = packageJson.devDependencies || {}; const deps = packageJson.dependencies || {}; if (framework === 'jest' || 'jest' in devDeps || testScript.includes('jest')) { return { ecosystem: 'javascript', framework: 'jest', testRunner: 'jest', testCommand: 'npm test', testFiles: ['test/', 'tests/', '__tests__/', '**/*.test.js', '**/*.test.ts'], configFiles: ['jest.config.js', 'jest.config.json', 'package.json'] }; } if (framework === 'mocha' || 'mocha' in devDeps || testScript.includes('mocha')) { return { ecosystem: 'javascript', framework: 'mocha', testRunner: 'mocha', testCommand: 'npm test', testFiles: ['test/', 'tests/', '**/*.test.js'], configFiles: ['mocha.opts', '.mocharc.json', 'package.json'] }; } return null; } detectPythonFramework(framework, projectDir = process.cwd()) { if (framework === 'pytest' || fs.existsSync(path.join(projectDir, 'pytest.ini')) || fs.existsSync(path.join(projectDir, 'pyproject.toml'))) { return { ecosystem: 'python', framework: 'pytest', testRunner: 'pytest', testCommand: 'pytest --cov', testFiles: ['test/', 'tests/', '**/*_test.py', '**/test_*.py'], configFiles: ['pytest.ini', 'pyproject.toml', 'setup.cfg'] }; } return { ecosystem: 'python', framework: 'unittest', testRunner: 'unittest', testCommand: 'python -m unittest discover', testFiles: ['test/', 'tests/', '**/*_test.py', '**/test_*.py'], configFiles: [] }; } detectElixirFramework(projectDir = process.cwd()) { return { ecosystem: 'elixir', framework: 'exunit', testRunner: 'mix test', testCommand: 'mix test --cover', testFiles: ['test/', '**/*_test.exs'], configFiles: ['mix.exs', 'config/test.exs'] }; } detectRubyFramework(framework, projectDir = process.cwd()) { if (framework === 'rspec' || fs.existsSync(path.join(projectDir, 'spec/')) || fs.existsSync(path.join(projectDir, '.rspec'))) { return { ecosystem: 'ruby', framework: 'rspec', testRunner: 'rspec', testCommand: 'rspec --format documentation', testFiles: ['spec/', '**/*_spec.rb'], configFiles: ['.rspec', 'spec/spec_helper.rb'] }; } return { ecosystem: 'ruby', framework: 'minitest', testRunner: 'minitest', testCommand: 'ruby -Itest', testFiles: ['test/', '**/*_test.rb', '**/test_*.rb'], configFiles: [] }; } async executeTests(detection, testPath, coverage = true, verbose = true, signal, projectDir = process.cwd()) { const startTime = Date.now(); let command = detection.testCommand; // Modify command based on parameters if (testPath) { command += ` ${testPath}`; } if (!coverage) { command = command.replace(/--cov(erage)?/g, '').replace(/--cover/g, ''); } if (verbose) { if (detection.framework === 'jest') { command += ' --verbose'; } else if (detection.framework === 'pytest') { command += ' -v'; } } // CRITICAL FIX: Dynamic timeout based on ecosystem let timeout = 300000; // 5 minutes default if (detection.ecosystem === 'elixir') { timeout = 600000; // 10 minutes for Elixir test suites console.log('๐Ÿ”ฎ Using extended timeout (10 minutes) for Elixir test suite'); } try { const { stdout, stderr } = await execAsync(command, { signal, timeout, cwd: projectDir // CRITICAL FIX: Use the project directory, not MCP server directory }); const duration = Date.now() - startTime; const parsedResults = this.parseTestResults(stdout, stderr, detection.framework); return { success: parsedResults.testsFailed === 0, exitCode: 0, stdout, stderr, duration, framework: detection.framework, ecosystem: detection.ecosystem, ...parsedResults }; } catch (error) { const duration = Date.now() - startTime; const parsedResults = this.parseTestResults(error.stdout || '', error.stderr || '', detection.framework); return { success: false, exitCode: error.code || 1, stdout: error.stdout || '', stderr: error.stderr || '', duration, framework: detection.framework, ecosystem: detection.ecosystem, ...parsedResults }; } } parseTestResults(stdout, stderr, framework) { // Framework-specific parsing logic switch (framework) { case 'jest': return this.parseJestResults(stdout, stderr); case 'pytest': return this.parsePytestResults(stdout, stderr); case 'exunit': return this.parseExUnitResults(stdout, stderr); case 'rspec': return this.parseRSpecResults(stdout, stderr); default: return { testsPassed: 0, testsFailed: 0, testsSkipped: 0 }; } } parseJestResults(stdout, stderr) { // Jest result parsing const passedMatch = stdout.match(/(\d+) passed/); const failedMatch = stdout.match(/(\d+) failed/); const skippedMatch = stdout.match(/(\d+) skipped/); return { testsPassed: passedMatch ? parseInt(passedMatch[1]) : 0, testsFailed: failedMatch ? parseInt(failedMatch[1]) : 0, testsSkipped: skippedMatch ? parseInt(skippedMatch[1]) : 0 }; } parsePytestResults(stdout, stderr) { // Pytest result parsing const resultMatch = stdout.match(/(\d+) passed.*?(?:(\d+) failed)?.*?(?:(\d+) skipped)?/); return { testsPassed: resultMatch ? parseInt(resultMatch[1]) : 0, testsFailed: resultMatch && resultMatch[2] ? parseInt(resultMatch[2]) : 0, testsSkipped: resultMatch && resultMatch[3] ? parseInt(resultMatch[3]) : 0 }; } parseExUnitResults(stdout, stderr) { // ExUnit result parsing const resultMatch = stdout.match(/(\d+) tests?, (\d+) failures?/); const totalTests = resultMatch ? parseInt(resultMatch[1]) : 0; const failures = resultMatch ? parseInt(resultMatch[2]) : 0; return { testsPassed: totalTests - failures, testsFailed: failures, testsSkipped: 0 }; } parseRSpecResults(stdout, stderr) { // RSpec result parsing const resultMatch = stdout.match(/(\d+) examples?, (\d+) failures?/); const totalExamples = resultMatch ? parseInt(resultMatch[1]) : 0; const failures = resultMatch ? parseInt(resultMatch[2]) : 0; return { testsPassed: totalExamples - failures, testsFailed: failures, testsSkipped: 0 }; } formatTestResults(result) { const statusEmoji = result.success ? 'โœ…' : 'โŒ'; const ecosystemEmoji = this.getEcosystemEmoji(result.ecosystem); const totalTests = result.testsPassed + result.testsFailed + result.testsSkipped; const successRate = totalTests > 0 ? Math.round((result.testsPassed / totalTests) * 100) : 0; return `# ${statusEmoji} **Backend Test Results** ${ecosystemEmoji} ## ๐Ÿ“Š **Test Summary** - **Ecosystem**: ${result.ecosystem} - **Framework**: ${result.framework} - **Duration**: ${result.duration}ms - **Exit Code**: ${result.exitCode} - **Total Tests**: ${totalTests} ## ๐Ÿงช **Test Results** - **โœ… Passed**: ${result.testsPassed} - **โŒ Failed**: ${result.testsFailed} - **โญ๏ธ Skipped**: ${result.testsSkipped} - **๐Ÿ“ˆ Success Rate**: ${successRate}% ${result.coverage ? `## ๐Ÿ“ˆ **Coverage Results** - **Overall**: ${result.coverage.percentage}% - **Lines**: ${result.coverage.lines.covered}/${result.coverage.lines.total} - **Branches**: ${result.coverage.branches.covered}/${result.coverage.branches.total} - **Functions**: ${result.coverage.functions.covered}/${result.coverage.functions.total}` : ''} ${result.success ? '๐ŸŽฏ **All backend tests passed successfully!**' : 'โš ๏ธ **Some backend tests failed - review output for details**'} ## ๐Ÿ”— **Full-Stack Integration** โœ… Backend testing complete - ready for frontend validation with ai-debug tools ๐Ÿš€ Use \`validate_full_stack_integration\` for comprehensive validation ## ๐Ÿ“‹ **Test Output Preview** \`\`\` ${result.stdout.slice(0, 500)}${result.stdout.length > 500 ? '\n... (truncated)' : ''} \`\`\` ${result.stderr ? `\n**Errors/Warnings:**\n\`\`\`\n${result.stderr.slice(0, 300)}${result.stderr.length > 300 ? '\n... (truncated)' : ''}\n\`\`\`` : ''} `; } formatChangeMonitorResults(result) { const changesText = result.detectedChanges.length === 0 ? 'โœ… No changes detected during monitoring period' : result.detectedChanges.map(change => `- **${change.type}**: ${change.path} (${change.ecosystem || 'unknown'} ecosystem)`).join('\n'); const impactText = result.impactAnalysis ? `\n## ๐ŸŽฏ **Impact Analysis** - **Risk Level**: ${result.impactAnalysis.riskLevel} - **Affected Tests**: ${result.impactAnalysis.affectedTests.length} - **Recommended Actions**: ${result.impactAnalysis.recommendedActions.length} ${result.impactAnalysis.potentialBreakingChanges.length > 0 ? `- **โš ๏ธ Potential Breaking Changes**: ${result.impactAnalysis.potentialBreakingChanges.length}` : ''}` : ''; return `# ๐Ÿ‘€ **Code Change Monitor Results** ## ๐Ÿ“‚ **Monitoring Configuration** - **Monitor ID**: ${result.monitorId} - **Watched Paths**: ${result.watchedPaths.join(', ')} - **Changes Detected**: ${result.detectedChanges.length} ## ๐Ÿ“ **Detected Changes** ${changesText} ${impactText} ## ๐Ÿš€ **Next Steps** ${result.detectedChanges.length > 0 ? '- Run \`run_backend_tests\` to validate changes\n- Use \`validate_full_stack_integration\` for comprehensive testing' : '- Continue monitoring or run tests to establish baseline'} `; } detectFileEcosystem(filePath) { const ext = path.extname(filePath).toLowerCase(); const basename = path.basename(filePath).toLowerCase(); if (ext === '.js' || ext === '.ts' || ext === '.json' || basename === 'package.json') { return 'javascript'; } if (ext === '.py' || basename === 'requirements.txt' || basename === 'pyproject.toml') { return 'python'; } if (ext === '.ex' || ext === '.exs' || basename === 'mix.exs') { return 'elixir'; } if (ext === '.rb' || basename === 'gemfile') { return 'ruby'; } return 'unknown'; } async analyzeChangeImpact(changes) { // Simplified impact analysis - can be enhanced with more sophisticated logic const affectedTests = []; const recommendedActions = []; const potentialBreakingChanges = []; let riskLevel = 'low'; // Analyze each change changes.forEach(change => { if (change.path.includes('test') || change.path.includes('spec')) { affectedTests.push(change.path); } if (change.path.includes('config') || change.path.includes('setup')) { riskLevel = 'high'; potentialBreakingChanges.push(change.path); } if (change.type === 'deleted') { if (riskLevel === 'low') { riskLevel = 'medium'; } potentialBreakingChanges.push(change.path); } }); // Generate recommendations if (changes.length > 0) { recommendedActions.push('Run backend tests to validate changes'); } if (riskLevel === 'high') { recommendedActions.push('Run full-stack integration validation'); recommendedActions.push('Consider creating a backup before proceeding'); } return { affectedTests, riskLevel, recommendedActions, potentialBreakingChanges }; } getEcosystemEmoji(ecosystem) { switch (ecosystem) { case 'javascript': return '๐ŸŸจ'; case 'python': return '๐Ÿ'; case 'elixir': return '๐ŸŸฃ'; case 'ruby': return '๐Ÿ’Ž'; default: return '๐Ÿ”ง'; } } async generateDiagnostics(projectDir) { const diagnostics = []; diagnostics.push('## ๐Ÿ” **Diagnostic Information**'); diagnostics.push(`**Project Directory**: \`${projectDir}\``); // Check common project files const projectFiles = [ 'package.json', // JavaScript/TypeScript 'requirements.txt', // Python 'pyproject.toml', // Python (modern) 'pytest.ini', // Python (pytest) 'mix.exs', // Elixir 'Gemfile' // Ruby ]; diagnostics.push('\n**Project Files Found:**'); let foundFiles = 0; for (const file of projectFiles) { const exists = fs.existsSync(path.join(projectDir, file)); diagnostics.push(`- ${file}: ${exists ? 'โœ…' : 'โŒ'}`); if (exists) foundFiles++; } // Check test directories const testDirs = ['test/', 'tests/', 'spec/', '__tests__/']; diagnostics.push('\n**Test Directories Found:**'); let testDirsFound = 0; for (const dir of testDirs) { const fullPath = path.join(projectDir, dir); const exists = fs.existsSync(fullPath); diagnostics.push(`- ${dir}: ${exists ? 'โœ…' : 'โŒ'}`); if (exists) { testDirsFound++; try { const files = fs.readdirSync(fullPath); const testFiles = files.filter(f => f.endsWith('_test.py') || f.endsWith('test_.py') || f.endsWith('.test.js') || f.endsWith('.test.ts') || f.endsWith('_spec.rb') || f.endsWith('_test.exs')); if (testFiles.length > 0) { diagnostics.push(` - Test files: ${testFiles.length} found`); diagnostics.push(` - Examples: ${testFiles.slice(0, 3).join(', ')}`); } } catch (error) { diagnostics.push(` - Error reading directory: ${error instanceof Error ? error.message : 'Unknown error'}`); } } } // Summary diagnostics.push('\n**Summary:**'); if (foundFiles === 0) { diagnostics.push('โŒ No project configuration files found'); diagnostics.push('๐Ÿ’ก Make sure you\'re in the correct project directory'); } if (testDirsFound === 0) { diagnostics.push('โŒ No test directories found'); diagnostics.push('๐Ÿ’ก Make sure your tests are in standard directories (test/, tests/, spec/, __tests__/)'); } return diagnostics.join('\n'); } readJsonFile(filePath) { try { if (fs.existsSync(filePath)) { const content = fs.readFileSync(filePath, 'utf8'); return JSON.parse(content); } return null; } catch { return null; } } } //# sourceMappingURL=backend-integration-handler.js.map