UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

874 lines 39.4 kB
import { randomUUID } from 'crypto'; import { createTool, createSuccessResult, createErrorResult } from '../../core/tool-framework.js'; /** * Get or create default TDD configuration */ async function ensureTDDConfig(context) { const existingConfig = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); if (!existingConfig.success || !existingConfig.data) { await context.db.run(`INSERT INTO development_tdd_config (project_id, enforce_test_first, minimum_test_coverage, require_tests_before_implementation, auto_generate_test_templates) VALUES (?, ?, ?, ?, ?)`, [context.projectId || 'default', true, 80.0, true, true]); } } /** * Create a new feature for TDD development */ const createFeatureTool = createTool({ name: 'create_feature', description: 'Create a new feature for Test-Driven Development', category: 'development', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Feature name', minLength: 1, maxLength: 200 }, description: { type: 'string', description: 'Feature description', maxLength: 1000 }, testCases: { type: 'array', items: { type: 'object', properties: { name: { type: 'string', description: 'Test case name', minLength: 1, maxLength: 200 }, description: { type: 'string', description: 'Test case description', maxLength: 500 }, type: { type: 'string', enum: ['unit', 'integration', 'e2e'], default: 'unit', description: 'Type of test' }, expectedBehavior: { type: 'string', description: 'Expected behavior', minLength: 1, maxLength: 1000 } }, required: ['name', 'expectedBehavior'], additionalProperties: false }, description: 'Initial test cases for the feature', maxItems: 50 } }, required: ['name'], additionalProperties: false }, async execute(input, context) { try { // Ensure TDD config exists await ensureTDDConfig(context); // Check TDD configuration const configResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); const config = configResult.data || {}; // Check for duplicate feature names const existingFeature = await context.db.get('SELECT id FROM development_features WHERE name = ? AND project_id = ?', [input.name, context.projectId || 'default']); if (existingFeature.success && existingFeature.data) { return createErrorResult({ code: 'DUPLICATE_RESOURCE', message: 'A feature with this name already exists', category: 'validation' }); } const featureId = randomUUID(); const now = Date.now(); // Create feature const result = await context.db.run(`INSERT INTO development_features (id, project_id, name, description, status, test_coverage, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [ featureId, context.projectId || 'default', input.name, input.description || '', 'planning', 0.0, now, now ]); if (!result.success) { return createErrorResult({ code: 'DATABASE_ERROR', message: 'Failed to create feature', details: { error: result.error }, category: 'system' }); } // Create initial test cases if provided const createdTests = []; if (input.testCases && input.testCases.length > 0) { for (const testCase of input.testCases) { const testId = randomUUID(); await context.db.run(`INSERT INTO development_test_cases (id, feature_id, project_id, name, description, type, status, expected_behavior, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ testId, featureId, context.projectId || 'default', testCase.name, testCase.description || '', testCase.type || 'unit', 'pending', testCase.expectedBehavior, now, now ]); createdTests.push({ id: testId, name: testCase.name, type: testCase.type || 'unit', status: 'pending' }); } } return createSuccessResult({ feature: { id: featureId, name: input.name, description: input.description || '', status: 'planning', testCoverage: 0, testCases: createdTests, createdAt: new Date(now).toISOString() }, message: `Feature "${input.name}" created successfully`, tddGuidance: config.enforce_test_first ? 'Remember: Write failing tests before implementing the feature (Red phase)' : 'TDD enforcement is disabled', nextSteps: [ createdTests.length > 0 ? `Write implementations for ${createdTests.length} test cases` : 'Create test cases for this feature', 'Start a TDD session to begin development', 'Follow the Red-Green-Refactor cycle' ] }); } catch (error) { return createErrorResult({ code: 'EXECUTION_ERROR', message: `Failed to create feature: ${error instanceof Error ? error.message : 'Unknown error'}`, category: 'execution' }); } } }); /** * Write a test case for a feature */ const writeTestTool = createTool({ name: 'write_test', description: 'Write a test case for a feature (enforces test-first approach)', category: 'development', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'Feature ID', pattern: '^[a-zA-Z0-9-]+$' }, name: { type: 'string', description: 'Test case name', minLength: 1, maxLength: 200 }, description: { type: 'string', description: 'Test case description', maxLength: 500 }, type: { type: 'string', enum: ['unit', 'integration', 'e2e'], default: 'unit', description: 'Type of test' }, expectedBehavior: { type: 'string', description: 'Expected behavior', minLength: 1, maxLength: 1000 } }, required: ['featureId', 'name', 'expectedBehavior'], additionalProperties: false }, async execute(input, context) { try { // Verify feature exists const featureResult = await context.db.get('SELECT * FROM development_features WHERE id = ? AND project_id = ?', [input.featureId, context.projectId || 'default']); if (!featureResult.success || !featureResult.data) { return createErrorResult({ code: 'RESOURCE_NOT_FOUND', message: 'Feature not found', category: 'validation' }); } const feature = featureResult.data; // Check TDD configuration const configResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); const config = configResult.data || {}; // Enforce test-first if enabled if (config.enforce_test_first && feature.status === 'implementing') { return createErrorResult({ code: 'TDD_VIOLATION', message: 'Cannot add tests during implementation phase. Complete current implementation first.', details: { currentStatus: feature.status, enforceTestFirst: true }, category: 'validation' }); } const testId = randomUUID(); const now = Date.now(); // Create test case const result = await context.db.run(`INSERT INTO development_test_cases (id, feature_id, project_id, name, description, type, status, expected_behavior, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ testId, input.featureId, context.projectId || 'default', input.name, input.description || '', input.type || 'unit', 'pending', input.expectedBehavior, now, now ]); if (!result.success) { return createErrorResult({ code: 'DATABASE_ERROR', message: 'Failed to create test case', details: { error: result.error }, category: 'system' }); } // Update feature status if needed if (feature.status === 'planning') { await context.db.run('UPDATE development_features SET status = ?, updated_at = ? WHERE id = ?', ['testing', now, input.featureId]); } // Get total test count const testCountResult = await context.db.get('SELECT COUNT(*) as count FROM development_test_cases WHERE feature_id = ?', [input.featureId]); return createSuccessResult({ test: { id: testId, name: input.name, type: input.type || 'unit', status: 'pending', expectedBehavior: input.expectedBehavior, createdAt: new Date(now).toISOString() }, feature: { id: feature.id, name: feature.name, status: feature.status === 'planning' ? 'testing' : feature.status, totalTests: testCountResult.data?.count || 1 }, message: `Test case "${input.name}" created successfully`, tddPhase: 'Red - Write failing tests', nextSteps: [ 'Run the test to ensure it fails', 'Write the minimum code to make it pass', 'Refactor while keeping tests green' ] }); } catch (error) { return createErrorResult({ code: 'EXECUTION_ERROR', message: `Failed to write test: ${error instanceof Error ? error.message : 'Unknown error'}`, category: 'execution' }); } } }); /** * Start a TDD session for a feature */ const startTDDSessionTool = createTool({ name: 'start_tdd_session', description: 'Start a Test-Driven Development session for a feature', category: 'development', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'Feature ID to start TDD session for', pattern: '^[a-zA-Z0-9-]+$' } }, required: ['featureId'], additionalProperties: false }, async execute(input, context) { try { // Verify feature exists and get its tests const featureResult = await context.db.get('SELECT * FROM development_features WHERE id = ? AND project_id = ?', [input.featureId, context.projectId || 'default']); if (!featureResult.success || !featureResult.data) { return createErrorResult({ code: 'RESOURCE_NOT_FOUND', message: 'Feature not found', category: 'validation' }); } const feature = featureResult.data; // Check for existing active session const existingSession = await context.db.get('SELECT * FROM development_tdd_sessions WHERE feature_id = ? AND completed_at IS NULL', [input.featureId]); if (existingSession.success && existingSession.data) { return createErrorResult({ code: 'INVALID_STATE', message: 'A TDD session is already active for this feature', details: { sessionId: existingSession.data.id }, category: 'validation' }); } // Get test statistics const testStatsResult = await context.db.get(`SELECT COUNT(*) as total, COUNT(CASE WHEN status = 'passing' THEN 1 END) as passing, COUNT(CASE WHEN status = 'failing' THEN 1 END) as failing, COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending FROM development_test_cases WHERE feature_id = ?`, [input.featureId]); const testStats = testStatsResult.data || { total: 0, passing: 0, failing: 0, pending: 0 }; // Check TDD config const configResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); const config = configResult.data || {}; // Enforce test requirements if enabled if (config.require_tests_before_implementation && testStats.total === 0) { return createErrorResult({ code: 'TDD_VIOLATION', message: 'Cannot start TDD session without any test cases', details: { requireTestsBeforeImplementation: true, totalTests: 0 }, category: 'validation' }); } const sessionId = randomUUID(); const now = Date.now(); // Determine initial phase based on test status let initialPhase = 'red'; if (testStats.total > 0 && testStats.failing === 0 && testStats.pending === 0) { initialPhase = 'refactor'; // All tests passing } else if (testStats.passing > 0 && (testStats.failing > 0 || testStats.pending > 0)) { initialPhase = 'green'; // Some tests need work } // Create TDD session const result = await context.db.run(`INSERT INTO development_tdd_sessions (id, feature_id, project_id, current_phase, started_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, [ sessionId, input.featureId, context.projectId || 'default', initialPhase, now, now, now ]); if (!result.success) { return createErrorResult({ code: 'DATABASE_ERROR', message: 'Failed to create TDD session', details: { error: result.error }, category: 'system' }); } // Create initial phase history entry await context.db.run(`INSERT INTO development_tdd_phase_history (id, session_id, project_id, phase, started_at) VALUES (?, ?, ?, ?, ?)`, [ randomUUID(), sessionId, context.projectId || 'default', initialPhase, now ]); // Update feature status await context.db.run('UPDATE development_features SET status = ?, updated_at = ? WHERE id = ?', [initialPhase === 'red' ? 'testing' : 'implementing', now, input.featureId]); const phaseGuidance = { red: 'Write failing tests that define the desired behavior', green: 'Write the minimum code necessary to make tests pass', refactor: 'Improve code quality while keeping all tests passing' }; return createSuccessResult({ session: { id: sessionId, featureId: input.featureId, currentPhase: initialPhase, startedAt: new Date(now).toISOString() }, feature: { id: feature.id, name: feature.name, status: initialPhase === 'red' ? 'testing' : 'implementing' }, testStatistics: testStats, message: `TDD session started in ${initialPhase.toUpperCase()} phase`, phaseGuidance: phaseGuidance[initialPhase], nextSteps: initialPhase === 'red' ? ['Write failing tests for the feature', 'Run tests to confirm they fail', 'Move to GREEN phase'] : initialPhase === 'green' ? ['Implement code to make tests pass', 'Run tests frequently', 'Use minimal implementation'] : ['Refactor for clarity and maintainability', 'Keep running tests', 'Look for code smells'] }); } catch (error) { return createErrorResult({ code: 'EXECUTION_ERROR', message: `Failed to start TDD session: ${error instanceof Error ? error.message : 'Unknown error'}`, category: 'execution' }); } } }); /** * Run tests for a feature */ const runTestsTool = createTool({ name: 'run_tests', description: 'Record test execution results for a feature', category: 'development', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'Feature ID', pattern: '^[a-zA-Z0-9-]+$' }, testResults: { type: 'array', items: { type: 'object', properties: { testId: { type: 'string', description: 'Test case ID', pattern: '^[a-zA-Z0-9-]+$' }, status: { type: 'string', enum: ['passing', 'failing', 'skipped'], description: 'Test execution status' }, actualBehavior: { type: 'string', description: 'Actual behavior observed', maxLength: 1000 }, errorMessage: { type: 'string', description: 'Error message if test failed', maxLength: 2000 } }, required: ['testId', 'status'], additionalProperties: false }, description: 'Test execution results', minItems: 1, maxItems: 100 } }, required: ['featureId', 'testResults'], additionalProperties: false }, async execute(input, context) { try { // Verify feature exists const featureResult = await context.db.get('SELECT * FROM development_features WHERE id = ? AND project_id = ?', [input.featureId, context.projectId || 'default']); if (!featureResult.success || !featureResult.data) { return createErrorResult({ code: 'RESOURCE_NOT_FOUND', message: 'Feature not found', category: 'validation' }); } const feature = featureResult.data; const now = Date.now(); // Update test results let passing = 0; let failing = 0; let skipped = 0; for (const result of input.testResults) { // Verify test belongs to feature const testCheck = await context.db.get('SELECT id FROM development_test_cases WHERE id = ? AND feature_id = ?', [result.testId, input.featureId]); if (!testCheck.success || !testCheck.data) { continue; // Skip invalid test IDs } await context.db.run(`UPDATE development_test_cases SET status = ?, actual_behavior = ?, error_message = ?, updated_at = ? WHERE id = ?`, [ result.status, result.actualBehavior || null, result.errorMessage || null, now, result.testId ]); if (result.status === 'passing') passing++; else if (result.status === 'failing') failing++; else if (result.status === 'skipped') skipped++; } // Calculate test coverage const totalTestsResult = await context.db.get('SELECT COUNT(*) as total FROM development_test_cases WHERE feature_id = ?', [input.featureId]); const totalTests = totalTestsResult.data?.total || 0; const testCoverage = totalTests > 0 ? (passing / totalTests) * 100 : 0; // Update feature test coverage await context.db.run('UPDATE development_features SET test_coverage = ?, updated_at = ? WHERE id = ?', [testCoverage, now, input.featureId]); // Check for active TDD session const sessionResult = await context.db.get('SELECT * FROM development_tdd_sessions WHERE feature_id = ? AND completed_at IS NULL', [input.featureId]); let phaseTransition = null; if (sessionResult.success && sessionResult.data) { const session = sessionResult.data; const currentPhase = session.current_phase; let newPhase = currentPhase; // Determine phase transition based on test results if (currentPhase === 'red' && failing === 0 && passing > 0) { // All tests passing, move to refactor newPhase = 'refactor'; } else if (currentPhase === 'green' && failing > 0) { // Tests failing during implementation, back to red newPhase = 'red'; } else if (currentPhase === 'refactor' && failing > 0) { // Tests broken during refactoring, back to green newPhase = 'green'; } if (newPhase !== currentPhase) { // Update session phase await context.db.run('UPDATE development_tdd_sessions SET current_phase = ?, updated_at = ? WHERE id = ?', [newPhase, now, session.id]); // Complete previous phase in history await context.db.run('UPDATE development_tdd_phase_history SET completed_at = ? WHERE session_id = ? AND completed_at IS NULL', [now, session.id]); // Start new phase in history await context.db.run(`INSERT INTO development_tdd_phase_history (id, session_id, project_id, phase, started_at) VALUES (?, ?, ?, ?, ?)`, [randomUUID(), session.id, context.projectId || 'default', newPhase, now]); phaseTransition = { from: currentPhase, to: newPhase }; } } // Check TDD config for coverage requirements const configResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); const config = configResult.data || {}; const meetsMinimumCoverage = testCoverage >= (config.minimum_test_coverage || 80); return createSuccessResult({ testResults: { total: totalTests, passing, failing, skipped, coverage: Math.round(testCoverage * 100) / 100 }, feature: { id: feature.id, name: feature.name, testCoverage: Math.round(testCoverage * 100) / 100 }, phaseTransition, meetsMinimumCoverage, message: failing > 0 ? `${failing} test(s) failing - fix them to proceed` : `All tests passing! Coverage: ${Math.round(testCoverage)}%`, nextSteps: failing > 0 ? ['Fix failing tests', 'Review error messages', 'Update implementation'] : phaseTransition?.to === 'refactor' ? ['Refactor code for clarity', 'Eliminate duplication', 'Improve design'] : ['Continue with implementation', 'Add more test cases', 'Check edge cases'] }); } catch (error) { return createErrorResult({ code: 'EXECUTION_ERROR', message: `Failed to run tests: ${error instanceof Error ? error.message : 'Unknown error'}`, category: 'execution' }); } } }); /** * Check TDD status */ const checkTDDStatusTool = createTool({ name: 'check_tdd_status', description: 'Check the status of TDD sessions and features', category: 'development', readOnly: true, inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'Feature ID to check status for', pattern: '^[a-zA-Z0-9-]+$' }, sessionId: { type: 'string', description: 'Session ID to check status for', pattern: '^[a-zA-Z0-9-]+$' } }, required: [], additionalProperties: false }, async execute(input, context) { try { if (input.sessionId) { // Get specific session status const sessionResult = await context.db.get(`SELECT s.*, f.name as feature_name, f.test_coverage FROM development_tdd_sessions s JOIN development_features f ON s.feature_id = f.id WHERE s.id = ? AND s.project_id = ?`, [input.sessionId, context.projectId || 'default']); if (!sessionResult.success || !sessionResult.data) { return createErrorResult({ code: 'RESOURCE_NOT_FOUND', message: 'Session not found', category: 'validation' }); } const session = sessionResult.data; // Get phase history const historyResult = await context.db.query('SELECT * FROM development_tdd_phase_history WHERE session_id = ? ORDER BY started_at', [input.sessionId]); const phaseHistory = (historyResult.data || []).map((ph) => ({ phase: ph.phase, startedAt: new Date(ph.started_at).toISOString(), completedAt: ph.completed_at ? new Date(ph.completed_at).toISOString() : null, duration: ph.completed_at ? ph.completed_at - ph.started_at : null })); return createSuccessResult({ session: { id: session.id, featureName: session.feature_name, currentPhase: session.current_phase, startedAt: new Date(session.started_at).toISOString(), completedAt: session.completed_at ? new Date(session.completed_at).toISOString() : null, testCoverage: session.test_coverage, phaseHistory } }); } else if (input.featureId) { // Get feature status with tests const featureResult = await context.db.get('SELECT * FROM development_features WHERE id = ? AND project_id = ?', [input.featureId, context.projectId || 'default']); if (!featureResult.success || !featureResult.data) { return createErrorResult({ code: 'RESOURCE_NOT_FOUND', message: 'Feature not found', category: 'validation' }); } const feature = featureResult.data; // Get test statistics const testStatsResult = await context.db.get(`SELECT COUNT(*) as total, COUNT(CASE WHEN status = 'passing' THEN 1 END) as passing, COUNT(CASE WHEN status = 'failing' THEN 1 END) as failing, COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending FROM development_test_cases WHERE feature_id = ?`, [input.featureId]); // Get active session if any const sessionResult = await context.db.get('SELECT * FROM development_tdd_sessions WHERE feature_id = ? AND completed_at IS NULL', [input.featureId]); return createSuccessResult({ feature: { id: feature.id, name: feature.name, status: feature.status, testCoverage: feature.test_coverage, createdAt: new Date(feature.created_at).toISOString() }, tests: testStatsResult.data || { total: 0, passing: 0, failing: 0, pending: 0 }, activeSession: sessionResult.data ? { id: sessionResult.data.id, currentPhase: sessionResult.data.current_phase, startedAt: new Date(sessionResult.data.started_at).toISOString() } : null }); } else { // Get overall TDD status const featuresResult = await context.db.query('SELECT * FROM development_features WHERE project_id = ? ORDER BY created_at DESC LIMIT 10', [context.projectId || 'default']); const activeSessions = await context.db.query(`SELECT s.*, f.name as feature_name FROM development_tdd_sessions s JOIN development_features f ON s.feature_id = f.id WHERE s.project_id = ? AND s.completed_at IS NULL`, [context.projectId || 'default']); const configResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); return createSuccessResult({ recentFeatures: (featuresResult.data || []).map((f) => ({ id: f.id, name: f.name, status: f.status, testCoverage: f.test_coverage })), activeSessions: (activeSessions.data || []).map((s) => ({ id: s.id, featureName: s.feature_name, currentPhase: s.current_phase, startedAt: new Date(s.started_at).toISOString() })), tddConfig: configResult.data || { enforceTestFirst: true, minimumTestCoverage: 80, requireTestsBeforeImplementation: true, autoGenerateTestTemplates: true } }); } } catch (error) { return createErrorResult({ code: 'EXECUTION_ERROR', message: `Failed to check TDD status: ${error instanceof Error ? error.message : 'Unknown error'}`, category: 'execution' }); } } }); /** * Show or update TDD enforcement configuration */ const enforceTDDTool = createTool({ name: 'enforce_tdd', description: 'Show or update Test-Driven Development enforcement configuration', category: 'development', inputSchema: { type: 'object', properties: { enforceTestFirst: { type: 'boolean', description: 'Enforce writing tests before implementation' }, minimumTestCoverage: { type: 'number', description: 'Minimum required test coverage percentage', minimum: 0, maximum: 100 }, requireTestsBeforeImplementation: { type: 'boolean', description: 'Require at least one test before allowing implementation' }, autoGenerateTestTemplates: { type: 'boolean', description: 'Automatically generate test templates for new features' } }, required: [], additionalProperties: false }, async execute(input, context) { try { // Ensure config exists await ensureTDDConfig(context); // If no input provided, just return current config if (Object.keys(input).length === 0) { const configResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); const config = configResult.data || {}; return createSuccessResult({ config: { enforceTestFirst: config.enforce_test_first, minimumTestCoverage: config.minimum_test_coverage, requireTestsBeforeImplementation: config.require_tests_before_implementation, autoGenerateTestTemplates: config.auto_generate_test_templates }, message: 'Current TDD enforcement configuration', description: { enforceTestFirst: 'Prevents adding tests during implementation phase', minimumTestCoverage: 'Required percentage of passing tests', requireTestsBeforeImplementation: 'Blocks implementation without tests', autoGenerateTestTemplates: 'Creates test stubs for new features' } }); } // Update configuration const updates = []; const values = []; if (input.enforceTestFirst !== undefined) { updates.push('enforce_test_first = ?'); values.push(input.enforceTestFirst); } if (input.minimumTestCoverage !== undefined) { updates.push('minimum_test_coverage = ?'); values.push(input.minimumTestCoverage); } if (input.requireTestsBeforeImplementation !== undefined) { updates.push('require_tests_before_implementation = ?'); values.push(input.requireTestsBeforeImplementation); } if (input.autoGenerateTestTemplates !== undefined) { updates.push('auto_generate_test_templates = ?'); values.push(input.autoGenerateTestTemplates); } if (updates.length > 0) { updates.push('updated_at = ?'); values.push(Date.now()); values.push(context.projectId || 'default'); await context.db.run(`UPDATE development_tdd_config SET ${updates.join(', ')} WHERE project_id = ?`, values); } // Get updated config const updatedResult = await context.db.get('SELECT * FROM development_tdd_config WHERE project_id = ?', [context.projectId || 'default']); const updatedConfig = updatedResult.data || {}; return createSuccessResult({ config: { enforceTestFirst: updatedConfig.enforce_test_first, minimumTestCoverage: updatedConfig.minimum_test_coverage, requireTestsBeforeImplementation: updatedConfig.require_tests_before_implementation, autoGenerateTestTemplates: updatedConfig.auto_generate_test_templates }, message: 'TDD configuration updated successfully', changes: updates.length > 0 ? updates.map(u => u.split(' = ')[0]) : ['No changes'] }); } catch (error) { return createErrorResult({ code: 'EXECUTION_ERROR', message: `Failed to update TDD config: ${error instanceof Error ? error.message : 'Unknown error'}`, category: 'execution' }); } } }); /** * Setup development tools */ export async function setupDevelopmentTools() { return { module: 'development', tools: [ createFeatureTool, writeTestTool, startTDDSessionTool, runTestsTool, checkTDDStatusTool, enforceTDDTool ] }; } //# sourceMappingURL=tools.js.map