codebakers 1.0.45 → 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.
Files changed (81) hide show
  1. package/README.md +275 -60
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +3999 -0
  4. package/install.bat +9 -0
  5. package/package.json +71 -115
  6. package/src/channels/discord.ts +5 -0
  7. package/src/channels/slack.ts +5 -0
  8. package/src/channels/sms.ts +4 -0
  9. package/src/channels/telegram.ts +5 -0
  10. package/src/channels/whatsapp.ts +7 -0
  11. package/src/commands/check.ts +365 -0
  12. package/src/commands/code.ts +684 -0
  13. package/src/commands/connect.ts +12 -0
  14. package/src/commands/deploy.ts +414 -0
  15. package/src/commands/fix.ts +20 -0
  16. package/src/commands/gateway.ts +604 -0
  17. package/src/commands/generate.ts +178 -0
  18. package/src/commands/init.ts +574 -0
  19. package/src/commands/learn.ts +36 -0
  20. package/src/commands/security.ts +102 -0
  21. package/src/commands/setup.ts +448 -0
  22. package/src/commands/status.ts +56 -0
  23. package/src/index.ts +268 -0
  24. package/src/patterns/loader.ts +337 -0
  25. package/src/services/github.ts +61 -0
  26. package/src/services/supabase.ts +147 -0
  27. package/src/services/vercel.ts +61 -0
  28. package/src/utils/claude-md.ts +287 -0
  29. package/src/utils/config.ts +282 -0
  30. package/src/utils/updates.ts +27 -0
  31. package/tsconfig.json +17 -10
  32. package/.vscodeignore +0 -18
  33. package/LICENSE +0 -21
  34. package/codebakers-1.0.0.vsix +0 -0
  35. package/codebakers-1.0.10.vsix +0 -0
  36. package/codebakers-1.0.11.vsix +0 -0
  37. package/codebakers-1.0.12.vsix +0 -0
  38. package/codebakers-1.0.13.vsix +0 -0
  39. package/codebakers-1.0.14.vsix +0 -0
  40. package/codebakers-1.0.15.vsix +0 -0
  41. package/codebakers-1.0.16.vsix +0 -0
  42. package/codebakers-1.0.17.vsix +0 -0
  43. package/codebakers-1.0.18.vsix +0 -0
  44. package/codebakers-1.0.19.vsix +0 -0
  45. package/codebakers-1.0.20.vsix +0 -0
  46. package/codebakers-1.0.21.vsix +0 -0
  47. package/codebakers-1.0.22.vsix +0 -0
  48. package/codebakers-1.0.23.vsix +0 -0
  49. package/codebakers-1.0.24.vsix +0 -0
  50. package/codebakers-1.0.25.vsix +0 -0
  51. package/codebakers-1.0.26.vsix +0 -0
  52. package/codebakers-1.0.27.vsix +0 -0
  53. package/codebakers-1.0.28.vsix +0 -0
  54. package/codebakers-1.0.29.vsix +0 -0
  55. package/codebakers-1.0.30.vsix +0 -0
  56. package/codebakers-1.0.31.vsix +0 -0
  57. package/codebakers-1.0.32.vsix +0 -0
  58. package/codebakers-1.0.35.vsix +0 -0
  59. package/codebakers-1.0.36.vsix +0 -0
  60. package/codebakers-1.0.37.vsix +0 -0
  61. package/codebakers-1.0.38.vsix +0 -0
  62. package/codebakers-1.0.39.vsix +0 -0
  63. package/codebakers-1.0.40.vsix +0 -0
  64. package/codebakers-1.0.41.vsix +0 -0
  65. package/codebakers-1.0.42.vsix +0 -0
  66. package/codebakers-1.0.43.vsix +0 -0
  67. package/codebakers-1.0.44.vsix +0 -0
  68. package/codebakers-1.0.45.vsix +0 -0
  69. package/dist/extension.js +0 -1394
  70. package/esbuild.js +0 -63
  71. package/media/icon.png +0 -0
  72. package/media/icon.svg +0 -7
  73. package/nul +0 -1
  74. package/preview.html +0 -547
  75. package/src/ChatPanelProvider.ts +0 -1815
  76. package/src/ChatViewProvider.ts +0 -749
  77. package/src/CodeBakersClient.ts +0 -1146
  78. package/src/CodeValidator.ts +0 -645
  79. package/src/FileOperations.ts +0 -410
  80. package/src/ProjectContext.ts +0 -526
  81. package/src/extension.ts +0 -332
package/install.bat ADDED
@@ -0,0 +1,9 @@
1
+ @echo off
2
+ echo Installing CodeBakers CLI...
3
+ npm install
4
+ echo Building...
5
+ npm run build
6
+ echo Publishing to npm...
7
+ npm publish
8
+ echo Done! Now run: npm install -g codebakers
9
+ pause
package/package.json CHANGED
@@ -1,127 +1,83 @@
1
1
  {
2
2
  "name": "codebakers",
3
- "displayName": "CodeBakers - Production Code, First Time",
4
- "description": "Stop fixing AI mistakes. Cursor and Claude Code ignore your standards — CodeBakers enforces them. Save 20+ hours per project with production-ready code the first time.",
5
- "version": "1.0.45",
6
- "publisher": "codebakers",
7
- "engines": {
8
- "vscode": "^1.85.0"
3
+ "version": "2.0.0",
4
+ "description": "AI dev team that follows the rules. Build apps from anywhere with pattern enforcement.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "codebakers": "./dist/index.js",
9
+ "cb": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsup src/index.ts --format esm --dts --clean",
13
+ "dev": "tsup src/index.ts --format esm --watch",
14
+ "start": "node dist/index.js",
15
+ "typecheck": "tsc --noEmit",
16
+ "lint": "eslint src/",
17
+ "test": "vitest",
18
+ "prepublishOnly": "npm run build"
9
19
  },
10
- "categories": [
11
- "Programming Languages",
12
- "Snippets",
13
- "Machine Learning",
14
- "Other"
15
- ],
16
20
  "keywords": [
21
+ "cli",
17
22
  "ai",
18
- "claude",
19
- "patterns",
20
- "code generation",
21
- "best practices",
22
- "copilot"
23
+ "codebakers",
24
+ "code-generation",
25
+ "pattern-enforcement",
26
+ "developer-tools",
27
+ "automation",
28
+ "nextjs",
29
+ "supabase",
30
+ "vercel"
23
31
  ],
24
- "icon": "media/icon.png",
25
- "activationEvents": [
26
- "*"
27
- ],
28
- "main": "./dist/extension.js",
29
- "contributes": {
30
- "commands": [
31
- {
32
- "command": "codebakers.openChat",
33
- "title": "Open CodeBakers",
34
- "category": "CodeBakers",
35
- "icon": {
36
- "light": "media/icon.svg",
37
- "dark": "media/icon.svg"
38
- }
39
- },
40
- {
41
- "command": "codebakers.login",
42
- "title": "Login to CodeBakers",
43
- "category": "CodeBakers"
44
- },
45
- {
46
- "command": "codebakers.logout",
47
- "title": "Logout from CodeBakers",
48
- "category": "CodeBakers"
49
- },
50
- {
51
- "command": "codebakers.showPatterns",
52
- "title": "Show Available Patterns",
53
- "category": "CodeBakers"
54
- },
55
- {
56
- "command": "codebakers.runAudit",
57
- "title": "Run Code Audit",
58
- "category": "CodeBakers"
59
- }
60
- ],
61
- "menus": {
62
- "editor/title": [
63
- {
64
- "command": "codebakers.openChat",
65
- "group": "navigation"
66
- }
67
- ],
68
- "commandPalette": [
69
- {
70
- "command": "codebakers.openChat"
71
- },
72
- {
73
- "command": "codebakers.login"
74
- },
75
- {
76
- "command": "codebakers.logout"
77
- }
78
- ]
79
- },
80
- "keybindings": [
81
- {
82
- "command": "codebakers.openChat",
83
- "key": "ctrl+alt+c",
84
- "mac": "cmd+alt+c"
85
- }
86
- ],
87
- "configuration": {
88
- "title": "CodeBakers",
89
- "properties": {
90
- "codebakers.apiEndpoint": {
91
- "type": "string",
92
- "default": "https://www.codebakers.ai",
93
- "description": "CodeBakers API endpoint"
94
- },
95
- "codebakers.autoShowFooter": {
96
- "type": "boolean",
97
- "default": true,
98
- "description": "Show CodeBakers footer after code generation"
99
- }
100
- }
101
- }
102
- },
103
- "scripts": {
104
- "vscode:prepublish": "npm run esbuild-base -- --production",
105
- "compile": "npm run esbuild-base",
106
- "esbuild-base": "node esbuild.js",
107
- "esbuild-watch": "node esbuild.js --watch",
108
- "watch": "npm run esbuild-watch",
109
- "package": "vsce package",
110
- "publish": "vsce publish"
32
+ "author": "BotMakers Inc",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/botmakers/codebakers-cli"
111
37
  },
112
- "devDependencies": {
113
- "@types/node": "^20.10.0",
114
- "@types/vscode": "^1.85.0",
115
- "@vscode/vsce": "^2.22.0",
116
- "esbuild": "^0.27.2",
117
- "typescript": "^5.3.0"
38
+ "homepage": "https://codebakers.dev",
39
+ "engines": {
40
+ "node": ">=18.0.0"
118
41
  },
119
42
  "dependencies": {
120
- "@anthropic-ai/sdk": "^0.32.0",
121
- "node-fetch": "^2.7.0"
43
+ "@anthropic-ai/sdk": "^0.32.1",
44
+ "@clack/prompts": "^0.7.0",
45
+ "@octokit/rest": "^21.0.0",
46
+ "@supabase/supabase-js": "^2.45.0",
47
+ "axios": "^1.7.0",
48
+ "boxen": "^8.0.0",
49
+ "chalk": "^5.3.0",
50
+ "cli-table3": "^0.6.5",
51
+ "commander": "^12.1.0",
52
+ "conf": "^13.0.0",
53
+ "dotenv": "^16.4.5",
54
+ "execa": "^9.3.0",
55
+ "fast-glob": "^3.3.2",
56
+ "figures": "^6.1.0",
57
+ "fs-extra": "^11.2.0",
58
+ "gradient-string": "^2.0.2",
59
+ "inquirer": "^10.0.0",
60
+ "node-fetch": "^3.3.2",
61
+ "open": "^10.1.0",
62
+ "ora": "^8.0.1",
63
+ "picocolors": "^1.0.1",
64
+ "simple-git": "^3.25.0",
65
+ "strip-ansi": "^7.1.0",
66
+ "terminal-link": "^3.0.0",
67
+ "update-notifier": "^7.1.0",
68
+ "which": "^4.0.0",
69
+ "zod": "^3.23.8"
122
70
  },
123
- "repository": {
124
- "type": "git",
125
- "url": "https://github.com/codebakers/codebakers-extension"
71
+ "devDependencies": {
72
+ "@types/fs-extra": "^11.0.4",
73
+ "@types/gradient-string": "^1.1.6",
74
+ "@types/inquirer": "^9.0.7",
75
+ "@types/node": "^22.0.0",
76
+ "@types/update-notifier": "^6.0.8",
77
+ "@types/which": "^3.0.4",
78
+ "eslint": "^9.8.0",
79
+ "tsup": "^8.2.0",
80
+ "typescript": "^5.5.0",
81
+ "vitest": "^2.0.0"
126
82
  }
127
83
  }
@@ -0,0 +1,5 @@
1
+ export class DiscordChannel {
2
+ async connect(token: string): Promise<void> {}
3
+ async disconnect(): Promise<void> {}
4
+ async sendMessage(channelId: string, message: string): Promise<void> {}
5
+ }
@@ -0,0 +1,5 @@
1
+ export class SlackChannel {
2
+ async connect(token: string): Promise<void> {}
3
+ async disconnect(): Promise<void> {}
4
+ async sendMessage(channel: string, message: string): Promise<void> {}
5
+ }
@@ -0,0 +1,4 @@
1
+ export class SMSChannel {
2
+ async connect(accountSid: string, authToken: string): Promise<void> {}
3
+ async sendMessage(to: string, message: string): Promise<void> {}
4
+ }
@@ -0,0 +1,5 @@
1
+ export class TelegramChannel {
2
+ async connect(token: string): Promise<void> {}
3
+ async disconnect(): Promise<void> {}
4
+ async sendMessage(chatId: string, message: string): Promise<void> {}
5
+ }
@@ -0,0 +1,7 @@
1
+ // WhatsApp Channel using Baileys
2
+ export class WhatsAppChannel {
3
+ // Implementation would use @whiskeysockets/baileys
4
+ async connect(): Promise<void> {}
5
+ async disconnect(): Promise<void> {}
6
+ async sendMessage(to: string, message: string): Promise<void> {}
7
+ }
@@ -0,0 +1,365 @@
1
+ import * as p from '@clack/prompts';
2
+ import chalk from 'chalk';
3
+ import * as fs from 'fs-extra';
4
+ import * as path from 'path';
5
+ import glob from 'fast-glob';
6
+ import { Config } from '../utils/config.js';
7
+
8
+ interface CheckOptions {
9
+ fix?: boolean;
10
+ watch?: boolean;
11
+ }
12
+
13
+ interface Violation {
14
+ file: string;
15
+ line: number;
16
+ rule: string;
17
+ message: string;
18
+ severity: 'error' | 'warning';
19
+ autoFixable: boolean;
20
+ }
21
+
22
+ interface CheckResult {
23
+ violations: Violation[];
24
+ filesChecked: number;
25
+ passed: boolean;
26
+ }
27
+
28
+ // Pattern rules extracted from your CLAUDE.md
29
+ const RULES = [
30
+ {
31
+ id: 'no-any',
32
+ name: 'No any type',
33
+ description: 'Avoid using the "any" type',
34
+ severity: 'error' as const,
35
+ pattern: /:\s*any\b(?!\s*\=\>)/g,
36
+ message: 'Avoid using "any" type. Use proper TypeScript types.',
37
+ autoFixable: false,
38
+ },
39
+ {
40
+ id: 'no-ts-ignore',
41
+ name: 'No @ts-ignore',
42
+ description: 'Avoid using @ts-ignore',
43
+ severity: 'error' as const,
44
+ pattern: /@ts-ignore/g,
45
+ message: '@ts-ignore bypasses type checking. Fix the underlying issue.',
46
+ autoFixable: false,
47
+ },
48
+ {
49
+ id: 'no-console-log',
50
+ name: 'No console.log',
51
+ description: 'Remove debug console.log statements',
52
+ severity: 'warning' as const,
53
+ pattern: /console\.log\(/g,
54
+ message: 'Remove console.log before committing. Use proper logging.',
55
+ autoFixable: true,
56
+ },
57
+ {
58
+ id: 'no-todo',
59
+ name: 'No TODO comments',
60
+ description: 'Complete all TODOs before committing',
61
+ severity: 'warning' as const,
62
+ pattern: /\/\/\s*TODO:/gi,
63
+ message: 'Complete or remove TODO comments before committing.',
64
+ autoFixable: false,
65
+ },
66
+ {
67
+ id: 'no-fixme',
68
+ name: 'No FIXME comments',
69
+ description: 'Fix all FIXME issues before committing',
70
+ severity: 'error' as const,
71
+ pattern: /\/\/\s*FIXME:/gi,
72
+ message: 'Address FIXME issues before committing.',
73
+ autoFixable: false,
74
+ },
75
+ {
76
+ id: 'button-has-handler',
77
+ name: 'Button has onClick',
78
+ description: 'Buttons should have onClick handlers',
79
+ severity: 'error' as const,
80
+ pattern: /<Button[^>]*(?!onClick)[^>]*>/g,
81
+ message: 'Button must have an onClick handler.',
82
+ autoFixable: false,
83
+ validator: (content: string, match: RegExpExecArray) => {
84
+ // Check if onClick exists in the button
85
+ const buttonTag = match[0];
86
+ return !buttonTag.includes('onClick') && !buttonTag.includes('type="submit"');
87
+ },
88
+ },
89
+ {
90
+ id: 'async-has-try-catch',
91
+ name: 'Async has try/catch',
92
+ description: 'Async functions should have error handling',
93
+ severity: 'error' as const,
94
+ pattern: /async\s+(?:function\s+\w+|\([^)]*\)\s*=>|\w+\s*=\s*async\s*\([^)]*\)\s*=>)\s*\{[^}]*\}/g,
95
+ message: 'Async function should have try/catch error handling.',
96
+ autoFixable: false,
97
+ validator: (content: string, match: RegExpExecArray) => {
98
+ const funcBody = match[0];
99
+ return !funcBody.includes('try') && !funcBody.includes('catch');
100
+ },
101
+ },
102
+ {
103
+ id: 'no-hardcoded-secrets',
104
+ name: 'No hardcoded secrets',
105
+ description: 'Secrets should be in environment variables',
106
+ severity: 'error' as const,
107
+ pattern: /(sk_live_|sk_test_|pk_live_|pk_test_|api_key\s*=\s*['"][^'"]+['"])/gi,
108
+ message: 'Possible hardcoded secret. Use environment variables.',
109
+ autoFixable: false,
110
+ },
111
+ {
112
+ id: 'no-eval',
113
+ name: 'No eval()',
114
+ description: 'Never use eval() - security risk',
115
+ severity: 'error' as const,
116
+ pattern: /\beval\s*\(/g,
117
+ message: 'Never use eval(). It is a security vulnerability.',
118
+ autoFixable: false,
119
+ },
120
+ {
121
+ id: 'no-innerhtml',
122
+ name: 'No innerHTML',
123
+ description: 'Avoid direct innerHTML assignment',
124
+ severity: 'error' as const,
125
+ pattern: /\.innerHTML\s*=/g,
126
+ message: 'Avoid innerHTML. Use proper React rendering or DOMPurify.',
127
+ autoFixable: false,
128
+ },
129
+ {
130
+ id: 'loading-state',
131
+ name: 'Loading state in components',
132
+ description: 'Components with fetch should have loading states',
133
+ severity: 'warning' as const,
134
+ pattern: /useEffect\s*\([^)]*fetch\([^)]*\)/g,
135
+ message: 'Components fetching data should have loading states.',
136
+ autoFixable: false,
137
+ validator: (content: string) => {
138
+ return content.includes('fetch(') &&
139
+ !content.includes('isLoading') &&
140
+ !content.includes('loading') &&
141
+ !content.includes('isPending');
142
+ },
143
+ },
144
+ {
145
+ id: 'form-validation',
146
+ name: 'Form has validation',
147
+ description: 'Forms should use Zod validation',
148
+ severity: 'warning' as const,
149
+ pattern: /<form[^>]*onSubmit/gi,
150
+ message: 'Forms should use Zod schema validation.',
151
+ autoFixable: false,
152
+ validator: (content: string) => {
153
+ return content.includes('<form') &&
154
+ !content.includes('zodResolver') &&
155
+ !content.includes('z.object');
156
+ },
157
+ },
158
+ {
159
+ id: 'use-client-directive',
160
+ name: 'use client directive',
161
+ description: 'Client components should have "use client" directive',
162
+ severity: 'error' as const,
163
+ pattern: /(useState|useEffect|useReducer|useContext|useRef|useCallback|useMemo)\s*\(/g,
164
+ message: 'Components using hooks must have "use client" directive.',
165
+ autoFixable: true,
166
+ validator: (content: string) => {
167
+ const hasHooks = /(useState|useEffect|useReducer|useContext|useRef|useCallback|useMemo)\s*\(/.test(content);
168
+ const hasDirective = content.trim().startsWith("'use client'") || content.trim().startsWith('"use client"');
169
+ return hasHooks && !hasDirective;
170
+ },
171
+ },
172
+ {
173
+ id: 'key-prop-in-map',
174
+ name: 'Key prop in map',
175
+ description: 'Elements in .map() should have key prop',
176
+ severity: 'error' as const,
177
+ pattern: /\.map\s*\([^)]*\)\s*=>\s*(?:\(?\s*<[A-Z][^>]*(?!key=)[^>]*>)/g,
178
+ message: 'Elements in .map() must have a unique key prop.',
179
+ autoFixable: false,
180
+ },
181
+ ];
182
+
183
+ export async function checkCommand(options: CheckOptions = {}): Promise<void> {
184
+ const config = new Config();
185
+
186
+ if (!config.isInProject()) {
187
+ p.log.error('Not in a CodeBakers project.');
188
+ return;
189
+ }
190
+
191
+ p.intro(chalk.bgCyan.black(' CodeBakers Pattern Check '));
192
+
193
+ const spinner = p.spinner();
194
+ spinner.start('Analyzing code...');
195
+
196
+ const result = await runPatternCheck(options.fix || false);
197
+
198
+ spinner.stop('Analysis complete');
199
+
200
+ // Display results
201
+ displayResults(result);
202
+
203
+ if (result.violations.length > 0 && options.fix) {
204
+ const fixable = result.violations.filter(v => v.autoFixable);
205
+ if (fixable.length > 0) {
206
+ spinner.start(`Auto-fixing ${fixable.length} violations...`);
207
+ await autoFix(fixable);
208
+ spinner.stop('Auto-fix complete');
209
+ }
210
+ }
211
+
212
+ // Exit with error code if there are errors
213
+ const errors = result.violations.filter(v => v.severity === 'error');
214
+ if (errors.length > 0) {
215
+ p.outro(chalk.red(`❌ ${errors.length} errors found. Fix before committing.`));
216
+ process.exit(1);
217
+ } else if (result.violations.length > 0) {
218
+ p.outro(chalk.yellow(`⚠️ ${result.violations.length} warnings. Consider fixing.`));
219
+ } else {
220
+ p.outro(chalk.green('✓ All patterns satisfied!'));
221
+ }
222
+ }
223
+
224
+ export async function runPatternCheck(autoFix: boolean): Promise<CheckResult> {
225
+ const cwd = process.cwd();
226
+ const violations: Violation[] = [];
227
+
228
+ // Find all TypeScript/JavaScript files
229
+ const files = await glob(['src/**/*.{ts,tsx,js,jsx}'], {
230
+ cwd,
231
+ ignore: ['**/node_modules/**', '**/.next/**', '**/dist/**'],
232
+ });
233
+
234
+ for (const file of files) {
235
+ const filePath = path.join(cwd, file);
236
+ const content = await fs.readFile(filePath, 'utf-8');
237
+ const lines = content.split('\n');
238
+
239
+ for (const rule of RULES) {
240
+ // If rule has custom validator, use it
241
+ if (rule.validator) {
242
+ // Create a fresh regex for each file
243
+ const regex = new RegExp(rule.pattern.source, rule.pattern.flags);
244
+ let match;
245
+ while ((match = regex.exec(content)) !== null) {
246
+ if (rule.validator(content, match)) {
247
+ const line = content.substring(0, match.index).split('\n').length;
248
+ violations.push({
249
+ file,
250
+ line,
251
+ rule: rule.id,
252
+ message: rule.message,
253
+ severity: rule.severity,
254
+ autoFixable: rule.autoFixable,
255
+ });
256
+ break; // One violation per rule per file
257
+ }
258
+ }
259
+ } else {
260
+ // Standard pattern matching
261
+ const regex = new RegExp(rule.pattern.source, rule.pattern.flags);
262
+ let match;
263
+ while ((match = regex.exec(content)) !== null) {
264
+ const line = content.substring(0, match.index).split('\n').length;
265
+ violations.push({
266
+ file,
267
+ line,
268
+ rule: rule.id,
269
+ message: rule.message,
270
+ severity: rule.severity,
271
+ autoFixable: rule.autoFixable,
272
+ });
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ return {
279
+ violations,
280
+ filesChecked: files.length,
281
+ passed: violations.length === 0,
282
+ };
283
+ }
284
+
285
+ function displayResults(result: CheckResult): void {
286
+ console.log(chalk.dim(`\nChecked ${result.filesChecked} files\n`));
287
+
288
+ if (result.violations.length === 0) {
289
+ console.log(chalk.green(' ✓ No violations found\n'));
290
+ return;
291
+ }
292
+
293
+ // Group by file
294
+ const byFile = new Map<string, Violation[]>();
295
+ for (const v of result.violations) {
296
+ if (!byFile.has(v.file)) {
297
+ byFile.set(v.file, []);
298
+ }
299
+ byFile.get(v.file)!.push(v);
300
+ }
301
+
302
+ for (const [file, fileViolations] of byFile) {
303
+ console.log(chalk.bold(file));
304
+
305
+ for (const v of fileViolations) {
306
+ const icon = v.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');
307
+ const fixable = v.autoFixable ? chalk.dim(' (auto-fixable)') : '';
308
+ console.log(` ${icon} Line ${v.line}: ${v.message}${fixable}`);
309
+ }
310
+
311
+ console.log('');
312
+ }
313
+
314
+ // Summary
315
+ const errors = result.violations.filter(v => v.severity === 'error').length;
316
+ const warnings = result.violations.filter(v => v.severity === 'warning').length;
317
+
318
+ console.log(chalk.bold('Summary:'));
319
+ if (errors > 0) {
320
+ console.log(chalk.red(` ${errors} error(s)`));
321
+ }
322
+ if (warnings > 0) {
323
+ console.log(chalk.yellow(` ${warnings} warning(s)`));
324
+ }
325
+ console.log('');
326
+ }
327
+
328
+ async function autoFix(violations: Violation[]): Promise<void> {
329
+ const cwd = process.cwd();
330
+
331
+ // Group by file
332
+ const byFile = new Map<string, Violation[]>();
333
+ for (const v of violations) {
334
+ if (!byFile.has(v.file)) {
335
+ byFile.set(v.file, []);
336
+ }
337
+ byFile.get(v.file)!.push(v);
338
+ }
339
+
340
+ for (const [file, fileViolations] of byFile) {
341
+ const filePath = path.join(cwd, file);
342
+ let content = await fs.readFile(filePath, 'utf-8');
343
+
344
+ for (const v of fileViolations) {
345
+ switch (v.rule) {
346
+ case 'no-console-log':
347
+ // Remove console.log statements
348
+ content = content.replace(/console\.log\([^)]*\);?\n?/g, '');
349
+ break;
350
+
351
+ case 'use-client-directive':
352
+ // Add "use client" directive at the top
353
+ if (!content.startsWith("'use client'") && !content.startsWith('"use client"')) {
354
+ content = "'use client';\n\n" + content;
355
+ }
356
+ break;
357
+ }
358
+ }
359
+
360
+ await fs.writeFile(filePath, content);
361
+ }
362
+ }
363
+
364
+ // Export for use in other commands
365
+ export { Violation, CheckResult };