workflow-agent-cli 2.2.2 → 2.3.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/LICENSE +21 -0
- package/dist/chunk-YI3LBZ7I.js +1425 -0
- package/dist/chunk-YI3LBZ7I.js.map +1 -0
- package/dist/cli/index.js +208 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +68 -1
- package/dist/index.js +9 -1
- package/dist/scripts/postinstall.js +0 -0
- package/package.json +12 -12
- package/dist/chunk-AN6UOHBB.js +0 -308
- package/dist/chunk-AN6UOHBB.js.map +0 -1
|
@@ -0,0 +1,1425 @@
|
|
|
1
|
+
// src/utils/check-runner.ts
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
var QUALITY_CHECKS = [
|
|
5
|
+
{
|
|
6
|
+
name: "typecheck",
|
|
7
|
+
displayName: "Type Check",
|
|
8
|
+
command: "pnpm",
|
|
9
|
+
args: ["typecheck"],
|
|
10
|
+
canAutoFix: false
|
|
11
|
+
// TypeScript errors need manual/LLM fix
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "lint",
|
|
15
|
+
displayName: "Lint",
|
|
16
|
+
command: "pnpm",
|
|
17
|
+
args: ["lint"],
|
|
18
|
+
fixCommand: "pnpm",
|
|
19
|
+
fixArgs: ["lint", "--fix"],
|
|
20
|
+
canAutoFix: true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "format",
|
|
24
|
+
displayName: "Format",
|
|
25
|
+
command: "pnpm",
|
|
26
|
+
args: ["format", "--check"],
|
|
27
|
+
fixCommand: "pnpm",
|
|
28
|
+
fixArgs: ["format"],
|
|
29
|
+
canAutoFix: true
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "test",
|
|
33
|
+
displayName: "Tests",
|
|
34
|
+
command: "pnpm",
|
|
35
|
+
args: ["test"],
|
|
36
|
+
canAutoFix: false
|
|
37
|
+
// Tests need manual/LLM fix
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "build",
|
|
41
|
+
displayName: "Build",
|
|
42
|
+
command: "pnpm",
|
|
43
|
+
args: ["build"],
|
|
44
|
+
canAutoFix: false
|
|
45
|
+
// Build errors need manual/LLM fix
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
async function runCheck(check, cwd) {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
try {
|
|
51
|
+
const result = await execa(check.command, check.args, {
|
|
52
|
+
cwd,
|
|
53
|
+
reject: false,
|
|
54
|
+
all: true
|
|
55
|
+
});
|
|
56
|
+
const duration = Date.now() - startTime;
|
|
57
|
+
if (result.exitCode === 0) {
|
|
58
|
+
return {
|
|
59
|
+
check,
|
|
60
|
+
success: true,
|
|
61
|
+
output: result.all || "",
|
|
62
|
+
duration
|
|
63
|
+
};
|
|
64
|
+
} else {
|
|
65
|
+
return {
|
|
66
|
+
check,
|
|
67
|
+
success: false,
|
|
68
|
+
output: result.all || "",
|
|
69
|
+
error: result.stderr || result.all || "Check failed",
|
|
70
|
+
duration
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const duration = Date.now() - startTime;
|
|
75
|
+
const execaError = error;
|
|
76
|
+
return {
|
|
77
|
+
check,
|
|
78
|
+
success: false,
|
|
79
|
+
output: execaError.all?.toString() || execaError.message || "",
|
|
80
|
+
error: execaError.message,
|
|
81
|
+
duration
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function applyFix(check, cwd) {
|
|
86
|
+
if (!check.canAutoFix || !check.fixCommand) {
|
|
87
|
+
return { success: false, output: "Check does not support auto-fix" };
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const result = await execa(check.fixCommand, check.fixArgs || [], {
|
|
91
|
+
cwd,
|
|
92
|
+
reject: false,
|
|
93
|
+
all: true
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
success: result.exitCode === 0,
|
|
97
|
+
output: result.all || ""
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const execaError = error;
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
output: execaError.message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function formatFixCommand(check) {
|
|
108
|
+
if (!check.fixCommand) return "";
|
|
109
|
+
return `${check.fixCommand} ${(check.fixArgs || []).join(" ")}`;
|
|
110
|
+
}
|
|
111
|
+
async function runAllChecks(cwd, options = {}) {
|
|
112
|
+
const {
|
|
113
|
+
maxRetries = 10,
|
|
114
|
+
autoFix = true,
|
|
115
|
+
dryRun = false,
|
|
116
|
+
onProgress
|
|
117
|
+
} = options;
|
|
118
|
+
const log = (message, type = "info") => {
|
|
119
|
+
if (onProgress) {
|
|
120
|
+
onProgress(message, type);
|
|
121
|
+
} else {
|
|
122
|
+
switch (type) {
|
|
123
|
+
case "success":
|
|
124
|
+
console.log(chalk.green(message));
|
|
125
|
+
break;
|
|
126
|
+
case "error":
|
|
127
|
+
console.log(chalk.red(message));
|
|
128
|
+
break;
|
|
129
|
+
case "warning":
|
|
130
|
+
console.log(chalk.yellow(message));
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
console.log(message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
let attempt = 0;
|
|
138
|
+
let fixesApplied = 0;
|
|
139
|
+
const pendingFixes = [];
|
|
140
|
+
while (attempt < maxRetries) {
|
|
141
|
+
attempt++;
|
|
142
|
+
log(`
|
|
143
|
+
${"\u2501".repeat(50)}`, "info");
|
|
144
|
+
log(`\u{1F504} Validation Cycle ${attempt}/${maxRetries}`, "info");
|
|
145
|
+
log(`${"\u2501".repeat(50)}
|
|
146
|
+
`, "info");
|
|
147
|
+
const results = [];
|
|
148
|
+
let allPassed = true;
|
|
149
|
+
let fixAppliedThisCycle = false;
|
|
150
|
+
for (let i = 0; i < QUALITY_CHECKS.length; i++) {
|
|
151
|
+
const check = QUALITY_CHECKS[i];
|
|
152
|
+
const stepNum = i + 1;
|
|
153
|
+
const totalSteps = QUALITY_CHECKS.length;
|
|
154
|
+
log(`\u{1F4CB} Step ${stepNum}/${totalSteps}: ${check.displayName}...`, "info");
|
|
155
|
+
const result = await runCheck(check, cwd);
|
|
156
|
+
results.push(result);
|
|
157
|
+
if (result.success) {
|
|
158
|
+
log(`\u2705 ${check.displayName} passed (${result.duration}ms)`, "success");
|
|
159
|
+
} else {
|
|
160
|
+
allPassed = false;
|
|
161
|
+
log(`\u274C ${check.displayName} failed`, "error");
|
|
162
|
+
if (autoFix && check.canAutoFix && check.fixCommand) {
|
|
163
|
+
if (dryRun) {
|
|
164
|
+
log(
|
|
165
|
+
`\u{1F527} [DRY-RUN] Would run: ${formatFixCommand(check)}`,
|
|
166
|
+
"warning"
|
|
167
|
+
);
|
|
168
|
+
pendingFixes.push({ check, command: formatFixCommand(check) });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
log(`\u{1F527} Attempting auto-fix for ${check.displayName}...`, "warning");
|
|
172
|
+
const fixResult = await applyFix(check, cwd);
|
|
173
|
+
if (fixResult.success) {
|
|
174
|
+
log(`\u2728 Auto-fix applied for ${check.displayName}`, "success");
|
|
175
|
+
fixesApplied++;
|
|
176
|
+
fixAppliedThisCycle = true;
|
|
177
|
+
log(
|
|
178
|
+
`
|
|
179
|
+
\u{1F504} Fix applied - restarting all checks to verify...`,
|
|
180
|
+
"warning"
|
|
181
|
+
);
|
|
182
|
+
break;
|
|
183
|
+
} else {
|
|
184
|
+
log(`\u26A0\uFE0F Auto-fix failed for ${check.displayName}`, "error");
|
|
185
|
+
log(` Manual intervention required`, "error");
|
|
186
|
+
if (result.error) {
|
|
187
|
+
const errorPreview = result.error.slice(0, 500);
|
|
188
|
+
log(`
|
|
189
|
+
${chalk.dim(errorPreview)}`, "error");
|
|
190
|
+
if (result.error.length > 500) {
|
|
191
|
+
log(
|
|
192
|
+
chalk.dim(
|
|
193
|
+
`... (${result.error.length - 500} more characters)`
|
|
194
|
+
),
|
|
195
|
+
"error"
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
results,
|
|
202
|
+
totalAttempts: attempt,
|
|
203
|
+
fixesApplied
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
if (check.canAutoFix) {
|
|
208
|
+
log(
|
|
209
|
+
`\u26A0\uFE0F ${check.displayName} can be fixed with: ${formatFixCommand(check)}`,
|
|
210
|
+
"warning"
|
|
211
|
+
);
|
|
212
|
+
} else {
|
|
213
|
+
log(`\u26A0\uFE0F ${check.displayName} requires manual fix`, "error");
|
|
214
|
+
}
|
|
215
|
+
if (result.error) {
|
|
216
|
+
const errorPreview = result.error.slice(0, 500);
|
|
217
|
+
log(`
|
|
218
|
+
${chalk.dim(errorPreview)}`, "error");
|
|
219
|
+
if (result.error.length > 500) {
|
|
220
|
+
log(
|
|
221
|
+
chalk.dim(`... (${result.error.length - 500} more characters)`),
|
|
222
|
+
"error"
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!dryRun) {
|
|
227
|
+
return {
|
|
228
|
+
success: false,
|
|
229
|
+
results,
|
|
230
|
+
totalAttempts: attempt,
|
|
231
|
+
fixesApplied
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (dryRun && pendingFixes.length > 0) {
|
|
238
|
+
log(`
|
|
239
|
+
${"\u2501".repeat(50)}`, "info");
|
|
240
|
+
log(`\u{1F4CB} DRY-RUN SUMMARY`, "info");
|
|
241
|
+
log(`${"\u2501".repeat(50)}`, "info");
|
|
242
|
+
log(`
|
|
243
|
+
The following fixes would be applied:`, "warning");
|
|
244
|
+
for (const fix of pendingFixes) {
|
|
245
|
+
log(` \u2022 ${fix.check.displayName}: ${fix.command}`, "info");
|
|
246
|
+
}
|
|
247
|
+
log(`
|
|
248
|
+
Run without --dry-run to apply fixes.`, "info");
|
|
249
|
+
return {
|
|
250
|
+
success: false,
|
|
251
|
+
results,
|
|
252
|
+
totalAttempts: attempt,
|
|
253
|
+
fixesApplied: 0,
|
|
254
|
+
pendingFixes
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
if (allPassed) {
|
|
258
|
+
return {
|
|
259
|
+
success: true,
|
|
260
|
+
results,
|
|
261
|
+
totalAttempts: attempt,
|
|
262
|
+
fixesApplied
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (!fixAppliedThisCycle) {
|
|
266
|
+
return {
|
|
267
|
+
success: false,
|
|
268
|
+
results,
|
|
269
|
+
totalAttempts: attempt,
|
|
270
|
+
fixesApplied
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
log(`
|
|
275
|
+
\u274C Maximum retries (${maxRetries}) exceeded`, "error");
|
|
276
|
+
return {
|
|
277
|
+
success: false,
|
|
278
|
+
results: [],
|
|
279
|
+
totalAttempts: attempt,
|
|
280
|
+
fixesApplied
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async function hasUncommittedChanges(cwd) {
|
|
284
|
+
try {
|
|
285
|
+
const result = await execa("git", ["status", "--porcelain"], { cwd });
|
|
286
|
+
return result.stdout.trim().length > 0;
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function stageAllChanges(cwd) {
|
|
292
|
+
try {
|
|
293
|
+
await execa("git", ["add", "-A"], { cwd });
|
|
294
|
+
return true;
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/utils/auto-setup.ts
|
|
301
|
+
import { existsSync as existsSync2 } from "fs";
|
|
302
|
+
import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
|
|
303
|
+
import { join as join2 } from "path";
|
|
304
|
+
import { execa as execa3 } from "execa";
|
|
305
|
+
|
|
306
|
+
// src/utils/git-repo.ts
|
|
307
|
+
import { execa as execa2 } from "execa";
|
|
308
|
+
import { existsSync } from "fs";
|
|
309
|
+
import { readFile } from "fs/promises";
|
|
310
|
+
import { join } from "path";
|
|
311
|
+
async function detectPackageManager(projectPath = process.cwd()) {
|
|
312
|
+
if (existsSync(join(projectPath, "pnpm-lock.yaml"))) {
|
|
313
|
+
return "pnpm";
|
|
314
|
+
}
|
|
315
|
+
if (existsSync(join(projectPath, "yarn.lock"))) {
|
|
316
|
+
return "yarn";
|
|
317
|
+
}
|
|
318
|
+
if (existsSync(join(projectPath, "bun.lockb"))) {
|
|
319
|
+
return "bun";
|
|
320
|
+
}
|
|
321
|
+
if (existsSync(join(projectPath, "package-lock.json"))) {
|
|
322
|
+
return "npm";
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
const pkgPath = join(projectPath, "package.json");
|
|
326
|
+
if (existsSync(pkgPath)) {
|
|
327
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
328
|
+
if (pkg.packageManager) {
|
|
329
|
+
if (pkg.packageManager.startsWith("pnpm")) return "pnpm";
|
|
330
|
+
if (pkg.packageManager.startsWith("yarn")) return "yarn";
|
|
331
|
+
if (pkg.packageManager.startsWith("bun")) return "bun";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
return "npm";
|
|
337
|
+
}
|
|
338
|
+
async function isMonorepo(projectPath = process.cwd()) {
|
|
339
|
+
if (existsSync(join(projectPath, "pnpm-workspace.yaml"))) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
const pkgPath = join(projectPath, "package.json");
|
|
344
|
+
if (existsSync(pkgPath)) {
|
|
345
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
346
|
+
if (pkg.workspaces) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} catch {
|
|
351
|
+
}
|
|
352
|
+
if (existsSync(join(projectPath, "lerna.json"))) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
if (existsSync(join(projectPath, "nx.json"))) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
if (existsSync(join(projectPath, "turbo.json"))) {
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
async function getPackageScripts(projectPath = process.cwd()) {
|
|
364
|
+
try {
|
|
365
|
+
const pkgPath = join(projectPath, "package.json");
|
|
366
|
+
if (!existsSync(pkgPath)) {
|
|
367
|
+
return {};
|
|
368
|
+
}
|
|
369
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
370
|
+
return pkg.scripts || {};
|
|
371
|
+
} catch {
|
|
372
|
+
return {};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/utils/auto-setup.ts
|
|
377
|
+
async function analyzeProject(projectPath = process.cwd()) {
|
|
378
|
+
const packageManager = await detectPackageManager(projectPath);
|
|
379
|
+
const mono = await isMonorepo(projectPath);
|
|
380
|
+
const scripts = await getPackageScripts(projectPath);
|
|
381
|
+
const isTypeScript = existsSync2(join2(projectPath, "tsconfig.json")) || existsSync2(join2(projectPath, "src/index.ts")) || existsSync2(join2(projectPath, "index.ts"));
|
|
382
|
+
const framework = await detectFramework(projectPath);
|
|
383
|
+
const existing = {
|
|
384
|
+
typescript: existsSync2(join2(projectPath, "tsconfig.json")),
|
|
385
|
+
eslint: existsSync2(join2(projectPath, "eslint.config.js")) || existsSync2(join2(projectPath, "eslint.config.mjs")) || existsSync2(join2(projectPath, ".eslintrc.js")) || existsSync2(join2(projectPath, ".eslintrc.json")) || existsSync2(join2(projectPath, ".eslintrc")),
|
|
386
|
+
eslintFlat: existsSync2(join2(projectPath, "eslint.config.js")) || existsSync2(join2(projectPath, "eslint.config.mjs")),
|
|
387
|
+
prettier: existsSync2(join2(projectPath, ".prettierrc")) || existsSync2(join2(projectPath, ".prettierrc.json")) || existsSync2(join2(projectPath, "prettier.config.js")) || existsSync2(join2(projectPath, "prettier.config.mjs")),
|
|
388
|
+
vitest: existsSync2(join2(projectPath, "vitest.config.ts")) || existsSync2(join2(projectPath, "vitest.config.js")),
|
|
389
|
+
jest: existsSync2(join2(projectPath, "jest.config.js")) || existsSync2(join2(projectPath, "jest.config.ts")),
|
|
390
|
+
husky: existsSync2(join2(projectPath, ".husky")),
|
|
391
|
+
simpleGitHooks: existsSync2(join2(projectPath, ".git/hooks/pre-commit")) || await hasSimpleGitHooksConfig(projectPath),
|
|
392
|
+
githubActions: existsSync2(join2(projectPath, ".github/workflows"))
|
|
393
|
+
};
|
|
394
|
+
const existingScripts = {
|
|
395
|
+
build: !!scripts.build,
|
|
396
|
+
test: !!scripts.test,
|
|
397
|
+
lint: !!scripts.lint,
|
|
398
|
+
format: !!scripts.format,
|
|
399
|
+
typecheck: !!scripts.typecheck,
|
|
400
|
+
verify: !!scripts.verify
|
|
401
|
+
};
|
|
402
|
+
const setupPlans = await generateSetupPlans(
|
|
403
|
+
projectPath,
|
|
404
|
+
packageManager,
|
|
405
|
+
isTypeScript,
|
|
406
|
+
framework,
|
|
407
|
+
existing,
|
|
408
|
+
existingScripts,
|
|
409
|
+
mono
|
|
410
|
+
);
|
|
411
|
+
return {
|
|
412
|
+
packageManager,
|
|
413
|
+
isMonorepo: mono,
|
|
414
|
+
isTypeScript,
|
|
415
|
+
framework,
|
|
416
|
+
existing,
|
|
417
|
+
scripts: existingScripts,
|
|
418
|
+
setupPlans
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async function hasSimpleGitHooksConfig(projectPath) {
|
|
422
|
+
try {
|
|
423
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
424
|
+
if (!existsSync2(pkgPath)) return false;
|
|
425
|
+
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
426
|
+
return !!pkg["simple-git-hooks"];
|
|
427
|
+
} catch {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async function detectFramework(projectPath) {
|
|
432
|
+
try {
|
|
433
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
434
|
+
if (!existsSync2(pkgPath)) return "unknown";
|
|
435
|
+
const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
436
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
437
|
+
if (existsSync2(join2(projectPath, "shopify.theme.toml")) || existsSync2(join2(projectPath, "config/settings_schema.json")) || deps["@shopify/cli"] || deps["@shopify/theme"]) {
|
|
438
|
+
return "shopify";
|
|
439
|
+
}
|
|
440
|
+
if (deps.next) return "nextjs";
|
|
441
|
+
if (deps["@remix-run/react"]) return "remix";
|
|
442
|
+
if (deps.nuxt) return "nuxt";
|
|
443
|
+
if (deps.vue) return "vue";
|
|
444
|
+
if (deps.svelte || deps["@sveltejs/kit"]) return "svelte";
|
|
445
|
+
if (deps.react && !deps.next) return "react";
|
|
446
|
+
if (deps.hono) return "hono";
|
|
447
|
+
if (deps.express) return "express";
|
|
448
|
+
if (deps["@types/node"] || pkg.type === "module") return "node";
|
|
449
|
+
return "unknown";
|
|
450
|
+
} catch {
|
|
451
|
+
return "unknown";
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async function generateSetupPlans(projectPath, packageManager, isTypeScript, framework, existing, scripts, isMonorepo2) {
|
|
455
|
+
const plans = [];
|
|
456
|
+
if (isTypeScript) {
|
|
457
|
+
plans.push(
|
|
458
|
+
await planTypeScriptSetup(
|
|
459
|
+
projectPath,
|
|
460
|
+
framework,
|
|
461
|
+
existing.typescript,
|
|
462
|
+
isMonorepo2
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
plans.push(
|
|
467
|
+
await planESLintSetup(projectPath, isTypeScript, framework, existing)
|
|
468
|
+
);
|
|
469
|
+
plans.push(await planPrettierSetup(projectPath, existing.prettier));
|
|
470
|
+
plans.push(
|
|
471
|
+
await planTestingSetup(projectPath, isTypeScript, framework, existing)
|
|
472
|
+
);
|
|
473
|
+
if (isTypeScript && !["nextjs", "remix", "nuxt"].includes(framework)) {
|
|
474
|
+
plans.push(await planBuildSetup(projectPath, scripts.build));
|
|
475
|
+
}
|
|
476
|
+
plans.push(
|
|
477
|
+
await planScriptsSetup(projectPath, isTypeScript, framework, scripts)
|
|
478
|
+
);
|
|
479
|
+
plans.push(await planHooksSetup(projectPath, existing));
|
|
480
|
+
plans.push(
|
|
481
|
+
await planCISetup(
|
|
482
|
+
projectPath,
|
|
483
|
+
packageManager,
|
|
484
|
+
isTypeScript,
|
|
485
|
+
framework,
|
|
486
|
+
existing.githubActions,
|
|
487
|
+
isMonorepo2
|
|
488
|
+
)
|
|
489
|
+
);
|
|
490
|
+
return plans;
|
|
491
|
+
}
|
|
492
|
+
async function planTypeScriptSetup(projectPath, _framework, hasExisting, isMonorepo2) {
|
|
493
|
+
const changes = [];
|
|
494
|
+
const devDeps = [];
|
|
495
|
+
const pkg = await readPackageJson(projectPath);
|
|
496
|
+
const deps = pkg.dependencies || {};
|
|
497
|
+
const devDepsPkg = pkg.devDependencies || {};
|
|
498
|
+
const allDeps = { ...deps, ...devDepsPkg };
|
|
499
|
+
if (!allDeps.typescript) {
|
|
500
|
+
devDeps.push("typescript");
|
|
501
|
+
}
|
|
502
|
+
if (hasExisting) {
|
|
503
|
+
const tsconfig = await readTSConfig(join2(projectPath, "tsconfig.json"));
|
|
504
|
+
const opts = tsconfig.compilerOptions || {};
|
|
505
|
+
if (!opts.strict) {
|
|
506
|
+
changes.push({
|
|
507
|
+
type: "modify",
|
|
508
|
+
file: "tsconfig.json",
|
|
509
|
+
key: "compilerOptions.strict",
|
|
510
|
+
oldValue: opts.strict,
|
|
511
|
+
newValue: true,
|
|
512
|
+
description: "Enable strict type checking"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
if (!opts.skipLibCheck) {
|
|
516
|
+
changes.push({
|
|
517
|
+
type: "modify",
|
|
518
|
+
file: "tsconfig.json",
|
|
519
|
+
key: "compilerOptions.skipLibCheck",
|
|
520
|
+
oldValue: opts.skipLibCheck,
|
|
521
|
+
newValue: true,
|
|
522
|
+
description: "Skip type checking of declaration files for faster builds"
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (opts.target !== "ES2022" && opts.target !== "ESNext") {
|
|
526
|
+
changes.push({
|
|
527
|
+
type: "modify",
|
|
528
|
+
file: "tsconfig.json",
|
|
529
|
+
key: "compilerOptions.target",
|
|
530
|
+
oldValue: opts.target,
|
|
531
|
+
newValue: "ES2022",
|
|
532
|
+
description: "Use modern JavaScript target"
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
changes.push({
|
|
537
|
+
type: "add",
|
|
538
|
+
file: isMonorepo2 ? "tsconfig.base.json" : "tsconfig.json",
|
|
539
|
+
description: `Create TypeScript configuration${isMonorepo2 ? " (shared base for monorepo)" : ""}`
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
name: "typescript",
|
|
544
|
+
description: hasExisting ? "Improve TypeScript configuration" : "Set up TypeScript configuration",
|
|
545
|
+
priority: "high",
|
|
546
|
+
changes,
|
|
547
|
+
dependencies: [],
|
|
548
|
+
devDependencies: devDeps
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
async function planESLintSetup(projectPath, isTypeScript, framework, existing) {
|
|
552
|
+
const changes = [];
|
|
553
|
+
const devDeps = [];
|
|
554
|
+
const pkg = await readPackageJson(projectPath);
|
|
555
|
+
const deps = pkg.dependencies || {};
|
|
556
|
+
const devDepsPkg = pkg.devDependencies || {};
|
|
557
|
+
const allDeps = { ...deps, ...devDepsPkg };
|
|
558
|
+
if (!allDeps.eslint) {
|
|
559
|
+
devDeps.push("eslint");
|
|
560
|
+
}
|
|
561
|
+
if (isTypeScript) {
|
|
562
|
+
if (!allDeps["@typescript-eslint/eslint-plugin"]) {
|
|
563
|
+
devDeps.push("@typescript-eslint/eslint-plugin");
|
|
564
|
+
}
|
|
565
|
+
if (!allDeps["@typescript-eslint/parser"]) {
|
|
566
|
+
devDeps.push("@typescript-eslint/parser");
|
|
567
|
+
}
|
|
568
|
+
if (!allDeps["typescript-eslint"]) {
|
|
569
|
+
devDeps.push("typescript-eslint");
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (framework === "react" || framework === "nextjs") {
|
|
573
|
+
if (!allDeps["eslint-plugin-react"]) devDeps.push("eslint-plugin-react");
|
|
574
|
+
if (!allDeps["eslint-plugin-react-hooks"])
|
|
575
|
+
devDeps.push("eslint-plugin-react-hooks");
|
|
576
|
+
}
|
|
577
|
+
if (existing.eslint) {
|
|
578
|
+
if (!existing.eslintFlat) {
|
|
579
|
+
changes.push({
|
|
580
|
+
type: "modify",
|
|
581
|
+
file: "eslint.config.mjs",
|
|
582
|
+
description: "Migrate to ESLint 9 flat config format (recommended)"
|
|
583
|
+
});
|
|
584
|
+
} else {
|
|
585
|
+
changes.push({
|
|
586
|
+
type: "unchanged",
|
|
587
|
+
file: "eslint.config.mjs",
|
|
588
|
+
description: "ESLint flat config already exists"
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
} else {
|
|
592
|
+
changes.push({
|
|
593
|
+
type: "add",
|
|
594
|
+
file: "eslint.config.mjs",
|
|
595
|
+
description: "Create ESLint configuration with TypeScript support"
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
name: "eslint",
|
|
600
|
+
description: existing.eslint ? "Audit ESLint configuration and dependencies" : "Set up ESLint for code linting",
|
|
601
|
+
priority: "high",
|
|
602
|
+
changes,
|
|
603
|
+
dependencies: [],
|
|
604
|
+
devDependencies: devDeps
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async function planPrettierSetup(projectPath, hasExisting) {
|
|
608
|
+
const changes = [];
|
|
609
|
+
const devDeps = [];
|
|
610
|
+
const pkg = await readPackageJson(projectPath);
|
|
611
|
+
const deps = pkg.dependencies || {};
|
|
612
|
+
const devDepsPkg = pkg.devDependencies || {};
|
|
613
|
+
const allDeps = { ...deps, ...devDepsPkg };
|
|
614
|
+
if (!allDeps.prettier) {
|
|
615
|
+
devDeps.push("prettier");
|
|
616
|
+
}
|
|
617
|
+
if (hasExisting) {
|
|
618
|
+
const prettierConfig = await readPrettierConfig(projectPath);
|
|
619
|
+
if (prettierConfig.printWidth === void 0) {
|
|
620
|
+
changes.push({
|
|
621
|
+
type: "modify",
|
|
622
|
+
file: ".prettierrc",
|
|
623
|
+
key: "printWidth",
|
|
624
|
+
newValue: 100,
|
|
625
|
+
description: "Add printWidth setting"
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
if (prettierConfig.trailingComma === void 0) {
|
|
629
|
+
changes.push({
|
|
630
|
+
type: "modify",
|
|
631
|
+
file: ".prettierrc",
|
|
632
|
+
key: "trailingComma",
|
|
633
|
+
newValue: "es5",
|
|
634
|
+
description: "Add trailing comma setting"
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (changes.length === 0) {
|
|
638
|
+
changes.push({
|
|
639
|
+
type: "unchanged",
|
|
640
|
+
file: ".prettierrc",
|
|
641
|
+
description: "Prettier configuration is complete"
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
changes.push({
|
|
646
|
+
type: "add",
|
|
647
|
+
file: ".prettierrc",
|
|
648
|
+
description: "Create Prettier configuration"
|
|
649
|
+
});
|
|
650
|
+
changes.push({
|
|
651
|
+
type: "add",
|
|
652
|
+
file: ".prettierignore",
|
|
653
|
+
description: "Create Prettier ignore file"
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
name: "prettier",
|
|
658
|
+
description: hasExisting ? "Audit Prettier configuration" : "Set up Prettier for code formatting",
|
|
659
|
+
priority: "high",
|
|
660
|
+
changes,
|
|
661
|
+
dependencies: [],
|
|
662
|
+
devDependencies: devDeps
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
async function planTestingSetup(projectPath, isTypeScript, framework, existing) {
|
|
666
|
+
const changes = [];
|
|
667
|
+
const devDeps = [];
|
|
668
|
+
const pkg = await readPackageJson(projectPath);
|
|
669
|
+
const deps = pkg.dependencies || {};
|
|
670
|
+
const devDepsPkg = pkg.devDependencies || {};
|
|
671
|
+
const allDeps = { ...deps, ...devDepsPkg };
|
|
672
|
+
if (existing.jest) {
|
|
673
|
+
changes.push({
|
|
674
|
+
type: "unchanged",
|
|
675
|
+
file: "jest.config.*",
|
|
676
|
+
description: "Jest configuration exists (preserving existing setup)"
|
|
677
|
+
});
|
|
678
|
+
return {
|
|
679
|
+
name: "testing",
|
|
680
|
+
description: "Jest testing already configured",
|
|
681
|
+
priority: "high",
|
|
682
|
+
changes,
|
|
683
|
+
dependencies: [],
|
|
684
|
+
devDependencies: []
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
if (!allDeps.vitest) {
|
|
688
|
+
devDeps.push("vitest");
|
|
689
|
+
}
|
|
690
|
+
if (!allDeps["@vitest/coverage-v8"]) {
|
|
691
|
+
devDeps.push("@vitest/coverage-v8");
|
|
692
|
+
}
|
|
693
|
+
if (["react", "nextjs", "vue", "nuxt", "svelte"].includes(framework)) {
|
|
694
|
+
if (!allDeps.jsdom) devDeps.push("jsdom");
|
|
695
|
+
if (framework === "react" || framework === "nextjs") {
|
|
696
|
+
if (!allDeps["@testing-library/react"])
|
|
697
|
+
devDeps.push("@testing-library/react");
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (existing.vitest) {
|
|
701
|
+
changes.push({
|
|
702
|
+
type: "unchanged",
|
|
703
|
+
file: `vitest.config.${isTypeScript ? "ts" : "js"}`,
|
|
704
|
+
description: "Vitest configuration already exists"
|
|
705
|
+
});
|
|
706
|
+
} else {
|
|
707
|
+
changes.push({
|
|
708
|
+
type: "add",
|
|
709
|
+
file: `vitest.config.${isTypeScript ? "ts" : "js"}`,
|
|
710
|
+
description: "Create Vitest configuration"
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
name: "testing",
|
|
715
|
+
description: existing.vitest ? "Audit Vitest dependencies" : "Set up Vitest for testing",
|
|
716
|
+
priority: "high",
|
|
717
|
+
changes,
|
|
718
|
+
dependencies: [],
|
|
719
|
+
devDependencies: devDeps
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
async function planBuildSetup(projectPath, hasBuildScript) {
|
|
723
|
+
const changes = [];
|
|
724
|
+
const devDeps = [];
|
|
725
|
+
const pkg = await readPackageJson(projectPath);
|
|
726
|
+
const deps = pkg.dependencies || {};
|
|
727
|
+
const devDepsPkg = pkg.devDependencies || {};
|
|
728
|
+
const allDeps = { ...deps, ...devDepsPkg };
|
|
729
|
+
if (hasBuildScript) {
|
|
730
|
+
changes.push({
|
|
731
|
+
type: "unchanged",
|
|
732
|
+
file: "package.json",
|
|
733
|
+
key: "scripts.build",
|
|
734
|
+
description: "Build script already configured"
|
|
735
|
+
});
|
|
736
|
+
} else {
|
|
737
|
+
if (!allDeps.tsup) {
|
|
738
|
+
devDeps.push("tsup");
|
|
739
|
+
}
|
|
740
|
+
changes.push({
|
|
741
|
+
type: "add",
|
|
742
|
+
file: "tsup.config.ts",
|
|
743
|
+
description: "Create tsup build configuration"
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
name: "build",
|
|
748
|
+
description: hasBuildScript ? "Build configuration exists" : "Set up tsup for TypeScript builds",
|
|
749
|
+
priority: "medium",
|
|
750
|
+
changes,
|
|
751
|
+
dependencies: [],
|
|
752
|
+
devDependencies: devDeps
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
async function planScriptsSetup(_projectPath, isTypeScript, _framework, scripts) {
|
|
756
|
+
const changes = [];
|
|
757
|
+
const scriptsToAdd = {};
|
|
758
|
+
if (!scripts.lint) {
|
|
759
|
+
scriptsToAdd.lint = "eslint src";
|
|
760
|
+
changes.push({
|
|
761
|
+
type: "add",
|
|
762
|
+
file: "package.json",
|
|
763
|
+
key: "scripts.lint",
|
|
764
|
+
newValue: "eslint src",
|
|
765
|
+
description: "Add lint script"
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
if (!scripts.format) {
|
|
769
|
+
scriptsToAdd.format = 'prettier --write "src/**/*.{ts,tsx,js,jsx,json}"';
|
|
770
|
+
changes.push({
|
|
771
|
+
type: "add",
|
|
772
|
+
file: "package.json",
|
|
773
|
+
key: "scripts.format",
|
|
774
|
+
newValue: scriptsToAdd.format,
|
|
775
|
+
description: "Add format script"
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
if (isTypeScript && !scripts.typecheck) {
|
|
779
|
+
scriptsToAdd.typecheck = "tsc --noEmit";
|
|
780
|
+
changes.push({
|
|
781
|
+
type: "add",
|
|
782
|
+
file: "package.json",
|
|
783
|
+
key: "scripts.typecheck",
|
|
784
|
+
newValue: "tsc --noEmit",
|
|
785
|
+
description: "Add typecheck script"
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
if (!scripts.test) {
|
|
789
|
+
scriptsToAdd.test = "vitest run";
|
|
790
|
+
changes.push({
|
|
791
|
+
type: "add",
|
|
792
|
+
file: "package.json",
|
|
793
|
+
key: "scripts.test",
|
|
794
|
+
newValue: "vitest run",
|
|
795
|
+
description: "Add test script"
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
if (!scripts.verify) {
|
|
799
|
+
scriptsToAdd.verify = "workflow-agent verify";
|
|
800
|
+
changes.push({
|
|
801
|
+
type: "add",
|
|
802
|
+
file: "package.json",
|
|
803
|
+
key: "scripts.verify",
|
|
804
|
+
newValue: "workflow-agent verify",
|
|
805
|
+
description: "Add verify script"
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
if (Object.keys(scriptsToAdd).length === 0) {
|
|
809
|
+
changes.push({
|
|
810
|
+
type: "unchanged",
|
|
811
|
+
file: "package.json",
|
|
812
|
+
description: "All standard scripts already configured"
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
return {
|
|
816
|
+
name: "scripts",
|
|
817
|
+
description: "Configure npm scripts",
|
|
818
|
+
priority: "medium",
|
|
819
|
+
changes,
|
|
820
|
+
dependencies: [],
|
|
821
|
+
devDependencies: []
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async function planHooksSetup(projectPath, existing) {
|
|
825
|
+
const changes = [];
|
|
826
|
+
const devDeps = [];
|
|
827
|
+
const pkg = await readPackageJson(projectPath);
|
|
828
|
+
const deps = pkg.dependencies || {};
|
|
829
|
+
const devDepsPkg = pkg.devDependencies || {};
|
|
830
|
+
const allDeps = { ...deps, ...devDepsPkg };
|
|
831
|
+
if (!allDeps["simple-git-hooks"]) {
|
|
832
|
+
devDeps.push("simple-git-hooks");
|
|
833
|
+
}
|
|
834
|
+
if (!allDeps["lint-staged"]) {
|
|
835
|
+
devDeps.push("lint-staged");
|
|
836
|
+
}
|
|
837
|
+
if (existing.husky || existing.simpleGitHooks) {
|
|
838
|
+
changes.push({
|
|
839
|
+
type: "modify",
|
|
840
|
+
file: "package.json",
|
|
841
|
+
key: "simple-git-hooks",
|
|
842
|
+
description: "Ensure pre-commit hook configuration"
|
|
843
|
+
});
|
|
844
|
+
} else {
|
|
845
|
+
changes.push({
|
|
846
|
+
type: "add",
|
|
847
|
+
file: "package.json",
|
|
848
|
+
key: "simple-git-hooks",
|
|
849
|
+
newValue: { "pre-commit": "npx lint-staged" },
|
|
850
|
+
description: "Add pre-commit hook configuration"
|
|
851
|
+
});
|
|
852
|
+
changes.push({
|
|
853
|
+
type: "add",
|
|
854
|
+
file: "package.json",
|
|
855
|
+
key: "lint-staged",
|
|
856
|
+
description: "Add lint-staged configuration"
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return {
|
|
860
|
+
name: "hooks",
|
|
861
|
+
description: existing.husky || existing.simpleGitHooks ? "Audit pre-commit hooks" : "Set up pre-commit hooks",
|
|
862
|
+
priority: "medium",
|
|
863
|
+
changes,
|
|
864
|
+
dependencies: [],
|
|
865
|
+
devDependencies: devDeps
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
async function planCISetup(projectPath, _packageManager, _isTypeScript, _framework, hasExisting, _isMonorepo) {
|
|
869
|
+
const changes = [];
|
|
870
|
+
if (hasExisting) {
|
|
871
|
+
if (existsSync2(join2(projectPath, ".github/workflows/ci.yml"))) {
|
|
872
|
+
changes.push({
|
|
873
|
+
type: "unchanged",
|
|
874
|
+
file: ".github/workflows/ci.yml",
|
|
875
|
+
description: "CI workflow already exists"
|
|
876
|
+
});
|
|
877
|
+
} else {
|
|
878
|
+
changes.push({
|
|
879
|
+
type: "add",
|
|
880
|
+
file: ".github/workflows/ci.yml",
|
|
881
|
+
description: "Add CI workflow (other workflows exist)"
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
} else {
|
|
885
|
+
changes.push({
|
|
886
|
+
type: "add",
|
|
887
|
+
file: ".github/workflows/ci.yml",
|
|
888
|
+
description: "Create GitHub Actions CI workflow"
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
name: "ci",
|
|
893
|
+
description: hasExisting ? "Audit CI configuration" : "Set up GitHub Actions CI",
|
|
894
|
+
priority: "low",
|
|
895
|
+
changes,
|
|
896
|
+
dependencies: [],
|
|
897
|
+
devDependencies: []
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
async function generateAuditReport(projectPath = process.cwd()) {
|
|
901
|
+
const analysis = await analyzeProject(projectPath);
|
|
902
|
+
const allDeps = /* @__PURE__ */ new Set();
|
|
903
|
+
const allDevDeps = /* @__PURE__ */ new Set();
|
|
904
|
+
for (const plan of analysis.setupPlans) {
|
|
905
|
+
plan.dependencies.forEach((d) => allDeps.add(d));
|
|
906
|
+
plan.devDependencies.forEach((d) => allDevDeps.add(d));
|
|
907
|
+
}
|
|
908
|
+
const totalChanges = analysis.setupPlans.reduce(
|
|
909
|
+
(sum, plan) => sum + plan.changes.filter((c) => c.type !== "unchanged").length,
|
|
910
|
+
0
|
|
911
|
+
);
|
|
912
|
+
return {
|
|
913
|
+
analysis,
|
|
914
|
+
totalChanges,
|
|
915
|
+
allDependencies: Array.from(allDeps),
|
|
916
|
+
allDevDependencies: Array.from(allDevDeps),
|
|
917
|
+
plans: analysis.setupPlans
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
function formatAuditReport(report) {
|
|
921
|
+
const lines = [];
|
|
922
|
+
lines.push("\u{1F4CB} Auto-Setup Audit Report\n");
|
|
923
|
+
lines.push(`Framework: ${report.analysis.framework}`);
|
|
924
|
+
lines.push(`Package Manager: ${report.analysis.packageManager}`);
|
|
925
|
+
lines.push(`TypeScript: ${report.analysis.isTypeScript ? "Yes" : "No"}`);
|
|
926
|
+
lines.push(`Monorepo: ${report.analysis.isMonorepo ? "Yes" : "No"}
|
|
927
|
+
`);
|
|
928
|
+
for (const plan of report.plans) {
|
|
929
|
+
const hasChanges = plan.changes.some((c) => c.type !== "unchanged");
|
|
930
|
+
const icon = hasChanges ? "\u{1F527}" : "\u2713";
|
|
931
|
+
lines.push(
|
|
932
|
+
`${icon} ${plan.name.charAt(0).toUpperCase() + plan.name.slice(1)}`
|
|
933
|
+
);
|
|
934
|
+
for (const change of plan.changes) {
|
|
935
|
+
const symbol = change.type === "add" ? "+" : change.type === "modify" ? "~" : "=";
|
|
936
|
+
let line = ` ${symbol} ${change.description}`;
|
|
937
|
+
if (change.key && change.oldValue !== void 0 && change.newValue !== void 0) {
|
|
938
|
+
line += ` (${String(change.oldValue)} \u2192 ${String(change.newValue)})`;
|
|
939
|
+
}
|
|
940
|
+
lines.push(line);
|
|
941
|
+
}
|
|
942
|
+
if (plan.devDependencies.length > 0) {
|
|
943
|
+
lines.push(` \u{1F4E6} Install: ${plan.devDependencies.join(", ")}`);
|
|
944
|
+
}
|
|
945
|
+
lines.push("");
|
|
946
|
+
}
|
|
947
|
+
if (report.allDevDependencies.length > 0) {
|
|
948
|
+
lines.push("Dependencies to install (batched):");
|
|
949
|
+
const pm = report.analysis.packageManager;
|
|
950
|
+
const cmd = pm === "npm" ? "npm install" : pm === "yarn" ? "yarn add" : `${pm} add`;
|
|
951
|
+
lines.push(` ${cmd} -D ${report.allDevDependencies.join(" ")}`);
|
|
952
|
+
lines.push("");
|
|
953
|
+
}
|
|
954
|
+
lines.push(`Total changes: ${report.totalChanges}`);
|
|
955
|
+
return lines.join("\n");
|
|
956
|
+
}
|
|
957
|
+
async function runAllSetups(projectPath = process.cwd(), onProgress) {
|
|
958
|
+
const report = await generateAuditReport(projectPath);
|
|
959
|
+
const results = [];
|
|
960
|
+
const filesCreated = [];
|
|
961
|
+
const filesUpdated = [];
|
|
962
|
+
if (report.allDevDependencies.length > 0) {
|
|
963
|
+
onProgress?.("Installing dependencies", "start");
|
|
964
|
+
try {
|
|
965
|
+
await installDependencies(
|
|
966
|
+
projectPath,
|
|
967
|
+
report.analysis.packageManager,
|
|
968
|
+
report.allDevDependencies
|
|
969
|
+
);
|
|
970
|
+
onProgress?.("Installing dependencies", "done");
|
|
971
|
+
} catch (error) {
|
|
972
|
+
onProgress?.("Installing dependencies", "error");
|
|
973
|
+
results.push({
|
|
974
|
+
success: false,
|
|
975
|
+
name: "dependencies",
|
|
976
|
+
message: `Failed to install: ${error instanceof Error ? error.message : String(error)}`,
|
|
977
|
+
filesCreated: [],
|
|
978
|
+
filesUpdated: [],
|
|
979
|
+
packagesInstalled: []
|
|
980
|
+
});
|
|
981
|
+
return results;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
for (const plan of report.plans) {
|
|
985
|
+
const hasChanges = plan.changes.some((c) => c.type !== "unchanged");
|
|
986
|
+
if (!hasChanges && plan.devDependencies.length === 0) continue;
|
|
987
|
+
onProgress?.(`Setting up ${plan.name}`, "start");
|
|
988
|
+
try {
|
|
989
|
+
const result = await applySetupPlan(projectPath, plan, report.analysis);
|
|
990
|
+
results.push(result);
|
|
991
|
+
filesCreated.push(...result.filesCreated);
|
|
992
|
+
filesUpdated.push(...result.filesUpdated);
|
|
993
|
+
onProgress?.(`Setting up ${plan.name}`, "done");
|
|
994
|
+
} catch (error) {
|
|
995
|
+
onProgress?.(`Setting up ${plan.name}`, "error");
|
|
996
|
+
results.push({
|
|
997
|
+
success: false,
|
|
998
|
+
name: plan.name,
|
|
999
|
+
message: `Failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1000
|
+
filesCreated: [],
|
|
1001
|
+
filesUpdated: [],
|
|
1002
|
+
packagesInstalled: []
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
onProgress?.("Initializing git hooks", "start");
|
|
1007
|
+
try {
|
|
1008
|
+
await execa3("npx", ["simple-git-hooks"], {
|
|
1009
|
+
cwd: projectPath,
|
|
1010
|
+
stdio: "pipe"
|
|
1011
|
+
});
|
|
1012
|
+
onProgress?.("Initializing git hooks", "done");
|
|
1013
|
+
} catch {
|
|
1014
|
+
onProgress?.("Initializing git hooks", "error");
|
|
1015
|
+
}
|
|
1016
|
+
return results;
|
|
1017
|
+
}
|
|
1018
|
+
async function installDependencies(projectPath, packageManager, packages) {
|
|
1019
|
+
if (packages.length === 0) return;
|
|
1020
|
+
const commands = {
|
|
1021
|
+
npm: { cmd: "npm", args: ["install", "--save-dev"] },
|
|
1022
|
+
pnpm: { cmd: "pnpm", args: ["add", "-D"] },
|
|
1023
|
+
yarn: { cmd: "yarn", args: ["add", "-D"] },
|
|
1024
|
+
bun: { cmd: "bun", args: ["add", "-D"] }
|
|
1025
|
+
};
|
|
1026
|
+
const { cmd, args } = commands[packageManager];
|
|
1027
|
+
await execa3(cmd, [...args, ...packages], {
|
|
1028
|
+
cwd: projectPath,
|
|
1029
|
+
stdio: "pipe"
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
async function applySetupPlan(projectPath, plan, analysis) {
|
|
1033
|
+
const filesCreated = [];
|
|
1034
|
+
const filesUpdated = [];
|
|
1035
|
+
switch (plan.name) {
|
|
1036
|
+
case "typescript":
|
|
1037
|
+
await applyTypeScriptSetup(
|
|
1038
|
+
projectPath,
|
|
1039
|
+
analysis,
|
|
1040
|
+
filesCreated,
|
|
1041
|
+
filesUpdated
|
|
1042
|
+
);
|
|
1043
|
+
break;
|
|
1044
|
+
case "eslint":
|
|
1045
|
+
await applyESLintSetup(projectPath, analysis, filesCreated, filesUpdated);
|
|
1046
|
+
break;
|
|
1047
|
+
case "prettier":
|
|
1048
|
+
await applyPrettierSetup(projectPath, filesCreated, filesUpdated);
|
|
1049
|
+
break;
|
|
1050
|
+
case "testing":
|
|
1051
|
+
await applyTestingSetup(
|
|
1052
|
+
projectPath,
|
|
1053
|
+
analysis,
|
|
1054
|
+
filesCreated,
|
|
1055
|
+
filesUpdated
|
|
1056
|
+
);
|
|
1057
|
+
break;
|
|
1058
|
+
case "build":
|
|
1059
|
+
await applyBuildSetup(projectPath, filesCreated, filesUpdated);
|
|
1060
|
+
break;
|
|
1061
|
+
case "scripts":
|
|
1062
|
+
await applyScriptsSetup(projectPath, analysis, filesUpdated);
|
|
1063
|
+
break;
|
|
1064
|
+
case "hooks":
|
|
1065
|
+
await applyHooksSetup(projectPath, filesUpdated);
|
|
1066
|
+
break;
|
|
1067
|
+
case "ci":
|
|
1068
|
+
await applyCISetup(projectPath, analysis, filesCreated, filesUpdated);
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
return {
|
|
1072
|
+
success: true,
|
|
1073
|
+
name: plan.name,
|
|
1074
|
+
message: `${plan.name} configured successfully`,
|
|
1075
|
+
filesCreated,
|
|
1076
|
+
filesUpdated,
|
|
1077
|
+
packagesInstalled: plan.devDependencies
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
async function applyTypeScriptSetup(projectPath, analysis, filesCreated, filesUpdated) {
|
|
1081
|
+
const configName = analysis.isMonorepo ? "tsconfig.base.json" : "tsconfig.json";
|
|
1082
|
+
const configPath = join2(projectPath, configName);
|
|
1083
|
+
let tsconfig = {};
|
|
1084
|
+
if (existsSync2(configPath)) {
|
|
1085
|
+
tsconfig = await readJsonFile(configPath);
|
|
1086
|
+
filesUpdated.push(configName);
|
|
1087
|
+
} else {
|
|
1088
|
+
filesCreated.push(configName);
|
|
1089
|
+
}
|
|
1090
|
+
if (!tsconfig.compilerOptions) {
|
|
1091
|
+
tsconfig.compilerOptions = {};
|
|
1092
|
+
}
|
|
1093
|
+
const opts = tsconfig.compilerOptions;
|
|
1094
|
+
const improvements = {
|
|
1095
|
+
target: opts.target || "ES2022",
|
|
1096
|
+
module: opts.module || "ESNext",
|
|
1097
|
+
moduleResolution: opts.moduleResolution || "bundler",
|
|
1098
|
+
esModuleInterop: opts.esModuleInterop ?? true,
|
|
1099
|
+
strict: opts.strict ?? true,
|
|
1100
|
+
skipLibCheck: opts.skipLibCheck ?? true,
|
|
1101
|
+
resolveJsonModule: opts.resolveJsonModule ?? true,
|
|
1102
|
+
isolatedModules: opts.isolatedModules ?? true,
|
|
1103
|
+
declaration: opts.declaration ?? true,
|
|
1104
|
+
declarationMap: opts.declarationMap ?? true,
|
|
1105
|
+
sourceMap: opts.sourceMap ?? true
|
|
1106
|
+
};
|
|
1107
|
+
const frameworkOpts = getFrameworkTsOptions(analysis.framework);
|
|
1108
|
+
tsconfig.compilerOptions = { ...opts, ...improvements, ...frameworkOpts };
|
|
1109
|
+
if (!tsconfig.include) {
|
|
1110
|
+
tsconfig.include = ["src/**/*"];
|
|
1111
|
+
}
|
|
1112
|
+
if (!tsconfig.exclude) {
|
|
1113
|
+
tsconfig.exclude = ["node_modules", "dist", "coverage"];
|
|
1114
|
+
}
|
|
1115
|
+
await writeFile(configPath, JSON.stringify(tsconfig, null, 2) + "\n");
|
|
1116
|
+
}
|
|
1117
|
+
function getFrameworkTsOptions(framework) {
|
|
1118
|
+
switch (framework) {
|
|
1119
|
+
case "nextjs":
|
|
1120
|
+
return {
|
|
1121
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
1122
|
+
jsx: "preserve",
|
|
1123
|
+
incremental: true,
|
|
1124
|
+
plugins: [{ name: "next" }]
|
|
1125
|
+
};
|
|
1126
|
+
case "react":
|
|
1127
|
+
case "remix":
|
|
1128
|
+
return {
|
|
1129
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
1130
|
+
jsx: "react-jsx"
|
|
1131
|
+
};
|
|
1132
|
+
case "vue":
|
|
1133
|
+
case "nuxt":
|
|
1134
|
+
return {
|
|
1135
|
+
lib: ["esnext", "dom"],
|
|
1136
|
+
jsx: "preserve"
|
|
1137
|
+
};
|
|
1138
|
+
default:
|
|
1139
|
+
return {};
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
async function applyESLintSetup(projectPath, analysis, filesCreated, _filesUpdated) {
|
|
1143
|
+
if (analysis.existing.eslintFlat) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
const configPath = join2(projectPath, "eslint.config.mjs");
|
|
1147
|
+
const config = generateESLintFlatConfig(
|
|
1148
|
+
analysis.isTypeScript,
|
|
1149
|
+
analysis.framework
|
|
1150
|
+
);
|
|
1151
|
+
await writeFile(configPath, config);
|
|
1152
|
+
filesCreated.push("eslint.config.mjs");
|
|
1153
|
+
}
|
|
1154
|
+
function generateESLintFlatConfig(isTypeScript, framework) {
|
|
1155
|
+
const imports = [];
|
|
1156
|
+
const configs = [];
|
|
1157
|
+
if (isTypeScript) {
|
|
1158
|
+
imports.push(`import tseslint from "typescript-eslint";`);
|
|
1159
|
+
}
|
|
1160
|
+
if (framework === "react" || framework === "nextjs") {
|
|
1161
|
+
imports.push(`import react from "eslint-plugin-react";`);
|
|
1162
|
+
imports.push(`import reactHooks from "eslint-plugin-react-hooks";`);
|
|
1163
|
+
}
|
|
1164
|
+
if (isTypeScript) {
|
|
1165
|
+
configs.push(` ...tseslint.configs.recommended`);
|
|
1166
|
+
}
|
|
1167
|
+
configs.push(` {
|
|
1168
|
+
files: ["**/*.${isTypeScript ? "{ts,tsx}" : "{js,jsx}"}"],
|
|
1169
|
+
rules: {
|
|
1170
|
+
${isTypeScript ? `"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],` : ""}
|
|
1171
|
+
"no-console": "warn",
|
|
1172
|
+
},
|
|
1173
|
+
}`);
|
|
1174
|
+
configs.push(` {
|
|
1175
|
+
ignores: ["dist/", "node_modules/", "coverage/", ".next/", "build/"],
|
|
1176
|
+
}`);
|
|
1177
|
+
return `${imports.join("\n")}
|
|
1178
|
+
|
|
1179
|
+
export default [
|
|
1180
|
+
${configs.join(",\n")}
|
|
1181
|
+
];
|
|
1182
|
+
`;
|
|
1183
|
+
}
|
|
1184
|
+
async function applyPrettierSetup(projectPath, filesCreated, filesUpdated) {
|
|
1185
|
+
const prettierPath = join2(projectPath, ".prettierrc");
|
|
1186
|
+
let config = {
|
|
1187
|
+
semi: true,
|
|
1188
|
+
singleQuote: false,
|
|
1189
|
+
tabWidth: 2,
|
|
1190
|
+
trailingComma: "es5",
|
|
1191
|
+
printWidth: 100,
|
|
1192
|
+
bracketSpacing: true
|
|
1193
|
+
};
|
|
1194
|
+
if (existsSync2(prettierPath)) {
|
|
1195
|
+
const existing = await readPrettierConfig(projectPath);
|
|
1196
|
+
config = { ...config, ...existing };
|
|
1197
|
+
filesUpdated.push(".prettierrc");
|
|
1198
|
+
} else {
|
|
1199
|
+
filesCreated.push(".prettierrc");
|
|
1200
|
+
}
|
|
1201
|
+
await writeFile(prettierPath, JSON.stringify(config, null, 2) + "\n");
|
|
1202
|
+
const ignorePath = join2(projectPath, ".prettierignore");
|
|
1203
|
+
if (!existsSync2(ignorePath)) {
|
|
1204
|
+
const ignoreContent = `dist/
|
|
1205
|
+
node_modules/
|
|
1206
|
+
coverage/
|
|
1207
|
+
.next/
|
|
1208
|
+
build/
|
|
1209
|
+
*.min.js
|
|
1210
|
+
pnpm-lock.yaml
|
|
1211
|
+
package-lock.json
|
|
1212
|
+
yarn.lock
|
|
1213
|
+
`;
|
|
1214
|
+
await writeFile(ignorePath, ignoreContent);
|
|
1215
|
+
filesCreated.push(".prettierignore");
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
async function applyTestingSetup(projectPath, analysis, filesCreated, _filesUpdated) {
|
|
1219
|
+
if (analysis.existing.jest || analysis.existing.vitest) {
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
const ext = analysis.isTypeScript ? "ts" : "js";
|
|
1223
|
+
const configPath = join2(projectPath, `vitest.config.${ext}`);
|
|
1224
|
+
const environment = ["react", "nextjs", "vue", "nuxt", "svelte"].includes(
|
|
1225
|
+
analysis.framework
|
|
1226
|
+
) ? "jsdom" : "node";
|
|
1227
|
+
const config = `import { defineConfig } from "vitest/config";
|
|
1228
|
+
|
|
1229
|
+
export default defineConfig({
|
|
1230
|
+
test: {
|
|
1231
|
+
globals: true,
|
|
1232
|
+
environment: "${environment}",
|
|
1233
|
+
coverage: {
|
|
1234
|
+
provider: "v8",
|
|
1235
|
+
reporter: ["text", "json", "html"],
|
|
1236
|
+
exclude: ["node_modules/", "dist/", "**/*.test.${ext}"],
|
|
1237
|
+
},
|
|
1238
|
+
include: ["src/**/*.test.${ext}", "tests/**/*.test.${ext}"],
|
|
1239
|
+
},
|
|
1240
|
+
});
|
|
1241
|
+
`;
|
|
1242
|
+
await writeFile(configPath, config);
|
|
1243
|
+
filesCreated.push(`vitest.config.${ext}`);
|
|
1244
|
+
}
|
|
1245
|
+
async function applyBuildSetup(projectPath, filesCreated, _filesUpdated) {
|
|
1246
|
+
const configPath = join2(projectPath, "tsup.config.ts");
|
|
1247
|
+
if (existsSync2(configPath)) {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
const config = `import { defineConfig } from "tsup";
|
|
1251
|
+
|
|
1252
|
+
export default defineConfig({
|
|
1253
|
+
entry: ["src/index.ts"],
|
|
1254
|
+
format: ["esm"],
|
|
1255
|
+
dts: true,
|
|
1256
|
+
clean: true,
|
|
1257
|
+
sourcemap: true,
|
|
1258
|
+
});
|
|
1259
|
+
`;
|
|
1260
|
+
await writeFile(configPath, config);
|
|
1261
|
+
filesCreated.push("tsup.config.ts");
|
|
1262
|
+
}
|
|
1263
|
+
async function applyScriptsSetup(projectPath, analysis, filesUpdated) {
|
|
1264
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
1265
|
+
const pkg = await readPackageJson(projectPath);
|
|
1266
|
+
const scripts = pkg.scripts || {};
|
|
1267
|
+
const scriptsToAdd = {
|
|
1268
|
+
lint: "eslint src",
|
|
1269
|
+
"lint:fix": "eslint src --fix",
|
|
1270
|
+
format: 'prettier --write "src/**/*.{ts,tsx,js,jsx,json}"',
|
|
1271
|
+
"format:check": 'prettier --check "src/**/*.{ts,tsx,js,jsx,json}"',
|
|
1272
|
+
test: "vitest run",
|
|
1273
|
+
"test:watch": "vitest",
|
|
1274
|
+
"test:coverage": "vitest run --coverage",
|
|
1275
|
+
verify: "workflow-agent verify",
|
|
1276
|
+
"verify:fix": "workflow-agent verify --fix",
|
|
1277
|
+
"pre-commit": "workflow-agent verify --fix"
|
|
1278
|
+
};
|
|
1279
|
+
if (analysis.isTypeScript) {
|
|
1280
|
+
scriptsToAdd.typecheck = "tsc --noEmit";
|
|
1281
|
+
}
|
|
1282
|
+
let added = false;
|
|
1283
|
+
for (const [name, cmd] of Object.entries(scriptsToAdd)) {
|
|
1284
|
+
if (!scripts[name]) {
|
|
1285
|
+
scripts[name] = cmd;
|
|
1286
|
+
added = true;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
if (added) {
|
|
1290
|
+
pkg.scripts = scripts;
|
|
1291
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1292
|
+
filesUpdated.push("package.json");
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
async function applyHooksSetup(projectPath, filesUpdated) {
|
|
1296
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
1297
|
+
const pkg = await readPackageJson(projectPath);
|
|
1298
|
+
if (!pkg["simple-git-hooks"]) {
|
|
1299
|
+
pkg["simple-git-hooks"] = {
|
|
1300
|
+
"pre-commit": "npx lint-staged"
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
if (!pkg["lint-staged"]) {
|
|
1304
|
+
pkg["lint-staged"] = {
|
|
1305
|
+
"*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"],
|
|
1306
|
+
"*.{json,md,yml,yaml}": ["prettier --write"]
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1310
|
+
filesUpdated.push("package.json");
|
|
1311
|
+
}
|
|
1312
|
+
async function applyCISetup(projectPath, analysis, filesCreated, _filesUpdated) {
|
|
1313
|
+
const workflowsDir = join2(projectPath, ".github/workflows");
|
|
1314
|
+
await mkdir(workflowsDir, { recursive: true });
|
|
1315
|
+
const ciPath = join2(workflowsDir, "ci.yml");
|
|
1316
|
+
if (existsSync2(ciPath)) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const workflow = generateCIWorkflow(
|
|
1320
|
+
analysis.packageManager,
|
|
1321
|
+
analysis.isTypeScript,
|
|
1322
|
+
analysis.framework,
|
|
1323
|
+
analysis.isMonorepo
|
|
1324
|
+
);
|
|
1325
|
+
await writeFile(ciPath, workflow);
|
|
1326
|
+
filesCreated.push(".github/workflows/ci.yml");
|
|
1327
|
+
}
|
|
1328
|
+
function generateCIWorkflow(packageManager, isTypeScript, framework, _isMonorepo) {
|
|
1329
|
+
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "yarn" ? "yarn" : packageManager;
|
|
1330
|
+
const isPnpm = packageManager === "pnpm";
|
|
1331
|
+
return `name: CI
|
|
1332
|
+
|
|
1333
|
+
on:
|
|
1334
|
+
push:
|
|
1335
|
+
branches: [main, master]
|
|
1336
|
+
pull_request:
|
|
1337
|
+
branches: [main, master]
|
|
1338
|
+
|
|
1339
|
+
jobs:
|
|
1340
|
+
ci:
|
|
1341
|
+
runs-on: ubuntu-latest
|
|
1342
|
+
|
|
1343
|
+
steps:
|
|
1344
|
+
- name: Checkout
|
|
1345
|
+
uses: actions/checkout@v4
|
|
1346
|
+
|
|
1347
|
+
- name: Setup Node.js
|
|
1348
|
+
uses: actions/setup-node@v4
|
|
1349
|
+
with:
|
|
1350
|
+
node-version: "20"
|
|
1351
|
+
${isPnpm ? `
|
|
1352
|
+
- name: Install pnpm
|
|
1353
|
+
uses: pnpm/action-setup@v4
|
|
1354
|
+
with:
|
|
1355
|
+
version: 9
|
|
1356
|
+
` : ""}
|
|
1357
|
+
- name: Install dependencies
|
|
1358
|
+
run: ${packageManager} install
|
|
1359
|
+
|
|
1360
|
+
${isTypeScript ? ` - name: Type check
|
|
1361
|
+
run: ${runCmd} typecheck
|
|
1362
|
+
|
|
1363
|
+
` : ""} - name: Lint
|
|
1364
|
+
run: ${runCmd} lint
|
|
1365
|
+
|
|
1366
|
+
- name: Format check
|
|
1367
|
+
run: ${runCmd} format:check || true
|
|
1368
|
+
|
|
1369
|
+
- name: Test
|
|
1370
|
+
run: ${runCmd} test
|
|
1371
|
+
${isTypeScript && !["nextjs", "remix", "nuxt"].includes(framework) ? `
|
|
1372
|
+
- name: Build
|
|
1373
|
+
run: ${runCmd} build
|
|
1374
|
+
` : ""}`;
|
|
1375
|
+
}
|
|
1376
|
+
async function readPackageJson(projectPath) {
|
|
1377
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
1378
|
+
if (!existsSync2(pkgPath)) {
|
|
1379
|
+
return {};
|
|
1380
|
+
}
|
|
1381
|
+
return JSON.parse(await readFile2(pkgPath, "utf-8"));
|
|
1382
|
+
}
|
|
1383
|
+
async function readTSConfig(filePath) {
|
|
1384
|
+
if (!existsSync2(filePath)) {
|
|
1385
|
+
return {};
|
|
1386
|
+
}
|
|
1387
|
+
return JSON.parse(await readFile2(filePath, "utf-8"));
|
|
1388
|
+
}
|
|
1389
|
+
async function readJsonFile(filePath) {
|
|
1390
|
+
if (!existsSync2(filePath)) {
|
|
1391
|
+
return {};
|
|
1392
|
+
}
|
|
1393
|
+
return JSON.parse(await readFile2(filePath, "utf-8"));
|
|
1394
|
+
}
|
|
1395
|
+
async function readPrettierConfig(projectPath) {
|
|
1396
|
+
const files = [".prettierrc", ".prettierrc.json", "prettier.config.js"];
|
|
1397
|
+
for (const file of files) {
|
|
1398
|
+
const filePath = join2(projectPath, file);
|
|
1399
|
+
if (existsSync2(filePath)) {
|
|
1400
|
+
if (file.endsWith(".js")) {
|
|
1401
|
+
return {};
|
|
1402
|
+
}
|
|
1403
|
+
try {
|
|
1404
|
+
return JSON.parse(await readFile2(filePath, "utf-8"));
|
|
1405
|
+
} catch {
|
|
1406
|
+
return {};
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
return {};
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
export {
|
|
1414
|
+
QUALITY_CHECKS,
|
|
1415
|
+
runCheck,
|
|
1416
|
+
applyFix,
|
|
1417
|
+
runAllChecks,
|
|
1418
|
+
hasUncommittedChanges,
|
|
1419
|
+
stageAllChanges,
|
|
1420
|
+
analyzeProject,
|
|
1421
|
+
generateAuditReport,
|
|
1422
|
+
formatAuditReport,
|
|
1423
|
+
runAllSetups
|
|
1424
|
+
};
|
|
1425
|
+
//# sourceMappingURL=chunk-YI3LBZ7I.js.map
|