@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
874 lines • 39.4 kB
JavaScript
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