@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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.3.15",
3
+ "version": "3.3.17",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
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