technical-debt-radar 1.11.0 → 1.13.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/dist/index.js +237 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -161,6 +161,7 @@ var require_credits = __commonJS({
|
|
|
161
161
|
(function(AiOperation2) {
|
|
162
162
|
AiOperation2["FIX"] = "fix";
|
|
163
163
|
AiOperation2["FIX_GROUPED"] = "fix-grouped";
|
|
164
|
+
AiOperation2["FIX_MULTI"] = "fix-multi";
|
|
164
165
|
AiOperation2["SCAN_SUMMARY"] = "scan-summary";
|
|
165
166
|
AiOperation2["PR_COMMENT"] = "pr-comment";
|
|
166
167
|
AiOperation2["CROSS_FILE"] = "cross-file";
|
|
@@ -168,6 +169,7 @@ var require_credits = __commonJS({
|
|
|
168
169
|
exports2.CREDIT_COSTS = {
|
|
169
170
|
[AiOperation.FIX]: 1,
|
|
170
171
|
[AiOperation.FIX_GROUPED]: 1,
|
|
172
|
+
[AiOperation.FIX_MULTI]: 3,
|
|
171
173
|
[AiOperation.SCAN_SUMMARY]: 1,
|
|
172
174
|
[AiOperation.PR_COMMENT]: 3,
|
|
173
175
|
[AiOperation.CROSS_FILE]: 5
|
|
@@ -20737,6 +20739,12 @@ var RadarApiClient = class _RadarApiClient {
|
|
|
20737
20739
|
body: JSON.stringify(data)
|
|
20738
20740
|
});
|
|
20739
20741
|
}
|
|
20742
|
+
async requestMultiFix(data) {
|
|
20743
|
+
return this.fetch("/cli/ai/fix-multi", {
|
|
20744
|
+
method: "POST",
|
|
20745
|
+
body: JSON.stringify(data)
|
|
20746
|
+
});
|
|
20747
|
+
}
|
|
20740
20748
|
};
|
|
20741
20749
|
|
|
20742
20750
|
// src/commands/scan.ts
|
|
@@ -21949,6 +21957,7 @@ async function validateCommand(options) {
|
|
|
21949
21957
|
var fs5 = __toESM(require("fs/promises"));
|
|
21950
21958
|
var fsSync2 = __toESM(require("fs"));
|
|
21951
21959
|
var path5 = __toESM(require("path"));
|
|
21960
|
+
var import_child_process2 = require("child_process");
|
|
21952
21961
|
var import_chalk4 = __toESM(require("chalk"));
|
|
21953
21962
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
21954
21963
|
var import_analyzers3 = __toESM(require_dist3());
|
|
@@ -22003,6 +22012,38 @@ async function runScan(targetPath, options) {
|
|
|
22003
22012
|
policy
|
|
22004
22013
|
};
|
|
22005
22014
|
}
|
|
22015
|
+
async function quickScanFile(targetPath, filePath, options) {
|
|
22016
|
+
try {
|
|
22017
|
+
const result = await runScan(targetPath, { ...options, file: filePath });
|
|
22018
|
+
return result.violations;
|
|
22019
|
+
} catch {
|
|
22020
|
+
return [];
|
|
22021
|
+
}
|
|
22022
|
+
}
|
|
22023
|
+
function resolveViolationPath(vFile, projectRoot) {
|
|
22024
|
+
if (path5.isAbsolute(vFile)) return vFile;
|
|
22025
|
+
return path5.resolve(projectRoot, vFile);
|
|
22026
|
+
}
|
|
22027
|
+
function detectTestCommand(projectRoot) {
|
|
22028
|
+
try {
|
|
22029
|
+
const pkg = JSON.parse(fsSync2.readFileSync(path5.join(projectRoot, "package.json"), "utf-8"));
|
|
22030
|
+
const scripts = pkg.scripts ?? {};
|
|
22031
|
+
if (scripts.test && scripts.test !== 'echo "Error: no test specified" && exit 1') return "npm test";
|
|
22032
|
+
if (scripts["test:unit"]) return "npm run test:unit";
|
|
22033
|
+
if (scripts.jest) return "npm run jest";
|
|
22034
|
+
return null;
|
|
22035
|
+
} catch {
|
|
22036
|
+
return null;
|
|
22037
|
+
}
|
|
22038
|
+
}
|
|
22039
|
+
function runTests(projectRoot, testCmd) {
|
|
22040
|
+
try {
|
|
22041
|
+
(0, import_child_process2.execSync)(testCmd, { cwd: projectRoot, stdio: "pipe", timeout: 12e4 });
|
|
22042
|
+
return true;
|
|
22043
|
+
} catch {
|
|
22044
|
+
return false;
|
|
22045
|
+
}
|
|
22046
|
+
}
|
|
22006
22047
|
function groupBy(items, key) {
|
|
22007
22048
|
const result = {};
|
|
22008
22049
|
for (const item of items) {
|
|
@@ -22012,6 +22053,102 @@ function groupBy(items, key) {
|
|
|
22012
22053
|
}
|
|
22013
22054
|
return result;
|
|
22014
22055
|
}
|
|
22056
|
+
async function handleMultiFileFix(client, violation, violationFile, projectRoot, framework, architecture, filesToChange, options) {
|
|
22057
|
+
console.log(import_chalk4.default.magenta.bold("\n MULTI-FILE FIX"));
|
|
22058
|
+
console.log(import_chalk4.default.dim(" Gathering file contents..."));
|
|
22059
|
+
const files = {};
|
|
22060
|
+
const allPaths = [violationFile, ...filesToChange.map((f) => resolveViolationPath(f, projectRoot))];
|
|
22061
|
+
for (const fp of allPaths) {
|
|
22062
|
+
try {
|
|
22063
|
+
files[fp] = await fs5.readFile(fp, "utf-8");
|
|
22064
|
+
} catch {
|
|
22065
|
+
console.log(import_chalk4.default.yellow(` Could not read: ${fp} \u2014 skipping`));
|
|
22066
|
+
}
|
|
22067
|
+
}
|
|
22068
|
+
if (Object.keys(files).length < 2) {
|
|
22069
|
+
console.log(import_chalk4.default.yellow(" Not enough files found for multi-file fix."));
|
|
22070
|
+
return;
|
|
22071
|
+
}
|
|
22072
|
+
console.log(import_chalk4.default.dim(` Sending ${Object.keys(files).length} files to Radar for analysis...`));
|
|
22073
|
+
const result = await client.requestMultiFix({
|
|
22074
|
+
violation: {
|
|
22075
|
+
file: violation.file,
|
|
22076
|
+
line: violation.line,
|
|
22077
|
+
ruleId: violation.ruleId,
|
|
22078
|
+
message: violation.message,
|
|
22079
|
+
severity: violation.severity,
|
|
22080
|
+
category: violation.category
|
|
22081
|
+
},
|
|
22082
|
+
files,
|
|
22083
|
+
framework,
|
|
22084
|
+
architecture
|
|
22085
|
+
});
|
|
22086
|
+
console.log("");
|
|
22087
|
+
if (result.explanation) {
|
|
22088
|
+
console.log(import_chalk4.default.white.bold(" WHY:"));
|
|
22089
|
+
console.log(import_chalk4.default.white(` ${result.explanation}`));
|
|
22090
|
+
}
|
|
22091
|
+
if (result.risk) {
|
|
22092
|
+
console.log(import_chalk4.default.yellow(` RISK: ${result.risk}`));
|
|
22093
|
+
}
|
|
22094
|
+
if (result.pattern) {
|
|
22095
|
+
console.log(import_chalk4.default.cyan(` PATTERN: ${result.pattern}`));
|
|
22096
|
+
}
|
|
22097
|
+
console.log("");
|
|
22098
|
+
for (let i = 0; i < result.changes.length; i++) {
|
|
22099
|
+
const change = result.changes[i];
|
|
22100
|
+
const rel = path5.relative(projectRoot, resolveViolationPath(change.filePath, projectRoot));
|
|
22101
|
+
console.log(import_chalk4.default.bold(` FILE ${i + 1}: ${rel}`));
|
|
22102
|
+
console.log(import_chalk4.default.dim(` ${change.description}`));
|
|
22103
|
+
const beforeLines = change.before.split("\n");
|
|
22104
|
+
const afterLines = change.after.split("\n");
|
|
22105
|
+
let diffShown = 0;
|
|
22106
|
+
for (let j = 0; j < Math.max(beforeLines.length, afterLines.length) && diffShown < 10; j++) {
|
|
22107
|
+
if (beforeLines[j] !== afterLines[j]) {
|
|
22108
|
+
if (beforeLines[j]) console.log(import_chalk4.default.red(` - ${beforeLines[j]}`));
|
|
22109
|
+
if (afterLines[j]) console.log(import_chalk4.default.green(` + ${afterLines[j]}`));
|
|
22110
|
+
diffShown++;
|
|
22111
|
+
}
|
|
22112
|
+
}
|
|
22113
|
+
if (diffShown >= 10) console.log(import_chalk4.default.dim(" ... (more changes)"));
|
|
22114
|
+
console.log("");
|
|
22115
|
+
}
|
|
22116
|
+
console.log(import_chalk4.default.cyan(` ${result.creditsUsed} AI credits used (${result.creditsRemaining} remaining)`));
|
|
22117
|
+
if (options.dryRun) {
|
|
22118
|
+
console.log(import_chalk4.default.dim(" [dry-run] Would apply all changes"));
|
|
22119
|
+
return;
|
|
22120
|
+
}
|
|
22121
|
+
const originals = {};
|
|
22122
|
+
for (const change of result.changes) {
|
|
22123
|
+
const fp = resolveViolationPath(change.filePath, projectRoot);
|
|
22124
|
+
try {
|
|
22125
|
+
originals[fp] = await fs5.readFile(fp, "utf-8");
|
|
22126
|
+
} catch {
|
|
22127
|
+
originals[fp] = "";
|
|
22128
|
+
}
|
|
22129
|
+
}
|
|
22130
|
+
for (const change of result.changes) {
|
|
22131
|
+
const fp = resolveViolationPath(change.filePath, projectRoot);
|
|
22132
|
+
fsSync2.writeFileSync(fp, change.after, "utf-8");
|
|
22133
|
+
}
|
|
22134
|
+
console.log(import_chalk4.default.green(` Applied changes to ${result.changes.length} files`));
|
|
22135
|
+
if (options.safe) {
|
|
22136
|
+
const testCmd = detectTestCommand(projectRoot);
|
|
22137
|
+
if (testCmd) {
|
|
22138
|
+
console.log(import_chalk4.default.dim(` Running tests: ${testCmd}...`));
|
|
22139
|
+
const passed = runTests(projectRoot, testCmd);
|
|
22140
|
+
if (passed) {
|
|
22141
|
+
console.log(import_chalk4.default.green(" \u2705 Tests passed"));
|
|
22142
|
+
} else {
|
|
22143
|
+
console.log(import_chalk4.default.red(" \u274C Tests failed \u2014 reverting all changes"));
|
|
22144
|
+
for (const [fp, content] of Object.entries(originals)) {
|
|
22145
|
+
fsSync2.writeFileSync(fp, content, "utf-8");
|
|
22146
|
+
}
|
|
22147
|
+
return;
|
|
22148
|
+
}
|
|
22149
|
+
}
|
|
22150
|
+
}
|
|
22151
|
+
}
|
|
22015
22152
|
async function fixCommand(targetPath, options) {
|
|
22016
22153
|
const client = RadarApiClient.fromConfigOrEnv();
|
|
22017
22154
|
if (!client) {
|
|
@@ -22034,6 +22171,16 @@ async function fixCommand(targetPath, options) {
|
|
|
22034
22171
|
console.error(import_chalk4.default.red("\u274C Invalid or expired token. Run: radar login"));
|
|
22035
22172
|
process.exit(1);
|
|
22036
22173
|
}
|
|
22174
|
+
const projectRoot = path5.resolve(targetPath);
|
|
22175
|
+
let testCmd = null;
|
|
22176
|
+
if (options.safe) {
|
|
22177
|
+
testCmd = detectTestCommand(projectRoot);
|
|
22178
|
+
if (!testCmd) {
|
|
22179
|
+
console.log(import_chalk4.default.yellow("\u26A0\uFE0F No test command found in package.json. --safe will skip test verification."));
|
|
22180
|
+
} else {
|
|
22181
|
+
console.log(import_chalk4.default.dim(` Test command: ${testCmd}`));
|
|
22182
|
+
}
|
|
22183
|
+
}
|
|
22037
22184
|
console.log(import_chalk4.default.blue("Scanning for violations..."));
|
|
22038
22185
|
const scanResult = await runScan(targetPath, {
|
|
22039
22186
|
config: options.config,
|
|
@@ -22054,10 +22201,11 @@ async function fixCommand(targetPath, options) {
|
|
|
22054
22201
|
}
|
|
22055
22202
|
console.log(import_chalk4.default.yellow(`Found ${violations.length} violations. Generating AI fixes...
|
|
22056
22203
|
`));
|
|
22057
|
-
const byFile = groupBy(violations, (v) => v.file);
|
|
22204
|
+
const byFile = groupBy(violations, (v) => resolveViolationPath(v.file, projectRoot));
|
|
22058
22205
|
let totalFixed = 0;
|
|
22059
22206
|
let totalSkipped = 0;
|
|
22060
22207
|
let totalFailed = 0;
|
|
22208
|
+
let totalReverted = 0;
|
|
22061
22209
|
let totalCreditsUsed = 0;
|
|
22062
22210
|
let creditsRemaining = -1;
|
|
22063
22211
|
let fixCount = 0;
|
|
@@ -22067,15 +22215,16 @@ async function fixCommand(targetPath, options) {
|
|
|
22067
22215
|
const orm = scanResult.config?.stack?.orm || "Prisma";
|
|
22068
22216
|
for (const [filePath, fileViolations] of Object.entries(byFile)) {
|
|
22069
22217
|
if (quit) break;
|
|
22070
|
-
const
|
|
22218
|
+
const absPath = path5.resolve(filePath);
|
|
22219
|
+
const relativePath = path5.relative(projectRoot, absPath);
|
|
22071
22220
|
console.log(import_chalk4.default.bold(`
|
|
22072
22221
|
${relativePath} (${fileViolations.length} violations)
|
|
22073
22222
|
`));
|
|
22074
22223
|
let fileContent;
|
|
22075
22224
|
try {
|
|
22076
|
-
fileContent = await fs5.readFile(
|
|
22225
|
+
fileContent = await fs5.readFile(absPath, "utf-8");
|
|
22077
22226
|
} catch {
|
|
22078
|
-
console.log(import_chalk4.default.red(` Could not read file: ${
|
|
22227
|
+
console.log(import_chalk4.default.red(` Could not read file: ${absPath}`));
|
|
22079
22228
|
totalFailed += fileViolations.length;
|
|
22080
22229
|
continue;
|
|
22081
22230
|
}
|
|
@@ -22150,13 +22299,29 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22150
22299
|
totalCreditsUsed += fix.creditsUsed;
|
|
22151
22300
|
creditsRemaining = fix.creditsRemaining;
|
|
22152
22301
|
fixCount++;
|
|
22302
|
+
if (fix.explanation) {
|
|
22303
|
+
console.log(import_chalk4.default.white.bold(" WHY THIS IS A VIOLATION:"));
|
|
22304
|
+
console.log(import_chalk4.default.white(` ${fix.explanation}`));
|
|
22305
|
+
}
|
|
22306
|
+
if (fix.risk) {
|
|
22307
|
+
console.log(import_chalk4.default.yellow(` RISK: ${fix.risk}`));
|
|
22308
|
+
}
|
|
22309
|
+
if (fix.pattern) {
|
|
22310
|
+
console.log(import_chalk4.default.cyan(` PATTERN: ${fix.pattern}`));
|
|
22311
|
+
}
|
|
22312
|
+
if (fix.multiFile && fix.filesToChange && fix.filesToChange.length > 0) {
|
|
22313
|
+
console.log(import_chalk4.default.magenta(` MULTI-FILE: Also needs changes in ${fix.filesToChange.length} other file(s):`));
|
|
22314
|
+
for (const f of fix.filesToChange) {
|
|
22315
|
+
console.log(import_chalk4.default.magenta(` \u2192 ${f}`));
|
|
22316
|
+
}
|
|
22317
|
+
}
|
|
22318
|
+
console.log("");
|
|
22153
22319
|
console.log(import_chalk4.default.red(" BEFORE:"));
|
|
22154
22320
|
fix.originalCode.split("\n").forEach((l) => console.log(import_chalk4.default.red(` - ${l}`)));
|
|
22155
22321
|
console.log("");
|
|
22156
22322
|
console.log(import_chalk4.default.green(" AFTER:"));
|
|
22157
22323
|
fix.fixedCode.split("\n").forEach((l) => console.log(import_chalk4.default.green(` + ${l}`)));
|
|
22158
22324
|
console.log("");
|
|
22159
|
-
console.log(import_chalk4.default.dim(` ${fix.explanation}`));
|
|
22160
22325
|
console.log(import_chalk4.default.dim(` Confidence: ${fix.confidence}`));
|
|
22161
22326
|
console.log(import_chalk4.default.cyan(` ${fix.creditsUsed} AI credit used (${fix.creditsRemaining} remaining)`));
|
|
22162
22327
|
console.log("");
|
|
@@ -22164,21 +22329,23 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22164
22329
|
if (options.dryRun) {
|
|
22165
22330
|
console.log(import_chalk4.default.dim(" [dry-run] Would apply this fix"));
|
|
22166
22331
|
totalSkipped += lineViolations.length;
|
|
22167
|
-
} else if (applyAll || options.auto
|
|
22332
|
+
} else if (applyAll || options.auto) {
|
|
22168
22333
|
shouldApply = true;
|
|
22169
|
-
console.log(import_chalk4.default.green(" Auto-applying..."));
|
|
22334
|
+
if (!options.auto) console.log(import_chalk4.default.green(" Auto-applying (all)..."));
|
|
22170
22335
|
} else {
|
|
22336
|
+
const choices = [
|
|
22337
|
+
{ name: "Yes - apply this fix", value: "yes" },
|
|
22338
|
+
...fix.multiFile && fix.filesToChange?.length ? [{ name: `Multi-file fix (${fix.filesToChange.length + 1} files, 3 credits)`, value: "multi" }] : [],
|
|
22339
|
+
{ name: "No - skip", value: "no" },
|
|
22340
|
+
{ name: "All - apply all remaining fixes", value: "all" },
|
|
22341
|
+
{ name: "Quit - stop fixing", value: "quit" }
|
|
22342
|
+
];
|
|
22171
22343
|
const { action } = await import_inquirer2.default.prompt([
|
|
22172
22344
|
{
|
|
22173
22345
|
type: "list",
|
|
22174
22346
|
name: "action",
|
|
22175
22347
|
message: `Apply this fix? (${lineViolations.length} violation${lineViolations.length > 1 ? "s" : ""})`,
|
|
22176
|
-
choices
|
|
22177
|
-
{ name: "Yes - apply this fix", value: "yes" },
|
|
22178
|
-
{ name: "No - skip", value: "no" },
|
|
22179
|
-
{ name: "All - apply all remaining fixes", value: "all" },
|
|
22180
|
-
{ name: "Quit - stop fixing", value: "quit" }
|
|
22181
|
-
]
|
|
22348
|
+
choices
|
|
22182
22349
|
}
|
|
22183
22350
|
]);
|
|
22184
22351
|
if (action === "quit") {
|
|
@@ -22194,26 +22361,70 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22194
22361
|
totalSkipped += lineViolations.length;
|
|
22195
22362
|
continue;
|
|
22196
22363
|
}
|
|
22364
|
+
if (action === "multi") {
|
|
22365
|
+
await handleMultiFileFix(
|
|
22366
|
+
client,
|
|
22367
|
+
lineViolations[0],
|
|
22368
|
+
absPath,
|
|
22369
|
+
projectRoot,
|
|
22370
|
+
framework,
|
|
22371
|
+
scanResult.config?.architecture,
|
|
22372
|
+
fix.filesToChange ?? [],
|
|
22373
|
+
options
|
|
22374
|
+
);
|
|
22375
|
+
totalFixed += lineViolations.length;
|
|
22376
|
+
fileContent = await fs5.readFile(absPath, "utf-8");
|
|
22377
|
+
lines = fileContent.split("\n");
|
|
22378
|
+
continue;
|
|
22379
|
+
}
|
|
22197
22380
|
}
|
|
22198
22381
|
if (shouldApply) {
|
|
22199
|
-
|
|
22200
|
-
|
|
22201
|
-
console.log(import_chalk4.default.green(`
|
|
22202
|
-
|
|
22382
|
+
const originalContent = options.safe ? fileContent : null;
|
|
22383
|
+
applyFix(absPath, fix);
|
|
22384
|
+
console.log(import_chalk4.default.green(` Applied fix (${lineViolations.length} violation${lineViolations.length > 1 ? "s" : ""})`));
|
|
22385
|
+
const remaining = await quickScanFile(targetPath, absPath, {
|
|
22386
|
+
config: options.config,
|
|
22387
|
+
rules: options.rules
|
|
22388
|
+
});
|
|
22389
|
+
const stillPresent = remaining.some(
|
|
22390
|
+
(rv) => lineViolations.some(
|
|
22391
|
+
(lv) => rv.ruleId === lv.ruleId && rv.file === lv.file && Math.abs(rv.line - lv.line) <= 3
|
|
22392
|
+
)
|
|
22393
|
+
);
|
|
22394
|
+
if (stillPresent) {
|
|
22395
|
+
console.log(import_chalk4.default.yellow(" \u26A0\uFE0F Fix applied but violation persists \u2014 manual review needed"));
|
|
22396
|
+
} else {
|
|
22397
|
+
console.log(import_chalk4.default.green(" \u2705 Violation resolved"));
|
|
22398
|
+
}
|
|
22399
|
+
if (options.safe && testCmd) {
|
|
22400
|
+
console.log(import_chalk4.default.dim(` Running tests: ${testCmd}...`));
|
|
22401
|
+
const testsPassed = runTests(projectRoot, testCmd);
|
|
22402
|
+
if (testsPassed) {
|
|
22403
|
+
console.log(import_chalk4.default.green(" \u2705 Tests passed"));
|
|
22404
|
+
totalFixed += lineViolations.length;
|
|
22405
|
+
} else {
|
|
22406
|
+
console.log(import_chalk4.default.red(" \u274C Tests failed \u2014 reverting fix"));
|
|
22407
|
+
fsSync2.writeFileSync(absPath, originalContent, "utf-8");
|
|
22408
|
+
totalReverted += lineViolations.length;
|
|
22409
|
+
}
|
|
22410
|
+
} else {
|
|
22411
|
+
totalFixed += lineViolations.length;
|
|
22412
|
+
}
|
|
22413
|
+
fileContent = await fs5.readFile(absPath, "utf-8");
|
|
22203
22414
|
lines = fileContent.split("\n");
|
|
22204
22415
|
}
|
|
22205
22416
|
} catch (error) {
|
|
22206
22417
|
const message = error instanceof Error ? error.message : String(error);
|
|
22207
22418
|
if (message.includes("403") && message.includes("credits exhausted")) {
|
|
22208
|
-
console.log(import_chalk4.default.red(
|
|
22419
|
+
console.log(import_chalk4.default.red(" \u274C AI credits exhausted. Upgrade for more: radar upgrade"));
|
|
22209
22420
|
quit = true;
|
|
22210
22421
|
break;
|
|
22211
22422
|
} else if (message.includes("403") && message.includes("Solo")) {
|
|
22212
|
-
console.log(import_chalk4.default.red(
|
|
22423
|
+
console.log(import_chalk4.default.red(" \u274C radar fix requires Solo plan or higher. Upgrade: radar upgrade"));
|
|
22213
22424
|
quit = true;
|
|
22214
22425
|
break;
|
|
22215
22426
|
} else if (message.includes("401")) {
|
|
22216
|
-
console.log(import_chalk4.default.red(
|
|
22427
|
+
console.log(import_chalk4.default.red(" \u274C Authentication failed. Run: radar login"));
|
|
22217
22428
|
quit = true;
|
|
22218
22429
|
break;
|
|
22219
22430
|
} else {
|
|
@@ -22227,16 +22438,17 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22227
22438
|
console.log(import_chalk4.default.bold("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
22228
22439
|
console.log(import_chalk4.default.bold(" FIX SUMMARY"));
|
|
22229
22440
|
console.log(import_chalk4.default.bold("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
22230
|
-
console.log(import_chalk4.default.green(` Fixed:
|
|
22231
|
-
console.log(import_chalk4.default.yellow(` Skipped:
|
|
22232
|
-
if (
|
|
22441
|
+
console.log(import_chalk4.default.green(` Fixed: ${totalFixed}`));
|
|
22442
|
+
console.log(import_chalk4.default.yellow(` Skipped: ${totalSkipped}`));
|
|
22443
|
+
if (totalReverted > 0) console.log(import_chalk4.default.red(` Reverted: ${totalReverted} (tests failed)`));
|
|
22444
|
+
if (totalFailed > 0) console.log(import_chalk4.default.red(` Failed: ${totalFailed}`));
|
|
22233
22445
|
console.log(import_chalk4.default.cyan(` AI credits used: ${totalCreditsUsed}`));
|
|
22234
22446
|
if (creditsRemaining >= 0) {
|
|
22235
22447
|
console.log(import_chalk4.default.cyan(` Credits remaining: ${creditsRemaining}`));
|
|
22236
22448
|
}
|
|
22237
22449
|
console.log("");
|
|
22238
22450
|
if (totalFixed > 0 && !options.dryRun) {
|
|
22239
|
-
console.log(import_chalk4.default.blue("Re-scanning to verify fixes...\n"));
|
|
22451
|
+
console.log(import_chalk4.default.blue("Re-scanning to verify all fixes...\n"));
|
|
22240
22452
|
const newResult = await runScan(targetPath, {
|
|
22241
22453
|
config: options.config,
|
|
22242
22454
|
rules: options.rules,
|
|
@@ -22627,7 +22839,7 @@ program.command("check <file>").description("Check a single file against policy"
|
|
|
22627
22839
|
program.command("init").description("Generate radar.yml + rules.yml from project structure").option("--path <dir>", "Target directory", ".").option("--architecture <pattern>", "Force architecture: ddd, hexagonal, clean, layered, mvc, event-driven").option("--regenerate-rules", "Regenerate rules.yml from existing radar.yml", false).option("--no-ai", "Skip AI refinement (deterministic only)").action(async (options) => {
|
|
22628
22840
|
await initCommand(options);
|
|
22629
22841
|
});
|
|
22630
|
-
program.command("fix <path>").description("AI-powered fix: generates code diffs, applies with confirmation").option("-c, --config <path>", "Path to radar.yml", "./radar.yml").option("-r, --rules <path>", "Path to rules.yml (default: ./rules.yml)").option("--severity <level>", "Only include: critical, all", "all").option("--dry-run", "Show fixes without applying", false).option("--auto", "Apply all
|
|
22842
|
+
program.command("fix <path>").description("AI-powered fix: generates code diffs, applies with confirmation").option("-c, --config <path>", "Path to radar.yml", "./radar.yml").option("-r, --rules <path>", "Path to rules.yml (default: ./rules.yml)").option("--severity <level>", "Only include: critical, all", "all").option("--dry-run", "Show fixes without applying", false).option("--auto", "Apply all fixes automatically without asking", false).option("--safe", "Run tests after each fix, revert on failure", false).option("--file <file>", "Fix single file only").option("--max-fixes <count>", "Max number of AI fixes to generate (0 = unlimited)", "0").action(async (targetPath, options) => {
|
|
22631
22843
|
await fixCommand(targetPath, {
|
|
22632
22844
|
...options,
|
|
22633
22845
|
maxFixes: parseInt(options.maxFixes, 10)
|