technical-debt-radar 1.12.0 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +148 -9
  2. 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
@@ -21954,7 +21962,8 @@ var import_chalk4 = __toESM(require("chalk"));
21954
21962
  var import_inquirer2 = __toESM(require("inquirer"));
21955
21963
  var import_analyzers3 = __toESM(require_dist3());
21956
21964
  function applyFix(filePath, fix) {
21957
- const content = fsSync2.readFileSync(filePath, "utf-8");
21965
+ const fullPath = path5.isAbsolute(filePath) ? filePath : path5.resolve(filePath);
21966
+ const content = fsSync2.readFileSync(fullPath, "utf-8");
21958
21967
  const lines = content.split("\n");
21959
21968
  const fixedLines = fix.fixedCode.split("\n");
21960
21969
  const start = fix.startLine - 1;
@@ -21974,7 +21983,7 @@ function applyFix(filePath, fix) {
21974
21983
  }
21975
21984
  }
21976
21985
  }
21977
- fsSync2.writeFileSync(filePath, lines.join("\n"), "utf-8");
21986
+ fsSync2.writeFileSync(fullPath, lines.join("\n"), "utf-8");
21978
21987
  }
21979
21988
  async function runScan(targetPath, options) {
21980
21989
  const { compiled: policy } = await loadPolicy(options.config, options.rules);
@@ -22045,6 +22054,102 @@ function groupBy(items, key) {
22045
22054
  }
22046
22055
  return result;
22047
22056
  }
22057
+ async function handleMultiFileFix(client, violation, violationFile, projectRoot, framework, architecture, filesToChange, options) {
22058
+ console.log(import_chalk4.default.magenta.bold("\n MULTI-FILE FIX"));
22059
+ console.log(import_chalk4.default.dim(" Gathering file contents..."));
22060
+ const files = {};
22061
+ const allPaths = [violationFile, ...filesToChange.map((f) => resolveViolationPath(f, projectRoot))];
22062
+ for (const fp of allPaths) {
22063
+ try {
22064
+ files[fp] = await fs5.readFile(fp, "utf-8");
22065
+ } catch {
22066
+ console.log(import_chalk4.default.yellow(` Could not read: ${fp} \u2014 skipping`));
22067
+ }
22068
+ }
22069
+ if (Object.keys(files).length < 2) {
22070
+ console.log(import_chalk4.default.yellow(" Not enough files found for multi-file fix."));
22071
+ return;
22072
+ }
22073
+ console.log(import_chalk4.default.dim(` Sending ${Object.keys(files).length} files to Radar for analysis...`));
22074
+ const result = await client.requestMultiFix({
22075
+ violation: {
22076
+ file: violation.file,
22077
+ line: violation.line,
22078
+ ruleId: violation.ruleId,
22079
+ message: violation.message,
22080
+ severity: violation.severity,
22081
+ category: violation.category
22082
+ },
22083
+ files,
22084
+ framework,
22085
+ architecture
22086
+ });
22087
+ console.log("");
22088
+ if (result.explanation) {
22089
+ console.log(import_chalk4.default.white.bold(" WHY:"));
22090
+ console.log(import_chalk4.default.white(` ${result.explanation}`));
22091
+ }
22092
+ if (result.risk) {
22093
+ console.log(import_chalk4.default.yellow(` RISK: ${result.risk}`));
22094
+ }
22095
+ if (result.pattern) {
22096
+ console.log(import_chalk4.default.cyan(` PATTERN: ${result.pattern}`));
22097
+ }
22098
+ console.log("");
22099
+ for (let i = 0; i < result.changes.length; i++) {
22100
+ const change = result.changes[i];
22101
+ const rel = path5.relative(projectRoot, resolveViolationPath(change.filePath, projectRoot));
22102
+ console.log(import_chalk4.default.bold(` FILE ${i + 1}: ${rel}`));
22103
+ console.log(import_chalk4.default.dim(` ${change.description}`));
22104
+ const beforeLines = change.before.split("\n");
22105
+ const afterLines = change.after.split("\n");
22106
+ let diffShown = 0;
22107
+ for (let j = 0; j < Math.max(beforeLines.length, afterLines.length) && diffShown < 10; j++) {
22108
+ if (beforeLines[j] !== afterLines[j]) {
22109
+ if (beforeLines[j]) console.log(import_chalk4.default.red(` - ${beforeLines[j]}`));
22110
+ if (afterLines[j]) console.log(import_chalk4.default.green(` + ${afterLines[j]}`));
22111
+ diffShown++;
22112
+ }
22113
+ }
22114
+ if (diffShown >= 10) console.log(import_chalk4.default.dim(" ... (more changes)"));
22115
+ console.log("");
22116
+ }
22117
+ console.log(import_chalk4.default.cyan(` ${result.creditsUsed} AI credits used (${result.creditsRemaining} remaining)`));
22118
+ if (options.dryRun) {
22119
+ console.log(import_chalk4.default.dim(" [dry-run] Would apply all changes"));
22120
+ return;
22121
+ }
22122
+ const originals = {};
22123
+ for (const change of result.changes) {
22124
+ const fp = resolveViolationPath(change.filePath, projectRoot);
22125
+ try {
22126
+ originals[fp] = await fs5.readFile(fp, "utf-8");
22127
+ } catch {
22128
+ originals[fp] = "";
22129
+ }
22130
+ }
22131
+ for (const change of result.changes) {
22132
+ const fp = resolveViolationPath(change.filePath, projectRoot);
22133
+ fsSync2.writeFileSync(fp, change.after, "utf-8");
22134
+ }
22135
+ console.log(import_chalk4.default.green(` Applied changes to ${result.changes.length} files`));
22136
+ if (options.safe) {
22137
+ const testCmd = detectTestCommand(projectRoot);
22138
+ if (testCmd) {
22139
+ console.log(import_chalk4.default.dim(` Running tests: ${testCmd}...`));
22140
+ const passed = runTests(projectRoot, testCmd);
22141
+ if (passed) {
22142
+ console.log(import_chalk4.default.green(" \u2705 Tests passed"));
22143
+ } else {
22144
+ console.log(import_chalk4.default.red(" \u274C Tests failed \u2014 reverting all changes"));
22145
+ for (const [fp, content] of Object.entries(originals)) {
22146
+ fsSync2.writeFileSync(fp, content, "utf-8");
22147
+ }
22148
+ return;
22149
+ }
22150
+ }
22151
+ }
22152
+ }
22048
22153
  async function fixCommand(targetPath, options) {
22049
22154
  const client = RadarApiClient.fromConfigOrEnv();
22050
22155
  if (!client) {
@@ -22195,13 +22300,29 @@ ${relativePath} (${fileViolations.length} violations)
22195
22300
  totalCreditsUsed += fix.creditsUsed;
22196
22301
  creditsRemaining = fix.creditsRemaining;
22197
22302
  fixCount++;
22303
+ if (fix.explanation) {
22304
+ console.log(import_chalk4.default.white.bold(" WHY THIS IS A VIOLATION:"));
22305
+ console.log(import_chalk4.default.white(` ${fix.explanation}`));
22306
+ }
22307
+ if (fix.risk) {
22308
+ console.log(import_chalk4.default.yellow(` RISK: ${fix.risk}`));
22309
+ }
22310
+ if (fix.pattern) {
22311
+ console.log(import_chalk4.default.cyan(` PATTERN: ${fix.pattern}`));
22312
+ }
22313
+ if (fix.multiFile && fix.filesToChange && fix.filesToChange.length > 0) {
22314
+ console.log(import_chalk4.default.magenta(` MULTI-FILE: Also needs changes in ${fix.filesToChange.length} other file(s):`));
22315
+ for (const f of fix.filesToChange) {
22316
+ console.log(import_chalk4.default.magenta(` \u2192 ${f}`));
22317
+ }
22318
+ }
22319
+ console.log("");
22198
22320
  console.log(import_chalk4.default.red(" BEFORE:"));
22199
22321
  fix.originalCode.split("\n").forEach((l) => console.log(import_chalk4.default.red(` - ${l}`)));
22200
22322
  console.log("");
22201
22323
  console.log(import_chalk4.default.green(" AFTER:"));
22202
22324
  fix.fixedCode.split("\n").forEach((l) => console.log(import_chalk4.default.green(` + ${l}`)));
22203
22325
  console.log("");
22204
- console.log(import_chalk4.default.dim(` ${fix.explanation}`));
22205
22326
  console.log(import_chalk4.default.dim(` Confidence: ${fix.confidence}`));
22206
22327
  console.log(import_chalk4.default.cyan(` ${fix.creditsUsed} AI credit used (${fix.creditsRemaining} remaining)`));
22207
22328
  console.log("");
@@ -22213,17 +22334,19 @@ ${relativePath} (${fileViolations.length} violations)
22213
22334
  shouldApply = true;
22214
22335
  if (!options.auto) console.log(import_chalk4.default.green(" Auto-applying (all)..."));
22215
22336
  } else {
22337
+ const choices = [
22338
+ { name: "Yes - apply this fix", value: "yes" },
22339
+ ...fix.multiFile && fix.filesToChange?.length ? [{ name: `Multi-file fix (${fix.filesToChange.length + 1} files, 3 credits)`, value: "multi" }] : [],
22340
+ { name: "No - skip", value: "no" },
22341
+ { name: "All - apply all remaining fixes", value: "all" },
22342
+ { name: "Quit - stop fixing", value: "quit" }
22343
+ ];
22216
22344
  const { action } = await import_inquirer2.default.prompt([
22217
22345
  {
22218
22346
  type: "list",
22219
22347
  name: "action",
22220
22348
  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
- ]
22349
+ choices
22227
22350
  }
22228
22351
  ]);
22229
22352
  if (action === "quit") {
@@ -22239,6 +22362,22 @@ ${relativePath} (${fileViolations.length} violations)
22239
22362
  totalSkipped += lineViolations.length;
22240
22363
  continue;
22241
22364
  }
22365
+ if (action === "multi") {
22366
+ await handleMultiFileFix(
22367
+ client,
22368
+ lineViolations[0],
22369
+ absPath,
22370
+ projectRoot,
22371
+ framework,
22372
+ scanResult.config?.architecture,
22373
+ fix.filesToChange ?? [],
22374
+ options
22375
+ );
22376
+ totalFixed += lineViolations.length;
22377
+ fileContent = await fs5.readFile(absPath, "utf-8");
22378
+ lines = fileContent.split("\n");
22379
+ continue;
22380
+ }
22242
22381
  }
22243
22382
  if (shouldApply) {
22244
22383
  const originalContent = options.safe ? fileContent : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "technical-debt-radar",
3
- "version": "1.12.0",
3
+ "version": "1.13.1",
4
4
  "description": "Stop Node.js production crashes before merge. 47 detection patterns across 5 categories.",
5
5
  "bin": {
6
6
  "radar": "dist/index.js",