@codebakers/cli 1.5.0 → 2.0.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.
@@ -0,0 +1,734 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.heal = heal;
7
+ exports.healWatch = healWatch;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const child_process_1 = require("child_process");
12
+ const ERROR_PATTERNS = [
13
+ // TypeScript Errors
14
+ {
15
+ category: 'typescript',
16
+ severity: 'high',
17
+ pattern: /TS2307.*Cannot find module '([^']+)'/,
18
+ autoFixable: true,
19
+ confidence: 90,
20
+ fixId: 'install_module',
21
+ fixDescription: 'Install missing module',
22
+ risk: 'safe'
23
+ },
24
+ {
25
+ category: 'typescript',
26
+ severity: 'medium',
27
+ pattern: /TS2322.*Type '([^']+)' is not assignable to type '([^']+)'/,
28
+ autoFixable: false,
29
+ confidence: 60,
30
+ fixId: 'fix_type',
31
+ fixDescription: 'Fix type mismatch',
32
+ risk: 'moderate'
33
+ },
34
+ {
35
+ category: 'typescript',
36
+ severity: 'medium',
37
+ pattern: /TS2339.*Property '([^']+)' does not exist on type '([^']+)'/,
38
+ autoFixable: false,
39
+ confidence: 70,
40
+ fixId: 'add_property',
41
+ fixDescription: 'Add missing property to type',
42
+ risk: 'moderate'
43
+ },
44
+ {
45
+ category: 'typescript',
46
+ severity: 'low',
47
+ pattern: /TS7006.*Parameter '([^']+)' implicitly has an 'any' type/,
48
+ autoFixable: false,
49
+ confidence: 80,
50
+ fixId: 'add_type',
51
+ fixDescription: 'Add explicit type annotation',
52
+ risk: 'safe'
53
+ },
54
+ // Dependency Errors
55
+ {
56
+ category: 'dependency',
57
+ severity: 'high',
58
+ pattern: /Module not found: Can't resolve '([^']+)'/,
59
+ autoFixable: true,
60
+ confidence: 90,
61
+ fixId: 'install_package',
62
+ fixDescription: 'Install missing package',
63
+ risk: 'safe'
64
+ },
65
+ {
66
+ category: 'dependency',
67
+ severity: 'high',
68
+ pattern: /Cannot find module '([^']+)'/,
69
+ autoFixable: true,
70
+ confidence: 85,
71
+ fixId: 'install_package',
72
+ fixDescription: 'Install missing package',
73
+ risk: 'safe'
74
+ },
75
+ // Database Errors
76
+ {
77
+ category: 'database',
78
+ severity: 'critical',
79
+ pattern: /connect ECONNREFUSED/,
80
+ autoFixable: false,
81
+ confidence: 95,
82
+ fixId: 'check_db',
83
+ fixDescription: 'Check database connection - ensure database is running',
84
+ risk: 'safe'
85
+ },
86
+ {
87
+ category: 'database',
88
+ severity: 'high',
89
+ pattern: /relation "([^"]+)" does not exist/,
90
+ autoFixable: true,
91
+ confidence: 90,
92
+ fixId: 'run_migrations',
93
+ fixDescription: 'Run database migrations',
94
+ fixCommand: 'npx drizzle-kit push',
95
+ risk: 'moderate'
96
+ },
97
+ // Auth Errors
98
+ {
99
+ category: 'auth',
100
+ severity: 'critical',
101
+ pattern: /AUTH_SECRET.*missing|undefined|AUTH_SECRET is not set/i,
102
+ autoFixable: true,
103
+ confidence: 95,
104
+ fixId: 'generate_auth_secret',
105
+ fixDescription: 'Generate AUTH_SECRET',
106
+ risk: 'safe'
107
+ },
108
+ // Configuration Errors
109
+ {
110
+ category: 'configuration',
111
+ severity: 'high',
112
+ pattern: /Missing required environment variable:?\s*([A-Z_]+)/i,
113
+ autoFixable: false,
114
+ confidence: 95,
115
+ fixId: 'add_env_var',
116
+ fixDescription: 'Add missing environment variable',
117
+ risk: 'safe'
118
+ },
119
+ {
120
+ category: 'configuration',
121
+ severity: 'high',
122
+ pattern: /NEXT_PUBLIC_([A-Z_]+).*undefined/,
123
+ autoFixable: false,
124
+ confidence: 90,
125
+ fixId: 'add_env_var',
126
+ fixDescription: 'Add missing public environment variable',
127
+ risk: 'safe'
128
+ },
129
+ // Security Errors
130
+ {
131
+ category: 'security',
132
+ severity: 'high',
133
+ pattern: /found (\d+) vulnerabilit(y|ies)/i,
134
+ autoFixable: true,
135
+ confidence: 80,
136
+ fixId: 'npm_audit_fix',
137
+ fixDescription: 'Run npm audit fix',
138
+ fixCommand: 'npm audit fix',
139
+ risk: 'moderate'
140
+ },
141
+ {
142
+ category: 'security',
143
+ severity: 'critical',
144
+ pattern: /(\d+) critical/i,
145
+ autoFixable: false,
146
+ confidence: 90,
147
+ fixId: 'npm_audit_fix_critical',
148
+ fixDescription: 'Review and fix critical vulnerabilities manually',
149
+ risk: 'risky'
150
+ },
151
+ // Build Errors
152
+ {
153
+ category: 'build',
154
+ severity: 'high',
155
+ pattern: /Build failed|Failed to compile/i,
156
+ autoFixable: false,
157
+ confidence: 50,
158
+ fixId: 'fix_build',
159
+ fixDescription: 'Review build errors',
160
+ risk: 'moderate'
161
+ },
162
+ // Runtime Errors
163
+ {
164
+ category: 'runtime',
165
+ severity: 'high',
166
+ pattern: /Cannot read propert(y|ies) of (undefined|null)/,
167
+ autoFixable: false,
168
+ confidence: 70,
169
+ fixId: 'add_null_check',
170
+ fixDescription: 'Add null/undefined check',
171
+ risk: 'safe'
172
+ },
173
+ {
174
+ category: 'runtime',
175
+ severity: 'high',
176
+ pattern: /([A-Za-z]+) is not defined/,
177
+ autoFixable: false,
178
+ confidence: 75,
179
+ fixId: 'add_import',
180
+ fixDescription: 'Add missing import or declaration',
181
+ risk: 'safe'
182
+ }
183
+ ];
184
+ function generateErrorId() {
185
+ return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
186
+ }
187
+ function classifyError(errorText, file, line) {
188
+ for (const rule of ERROR_PATTERNS) {
189
+ const match = errorText.match(rule.pattern);
190
+ if (match) {
191
+ return {
192
+ id: generateErrorId(),
193
+ timestamp: new Date(),
194
+ category: rule.category,
195
+ severity: rule.severity,
196
+ message: errorText.trim(),
197
+ file,
198
+ line,
199
+ autoFixable: rule.autoFixable,
200
+ confidence: rule.confidence,
201
+ suggestedFixes: [{
202
+ id: rule.fixId,
203
+ description: rule.fixDescription,
204
+ confidence: rule.confidence,
205
+ risk: rule.risk,
206
+ requiresReview: rule.risk !== 'safe',
207
+ command: rule.fixCommand
208
+ }]
209
+ };
210
+ }
211
+ }
212
+ // Unknown error
213
+ return {
214
+ id: generateErrorId(),
215
+ timestamp: new Date(),
216
+ category: 'unknown',
217
+ severity: 'medium',
218
+ message: errorText.trim(),
219
+ file,
220
+ line,
221
+ autoFixable: false,
222
+ confidence: 0,
223
+ suggestedFixes: []
224
+ };
225
+ }
226
+ // ============================================================================
227
+ // ERROR SCANNING
228
+ // ============================================================================
229
+ async function scanTypeScriptErrors() {
230
+ const errors = [];
231
+ try {
232
+ (0, child_process_1.execSync)('npx tsc --noEmit 2>&1', { encoding: 'utf-8', stdio: 'pipe' });
233
+ }
234
+ catch (error) {
235
+ const output = error.stdout || error.stderr || error.message || '';
236
+ const lines = output.split('\n');
237
+ let currentFile = '';
238
+ let currentLine = 0;
239
+ for (const line of lines) {
240
+ // Match file:line:col pattern
241
+ const fileMatch = line.match(/^([^:]+):(\d+):(\d+)/);
242
+ if (fileMatch) {
243
+ currentFile = fileMatch[1];
244
+ currentLine = parseInt(fileMatch[2], 10);
245
+ }
246
+ // Match TS error codes
247
+ const tsMatch = line.match(/error (TS\d+):/);
248
+ if (tsMatch) {
249
+ const classified = classifyError(line, currentFile, currentLine);
250
+ errors.push(classified);
251
+ }
252
+ }
253
+ }
254
+ return errors;
255
+ }
256
+ async function scanBuildErrors() {
257
+ const errors = [];
258
+ try {
259
+ // Check if build script exists
260
+ const packageJson = JSON.parse(await promises_1.default.readFile('package.json', 'utf-8'));
261
+ if (!packageJson.scripts?.build) {
262
+ return errors;
263
+ }
264
+ (0, child_process_1.execSync)('npm run build 2>&1', { encoding: 'utf-8', stdio: 'pipe' });
265
+ }
266
+ catch (error) {
267
+ const output = error.stdout || error.stderr || error.message || '';
268
+ const lines = output.split('\n');
269
+ for (const line of lines) {
270
+ if (line.includes('error') || line.includes('Error') || line.includes('failed')) {
271
+ const classified = classifyError(line);
272
+ // Avoid duplicates from TS errors
273
+ if (classified.category !== 'typescript') {
274
+ errors.push(classified);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ return errors;
280
+ }
281
+ async function scanEnvironmentIssues() {
282
+ const errors = [];
283
+ // Check for .env files
284
+ const envFiles = ['.env', '.env.local', '.env.development'];
285
+ let hasEnvFile = false;
286
+ for (const file of envFiles) {
287
+ try {
288
+ await promises_1.default.access(file);
289
+ hasEnvFile = true;
290
+ break;
291
+ }
292
+ catch {
293
+ // File doesn't exist
294
+ }
295
+ }
296
+ if (!hasEnvFile) {
297
+ errors.push({
298
+ id: generateErrorId(),
299
+ timestamp: new Date(),
300
+ category: 'configuration',
301
+ severity: 'high',
302
+ message: 'No .env file found - environment variables may not be configured',
303
+ autoFixable: false,
304
+ confidence: 90,
305
+ suggestedFixes: [{
306
+ id: 'create_env',
307
+ description: 'Create .env.local file with required variables',
308
+ confidence: 90,
309
+ risk: 'safe',
310
+ requiresReview: true
311
+ }]
312
+ });
313
+ }
314
+ // Check for AUTH_SECRET if using auth
315
+ try {
316
+ const envContent = await promises_1.default.readFile('.env.local', 'utf-8').catch(() => '');
317
+ const packageJson = JSON.parse(await promises_1.default.readFile('package.json', 'utf-8'));
318
+ const hasAuth = packageJson.dependencies?.['next-auth'] ||
319
+ packageJson.dependencies?.['@auth/core'] ||
320
+ packageJson.dependencies?.['@supabase/auth-helpers-nextjs'];
321
+ if (hasAuth && !envContent.includes('AUTH_SECRET')) {
322
+ errors.push({
323
+ id: generateErrorId(),
324
+ timestamp: new Date(),
325
+ category: 'auth',
326
+ severity: 'critical',
327
+ message: 'AUTH_SECRET not found in .env.local - authentication will not work',
328
+ autoFixable: true,
329
+ confidence: 95,
330
+ suggestedFixes: [{
331
+ id: 'generate_auth_secret',
332
+ description: 'Generate and add AUTH_SECRET to .env.local',
333
+ confidence: 95,
334
+ risk: 'safe',
335
+ requiresReview: false
336
+ }]
337
+ });
338
+ }
339
+ }
340
+ catch {
341
+ // Skip if can't read files
342
+ }
343
+ return errors;
344
+ }
345
+ async function scanSecurityIssues() {
346
+ const errors = [];
347
+ try {
348
+ const output = (0, child_process_1.execSync)('npm audit --json 2>&1', { encoding: 'utf-8', stdio: 'pipe' });
349
+ const audit = JSON.parse(output);
350
+ if (audit.metadata?.vulnerabilities) {
351
+ const { critical, high, moderate, low } = audit.metadata.vulnerabilities;
352
+ if (critical > 0) {
353
+ errors.push({
354
+ id: generateErrorId(),
355
+ timestamp: new Date(),
356
+ category: 'security',
357
+ severity: 'critical',
358
+ message: `${critical} critical vulnerabilities found`,
359
+ autoFixable: false,
360
+ confidence: 95,
361
+ suggestedFixes: [{
362
+ id: 'npm_audit_fix',
363
+ description: 'Run npm audit fix --force (may have breaking changes)',
364
+ confidence: 70,
365
+ risk: 'risky',
366
+ requiresReview: true,
367
+ command: 'npm audit fix --force'
368
+ }]
369
+ });
370
+ }
371
+ if (high > 0) {
372
+ errors.push({
373
+ id: generateErrorId(),
374
+ timestamp: new Date(),
375
+ category: 'security',
376
+ severity: 'high',
377
+ message: `${high} high-severity vulnerabilities found`,
378
+ autoFixable: true,
379
+ confidence: 85,
380
+ suggestedFixes: [{
381
+ id: 'npm_audit_fix',
382
+ description: 'Run npm audit fix',
383
+ confidence: 85,
384
+ risk: 'moderate',
385
+ requiresReview: false,
386
+ command: 'npm audit fix'
387
+ }]
388
+ });
389
+ }
390
+ if (moderate > 0) {
391
+ errors.push({
392
+ id: generateErrorId(),
393
+ timestamp: new Date(),
394
+ category: 'security',
395
+ severity: 'medium',
396
+ message: `${moderate} moderate vulnerabilities found`,
397
+ autoFixable: true,
398
+ confidence: 90,
399
+ suggestedFixes: [{
400
+ id: 'npm_audit_fix',
401
+ description: 'Run npm audit fix',
402
+ confidence: 90,
403
+ risk: 'safe',
404
+ requiresReview: false,
405
+ command: 'npm audit fix'
406
+ }]
407
+ });
408
+ }
409
+ }
410
+ }
411
+ catch {
412
+ // npm audit failed or no issues
413
+ }
414
+ return errors;
415
+ }
416
+ async function scanDatabaseIssues() {
417
+ const errors = [];
418
+ try {
419
+ // Check if using Drizzle
420
+ const packageJson = JSON.parse(await promises_1.default.readFile('package.json', 'utf-8'));
421
+ if (!packageJson.dependencies?.['drizzle-orm']) {
422
+ return errors;
423
+ }
424
+ // Check for migrations folder
425
+ const migrationsPaths = ['drizzle', 'src/db/migrations', 'migrations'];
426
+ let hasMigrations = false;
427
+ for (const migPath of migrationsPaths) {
428
+ try {
429
+ const stat = await promises_1.default.stat(migPath);
430
+ if (stat.isDirectory()) {
431
+ const files = await promises_1.default.readdir(migPath);
432
+ if (files.some(f => f.endsWith('.sql'))) {
433
+ hasMigrations = true;
434
+ break;
435
+ }
436
+ }
437
+ }
438
+ catch {
439
+ // Directory doesn't exist
440
+ }
441
+ }
442
+ if (!hasMigrations) {
443
+ errors.push({
444
+ id: generateErrorId(),
445
+ timestamp: new Date(),
446
+ category: 'database',
447
+ severity: 'medium',
448
+ message: 'No database migrations found - schema may not be pushed',
449
+ autoFixable: true,
450
+ confidence: 85,
451
+ suggestedFixes: [{
452
+ id: 'generate_migrations',
453
+ description: 'Generate and push migrations',
454
+ confidence: 85,
455
+ risk: 'moderate',
456
+ requiresReview: true,
457
+ command: 'npx drizzle-kit generate && npx drizzle-kit push'
458
+ }]
459
+ });
460
+ }
461
+ }
462
+ catch {
463
+ // Skip if can't read package.json
464
+ }
465
+ return errors;
466
+ }
467
+ // ============================================================================
468
+ // FIX APPLICATION
469
+ // ============================================================================
470
+ async function applyFix(error, fix) {
471
+ switch (fix.id) {
472
+ case 'install_package':
473
+ case 'install_module':
474
+ return await installMissingPackage(error);
475
+ case 'generate_auth_secret':
476
+ return await generateAuthSecret();
477
+ case 'npm_audit_fix':
478
+ return await runCommand('npm audit fix');
479
+ case 'run_migrations':
480
+ return await runCommand('npx drizzle-kit push');
481
+ case 'generate_migrations':
482
+ return await runCommand('npx drizzle-kit generate && npx drizzle-kit push');
483
+ default:
484
+ if (fix.command) {
485
+ return await runCommand(fix.command);
486
+ }
487
+ return false;
488
+ }
489
+ }
490
+ async function installMissingPackage(error) {
491
+ // Extract package name from error message
492
+ const match = error.message.match(/(?:Cannot find module|Can't resolve) '([^']+)'/);
493
+ if (!match)
494
+ return false;
495
+ let packageName = match[1];
496
+ // Skip relative imports
497
+ if (packageName.startsWith('.') || packageName.startsWith('@/')) {
498
+ return false;
499
+ }
500
+ // Extract base package name (handle scoped packages and subpaths)
501
+ if (packageName.startsWith('@')) {
502
+ packageName = packageName.split('/').slice(0, 2).join('/');
503
+ }
504
+ else {
505
+ packageName = packageName.split('/')[0];
506
+ }
507
+ return await runCommand(`npm install ${packageName}`);
508
+ }
509
+ async function generateAuthSecret() {
510
+ try {
511
+ const crypto = await import('crypto');
512
+ const secret = crypto.randomBytes(32).toString('base64');
513
+ let envContent = '';
514
+ try {
515
+ envContent = await promises_1.default.readFile('.env.local', 'utf-8');
516
+ }
517
+ catch {
518
+ // File doesn't exist, create it
519
+ }
520
+ if (envContent.includes('AUTH_SECRET=')) {
521
+ envContent = envContent.replace(/AUTH_SECRET=.*/, `AUTH_SECRET=${secret}`);
522
+ }
523
+ else {
524
+ envContent += `\nAUTH_SECRET=${secret}\n`;
525
+ }
526
+ await promises_1.default.writeFile('.env.local', envContent.trim() + '\n');
527
+ return true;
528
+ }
529
+ catch {
530
+ return false;
531
+ }
532
+ }
533
+ async function runCommand(command) {
534
+ try {
535
+ (0, child_process_1.execSync)(command, { stdio: 'pipe' });
536
+ return true;
537
+ }
538
+ catch {
539
+ return false;
540
+ }
541
+ }
542
+ // ============================================================================
543
+ // DISPLAY
544
+ // ============================================================================
545
+ function displayError(error) {
546
+ const severityColors = {
547
+ critical: chalk_1.default.red,
548
+ high: chalk_1.default.red,
549
+ medium: chalk_1.default.yellow,
550
+ low: chalk_1.default.blue,
551
+ info: chalk_1.default.gray
552
+ };
553
+ const severityIcons = {
554
+ critical: 'šŸ”“',
555
+ high: '🟠',
556
+ medium: '🟔',
557
+ low: 'šŸ”µ',
558
+ info: 'ā„¹ļø'
559
+ };
560
+ const color = severityColors[error.severity];
561
+ const icon = severityIcons[error.severity];
562
+ console.log(`\n${icon} ${color(chalk_1.default.bold(error.category.toUpperCase()))} (${error.severity})`);
563
+ console.log(chalk_1.default.white(` ${error.message}`));
564
+ if (error.file) {
565
+ console.log(chalk_1.default.gray(` šŸ“ ${error.file}${error.line ? `:${error.line}` : ''}`));
566
+ }
567
+ if (error.autoFixable) {
568
+ console.log(chalk_1.default.green(` āœ“ Auto-fixable (${error.confidence}% confidence)`));
569
+ }
570
+ for (const fix of error.suggestedFixes) {
571
+ const riskColor = fix.risk === 'safe' ? chalk_1.default.green :
572
+ fix.risk === 'moderate' ? chalk_1.default.yellow : chalk_1.default.red;
573
+ console.log(chalk_1.default.cyan(` → ${fix.description}`) + riskColor(` [${fix.risk}]`));
574
+ if (fix.command) {
575
+ console.log(chalk_1.default.gray(` $ ${fix.command}`));
576
+ }
577
+ }
578
+ }
579
+ // ============================================================================
580
+ // MAIN HEAL FUNCTION
581
+ // ============================================================================
582
+ async function heal(options = {}) {
583
+ console.log(chalk_1.default.blue('\nšŸ„ CodeBakers Self-Healing System\n'));
584
+ console.log(chalk_1.default.gray('Scanning for issues...\n'));
585
+ const allErrors = [];
586
+ // Scan for all types of errors
587
+ const scanners = [
588
+ { name: 'TypeScript', fn: scanTypeScriptErrors },
589
+ { name: 'Build', fn: scanBuildErrors },
590
+ { name: 'Environment', fn: scanEnvironmentIssues },
591
+ { name: 'Security', fn: scanSecurityIssues },
592
+ { name: 'Database', fn: scanDatabaseIssues }
593
+ ];
594
+ for (const scanner of scanners) {
595
+ const spinner = (0, ora_1.default)(`Checking ${scanner.name}...`).start();
596
+ try {
597
+ const errors = await scanner.fn();
598
+ allErrors.push(...errors);
599
+ if (errors.length > 0) {
600
+ spinner.warn(`${scanner.name}: ${errors.length} issue(s)`);
601
+ }
602
+ else {
603
+ spinner.succeed(`${scanner.name}: OK`);
604
+ }
605
+ }
606
+ catch (error) {
607
+ spinner.fail(`${scanner.name}: Error scanning`);
608
+ }
609
+ }
610
+ // Filter by severity if specified
611
+ let errors = allErrors;
612
+ if (options.severity) {
613
+ errors = errors.filter(e => e.severity === options.severity);
614
+ }
615
+ // Remove duplicates by message
616
+ const seen = new Set();
617
+ errors = errors.filter(e => {
618
+ if (seen.has(e.message))
619
+ return false;
620
+ seen.add(e.message);
621
+ return true;
622
+ });
623
+ console.log('');
624
+ if (errors.length === 0) {
625
+ console.log(chalk_1.default.green('✨ No issues found! Your project is healthy.\n'));
626
+ return { errors: [], fixed: 0, remaining: 0 };
627
+ }
628
+ console.log(chalk_1.default.yellow(`Found ${errors.length} issue(s):`));
629
+ // Sort by severity
630
+ const severityOrder = ['critical', 'high', 'medium', 'low', 'info'];
631
+ errors.sort((a, b) => severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity));
632
+ // Display all errors
633
+ for (const error of errors) {
634
+ displayError(error);
635
+ }
636
+ console.log('');
637
+ // Apply fixes
638
+ let fixed = 0;
639
+ if (!options.dryRun) {
640
+ const fixableErrors = errors.filter(e => e.autoFixable &&
641
+ e.confidence >= 80 &&
642
+ e.suggestedFixes.some(f => f.risk !== 'risky'));
643
+ if (fixableErrors.length > 0) {
644
+ console.log(chalk_1.default.blue(`\nšŸ”§ Applying ${fixableErrors.length} auto-fix(es)...\n`));
645
+ for (const error of fixableErrors) {
646
+ const safeFix = error.suggestedFixes.find(f => f.risk !== 'risky');
647
+ if (!safeFix)
648
+ continue;
649
+ if (!options.auto) {
650
+ // In non-auto mode, we just show what would be fixed
651
+ console.log(chalk_1.default.gray(`Would fix: ${error.category} - ${safeFix.description}`));
652
+ continue;
653
+ }
654
+ const spinner = (0, ora_1.default)(`Fixing: ${safeFix.description}`).start();
655
+ try {
656
+ const success = await applyFix(error, safeFix);
657
+ if (success) {
658
+ spinner.succeed(`Fixed: ${safeFix.description}`);
659
+ error.fixed = true;
660
+ fixed++;
661
+ }
662
+ else {
663
+ spinner.fail(`Failed: ${safeFix.description}`);
664
+ }
665
+ }
666
+ catch (err) {
667
+ spinner.fail(`Error: ${safeFix.description}`);
668
+ }
669
+ }
670
+ }
671
+ }
672
+ // Summary
673
+ const remaining = errors.length - fixed;
674
+ console.log(chalk_1.default.blue('\nšŸ“Š Summary'));
675
+ console.log(` Total issues: ${chalk_1.default.white(errors.length)}`);
676
+ console.log(` Fixed: ${chalk_1.default.green(fixed)}`);
677
+ console.log(` Remaining: ${remaining > 0 ? chalk_1.default.yellow(remaining) : chalk_1.default.green(0)}`);
678
+ if (options.dryRun) {
679
+ console.log(chalk_1.default.gray('\n [Dry run - no changes made]'));
680
+ }
681
+ else if (!options.auto && errors.some(e => e.autoFixable)) {
682
+ console.log(chalk_1.default.gray('\n Run with --auto to apply fixes automatically'));
683
+ }
684
+ console.log('');
685
+ return {
686
+ errors,
687
+ fixed,
688
+ remaining
689
+ };
690
+ }
691
+ // ============================================================================
692
+ // WATCH MODE
693
+ // ============================================================================
694
+ async function healWatch() {
695
+ console.log(chalk_1.default.blue('\nšŸ‘ļø Self-Healing Watch Mode\n'));
696
+ console.log(chalk_1.default.gray('Monitoring for errors... (Ctrl+C to stop)\n'));
697
+ // Initial scan
698
+ await heal({ auto: true });
699
+ // Watch for file changes using dynamic import
700
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
701
+ let chokidarModule = null;
702
+ try {
703
+ // @ts-ignore - chokidar is an optional dependency for watch mode
704
+ chokidarModule = await import('chokidar');
705
+ }
706
+ catch {
707
+ console.log(chalk_1.default.yellow('\nWatch mode requires chokidar:'));
708
+ console.log(chalk_1.default.cyan(' npm install -D chokidar\n'));
709
+ console.log(chalk_1.default.gray('For now, run `codebakers heal` manually after making changes.'));
710
+ return;
711
+ }
712
+ const watcher = chokidarModule.watch(['src/**/*.{ts,tsx}', 'app/**/*.{ts,tsx}'], {
713
+ ignored: /node_modules/,
714
+ persistent: true,
715
+ ignoreInitial: true
716
+ });
717
+ let debounceTimer = null;
718
+ watcher.on('change', (filePath) => {
719
+ console.log(chalk_1.default.gray(`\nFile changed: ${filePath}`));
720
+ // Debounce to avoid running multiple times
721
+ if (debounceTimer) {
722
+ clearTimeout(debounceTimer);
723
+ }
724
+ debounceTimer = setTimeout(async () => {
725
+ await heal({ auto: true });
726
+ }, 1000);
727
+ });
728
+ // Keep process alive
729
+ process.on('SIGINT', () => {
730
+ console.log(chalk_1.default.blue('\n\nšŸ‘‹ Watch mode stopped\n'));
731
+ watcher.close();
732
+ process.exit(0);
733
+ });
734
+ }