@codebakers/cli 3.3.17 → 3.4.0

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.
@@ -60,6 +60,7 @@ class CodeBakersServer {
60
60
  pendingUpdate = null;
61
61
  lastUpdateCheck = 0;
62
62
  updateCheckInterval = 60 * 60 * 1000; // Check every hour
63
+ currentSessionToken = null; // v6.0: Server-side enforcement session
63
64
  constructor() {
64
65
  this.apiKey = (0, config_js_1.getApiKey)();
65
66
  this.apiUrl = (0, config_js_1.getApiUrl)();
@@ -815,6 +816,30 @@ class CodeBakersServer {
815
816
  required: ['feature'],
816
817
  },
817
818
  },
819
+ {
820
+ name: 'discover_patterns',
821
+ description: 'MANDATORY: Call this BEFORE writing or modifying ANY code. Searches codebase for similar implementations and identifies relevant patterns. Returns existing code patterns you MUST follow. You are NOT ALLOWED to write code without calling this first.',
822
+ inputSchema: {
823
+ type: 'object',
824
+ properties: {
825
+ task: {
826
+ type: 'string',
827
+ description: 'What you are about to do (e.g., "add signup form", "fix auth bug", "create payment endpoint")',
828
+ },
829
+ files: {
830
+ type: 'array',
831
+ items: { type: 'string' },
832
+ description: 'Files you plan to create or modify',
833
+ },
834
+ keywords: {
835
+ type: 'array',
836
+ items: { type: 'string' },
837
+ description: 'Keywords to search for in codebase (e.g., ["auth", "login", "user"])',
838
+ },
839
+ },
840
+ required: ['task'],
841
+ },
842
+ },
818
843
  {
819
844
  name: 'report_pattern_gap',
820
845
  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.',
@@ -1477,6 +1502,8 @@ class CodeBakersServer {
1477
1502
  return this.handleRunTests(args);
1478
1503
  case 'validate_complete':
1479
1504
  return this.handleValidateComplete(args);
1505
+ case 'discover_patterns':
1506
+ return this.handleDiscoverPatterns(args);
1480
1507
  case 'report_pattern_gap':
1481
1508
  return this.handleReportPatternGap(args);
1482
1509
  case 'track_analytics':
@@ -3358,17 +3385,31 @@ Just describe what you want to build! I'll automatically:
3358
3385
  };
3359
3386
  }
3360
3387
  /**
3361
- * MANDATORY: Validate that a feature is complete before AI can say "done"
3362
- * Checks: tests exist, tests pass, TypeScript compiles
3388
+ * MANDATORY: Validate that a feature is complete before AI can say "done" (v6.0 Server-Side)
3389
+ * Runs local checks (tests, TypeScript), then validates with server
3363
3390
  */
3364
- handleValidateComplete(args) {
3391
+ async handleValidateComplete(args) {
3365
3392
  const { feature, files = [] } = args;
3366
3393
  const cwd = process.cwd();
3367
- const issues = [];
3368
3394
  let testsExist = false;
3369
3395
  let testsPass = false;
3370
3396
  let typescriptPass = false;
3371
- // Step 1: Check if test files exist
3397
+ const testsWritten = [];
3398
+ // Step 1: Get session token (from memory or state file)
3399
+ let sessionToken = this.currentSessionToken;
3400
+ if (!sessionToken) {
3401
+ try {
3402
+ const stateFile = path.join(cwd, '.codebakers.json');
3403
+ if (fs.existsSync(stateFile)) {
3404
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
3405
+ sessionToken = state.currentSessionToken || null;
3406
+ }
3407
+ }
3408
+ catch {
3409
+ // Ignore errors
3410
+ }
3411
+ }
3412
+ // Step 2: Check if test files exist and find them
3372
3413
  try {
3373
3414
  const testDirs = ['tests', 'test', '__tests__', 'src/__tests__', 'src/tests'];
3374
3415
  const testExtensions = ['.test.ts', '.test.tsx', '.spec.ts', '.spec.tsx'];
@@ -3379,29 +3420,30 @@ Just describe what you want to build! I'll automatically:
3379
3420
  .filter((f) => testExtensions.some(ext => String(f).endsWith(ext)));
3380
3421
  if (testFiles.length > 0) {
3381
3422
  testsExist = true;
3382
- break;
3423
+ testsWritten.push(...testFiles.map(f => path.join(dir, String(f))));
3383
3424
  }
3384
3425
  }
3385
3426
  }
3386
3427
  // Also check for test files adjacent to source files
3387
- if (!testsExist && files.length > 0) {
3428
+ if (files.length > 0) {
3388
3429
  for (const file of files) {
3389
3430
  const testFile = file.replace(/\.tsx?$/, '.test.ts');
3390
3431
  const specFile = file.replace(/\.tsx?$/, '.spec.ts');
3391
- if (fs.existsSync(path.join(cwd, testFile)) || fs.existsSync(path.join(cwd, specFile))) {
3432
+ if (fs.existsSync(path.join(cwd, testFile))) {
3392
3433
  testsExist = true;
3393
- break;
3434
+ testsWritten.push(testFile);
3435
+ }
3436
+ if (fs.existsSync(path.join(cwd, specFile))) {
3437
+ testsExist = true;
3438
+ testsWritten.push(specFile);
3394
3439
  }
3395
3440
  }
3396
3441
  }
3397
- if (!testsExist) {
3398
- issues.push('NO_TESTS: No test files found. You MUST write tests before completing this feature.');
3399
- }
3400
3442
  }
3401
3443
  catch {
3402
- issues.push('NO_TESTS: Could not verify test files exist.');
3444
+ // Ignore errors
3403
3445
  }
3404
- // Step 2: Run tests
3446
+ // Step 3: Run tests locally
3405
3447
  if (testsExist) {
3406
3448
  try {
3407
3449
  let testCommand = 'npm test';
@@ -3424,12 +3466,11 @@ Just describe what you want to build! I'll automatically:
3424
3466
  });
3425
3467
  testsPass = true;
3426
3468
  }
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) || ''}`);
3469
+ catch {
3470
+ testsPass = false;
3430
3471
  }
3431
3472
  }
3432
- // Step 3: Run TypeScript check
3473
+ // Step 4: Run TypeScript check locally
3433
3474
  try {
3434
3475
  (0, child_process_1.execSync)('npx tsc --noEmit', {
3435
3476
  cwd,
@@ -3439,39 +3480,356 @@ Just describe what you want to build! I'll automatically:
3439
3480
  });
3440
3481
  typescriptPass = true;
3441
3482
  }
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`;
3483
+ catch {
3484
+ typescriptPass = false;
3485
+ }
3486
+ // Step 5: Call server API for validation
3487
+ if (sessionToken) {
3488
+ try {
3489
+ const response = await fetch(`${this.apiUrl}/api/patterns/validate`, {
3490
+ method: 'POST',
3491
+ headers: {
3492
+ 'Content-Type': 'application/json',
3493
+ ...this.getAuthHeaders(),
3494
+ },
3495
+ body: JSON.stringify({
3496
+ sessionToken,
3497
+ featureName: feature,
3498
+ featureDescription: `Feature implementation for: ${feature}`,
3499
+ filesModified: files,
3500
+ testsWritten,
3501
+ testsRun: testsExist,
3502
+ testsPassed: testsPass,
3503
+ typescriptPassed: typescriptPass,
3504
+ }),
3505
+ });
3506
+ const result = await response.json();
3507
+ // Save validation result for pre-commit hook
3508
+ try {
3509
+ const stateFile = path.join(cwd, '.codebakers.json');
3510
+ let state = {};
3511
+ if (fs.existsSync(stateFile)) {
3512
+ state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
3513
+ }
3514
+ // Save validation details for pre-commit hook
3515
+ state.lastValidation = {
3516
+ passed: result.passed,
3517
+ timestamp: new Date().toISOString(),
3518
+ feature,
3519
+ issues: result.issues || [],
3520
+ testsExist,
3521
+ testsPassed: testsPass,
3522
+ typescriptPassed: typescriptPass,
3523
+ };
3524
+ // Clear session token only if validation passed
3525
+ if (result.passed) {
3526
+ this.currentSessionToken = null;
3527
+ delete state.currentSessionToken;
3528
+ state.lastValidationAt = new Date().toISOString();
3529
+ state.lastValidationPassed = true;
3530
+ }
3531
+ fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
3532
+ }
3533
+ catch {
3534
+ // Ignore errors
3535
+ }
3536
+ // Generate response from server result
3537
+ let responseText = `# ✅ Feature Validation: ${feature}\n\n`;
3538
+ responseText += `## Server Validation Result\n\n`;
3539
+ responseText += `**Status:** ${result.passed ? '✅ PASSED' : '❌ FAILED'}\n\n`;
3540
+ if (result.issues && result.issues.length > 0) {
3541
+ responseText += `### Issues:\n\n`;
3542
+ for (const issue of result.issues) {
3543
+ const icon = issue.severity === 'error' ? '❌' : '⚠️';
3544
+ responseText += `${icon} **${issue.type}**: ${issue.message}\n\n`;
3545
+ }
3546
+ }
3547
+ responseText += `### Local Checks:\n\n`;
3548
+ responseText += `| Check | Status |\n|-------|--------|\n`;
3549
+ responseText += `| Tests exist | ${testsExist ? '✅ PASS' : '❌ FAIL'} |\n`;
3550
+ responseText += `| Tests pass | ${testsPass ? '✅ PASS' : testsExist ? '❌ FAIL' : '⏭️ SKIP'} |\n`;
3551
+ responseText += `| TypeScript compiles | ${typescriptPass ? '✅ PASS' : '❌ FAIL'} |\n\n`;
3552
+ if (result.passed) {
3553
+ responseText += `## ✅ Feature is COMPLETE\n\n`;
3554
+ responseText += `Server has recorded this completion. You may now mark this feature as done.\n`;
3555
+ }
3556
+ else {
3557
+ responseText += `## ❌ Feature is NOT COMPLETE\n\n`;
3558
+ responseText += `**${result.nextSteps || 'Fix the issues above and try again.'}**\n`;
3559
+ }
3560
+ return {
3561
+ content: [{
3562
+ type: 'text',
3563
+ text: responseText,
3564
+ }],
3565
+ isError: !result.passed,
3566
+ };
3567
+ }
3568
+ catch (error) {
3569
+ // Server unreachable - fall back to local validation
3570
+ const message = error instanceof Error ? error.message : 'Unknown error';
3571
+ const valid = testsExist && testsPass && typescriptPass;
3572
+ let responseText = `# ✅ Feature Validation: ${feature}\n\n`;
3573
+ responseText += `## ⚠️ OFFLINE MODE - Server Unreachable\n\n`;
3574
+ responseText += `Error: ${message}\n\n`;
3575
+ responseText += `### Local Checks Only:\n\n`;
3576
+ responseText += `| Check | Status |\n|-------|--------|\n`;
3577
+ responseText += `| Tests exist | ${testsExist ? '✅ PASS' : '❌ FAIL'} |\n`;
3578
+ responseText += `| Tests pass | ${testsPass ? '✅ PASS' : testsExist ? '❌ FAIL' : '⏭️ SKIP'} |\n`;
3579
+ responseText += `| TypeScript compiles | ${typescriptPass ? '✅ PASS' : '❌ FAIL'} |\n\n`;
3580
+ responseText += `**Note:** Server validation skipped due to connection error.\n`;
3581
+ return {
3582
+ content: [{
3583
+ type: 'text',
3584
+ text: responseText,
3585
+ }],
3586
+ isError: !valid,
3587
+ };
3588
+ }
3457
3589
  }
3458
3590
  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`;
3591
+ // No session token - cannot validate with server
3592
+ let responseText = `# Feature Validation: ${feature}\n\n`;
3593
+ responseText += `## NO SESSION TOKEN\n\n`;
3594
+ responseText += `You must call \`discover_patterns\` BEFORE writing code to get a session token.\n\n`;
3595
+ responseText += `### Local Checks (not sufficient for completion):\n\n`;
3596
+ responseText += `| Check | Status |\n|-------|--------|\n`;
3597
+ responseText += `| Tests exist | ${testsExist ? '✅ PASS' : '❌ FAIL'} |\n`;
3598
+ responseText += `| Tests pass | ${testsPass ? '✅ PASS' : testsExist ? '❌ FAIL' : '⏭️ SKIP'} |\n`;
3599
+ responseText += `| TypeScript compiles | ${typescriptPass ? '✅ PASS' : '❌ FAIL'} |\n\n`;
3600
+ responseText += `**You CANNOT complete this feature without a valid session.**\n`;
3601
+ responseText += `Call \`discover_patterns\` first, then implement the feature, then call \`validate_complete\` again.`;
3602
+ return {
3603
+ content: [{
3604
+ type: 'text',
3605
+ text: responseText,
3606
+ }],
3607
+ isError: true,
3608
+ };
3609
+ }
3610
+ }
3611
+ /**
3612
+ * discover_patterns - START gate for pattern compliance (v6.0 Server-Side)
3613
+ * MUST be called before writing any code
3614
+ * Calls server API to get patterns and creates enforcement session
3615
+ */
3616
+ async handleDiscoverPatterns(args) {
3617
+ const { task, files = [], keywords = [] } = args;
3618
+ const cwd = process.cwd();
3619
+ // Generate project hash for context
3620
+ let projectHash;
3621
+ let projectName;
3622
+ try {
3623
+ const pkgPath = path.join(cwd, 'package.json');
3624
+ if (fs.existsSync(pkgPath)) {
3625
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
3626
+ projectName = pkg.name || path.basename(cwd);
3627
+ }
3628
+ else {
3629
+ projectName = path.basename(cwd);
3464
3630
  }
3465
- response += `---\n\n**Fix these issues and call \`validate_complete\` again.**`;
3631
+ // Simple hash of project path
3632
+ projectHash = Buffer.from(cwd).toString('base64').slice(0, 32);
3466
3633
  }
3467
- return {
3468
- content: [{
3469
- type: 'text',
3470
- text: response,
3471
- }],
3472
- // Return structured data for programmatic use
3473
- isError: !valid,
3474
- };
3634
+ catch {
3635
+ projectName = path.basename(cwd);
3636
+ }
3637
+ try {
3638
+ // Call server API to discover patterns and create enforcement session
3639
+ const response = await fetch(`${this.apiUrl}/api/patterns/discover`, {
3640
+ method: 'POST',
3641
+ headers: {
3642
+ 'Content-Type': 'application/json',
3643
+ ...this.getAuthHeaders(),
3644
+ },
3645
+ body: JSON.stringify({
3646
+ task,
3647
+ files,
3648
+ keywords,
3649
+ projectHash,
3650
+ projectName,
3651
+ }),
3652
+ });
3653
+ if (!response.ok) {
3654
+ const error = await response.json().catch(() => ({}));
3655
+ throw new Error(error.error || 'Server returned an error');
3656
+ }
3657
+ const result = await response.json();
3658
+ // Store session token for validate_complete
3659
+ this.currentSessionToken = result.sessionToken;
3660
+ // Also store in local state file for persistence across restarts
3661
+ try {
3662
+ const stateFile = path.join(cwd, '.codebakers.json');
3663
+ let state = {};
3664
+ if (fs.existsSync(stateFile)) {
3665
+ state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
3666
+ }
3667
+ state.currentSessionToken = result.sessionToken;
3668
+ state.lastDiscoveryTask = task;
3669
+ state.lastDiscoveryAt = new Date().toISOString();
3670
+ // Session expires in 2 hours (server default)
3671
+ state.sessionExpiresAt = result.expiresAt || new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
3672
+ // Clear any previous validation (new session = new work)
3673
+ delete state.lastValidation;
3674
+ fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
3675
+ }
3676
+ catch {
3677
+ // Ignore state file errors
3678
+ }
3679
+ // Generate response with ALL instructions from server
3680
+ let responseText = `# 🔍 Pattern Discovery: ${task}\n\n`;
3681
+ responseText += `## ⛔ SERVER-ENFORCED SESSION ACTIVE\n\n`;
3682
+ responseText += `**Session Token:** \`${result.sessionToken}\`\n\n`;
3683
+ responseText += `---\n\n`;
3684
+ // Section 1: Patterns from server
3685
+ if (result.patterns && result.patterns.length > 0) {
3686
+ responseText += `## 📦 MANDATORY PATTERNS\n\n`;
3687
+ responseText += `You MUST follow these patterns in your code:\n\n`;
3688
+ for (const pattern of result.patterns) {
3689
+ responseText += `### ${pattern.name}\n\n`;
3690
+ responseText += `**Relevance:** ${pattern.relevance}\n\n`;
3691
+ responseText += `\`\`\`typescript\n${pattern.content || ''}\n\`\`\`\n\n`;
3692
+ }
3693
+ }
3694
+ // Section 2: Test Requirements (ALL from server, not local file)
3695
+ responseText += `---\n\n`;
3696
+ responseText += `## 🧪 MANDATORY: TESTS REQUIRED\n\n`;
3697
+ responseText += `**You MUST write and run tests. Validation will FAIL without them.**\n\n`;
3698
+ responseText += `### Test Frameworks\n\n`;
3699
+ responseText += `| Type | Framework | Command |\n`;
3700
+ responseText += `|------|-----------|--------|\n`;
3701
+ responseText += `| Unit tests | Vitest | \`npm run test\` or \`npx vitest run\` |\n`;
3702
+ responseText += `| E2E tests | Playwright | \`npx playwright test\` |\n\n`;
3703
+ responseText += `### Test File Locations\n\n`;
3704
+ responseText += `| Code Type | Test Location |\n`;
3705
+ responseText += `|-----------|---------------|\n`;
3706
+ responseText += `| API routes | \`tests/api/[route].test.ts\` |\n`;
3707
+ responseText += `| Components | \`[component].test.tsx\` |\n`;
3708
+ responseText += `| Services | \`tests/services/[service].test.ts\` |\n`;
3709
+ responseText += `| E2E flows | \`e2e/[feature].spec.ts\` |\n\n`;
3710
+ responseText += `### Minimum Test Template\n\n`;
3711
+ responseText += `\`\`\`typescript\n`;
3712
+ responseText += `// Vitest unit test\n`;
3713
+ responseText += `import { describe, it, expect } from 'vitest';\n\n`;
3714
+ responseText += `describe('FeatureName', () => {\n`;
3715
+ responseText += ` it('should handle happy path', () => {\n`;
3716
+ responseText += ` // Test success case\n`;
3717
+ responseText += ` });\n\n`;
3718
+ responseText += ` it('should handle errors', () => {\n`;
3719
+ responseText += ` // Test error case\n`;
3720
+ responseText += ` });\n`;
3721
+ responseText += `});\n`;
3722
+ responseText += `\`\`\`\n\n`;
3723
+ // Section 3: Workflow
3724
+ responseText += `---\n\n`;
3725
+ responseText += `## 📋 REQUIRED WORKFLOW\n\n`;
3726
+ responseText += `1. ✅ **Read patterns above** - they are MANDATORY\n`;
3727
+ responseText += `2. ✅ **Write feature code** following the patterns\n`;
3728
+ responseText += `3. ✅ **Write test file(s)** - include happy path + error cases\n`;
3729
+ responseText += `4. ✅ **Run tests**: \`npm run test\`\n`;
3730
+ responseText += `5. ✅ **Fix TypeScript errors**: \`npx tsc --noEmit\`\n`;
3731
+ responseText += `6. ✅ **Call \`validate_complete\`** before saying "done"\n\n`;
3732
+ // Section 4: Validation
3733
+ responseText += `---\n\n`;
3734
+ responseText += `## ✅ BEFORE SAYING "DONE"\n\n`;
3735
+ responseText += `You MUST call the \`validate_complete\` MCP tool:\n\n`;
3736
+ responseText += `\`\`\`\n`;
3737
+ responseText += `Tool: validate_complete\n`;
3738
+ responseText += `Args: { feature: "${task}", files: ["list of files you modified"] }\n`;
3739
+ responseText += `\`\`\`\n\n`;
3740
+ responseText += `This tool will:\n`;
3741
+ responseText += `- Verify you called discover_patterns (server checks)\n`;
3742
+ responseText += `- Check that test files exist\n`;
3743
+ responseText += `- Run \`npm test\` and verify tests pass\n`;
3744
+ responseText += `- Run TypeScript check\n`;
3745
+ responseText += `- Report PASS or FAIL from server\n\n`;
3746
+ responseText += `**You CANNOT mark this feature complete without calling validate_complete.**\n\n`;
3747
+ // Section 5: Rules
3748
+ responseText += `---\n\n`;
3749
+ responseText += `## ⚠️ RULES (SERVER-ENFORCED)\n\n`;
3750
+ responseText += `1. You CANNOT skip these patterns - server tracks compliance\n`;
3751
+ responseText += `2. You CANNOT say "done" without validate_complete passing\n`;
3752
+ responseText += `3. Tests are MANDATORY - validation fails without them\n`;
3753
+ responseText += `4. TypeScript must compile - validation fails on errors\n`;
3754
+ responseText += `5. Pre-commit hook blocks commits without passed validation\n\n`;
3755
+ responseText += `**Server is tracking this session. Compliance is enforced.**`;
3756
+ return {
3757
+ content: [{
3758
+ type: 'text',
3759
+ text: responseText,
3760
+ }],
3761
+ };
3762
+ }
3763
+ catch (error) {
3764
+ // Fallback to local-only mode if server is unreachable
3765
+ const message = error instanceof Error ? error.message : 'Unknown error';
3766
+ // Generate local patterns as fallback
3767
+ const taskKeywords = this.extractKeywords(task);
3768
+ const allKeywords = [...new Set([...keywords, ...taskKeywords])];
3769
+ const patternMap = {
3770
+ 'auth': ['02-auth.md'], 'login': ['02-auth.md'], 'payment': ['05-payments.md'],
3771
+ 'stripe': ['05-payments.md'], 'database': ['01-database.md'], 'api': ['03-api.md'],
3772
+ 'form': ['04-frontend.md'], 'component': ['04-frontend.md'], 'test': ['08-testing.md'],
3773
+ };
3774
+ const patterns = ['00-core.md'];
3775
+ for (const keyword of allKeywords) {
3776
+ const lowerKeyword = keyword.toLowerCase();
3777
+ for (const [key, patternFiles] of Object.entries(patternMap)) {
3778
+ if (lowerKeyword.includes(key))
3779
+ patterns.push(...patternFiles);
3780
+ }
3781
+ }
3782
+ const uniquePatterns = [...new Set(patterns)];
3783
+ let responseText = `# 🔍 Pattern Discovery: ${task}\n\n`;
3784
+ responseText += `## ⚠️ OFFLINE MODE - Server Unreachable\n\n`;
3785
+ responseText += `Error: ${message}\n\n`;
3786
+ responseText += `**Using local pattern suggestions (not enforced):**\n\n`;
3787
+ for (const p of uniquePatterns) {
3788
+ responseText += `- \`${p}\`\n`;
3789
+ }
3790
+ responseText += `\n**Note:** Without server connection, enforcement is not active.\n`;
3791
+ responseText += `Validation will also be limited to local checks only.`;
3792
+ return {
3793
+ content: [{
3794
+ type: 'text',
3795
+ text: responseText,
3796
+ }],
3797
+ };
3798
+ }
3799
+ }
3800
+ /**
3801
+ * Extract keywords from a task description
3802
+ */
3803
+ extractKeywords(task) {
3804
+ const words = task.toLowerCase()
3805
+ .replace(/[^a-z0-9\s]/g, ' ')
3806
+ .split(/\s+/)
3807
+ .filter(w => w.length > 2);
3808
+ // Filter out common words
3809
+ const stopWords = ['the', 'and', 'for', 'add', 'fix', 'create', 'make', 'build', 'implement', 'update', 'modify', 'change', 'new', 'with', 'from', 'this', 'that'];
3810
+ return words.filter(w => !stopWords.includes(w));
3811
+ }
3812
+ /**
3813
+ * Find files recursively with given extensions
3814
+ */
3815
+ findFilesRecursive(dir, extensions) {
3816
+ const results = [];
3817
+ try {
3818
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
3819
+ for (const entry of entries) {
3820
+ const fullPath = path.join(dir, entry.name);
3821
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
3822
+ results.push(...this.findFilesRecursive(fullPath, extensions));
3823
+ }
3824
+ else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
3825
+ results.push(fullPath);
3826
+ }
3827
+ }
3828
+ }
3829
+ catch {
3830
+ // Ignore errors
3831
+ }
3832
+ return results;
3475
3833
  }
3476
3834
  async handleReportPatternGap(args) {
3477
3835
  const { category, request, context, handledWith, wasSuccessful = true } = args;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.3.17",
3
+ "version": "3.4.0",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {