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.
- package/README.md +275 -60
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3999 -0
- package/install.bat +9 -0
- package/package.json +71 -115
- package/src/channels/discord.ts +5 -0
- package/src/channels/slack.ts +5 -0
- package/src/channels/sms.ts +4 -0
- package/src/channels/telegram.ts +5 -0
- package/src/channels/whatsapp.ts +7 -0
- package/src/commands/check.ts +365 -0
- package/src/commands/code.ts +684 -0
- package/src/commands/connect.ts +12 -0
- package/src/commands/deploy.ts +414 -0
- package/src/commands/fix.ts +20 -0
- package/src/commands/gateway.ts +604 -0
- package/src/commands/generate.ts +178 -0
- package/src/commands/init.ts +574 -0
- package/src/commands/learn.ts +36 -0
- package/src/commands/security.ts +102 -0
- package/src/commands/setup.ts +448 -0
- package/src/commands/status.ts +56 -0
- package/src/index.ts +268 -0
- package/src/patterns/loader.ts +337 -0
- package/src/services/github.ts +61 -0
- package/src/services/supabase.ts +147 -0
- package/src/services/vercel.ts +61 -0
- package/src/utils/claude-md.ts +287 -0
- package/src/utils/config.ts +282 -0
- package/src/utils/updates.ts +27 -0
- package/tsconfig.json +17 -10
- package/.vscodeignore +0 -18
- package/LICENSE +0 -21
- package/codebakers-1.0.0.vsix +0 -0
- package/codebakers-1.0.10.vsix +0 -0
- package/codebakers-1.0.11.vsix +0 -0
- package/codebakers-1.0.12.vsix +0 -0
- package/codebakers-1.0.13.vsix +0 -0
- package/codebakers-1.0.14.vsix +0 -0
- package/codebakers-1.0.15.vsix +0 -0
- package/codebakers-1.0.16.vsix +0 -0
- package/codebakers-1.0.17.vsix +0 -0
- package/codebakers-1.0.18.vsix +0 -0
- package/codebakers-1.0.19.vsix +0 -0
- package/codebakers-1.0.20.vsix +0 -0
- package/codebakers-1.0.21.vsix +0 -0
- package/codebakers-1.0.22.vsix +0 -0
- package/codebakers-1.0.23.vsix +0 -0
- package/codebakers-1.0.24.vsix +0 -0
- package/codebakers-1.0.25.vsix +0 -0
- package/codebakers-1.0.26.vsix +0 -0
- package/codebakers-1.0.27.vsix +0 -0
- package/codebakers-1.0.28.vsix +0 -0
- package/codebakers-1.0.29.vsix +0 -0
- package/codebakers-1.0.30.vsix +0 -0
- package/codebakers-1.0.31.vsix +0 -0
- package/codebakers-1.0.32.vsix +0 -0
- package/codebakers-1.0.35.vsix +0 -0
- package/codebakers-1.0.36.vsix +0 -0
- package/codebakers-1.0.37.vsix +0 -0
- package/codebakers-1.0.38.vsix +0 -0
- package/codebakers-1.0.39.vsix +0 -0
- package/codebakers-1.0.40.vsix +0 -0
- package/codebakers-1.0.41.vsix +0 -0
- package/codebakers-1.0.42.vsix +0 -0
- package/codebakers-1.0.43.vsix +0 -0
- package/codebakers-1.0.44.vsix +0 -0
- package/codebakers-1.0.45.vsix +0 -0
- package/dist/extension.js +0 -1394
- package/esbuild.js +0 -63
- package/media/icon.png +0 -0
- package/media/icon.svg +0 -7
- package/nul +0 -1
- package/preview.html +0 -547
- package/src/ChatPanelProvider.ts +0 -1815
- package/src/ChatViewProvider.ts +0 -749
- package/src/CodeBakersClient.ts +0 -1146
- package/src/CodeValidator.ts +0 -645
- package/src/FileOperations.ts +0 -410
- package/src/ProjectContext.ts +0 -526
- package/src/extension.ts +0 -332
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import * as fs from 'fs-extra';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
7
|
+
import { Config } from '../utils/config.js';
|
|
8
|
+
import { VercelService } from '../services/vercel.js';
|
|
9
|
+
import { runPatternCheck } from './check.js';
|
|
10
|
+
|
|
11
|
+
interface DeployOptions {
|
|
12
|
+
preview?: boolean;
|
|
13
|
+
check?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function deployCommand(options: DeployOptions = {}): Promise<void> {
|
|
17
|
+
const config = new Config();
|
|
18
|
+
|
|
19
|
+
if (!config.isInProject()) {
|
|
20
|
+
p.log.error('Not in a CodeBakers project.');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
p.intro(chalk.bgCyan.black(' Deploy to Production '));
|
|
25
|
+
|
|
26
|
+
const spinner = p.spinner();
|
|
27
|
+
|
|
28
|
+
// Step 1: Run pattern check (unless skipped)
|
|
29
|
+
if (options.check !== false) {
|
|
30
|
+
spinner.start('Running CodeBakers check...');
|
|
31
|
+
const checkResult = await runPatternCheck(false);
|
|
32
|
+
|
|
33
|
+
if (!checkResult.passed) {
|
|
34
|
+
spinner.stop('');
|
|
35
|
+
const errors = checkResult.violations.filter(v => v.severity === 'error');
|
|
36
|
+
|
|
37
|
+
if (errors.length > 0) {
|
|
38
|
+
p.log.error(`${errors.length} pattern violations found. Fix before deploying.`);
|
|
39
|
+
|
|
40
|
+
const showViolations = await p.confirm({
|
|
41
|
+
message: 'Show violations?',
|
|
42
|
+
initialValue: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (showViolations && !p.isCancel(showViolations)) {
|
|
46
|
+
for (const v of errors) {
|
|
47
|
+
console.log(chalk.red(` ✗ ${v.file}:${v.line} - ${v.message}`));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const autoFix = await p.confirm({
|
|
52
|
+
message: 'Attempt auto-fix with AI?',
|
|
53
|
+
initialValue: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!autoFix || p.isCancel(autoFix)) {
|
|
57
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Auto-fix with AI
|
|
62
|
+
spinner.start('Auto-fixing with AI...');
|
|
63
|
+
await autoFixWithAI(config, errors);
|
|
64
|
+
spinner.stop('Auto-fix complete');
|
|
65
|
+
|
|
66
|
+
// Re-run check
|
|
67
|
+
const recheck = await runPatternCheck(false);
|
|
68
|
+
if (!recheck.passed) {
|
|
69
|
+
p.log.error('Some violations remain. Manual fix required.');
|
|
70
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
spinner.stop('Pattern check passed');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Step 2: Run TypeScript check
|
|
79
|
+
spinner.start('Running TypeScript check...');
|
|
80
|
+
try {
|
|
81
|
+
await execa('npx', ['tsc', '--noEmit'], { cwd: process.cwd() });
|
|
82
|
+
spinner.stop('TypeScript check passed');
|
|
83
|
+
} catch (error) {
|
|
84
|
+
spinner.stop('');
|
|
85
|
+
p.log.error('TypeScript errors found.');
|
|
86
|
+
|
|
87
|
+
const fix = await p.confirm({
|
|
88
|
+
message: 'Attempt to fix TypeScript errors?',
|
|
89
|
+
initialValue: true,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (fix && !p.isCancel(fix)) {
|
|
93
|
+
spinner.start('Fixing TypeScript errors...');
|
|
94
|
+
const fixed = await fixTypeScriptErrors(config);
|
|
95
|
+
spinner.stop(fixed ? 'TypeScript errors fixed' : 'Could not auto-fix all errors');
|
|
96
|
+
|
|
97
|
+
if (!fixed) {
|
|
98
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Step 3: Run build
|
|
108
|
+
spinner.start('Building project...');
|
|
109
|
+
try {
|
|
110
|
+
await execa('pnpm', ['build'], { cwd: process.cwd() });
|
|
111
|
+
spinner.stop('Build successful');
|
|
112
|
+
} catch (error) {
|
|
113
|
+
spinner.stop('');
|
|
114
|
+
p.log.error('Build failed.');
|
|
115
|
+
|
|
116
|
+
const errorOutput = error instanceof Error ? error.message : 'Unknown error';
|
|
117
|
+
console.log(chalk.dim(errorOutput));
|
|
118
|
+
|
|
119
|
+
const fix = await p.confirm({
|
|
120
|
+
message: 'Attempt to fix build errors with AI?',
|
|
121
|
+
initialValue: true,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (fix && !p.isCancel(fix)) {
|
|
125
|
+
spinner.start('Fixing build errors...');
|
|
126
|
+
const fixed = await fixBuildErrors(config, errorOutput);
|
|
127
|
+
spinner.stop(fixed ? 'Build errors fixed' : 'Could not auto-fix');
|
|
128
|
+
|
|
129
|
+
if (fixed) {
|
|
130
|
+
// Retry build
|
|
131
|
+
spinner.start('Retrying build...');
|
|
132
|
+
try {
|
|
133
|
+
await execa('pnpm', ['build'], { cwd: process.cwd() });
|
|
134
|
+
spinner.stop('Build successful');
|
|
135
|
+
} catch {
|
|
136
|
+
spinner.stop('Build still failing');
|
|
137
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
p.outro(chalk.red('Deploy cancelled.'));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Step 4: Git commit if there are changes
|
|
151
|
+
spinner.start('Checking for uncommitted changes...');
|
|
152
|
+
const { stdout: gitStatus } = await execa('git', ['status', '--porcelain'], { cwd: process.cwd() });
|
|
153
|
+
|
|
154
|
+
if (gitStatus.trim()) {
|
|
155
|
+
spinner.stop('');
|
|
156
|
+
const commit = await p.confirm({
|
|
157
|
+
message: 'You have uncommitted changes. Commit before deploying?',
|
|
158
|
+
initialValue: true,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (commit && !p.isCancel(commit)) {
|
|
162
|
+
const message = await p.text({
|
|
163
|
+
message: 'Commit message:',
|
|
164
|
+
placeholder: 'Pre-deploy changes',
|
|
165
|
+
initialValue: 'Pre-deploy changes',
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (!p.isCancel(message)) {
|
|
169
|
+
await execa('git', ['add', '.'], { cwd: process.cwd() });
|
|
170
|
+
await execa('git', ['commit', '-m', message as string], { cwd: process.cwd() });
|
|
171
|
+
|
|
172
|
+
spinner.start('Pushing to GitHub...');
|
|
173
|
+
await execa('git', ['push'], { cwd: process.cwd() });
|
|
174
|
+
spinner.stop('Pushed to GitHub');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
spinner.stop('No uncommitted changes');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Step 5: Deploy to Vercel
|
|
182
|
+
const deployType = options.preview ? 'preview' : 'production';
|
|
183
|
+
spinner.start(`Deploying to ${deployType}...`);
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const vercel = new VercelService(config);
|
|
187
|
+
const deployment = await vercel.deploy(process.cwd(), !options.preview);
|
|
188
|
+
|
|
189
|
+
spinner.stop('Deployment complete!');
|
|
190
|
+
|
|
191
|
+
console.log(boxedOutput(`
|
|
192
|
+
${chalk.green('✓')} Deployed successfully!
|
|
193
|
+
|
|
194
|
+
${chalk.bold('URL:')} ${deployment.url}
|
|
195
|
+
${chalk.bold('Type:')} ${deployType}
|
|
196
|
+
${chalk.bold('Time:')} ${new Date().toLocaleTimeString()}
|
|
197
|
+
|
|
198
|
+
${chalk.dim('View in Vercel Dashboard:')}
|
|
199
|
+
${chalk.dim(deployment.dashboardUrl || 'https://vercel.com/dashboard')}
|
|
200
|
+
`));
|
|
201
|
+
|
|
202
|
+
} catch (error) {
|
|
203
|
+
spinner.stop('');
|
|
204
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
205
|
+
p.log.error(`Deployment failed: ${errorMsg}`);
|
|
206
|
+
|
|
207
|
+
// Check if it's a build error from Vercel
|
|
208
|
+
if (errorMsg.includes('Build failed')) {
|
|
209
|
+
const retry = await p.confirm({
|
|
210
|
+
message: 'Attempt to fix and retry?',
|
|
211
|
+
initialValue: true,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (retry && !p.isCancel(retry)) {
|
|
215
|
+
spinner.start('Analyzing Vercel build error...');
|
|
216
|
+
// Would fetch Vercel logs and auto-fix
|
|
217
|
+
spinner.stop('Fix attempted');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
p.outro(chalk.red('Deploy failed.'));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function autoFixWithAI(
|
|
226
|
+
config: Config,
|
|
227
|
+
violations: Array<{ file: string; line: number; rule: string; message: string }>
|
|
228
|
+
): Promise<void> {
|
|
229
|
+
const anthropicCreds = config.getCredentials('anthropic');
|
|
230
|
+
if (!anthropicCreds?.apiKey) {
|
|
231
|
+
throw new Error('Anthropic API key not configured');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const anthropic = new Anthropic({
|
|
235
|
+
apiKey: anthropicCreds.apiKey,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Group by file
|
|
239
|
+
const byFile = new Map<string, typeof violations>();
|
|
240
|
+
for (const v of violations) {
|
|
241
|
+
if (!byFile.has(v.file)) {
|
|
242
|
+
byFile.set(v.file, []);
|
|
243
|
+
}
|
|
244
|
+
byFile.get(v.file)!.push(v);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const [file, fileViolations] of byFile) {
|
|
248
|
+
const filePath = path.join(process.cwd(), file);
|
|
249
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
250
|
+
|
|
251
|
+
const prompt = `Fix these CodeBakers pattern violations in this file:
|
|
252
|
+
|
|
253
|
+
Violations:
|
|
254
|
+
${fileViolations.map(v => `- Line ${v.line}: ${v.rule} - ${v.message}`).join('\n')}
|
|
255
|
+
|
|
256
|
+
Current file content:
|
|
257
|
+
\`\`\`typescript
|
|
258
|
+
${content}
|
|
259
|
+
\`\`\`
|
|
260
|
+
|
|
261
|
+
Output ONLY the corrected file content, no explanations. Keep all existing functionality.`;
|
|
262
|
+
|
|
263
|
+
const response = await anthropic.messages.create({
|
|
264
|
+
model: 'claude-sonnet-4-20250514',
|
|
265
|
+
max_tokens: 8192,
|
|
266
|
+
messages: [{ role: 'user', content: prompt }],
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
let fixed = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
270
|
+
|
|
271
|
+
// Extract code from markdown if present
|
|
272
|
+
const codeMatch = fixed.match(/```(?:typescript|tsx?)?\n([\s\S]+?)\n```/);
|
|
273
|
+
if (codeMatch) {
|
|
274
|
+
fixed = codeMatch[1];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await fs.writeFile(filePath, fixed);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function fixTypeScriptErrors(config: Config): Promise<boolean> {
|
|
282
|
+
try {
|
|
283
|
+
// Get TypeScript errors
|
|
284
|
+
const { stderr } = await execa('npx', ['tsc', '--noEmit'], {
|
|
285
|
+
cwd: process.cwd(),
|
|
286
|
+
reject: false,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
if (!stderr) return true;
|
|
290
|
+
|
|
291
|
+
const anthropicCreds = config.getCredentials('anthropic');
|
|
292
|
+
if (!anthropicCreds?.apiKey) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const anthropic = new Anthropic({
|
|
297
|
+
apiKey: anthropicCreds.apiKey,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Parse error locations
|
|
301
|
+
const errorLines = stderr.split('\n').filter(line => line.includes('error TS'));
|
|
302
|
+
|
|
303
|
+
for (const errorLine of errorLines.slice(0, 10)) { // Limit to 10 errors
|
|
304
|
+
const match = errorLine.match(/^(.+?)\((\d+),(\d+)\): error (TS\d+): (.+)$/);
|
|
305
|
+
if (!match) continue;
|
|
306
|
+
|
|
307
|
+
const [, file, line, , , message] = match;
|
|
308
|
+
const filePath = path.join(process.cwd(), file);
|
|
309
|
+
|
|
310
|
+
if (!await fs.pathExists(filePath)) continue;
|
|
311
|
+
|
|
312
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
313
|
+
|
|
314
|
+
const prompt = `Fix this TypeScript error:
|
|
315
|
+
|
|
316
|
+
File: ${file}
|
|
317
|
+
Line: ${line}
|
|
318
|
+
Error: ${message}
|
|
319
|
+
|
|
320
|
+
Current file content:
|
|
321
|
+
\`\`\`typescript
|
|
322
|
+
${content}
|
|
323
|
+
\`\`\`
|
|
324
|
+
|
|
325
|
+
Output ONLY the corrected file content, no explanations.`;
|
|
326
|
+
|
|
327
|
+
const response = await anthropic.messages.create({
|
|
328
|
+
model: 'claude-sonnet-4-20250514',
|
|
329
|
+
max_tokens: 8192,
|
|
330
|
+
messages: [{ role: 'user', content: prompt }],
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
let fixed = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
334
|
+
|
|
335
|
+
const codeMatch = fixed.match(/```(?:typescript|tsx?)?\n([\s\S]+?)\n```/);
|
|
336
|
+
if (codeMatch) {
|
|
337
|
+
fixed = codeMatch[1];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await fs.writeFile(filePath, fixed);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return true;
|
|
344
|
+
} catch {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function fixBuildErrors(config: Config, errorOutput: string): Promise<boolean> {
|
|
350
|
+
const anthropicCreds = config.getCredentials('anthropic');
|
|
351
|
+
if (!anthropicCreds?.apiKey) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const anthropic = new Anthropic({
|
|
356
|
+
apiKey: anthropicCreds.apiKey,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Parse error to find file
|
|
360
|
+
const fileMatch = errorOutput.match(/(?:Error|error).*?(?:in|at)\s+(.+?\.tsx?)(?::|$)/);
|
|
361
|
+
if (!fileMatch) return false;
|
|
362
|
+
|
|
363
|
+
const file = fileMatch[1];
|
|
364
|
+
const filePath = path.join(process.cwd(), file);
|
|
365
|
+
|
|
366
|
+
if (!await fs.pathExists(filePath)) return false;
|
|
367
|
+
|
|
368
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
369
|
+
|
|
370
|
+
const prompt = `Fix this build error:
|
|
371
|
+
|
|
372
|
+
Error output:
|
|
373
|
+
${errorOutput}
|
|
374
|
+
|
|
375
|
+
File: ${file}
|
|
376
|
+
|
|
377
|
+
Current file content:
|
|
378
|
+
\`\`\`typescript
|
|
379
|
+
${content}
|
|
380
|
+
\`\`\`
|
|
381
|
+
|
|
382
|
+
Output ONLY the corrected file content, no explanations.`;
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const response = await anthropic.messages.create({
|
|
386
|
+
model: 'claude-sonnet-4-20250514',
|
|
387
|
+
max_tokens: 8192,
|
|
388
|
+
messages: [{ role: 'user', content: prompt }],
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
let fixed = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
392
|
+
|
|
393
|
+
const codeMatch = fixed.match(/```(?:typescript|tsx?)?\n([\s\S]+?)\n```/);
|
|
394
|
+
if (codeMatch) {
|
|
395
|
+
fixed = codeMatch[1];
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
await fs.writeFile(filePath, fixed);
|
|
399
|
+
return true;
|
|
400
|
+
} catch {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function boxedOutput(content: string): string {
|
|
406
|
+
const lines = content.split('\n');
|
|
407
|
+
const maxLength = Math.max(...lines.map(l => l.replace(/\x1b\[[0-9;]*m/g, '').length));
|
|
408
|
+
const border = '─'.repeat(maxLength + 2);
|
|
409
|
+
|
|
410
|
+
return `
|
|
411
|
+
╭${border}╮
|
|
412
|
+
${lines.map(l => `│ ${l.padEnd(maxLength)} │`).join('\n')}
|
|
413
|
+
╰${border}╯`;
|
|
414
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { runPatternCheck } from './check.js';
|
|
4
|
+
|
|
5
|
+
export async function fixCommand(): Promise<void> {
|
|
6
|
+
p.intro(chalk.bgCyan.black(' Auto-Fix '));
|
|
7
|
+
|
|
8
|
+
const spinner = p.spinner();
|
|
9
|
+
spinner.start('Analyzing code...');
|
|
10
|
+
|
|
11
|
+
const result = await runPatternCheck(true);
|
|
12
|
+
|
|
13
|
+
if (result.passed) {
|
|
14
|
+
spinner.stop('No issues found!');
|
|
15
|
+
} else {
|
|
16
|
+
spinner.stop(`Fixed ${result.violations.length} issues`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
p.outro(chalk.green('Done!'));
|
|
20
|
+
}
|