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

213 lines • 8.8 kB
import { BaseToolHandler } from './base-handler.js'; import { TestEvolutionEngine } from '../test-evolution-engine.js'; /** * Handler for test evolution tools */ export class EvolutionHandler extends BaseToolHandler { evolutionEngine; tools = [ { name: 'evolve_tests', description: 'Automatically evolve tests based on uncommitted code changes. Updates selectors, deprecates obsolete tests, and maintains test health.', inputSchema: { type: 'object', properties: { autoCommit: { type: 'boolean', description: 'Automatically commit test updates (default: false)', default: false }, dryRun: { type: 'boolean', description: 'Preview changes without applying them (default: false)', default: false } } } }, { name: 'evolve_tests_for_file', description: 'Evolve tests for specific file changes. Useful for targeted test updates.', inputSchema: { type: 'object', properties: { filePath: { type: 'string', description: 'Path to the changed file' }, oldContent: { type: 'string', description: 'Previous content of the file (optional, will use git if not provided)' }, newContent: { type: 'string', description: 'New content of the file (optional, will use current file if not provided)' } }, required: ['filePath'] } }, { name: 'detect_test_evolution_opportunities', description: 'Analyze codebase for test evolution opportunities without making changes.', inputSchema: { type: 'object', properties: { includeSuggestions: { type: 'boolean', description: 'Include detailed suggestions for each opportunity (default: true)', default: true } } } }, { name: 'preview_test_evolution', description: 'Preview what test evolution would do for current uncommitted changes.', inputSchema: { type: 'object', properties: {} } } ]; constructor(evolutionEngine) { super(); this.evolutionEngine = evolutionEngine || new TestEvolutionEngine(); } async handle(toolName, args, sessions) { try { // Call the specific method based on tool name const methodName = toolName.replace(/-/g, '_'); const method = this[methodName]; if (typeof method === 'function') { return method.call(this, args); } throw new Error(`Unknown tool: ${toolName}`); } catch (error) { return this.createErrorResponse(error); } } async evolve_tests(args) { try { const result = await this.evolutionEngine.evolveTestsFromGitChanges({ autoCommit: args.autoCommit || false, dryRun: args.dryRun || false }); return this.createTextResponse(JSON.stringify({ success: result.success, summary: { filesUpdated: result.filesUpdated.length, testsEvolved: result.testsEvolved, errors: result.errors.length }, details: result, message: this.generateEvolutionSummary(result) }, null, 2)); } catch (error) { return this.createErrorResponse(error); } } async evolve_tests_for_file(args) { try { // Create a mock git change for the specific file const mockEngine = new TestEvolutionEngine(); // Use private method through reflection (not ideal but works for now) const result = await mockEngine.analyzeAndEvolveTests({ file: args.filePath, status: 'modified', oldContent: args.oldContent, newContent: args.newContent }, { success: false, filesUpdated: [], testsEvolved: 0, errors: [], suggestions: [] }, { dryRun: true }); return this.createTextResponse(JSON.stringify({ success: true, file: args.filePath, result }, null, 2)); } catch (error) { return this.createErrorResponse(error); } } async detect_test_evolution_opportunities(args) { try { // Run evolution in dry-run mode to detect opportunities const result = await this.evolutionEngine.evolveTestsFromGitChanges({ dryRun: true }); const opportunities = { totalOpportunities: result.suggestions.length + result.filesUpdated.length, categories: { selectorUpdates: result.filesUpdated.filter(f => f.includes('selector')).length, deprecations: result.suggestions.filter(s => s.includes('deleted')).length, signatureChanges: result.suggestions.filter(s => s.includes('signature')).length, propertyChanges: result.suggestions.filter(s => s.includes('props')).length }, suggestions: args.includeSuggestions ? result.suggestions : [], affectedFiles: result.filesUpdated }; return this.createTextResponse(JSON.stringify({ success: true, opportunities, message: `Found ${opportunities.totalOpportunities} test evolution opportunities` }, null, 2)); } catch (error) { return this.createErrorResponse(error); } } async preview_test_evolution(args) { try { // Run evolution in dry-run mode const result = await this.evolutionEngine.evolveTestsFromGitChanges({ dryRun: true }); const preview = { wouldUpdate: result.filesUpdated, wouldEvolve: result.testsEvolved, changes: this.categorizeChanges(result), suggestions: result.suggestions, safeToRun: result.errors.length === 0 }; return this.createTextResponse(JSON.stringify({ success: true, preview, message: this.generatePreviewMessage(preview) }, null, 2)); } catch (error) { return this.createErrorResponse(error); } } generateEvolutionSummary(result) { if (!result.success) { return `Test evolution failed: ${result.errors.join(', ')}`; } if (result.filesUpdated.length === 0) { return 'No test evolution needed - all tests are up to date!'; } return `Successfully evolved ${result.testsEvolved} tests across ${result.filesUpdated.length} files. ${result.suggestions.length} suggestions for manual review.`; } generatePreviewMessage(preview) { if (preview.wouldUpdate.length === 0) { return 'No changes would be made - tests are already synchronized with code.'; } return `Preview: Would update ${preview.wouldUpdate.length} test files with ${preview.wouldEvolve} test changes. ${preview.safeToRun ? 'Safe to run.' : 'Some issues detected - review before running.'}`; } categorizeChanges(result) { return { selectorUpdates: result.filesUpdated.filter((f) => result.suggestions.some((s) => s.includes('selector') && s.includes(f))).length, deprecations: result.suggestions.filter((s) => s.includes('deleted') || s.includes('deprecated')).length, signatureUpdates: result.suggestions.filter((s) => s.includes('signature') || s.includes('params')).length, newTests: result.suggestions.filter((s) => s.includes('add test') || s.includes('new test')).length }; } } //# sourceMappingURL=evolution-handler.js.map