technical-debt-radar 1.12.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 +145 -7
- 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
|
|
@@ -22045,6 +22053,102 @@ function groupBy(items, key) {
|
|
|
22045
22053
|
}
|
|
22046
22054
|
return result;
|
|
22047
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
|
+
}
|
|
22048
22152
|
async function fixCommand(targetPath, options) {
|
|
22049
22153
|
const client = RadarApiClient.fromConfigOrEnv();
|
|
22050
22154
|
if (!client) {
|
|
@@ -22195,13 +22299,29 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22195
22299
|
totalCreditsUsed += fix.creditsUsed;
|
|
22196
22300
|
creditsRemaining = fix.creditsRemaining;
|
|
22197
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("");
|
|
22198
22319
|
console.log(import_chalk4.default.red(" BEFORE:"));
|
|
22199
22320
|
fix.originalCode.split("\n").forEach((l) => console.log(import_chalk4.default.red(` - ${l}`)));
|
|
22200
22321
|
console.log("");
|
|
22201
22322
|
console.log(import_chalk4.default.green(" AFTER:"));
|
|
22202
22323
|
fix.fixedCode.split("\n").forEach((l) => console.log(import_chalk4.default.green(` + ${l}`)));
|
|
22203
22324
|
console.log("");
|
|
22204
|
-
console.log(import_chalk4.default.dim(` ${fix.explanation}`));
|
|
22205
22325
|
console.log(import_chalk4.default.dim(` Confidence: ${fix.confidence}`));
|
|
22206
22326
|
console.log(import_chalk4.default.cyan(` ${fix.creditsUsed} AI credit used (${fix.creditsRemaining} remaining)`));
|
|
22207
22327
|
console.log("");
|
|
@@ -22213,17 +22333,19 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22213
22333
|
shouldApply = true;
|
|
22214
22334
|
if (!options.auto) console.log(import_chalk4.default.green(" Auto-applying (all)..."));
|
|
22215
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
|
+
];
|
|
22216
22343
|
const { action } = await import_inquirer2.default.prompt([
|
|
22217
22344
|
{
|
|
22218
22345
|
type: "list",
|
|
22219
22346
|
name: "action",
|
|
22220
22347
|
message: `Apply this fix? (${lineViolations.length} violation${lineViolations.length > 1 ? "s" : ""})`,
|
|
22221
|
-
choices
|
|
22222
|
-
{ name: "Yes - apply this fix", value: "yes" },
|
|
22223
|
-
{ name: "No - skip", value: "no" },
|
|
22224
|
-
{ name: "All - apply all remaining fixes", value: "all" },
|
|
22225
|
-
{ name: "Quit - stop fixing", value: "quit" }
|
|
22226
|
-
]
|
|
22348
|
+
choices
|
|
22227
22349
|
}
|
|
22228
22350
|
]);
|
|
22229
22351
|
if (action === "quit") {
|
|
@@ -22239,6 +22361,22 @@ ${relativePath} (${fileViolations.length} violations)
|
|
|
22239
22361
|
totalSkipped += lineViolations.length;
|
|
22240
22362
|
continue;
|
|
22241
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
|
+
}
|
|
22242
22380
|
}
|
|
22243
22381
|
if (shouldApply) {
|
|
22244
22382
|
const originalContent = options.safe ? fileContent : null;
|