@codebakers/cli 3.3.15 → 3.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp/server.js +137 -0
- package/package.json +1 -1
- package/src/mcp/server.ts +144 -0
package/dist/mcp/server.js
CHANGED
|
@@ -796,6 +796,25 @@ class CodeBakersServer {
|
|
|
796
796
|
},
|
|
797
797
|
},
|
|
798
798
|
},
|
|
799
|
+
{
|
|
800
|
+
name: 'validate_complete',
|
|
801
|
+
description: 'MANDATORY: Call this BEFORE saying "done" or "complete" on any feature. Validates that tests exist, tests pass, and TypeScript compiles. Returns { valid: true } or { valid: false, missing: [...] }. You are NOT ALLOWED to complete a feature without calling this first.',
|
|
802
|
+
inputSchema: {
|
|
803
|
+
type: 'object',
|
|
804
|
+
properties: {
|
|
805
|
+
feature: {
|
|
806
|
+
type: 'string',
|
|
807
|
+
description: 'Name of the feature being completed (e.g., "login page", "payment form")',
|
|
808
|
+
},
|
|
809
|
+
files: {
|
|
810
|
+
type: 'array',
|
|
811
|
+
items: { type: 'string' },
|
|
812
|
+
description: 'Files that were created/modified for this feature',
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
required: ['feature'],
|
|
816
|
+
},
|
|
817
|
+
},
|
|
799
818
|
{
|
|
800
819
|
name: 'report_pattern_gap',
|
|
801
820
|
description: 'Report when a user request cannot be fully handled by existing patterns. This helps improve CodeBakers by tracking what patterns are missing. The AI should automatically call this when it encounters something outside pattern coverage.',
|
|
@@ -1456,6 +1475,8 @@ class CodeBakersServer {
|
|
|
1456
1475
|
return this.handleProjectStatus();
|
|
1457
1476
|
case 'run_tests':
|
|
1458
1477
|
return this.handleRunTests(args);
|
|
1478
|
+
case 'validate_complete':
|
|
1479
|
+
return this.handleValidateComplete(args);
|
|
1459
1480
|
case 'report_pattern_gap':
|
|
1460
1481
|
return this.handleReportPatternGap(args);
|
|
1461
1482
|
case 'track_analytics':
|
|
@@ -3336,6 +3357,122 @@ Just describe what you want to build! I'll automatically:
|
|
|
3336
3357
|
}],
|
|
3337
3358
|
};
|
|
3338
3359
|
}
|
|
3360
|
+
/**
|
|
3361
|
+
* MANDATORY: Validate that a feature is complete before AI can say "done"
|
|
3362
|
+
* Checks: tests exist, tests pass, TypeScript compiles
|
|
3363
|
+
*/
|
|
3364
|
+
handleValidateComplete(args) {
|
|
3365
|
+
const { feature, files = [] } = args;
|
|
3366
|
+
const cwd = process.cwd();
|
|
3367
|
+
const issues = [];
|
|
3368
|
+
let testsExist = false;
|
|
3369
|
+
let testsPass = false;
|
|
3370
|
+
let typescriptPass = false;
|
|
3371
|
+
// Step 1: Check if test files exist
|
|
3372
|
+
try {
|
|
3373
|
+
const testDirs = ['tests', 'test', '__tests__', 'src/__tests__', 'src/tests'];
|
|
3374
|
+
const testExtensions = ['.test.ts', '.test.tsx', '.spec.ts', '.spec.tsx'];
|
|
3375
|
+
for (const dir of testDirs) {
|
|
3376
|
+
const testDir = path.join(cwd, dir);
|
|
3377
|
+
if (fs.existsSync(testDir)) {
|
|
3378
|
+
const testFiles = fs.readdirSync(testDir, { recursive: true })
|
|
3379
|
+
.filter((f) => testExtensions.some(ext => String(f).endsWith(ext)));
|
|
3380
|
+
if (testFiles.length > 0) {
|
|
3381
|
+
testsExist = true;
|
|
3382
|
+
break;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
// Also check for test files adjacent to source files
|
|
3387
|
+
if (!testsExist && files.length > 0) {
|
|
3388
|
+
for (const file of files) {
|
|
3389
|
+
const testFile = file.replace(/\.tsx?$/, '.test.ts');
|
|
3390
|
+
const specFile = file.replace(/\.tsx?$/, '.spec.ts');
|
|
3391
|
+
if (fs.existsSync(path.join(cwd, testFile)) || fs.existsSync(path.join(cwd, specFile))) {
|
|
3392
|
+
testsExist = true;
|
|
3393
|
+
break;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
if (!testsExist) {
|
|
3398
|
+
issues.push('NO_TESTS: No test files found. You MUST write tests before completing this feature.');
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
catch {
|
|
3402
|
+
issues.push('NO_TESTS: Could not verify test files exist.');
|
|
3403
|
+
}
|
|
3404
|
+
// Step 2: Run tests
|
|
3405
|
+
if (testsExist) {
|
|
3406
|
+
try {
|
|
3407
|
+
let testCommand = 'npm test';
|
|
3408
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
3409
|
+
if (fs.existsSync(pkgPath)) {
|
|
3410
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
3411
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3412
|
+
if (deps['@playwright/test'])
|
|
3413
|
+
testCommand = 'npx playwright test';
|
|
3414
|
+
else if (deps['vitest'])
|
|
3415
|
+
testCommand = 'npx vitest run';
|
|
3416
|
+
else if (deps['jest'])
|
|
3417
|
+
testCommand = 'npx jest';
|
|
3418
|
+
}
|
|
3419
|
+
(0, child_process_1.execSync)(testCommand, {
|
|
3420
|
+
cwd,
|
|
3421
|
+
encoding: 'utf-8',
|
|
3422
|
+
timeout: 120000,
|
|
3423
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3424
|
+
});
|
|
3425
|
+
testsPass = true;
|
|
3426
|
+
}
|
|
3427
|
+
catch (error) {
|
|
3428
|
+
const execError = error;
|
|
3429
|
+
issues.push(`TESTS_FAIL: Tests are failing. Fix them before completing.\n${execError.stderr?.slice(0, 500) || ''}`);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
// Step 3: Run TypeScript check
|
|
3433
|
+
try {
|
|
3434
|
+
(0, child_process_1.execSync)('npx tsc --noEmit', {
|
|
3435
|
+
cwd,
|
|
3436
|
+
encoding: 'utf-8',
|
|
3437
|
+
timeout: 60000,
|
|
3438
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3439
|
+
});
|
|
3440
|
+
typescriptPass = true;
|
|
3441
|
+
}
|
|
3442
|
+
catch (error) {
|
|
3443
|
+
const execError = error;
|
|
3444
|
+
const output = execError.stdout || execError.stderr || '';
|
|
3445
|
+
issues.push(`TYPESCRIPT_ERRORS: TypeScript compilation failed.\n${output.slice(0, 500)}`);
|
|
3446
|
+
}
|
|
3447
|
+
// Generate response
|
|
3448
|
+
const valid = testsExist && testsPass && typescriptPass;
|
|
3449
|
+
let response = `# ✅ Feature Validation: ${feature}\n\n`;
|
|
3450
|
+
response += `| Check | Status |\n|-------|--------|\n`;
|
|
3451
|
+
response += `| Tests exist | ${testsExist ? '✅ PASS' : '❌ FAIL'} |\n`;
|
|
3452
|
+
response += `| Tests pass | ${testsPass ? '✅ PASS' : testsExist ? '❌ FAIL' : '⏭️ SKIP'} |\n`;
|
|
3453
|
+
response += `| TypeScript compiles | ${typescriptPass ? '✅ PASS' : '❌ FAIL'} |\n\n`;
|
|
3454
|
+
if (valid) {
|
|
3455
|
+
response += `## ✅ Feature is COMPLETE\n\n`;
|
|
3456
|
+
response += `All validation checks passed. You may now mark this feature as done.\n`;
|
|
3457
|
+
}
|
|
3458
|
+
else {
|
|
3459
|
+
response += `## ❌ Feature is NOT COMPLETE\n\n`;
|
|
3460
|
+
response += `**You are NOT ALLOWED to say "done" or "complete" until all checks pass.**\n\n`;
|
|
3461
|
+
response += `### Issues to fix:\n\n`;
|
|
3462
|
+
for (const issue of issues) {
|
|
3463
|
+
response += `- ${issue}\n\n`;
|
|
3464
|
+
}
|
|
3465
|
+
response += `---\n\n**Fix these issues and call \`validate_complete\` again.**`;
|
|
3466
|
+
}
|
|
3467
|
+
return {
|
|
3468
|
+
content: [{
|
|
3469
|
+
type: 'text',
|
|
3470
|
+
text: response,
|
|
3471
|
+
}],
|
|
3472
|
+
// Return structured data for programmatic use
|
|
3473
|
+
isError: !valid,
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3339
3476
|
async handleReportPatternGap(args) {
|
|
3340
3477
|
const { category, request, context, handledWith, wasSuccessful = true } = args;
|
|
3341
3478
|
try {
|
package/package.json
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -876,6 +876,26 @@ class CodeBakersServer {
|
|
|
876
876
|
},
|
|
877
877
|
},
|
|
878
878
|
},
|
|
879
|
+
{
|
|
880
|
+
name: 'validate_complete',
|
|
881
|
+
description:
|
|
882
|
+
'MANDATORY: Call this BEFORE saying "done" or "complete" on any feature. Validates that tests exist, tests pass, and TypeScript compiles. Returns { valid: true } or { valid: false, missing: [...] }. You are NOT ALLOWED to complete a feature without calling this first.',
|
|
883
|
+
inputSchema: {
|
|
884
|
+
type: 'object' as const,
|
|
885
|
+
properties: {
|
|
886
|
+
feature: {
|
|
887
|
+
type: 'string',
|
|
888
|
+
description: 'Name of the feature being completed (e.g., "login page", "payment form")',
|
|
889
|
+
},
|
|
890
|
+
files: {
|
|
891
|
+
type: 'array',
|
|
892
|
+
items: { type: 'string' },
|
|
893
|
+
description: 'Files that were created/modified for this feature',
|
|
894
|
+
},
|
|
895
|
+
},
|
|
896
|
+
required: ['feature'],
|
|
897
|
+
},
|
|
898
|
+
},
|
|
879
899
|
{
|
|
880
900
|
name: 'report_pattern_gap',
|
|
881
901
|
description:
|
|
@@ -1596,6 +1616,9 @@ class CodeBakersServer {
|
|
|
1596
1616
|
case 'run_tests':
|
|
1597
1617
|
return this.handleRunTests(args as { filter?: string; watch?: boolean });
|
|
1598
1618
|
|
|
1619
|
+
case 'validate_complete':
|
|
1620
|
+
return this.handleValidateComplete(args as { feature: string; files?: string[] });
|
|
1621
|
+
|
|
1599
1622
|
case 'report_pattern_gap':
|
|
1600
1623
|
return this.handleReportPatternGap(args as { category: string; request: string; context?: string; handledWith?: string; wasSuccessful?: boolean });
|
|
1601
1624
|
|
|
@@ -3771,6 +3794,127 @@ Just describe what you want to build! I'll automatically:
|
|
|
3771
3794
|
};
|
|
3772
3795
|
}
|
|
3773
3796
|
|
|
3797
|
+
/**
|
|
3798
|
+
* MANDATORY: Validate that a feature is complete before AI can say "done"
|
|
3799
|
+
* Checks: tests exist, tests pass, TypeScript compiles
|
|
3800
|
+
*/
|
|
3801
|
+
private handleValidateComplete(args: { feature: string; files?: string[] }) {
|
|
3802
|
+
const { feature, files = [] } = args;
|
|
3803
|
+
const cwd = process.cwd();
|
|
3804
|
+
const issues: string[] = [];
|
|
3805
|
+
let testsExist = false;
|
|
3806
|
+
let testsPass = false;
|
|
3807
|
+
let typescriptPass = false;
|
|
3808
|
+
|
|
3809
|
+
// Step 1: Check if test files exist
|
|
3810
|
+
try {
|
|
3811
|
+
const testDirs = ['tests', 'test', '__tests__', 'src/__tests__', 'src/tests'];
|
|
3812
|
+
const testExtensions = ['.test.ts', '.test.tsx', '.spec.ts', '.spec.tsx'];
|
|
3813
|
+
|
|
3814
|
+
for (const dir of testDirs) {
|
|
3815
|
+
const testDir = path.join(cwd, dir);
|
|
3816
|
+
if (fs.existsSync(testDir)) {
|
|
3817
|
+
const testFiles = fs.readdirSync(testDir, { recursive: true })
|
|
3818
|
+
.filter((f: string | Buffer) => testExtensions.some(ext => String(f).endsWith(ext)));
|
|
3819
|
+
if (testFiles.length > 0) {
|
|
3820
|
+
testsExist = true;
|
|
3821
|
+
break;
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
// Also check for test files adjacent to source files
|
|
3827
|
+
if (!testsExist && files.length > 0) {
|
|
3828
|
+
for (const file of files) {
|
|
3829
|
+
const testFile = file.replace(/\.tsx?$/, '.test.ts');
|
|
3830
|
+
const specFile = file.replace(/\.tsx?$/, '.spec.ts');
|
|
3831
|
+
if (fs.existsSync(path.join(cwd, testFile)) || fs.existsSync(path.join(cwd, specFile))) {
|
|
3832
|
+
testsExist = true;
|
|
3833
|
+
break;
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3838
|
+
if (!testsExist) {
|
|
3839
|
+
issues.push('NO_TESTS: No test files found. You MUST write tests before completing this feature.');
|
|
3840
|
+
}
|
|
3841
|
+
} catch {
|
|
3842
|
+
issues.push('NO_TESTS: Could not verify test files exist.');
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
// Step 2: Run tests
|
|
3846
|
+
if (testsExist) {
|
|
3847
|
+
try {
|
|
3848
|
+
let testCommand = 'npm test';
|
|
3849
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
3850
|
+
if (fs.existsSync(pkgPath)) {
|
|
3851
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
3852
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3853
|
+
if (deps['@playwright/test']) testCommand = 'npx playwright test';
|
|
3854
|
+
else if (deps['vitest']) testCommand = 'npx vitest run';
|
|
3855
|
+
else if (deps['jest']) testCommand = 'npx jest';
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3858
|
+
execSync(testCommand, {
|
|
3859
|
+
cwd,
|
|
3860
|
+
encoding: 'utf-8',
|
|
3861
|
+
timeout: 120000,
|
|
3862
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3863
|
+
});
|
|
3864
|
+
testsPass = true;
|
|
3865
|
+
} catch (error) {
|
|
3866
|
+
const execError = error as { stderr?: string };
|
|
3867
|
+
issues.push(`TESTS_FAIL: Tests are failing. Fix them before completing.\n${execError.stderr?.slice(0, 500) || ''}`);
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
// Step 3: Run TypeScript check
|
|
3872
|
+
try {
|
|
3873
|
+
execSync('npx tsc --noEmit', {
|
|
3874
|
+
cwd,
|
|
3875
|
+
encoding: 'utf-8',
|
|
3876
|
+
timeout: 60000,
|
|
3877
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3878
|
+
});
|
|
3879
|
+
typescriptPass = true;
|
|
3880
|
+
} catch (error) {
|
|
3881
|
+
const execError = error as { stdout?: string; stderr?: string };
|
|
3882
|
+
const output = execError.stdout || execError.stderr || '';
|
|
3883
|
+
issues.push(`TYPESCRIPT_ERRORS: TypeScript compilation failed.\n${output.slice(0, 500)}`);
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3886
|
+
// Generate response
|
|
3887
|
+
const valid = testsExist && testsPass && typescriptPass;
|
|
3888
|
+
|
|
3889
|
+
let response = `# ✅ Feature Validation: ${feature}\n\n`;
|
|
3890
|
+
response += `| Check | Status |\n|-------|--------|\n`;
|
|
3891
|
+
response += `| Tests exist | ${testsExist ? '✅ PASS' : '❌ FAIL'} |\n`;
|
|
3892
|
+
response += `| Tests pass | ${testsPass ? '✅ PASS' : testsExist ? '❌ FAIL' : '⏭️ SKIP'} |\n`;
|
|
3893
|
+
response += `| TypeScript compiles | ${typescriptPass ? '✅ PASS' : '❌ FAIL'} |\n\n`;
|
|
3894
|
+
|
|
3895
|
+
if (valid) {
|
|
3896
|
+
response += `## ✅ Feature is COMPLETE\n\n`;
|
|
3897
|
+
response += `All validation checks passed. You may now mark this feature as done.\n`;
|
|
3898
|
+
} else {
|
|
3899
|
+
response += `## ❌ Feature is NOT COMPLETE\n\n`;
|
|
3900
|
+
response += `**You are NOT ALLOWED to say "done" or "complete" until all checks pass.**\n\n`;
|
|
3901
|
+
response += `### Issues to fix:\n\n`;
|
|
3902
|
+
for (const issue of issues) {
|
|
3903
|
+
response += `- ${issue}\n\n`;
|
|
3904
|
+
}
|
|
3905
|
+
response += `---\n\n**Fix these issues and call \`validate_complete\` again.**`;
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
return {
|
|
3909
|
+
content: [{
|
|
3910
|
+
type: 'text' as const,
|
|
3911
|
+
text: response,
|
|
3912
|
+
}],
|
|
3913
|
+
// Return structured data for programmatic use
|
|
3914
|
+
isError: !valid,
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3774
3918
|
private async handleReportPatternGap(args: { category: string; request: string; context?: string; handledWith?: string; wasSuccessful?: boolean }) {
|
|
3775
3919
|
const { category, request, context, handledWith, wasSuccessful = true } = args;
|
|
3776
3920
|
|