@codacy/analysis-runner 0.6.2 → 0.8.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 (3) hide show
  1. package/dist/index.d.ts +133 -7
  2. package/dist/index.js +1341 -48
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -34,15 +34,18 @@ __export(index_exports, {
34
34
  applyCodacyYamlExcludes: () => applyCodacyYamlExcludes,
35
35
  buildToolConfig: () => buildToolConfig,
36
36
  clearAdapters: () => clearAdapters,
37
+ computePatternSummary: () => computePatternSummary,
37
38
  configureApiToken: () => configureApiToken,
38
39
  createCodacyConfig: () => createCodacyConfig,
39
40
  createLogger: () => createLogger,
40
41
  detectLanguages: () => detectLanguages,
42
+ discover: () => discover,
41
43
  formatOutput: () => formatOutput,
42
44
  formatTextSummary: () => formatTextSummary,
43
45
  getAdapter: () => getAdapter,
44
46
  getRegisteredAdapters: () => getRegisteredAdapters,
45
47
  getRegisteredDescriptors: () => getRegisteredDescriptors,
48
+ initAutoConfig: () => initAutoConfig,
46
49
  initContainerConfig: () => initContainerConfig,
47
50
  initDefaultConfig: () => initDefaultConfig,
48
51
  initLocalConfig: () => initLocalConfig,
@@ -51,6 +54,7 @@ __export(index_exports, {
51
54
  listIgnoredFiles: () => listIgnoredFiles,
52
55
  listPrFiles: () => listPrFiles,
53
56
  listStagedFiles: () => listStagedFiles,
57
+ parseAutoFilters: () => parseAutoFilters,
54
58
  readCodacyConfig: () => readCodacyConfig,
55
59
  readCodacyYaml: () => readCodacyYaml,
56
60
  registerAdapter: () => registerAdapter,
@@ -192,6 +196,7 @@ function createCodacyConfig(params = {}) {
192
196
  source: params.source ?? "local",
193
197
  provider: params.provider ?? null,
194
198
  organization: params.organization ?? null,
199
+ autoFilters: params.autoFilters,
195
200
  repositoryId: params.repositoryId ?? null,
196
201
  repositoryName: params.repositoryName ?? null,
197
202
  createdAt: now,
@@ -209,8 +214,10 @@ function createCodacyConfig(params = {}) {
209
214
 
210
215
  // src/files.ts
211
216
  var import_child_process = require("child_process");
217
+ var import_fs = __toESM(require("fs"));
212
218
  var import_path2 = __toESM(require("path"));
213
219
  var import_minimatch = require("minimatch");
220
+ var MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
214
221
  function listRepoFiles(repositoryRoot) {
215
222
  try {
216
223
  const tracked = (0, import_child_process.execSync)("git ls-files", {
@@ -235,6 +242,30 @@ function listRepoFiles(repositoryRoot) {
235
242
  return [];
236
243
  }
237
244
  }
245
+ function filterOversizedFiles(files, repositoryRoot, logger) {
246
+ const result = [];
247
+ let skipped = 0;
248
+ for (const f of files) {
249
+ try {
250
+ const stat = import_fs.default.statSync(import_path2.default.join(repositoryRoot, f));
251
+ if (stat.size > MAX_FILE_SIZE_BYTES) {
252
+ skipped++;
253
+ logger?.debug("Skipping oversized file", {
254
+ file: f,
255
+ sizeKB: Math.round(stat.size / 1024),
256
+ limitKB: MAX_FILE_SIZE_BYTES / 1024
257
+ });
258
+ continue;
259
+ }
260
+ } catch {
261
+ }
262
+ result.push(f);
263
+ }
264
+ if (skipped > 0) {
265
+ logger?.info("Filtered oversized files", { skipped, remaining: result.length });
266
+ }
267
+ return result;
268
+ }
238
269
  function applyExclusions(files, globalExclude, toolExclude) {
239
270
  const allExclusions = [...globalExclude, ...toolExclude];
240
271
  if (allExclusions.length === 0) return files;
@@ -426,6 +457,10 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
426
457
  async function runToolsSequential(tools, timeoutMs, callbacks) {
427
458
  const results = [];
428
459
  for (const { adapter, ctx, codacyToolConfig } of tools) {
460
+ if (callbacks?.onToolProgress) {
461
+ const cb = callbacks.onToolProgress;
462
+ ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
463
+ }
429
464
  callbacks?.onToolStart?.(adapter.id, adapter.displayName);
430
465
  const result = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
431
466
  callbacks?.onToolComplete?.(
@@ -445,6 +480,10 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
445
480
  while (nextIndex < tools.length) {
446
481
  const index = nextIndex++;
447
482
  const { adapter, ctx, codacyToolConfig } = tools[index];
483
+ if (callbacks?.onToolProgress) {
484
+ const cb = callbacks.onToolProgress;
485
+ ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
486
+ }
448
487
  callbacks?.onToolStart?.(adapter.id, adapter.displayName);
449
488
  results[index] = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
450
489
  callbacks?.onToolComplete?.(
@@ -460,6 +499,116 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
460
499
  return results;
461
500
  }
462
501
 
502
+ // src/summary.ts
503
+ var NOISY_THRESHOLD = 500;
504
+ var SEVERITY_ORDER = ["Error", "High", "Warning", "Info"];
505
+ var SEVERITY_LABELS = {
506
+ Error: "CRITICAL",
507
+ High: "HIGH",
508
+ Warning: "WARNING",
509
+ Info: "MINOR"
510
+ };
511
+ var CATEGORY_ORDER = [
512
+ "Security",
513
+ "ErrorProne",
514
+ "Performance",
515
+ "BestPractice",
516
+ "UnusedCode",
517
+ "Compatibility",
518
+ "Complexity",
519
+ "Comprehensibility",
520
+ "CodeStyle",
521
+ "Documentation"
522
+ ];
523
+ function formatCategoryName(category) {
524
+ return category.replace(/([a-z])([A-Z])/g, "$1 $2");
525
+ }
526
+ function computePatternSummary(issues, lookup) {
527
+ if (issues.length === 0) {
528
+ return {
529
+ groups: [],
530
+ totalIssues: 0,
531
+ totalPatterns: 0,
532
+ noisyPatternCount: 0,
533
+ noisyThreshold: NOISY_THRESHOLD
534
+ };
535
+ }
536
+ const accumMap = /* @__PURE__ */ new Map();
537
+ for (const issue of issues) {
538
+ const key = `${issue.toolId}|${issue.severity}|${issue.category}|${issue.patternId}`;
539
+ let accum = accumMap.get(key);
540
+ if (!accum) {
541
+ accum = {
542
+ patternId: issue.patternId,
543
+ toolId: issue.toolId,
544
+ severity: issue.severity,
545
+ category: issue.category,
546
+ issueCount: 0,
547
+ files: /* @__PURE__ */ new Set()
548
+ };
549
+ accumMap.set(key, accum);
550
+ }
551
+ accum.issueCount++;
552
+ accum.files.add(issue.filePath);
553
+ }
554
+ const groupMap = /* @__PURE__ */ new Map();
555
+ for (const accum of accumMap.values()) {
556
+ const groupKey = `${accum.severity}|${accum.category}`;
557
+ let list = groupMap.get(groupKey);
558
+ if (!list) {
559
+ list = [];
560
+ groupMap.set(groupKey, list);
561
+ }
562
+ list.push(accum);
563
+ }
564
+ let totalPatterns = 0;
565
+ let noisyPatternCount = 0;
566
+ const groups = [];
567
+ for (const severity of SEVERITY_ORDER) {
568
+ for (const category of CATEGORY_ORDER) {
569
+ const accums = groupMap.get(`${severity}|${category}`);
570
+ if (!accums || accums.length === 0) continue;
571
+ accums.sort((a, b) => b.issueCount - a.issueCount);
572
+ let groupIssueCount = 0;
573
+ const patterns = [];
574
+ for (const acc of accums) {
575
+ const meta = lookup.get(`${acc.toolId}|${acc.patternId}`);
576
+ const noisy = acc.issueCount >= NOISY_THRESHOLD;
577
+ if (noisy) noisyPatternCount++;
578
+ totalPatterns++;
579
+ groupIssueCount += acc.issueCount;
580
+ patterns.push({
581
+ patternId: acc.patternId,
582
+ title: meta?.title ?? acc.patternId,
583
+ toolId: acc.toolId,
584
+ toolDisplayName: meta?.toolDisplayName ?? acc.toolId,
585
+ category,
586
+ severity,
587
+ issueCount: acc.issueCount,
588
+ fileCount: acc.files.size,
589
+ noisy
590
+ });
591
+ }
592
+ groups.push({
593
+ severity,
594
+ severityLabel: SEVERITY_LABELS[severity],
595
+ category,
596
+ categoryLabel: formatCategoryName(category),
597
+ patterns,
598
+ totalIssues: groupIssueCount,
599
+ totalPatterns: patterns.length
600
+ });
601
+ }
602
+ }
603
+ return {
604
+ groups,
605
+ totalIssues: issues.length,
606
+ totalPatterns,
607
+ noisyPatternCount,
608
+ noisyThreshold: NOISY_THRESHOLD
609
+ };
610
+ }
611
+
463
612
  // src/analyze.ts
464
613
  var execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
465
614
  var nullLogger = {
@@ -478,6 +627,13 @@ async function analyze(options) {
478
627
  const toolTimeout = options.toolTimeout || 6e5;
479
628
  const parallelTools = options.parallelTools || 1;
480
629
  const logger = options.logger ?? nullLogger;
630
+ const phaseTimings = {
631
+ availabilityMs: 0,
632
+ fileDiscoveryMs: 0,
633
+ fileRoutingMs: 0,
634
+ toolExecutionMs: 0,
635
+ installMs: 0
636
+ };
481
637
  const emptyCapability = {
482
638
  ready: [],
483
639
  unavailable: [],
@@ -556,6 +712,7 @@ async function analyze(options) {
556
712
  const ready = [];
557
713
  const unavailable = [];
558
714
  const availabilityTimeout = 6e4;
715
+ const availabilityStartMs = Date.now();
559
716
  for (const adapter of adapters) {
560
717
  const ctx = makeToolContext(baseCtx, adapter, []);
561
718
  const checkStart = Date.now();
@@ -614,6 +771,7 @@ async function analyze(options) {
614
771
  });
615
772
  }
616
773
  }
774
+ phaseTimings.availabilityMs = Date.now() - availabilityStartMs;
617
775
  const capability = {
618
776
  ready,
619
777
  unavailable,
@@ -629,6 +787,7 @@ async function analyze(options) {
629
787
  configuredToolCount: configuredIds.size
630
788
  });
631
789
  if (mode === "auto-install" && capability.unavailable.length > 0) {
790
+ const installStartMs = Date.now();
632
791
  const installErrors = [];
633
792
  for (let i = capability.unavailable.length - 1; i >= 0; i--) {
634
793
  const status = capability.unavailable[i];
@@ -692,6 +851,7 @@ async function analyze(options) {
692
851
  });
693
852
  }
694
853
  }
854
+ phaseTimings.installMs = Date.now() - installStartMs;
695
855
  if (installErrors.length > 0) {
696
856
  return buildResult(
697
857
  startedAt,
@@ -700,7 +860,9 @@ async function analyze(options) {
700
860
  capability,
701
861
  [],
702
862
  installErrors,
703
- []
863
+ [],
864
+ void 0,
865
+ phaseTimings
704
866
  );
705
867
  }
706
868
  }
@@ -752,6 +914,7 @@ async function analyze(options) {
752
914
  []
753
915
  );
754
916
  }
917
+ const fileDiscoveryStartMs = Date.now();
755
918
  const globalExclude = codacyConfig.exclude || [];
756
919
  let allFiles = listRepoFiles(options.repositoryRoot);
757
920
  logger.debug("File discovery", { totalFiles: allFiles.length });
@@ -773,6 +936,9 @@ async function analyze(options) {
773
936
  allFiles = filterByTargetFiles(allFiles, options.files);
774
937
  logger.debug("Filtered by target files", { afterFilter: allFiles.length });
775
938
  }
939
+ allFiles = filterOversizedFiles(allFiles, options.repositoryRoot, logger);
940
+ phaseTimings.fileDiscoveryMs = Date.now() - fileDiscoveryStartMs;
941
+ const fileRoutingStartMs = Date.now();
776
942
  const readyAdapters = adapters.filter((a) => capability.ready.some((r) => r.toolId === a.id));
777
943
  const toolRuns = [];
778
944
  for (const adapter of readyAdapters) {
@@ -813,6 +979,8 @@ async function analyze(options) {
813
979
  });
814
980
  toolRuns.push({ adapter, ctx: configCtx, codacyToolConfig });
815
981
  }
982
+ phaseTimings.fileRoutingMs = Date.now() - fileRoutingStartMs;
983
+ const toolExecStartMs = Date.now();
816
984
  logger.info("Starting tool execution", {
817
985
  toolCount: toolRuns.length,
818
986
  parallel: parallelTools > 1,
@@ -820,7 +988,8 @@ async function analyze(options) {
820
988
  });
821
989
  const callbacks = {
822
990
  onToolStart: options.onToolStart,
823
- onToolComplete: options.onToolComplete
991
+ onToolComplete: options.onToolComplete,
992
+ onToolProgress: options.onToolProgress
824
993
  };
825
994
  let results;
826
995
  if (parallelTools > 1 && toolRuns.length > 1) {
@@ -828,13 +997,27 @@ async function analyze(options) {
828
997
  } else {
829
998
  results = await runToolsSequential(toolRuns, toolTimeout, callbacks);
830
999
  }
1000
+ phaseTimings.toolExecutionMs = Date.now() - toolExecStartMs;
831
1001
  const allIssues = normalizeIssues(results.flatMap((r) => r.issues));
832
1002
  const allErrors = results.flatMap((r) => r.errors);
833
1003
  const allSummaries = results.map((r) => r.summary);
1004
+ const patternLookup = /* @__PURE__ */ new Map();
1005
+ for (const { adapter } of toolRuns) {
1006
+ if (adapter.patternsMetadata) {
1007
+ for (const entry of adapter.patternsMetadata) {
1008
+ patternLookup.set(`${adapter.id}|${entry.id}`, {
1009
+ title: entry.title,
1010
+ toolDisplayName: adapter.displayName
1011
+ });
1012
+ }
1013
+ }
1014
+ }
1015
+ const patternSummary = allIssues.length > 0 ? computePatternSummary(allIssues, patternLookup) : void 0;
834
1016
  logger.info("Pipeline complete", {
835
1017
  totalIssues: allIssues.length,
836
1018
  totalErrors: allErrors.length,
837
- durationMs: Date.now() - startedAt.getTime()
1019
+ durationMs: Date.now() - startedAt.getTime(),
1020
+ phaseTimings
838
1021
  });
839
1022
  return buildResult(
840
1023
  startedAt,
@@ -843,7 +1026,9 @@ async function analyze(options) {
843
1026
  capability,
844
1027
  allIssues,
845
1028
  allErrors,
846
- allSummaries
1029
+ allSummaries,
1030
+ patternSummary,
1031
+ phaseTimings
847
1032
  );
848
1033
  }
849
1034
  function makeToolContext(base, adapter, targetFiles) {
@@ -869,7 +1054,7 @@ function resolveGitScope(repositoryRoot, scope) {
869
1054
  const base = scope.diff === true ? void 0 : scope.diff;
870
1055
  return listDiffFiles(repositoryRoot, base);
871
1056
  }
872
- function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors, toolResults) {
1057
+ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors, toolResults, patternSummary, phaseTimings) {
873
1058
  const completedAt = /* @__PURE__ */ new Date();
874
1059
  return {
875
1060
  metadata: {
@@ -877,12 +1062,14 @@ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors
877
1062
  completedAt: completedAt.toISOString(),
878
1063
  durationMs: completedAt.getTime() - startedAt.getTime(),
879
1064
  repositoryRoot,
880
- executionMode: mode
1065
+ executionMode: mode,
1066
+ ...phaseTimings ? { phaseTimings } : {}
881
1067
  },
882
1068
  capability,
883
1069
  issues,
884
1070
  errors,
885
- toolResults
1071
+ toolResults,
1072
+ ...patternSummary ? { patternSummary } : {}
886
1073
  };
887
1074
  }
888
1075
 
@@ -1045,11 +1232,7 @@ function formatText(result) {
1045
1232
  if (result.errors.length > 0) {
1046
1233
  lines.push("");
1047
1234
  lines.push("Errors:");
1048
- for (const error of result.errors) {
1049
- const prefix = `[${error.toolId}/${error.phase}]`;
1050
- const location = error.filePath ? ` ${error.filePath}:` : "";
1051
- lines.push(import_ansis.default.red(` ${prefix}${location} ${error.message}`));
1052
- }
1235
+ lines.push(...formatErrorSection(result));
1053
1236
  lines.push("");
1054
1237
  }
1055
1238
  lines.push(formatTextSummary(result, isInspect));
@@ -1148,6 +1331,33 @@ function formatTextSummary(result, isInspect) {
1148
1331
  lines.push(table.toString());
1149
1332
  }
1150
1333
  }
1334
+ if (!inspect && result.metadata.phaseTimings) {
1335
+ const pt = result.metadata.phaseTimings;
1336
+ const totalMs = result.metadata.durationMs;
1337
+ const toolSumMs = pt.toolExecutionMs;
1338
+ const overheadMs = totalMs - toolSumMs;
1339
+ if (overheadMs > 2e3 && overheadMs / totalMs > 0.2) {
1340
+ lines.push("");
1341
+ lines.push(import_ansis.default.bold("Pipeline timing:"));
1342
+ const entries2 = [
1343
+ ["Availability checks", pt.availabilityMs],
1344
+ ["File discovery", pt.fileDiscoveryMs],
1345
+ ["File routing & config", pt.fileRoutingMs]
1346
+ ];
1347
+ if (pt.installMs > 0) {
1348
+ entries2.push(["Install", pt.installMs]);
1349
+ }
1350
+ entries2.push(["Tool execution", pt.toolExecutionMs]);
1351
+ const accounted = entries2.reduce((sum, [, ms]) => sum + ms, 0);
1352
+ const other = totalMs - accounted;
1353
+ if (other > 500) {
1354
+ entries2.push(["Other", other]);
1355
+ }
1356
+ for (const [label, ms] of entries2) {
1357
+ lines.push(` ${label.padEnd(22)} ${(0, import_tooling4.formatDuration)(ms)}`);
1358
+ }
1359
+ }
1360
+ }
1151
1361
  const toolsWithHints = result.capability.unavailable.filter((t) => t.remediation);
1152
1362
  if (toolsWithHints.length > 0) {
1153
1363
  lines.push("");
@@ -1159,8 +1369,58 @@ function formatTextSummary(result, isInspect) {
1159
1369
  }
1160
1370
  }
1161
1371
  }
1372
+ const patternSummaryText = formatPatternSummary(result.patternSummary);
1373
+ if (patternSummaryText) {
1374
+ lines.push("");
1375
+ lines.push(patternSummaryText);
1376
+ }
1162
1377
  return lines.join("\n");
1163
1378
  }
1379
+ var MAX_ERRORS_PER_GROUP = 5;
1380
+ function formatErrorSection(result) {
1381
+ const lines = [];
1382
+ const displayNameMap = /* @__PURE__ */ new Map();
1383
+ for (const t of result.capability.ready) {
1384
+ displayNameMap.set(t.toolId, t.displayName ?? t.toolId);
1385
+ }
1386
+ for (const t of result.capability.unavailable) {
1387
+ displayNameMap.set(t.toolId, t.displayName ?? t.toolId);
1388
+ }
1389
+ const displayName = (toolId) => displayNameMap.get(toolId) ?? toolId;
1390
+ const groups = /* @__PURE__ */ new Map();
1391
+ for (const error of result.errors) {
1392
+ const key = `${displayName(error.toolId)}/${error.phase}`;
1393
+ const list = groups.get(key) ?? [];
1394
+ list.push(error);
1395
+ groups.set(key, list);
1396
+ }
1397
+ for (const [groupKey, groupErrors] of groups) {
1398
+ const deduped = [];
1399
+ const seen = /* @__PURE__ */ new Map();
1400
+ for (const err of groupErrors) {
1401
+ const dedupKey = `${err.filePath ?? ""}|||${err.message}`;
1402
+ const idx = seen.get(dedupKey);
1403
+ if (idx !== void 0) {
1404
+ deduped[idx].count++;
1405
+ } else {
1406
+ seen.set(dedupKey, deduped.length);
1407
+ deduped.push({ filePath: err.filePath, message: err.message, count: 1 });
1408
+ }
1409
+ }
1410
+ const shown = deduped.slice(0, MAX_ERRORS_PER_GROUP);
1411
+ const overflowCount = deduped.length - shown.length;
1412
+ for (const entry of shown) {
1413
+ const prefix = `[${groupKey}]`;
1414
+ const location = entry.filePath ? ` ${entry.filePath}:` : "";
1415
+ const suffix = entry.count > 1 ? ` (x${entry.count})` : "";
1416
+ lines.push(import_ansis.default.red(` ${prefix}${location} ${entry.message}${suffix}`));
1417
+ }
1418
+ if (overflowCount > 0) {
1419
+ lines.push(import_ansis.default.dim(` ... and ${overflowCount} more ${groupKey} errors`));
1420
+ }
1421
+ }
1422
+ return lines;
1423
+ }
1164
1424
  var MAX_LINE_LENGTH = 100;
1165
1425
  var MAX_MULTILINE_DISPLAY = 10;
1166
1426
  function formatMultilineIssue(output, issue) {
@@ -1241,23 +1501,801 @@ function colorSeverity(severity) {
1241
1501
  return `[${severity}]`;
1242
1502
  }
1243
1503
  }
1504
+ function severityColorFn(severity) {
1505
+ switch (severity) {
1506
+ case "Error":
1507
+ return (s) => import_ansis.default.hex("#ff6347")(s);
1508
+ case "High":
1509
+ return (s) => import_ansis.default.hex("#ff8c00")(s);
1510
+ case "Warning":
1511
+ return (s) => import_ansis.default.hex("#ffd700")(s);
1512
+ case "Info":
1513
+ return (s) => import_ansis.default.cyan(s);
1514
+ default:
1515
+ return (s) => s;
1516
+ }
1517
+ }
1518
+ var MAX_PATTERNS_PER_GROUP = 10;
1519
+ function formatPatternSummary(summary) {
1520
+ if (!summary || summary.groups.length === 0) return "";
1521
+ const lines = [];
1522
+ lines.push(import_ansis.default.bold("Pattern Summary"));
1523
+ lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1524
+ if (summary.noisyPatternCount > 0) {
1525
+ const n = summary.noisyPatternCount;
1526
+ lines.push(
1527
+ import_ansis.default.hex("#ff6347")(
1528
+ `${n} noisy pattern${n !== 1 ? "s" : ""} detected (${summary.noisyThreshold}+ issues) \u2014 consider disabling or reconfiguring`
1529
+ )
1530
+ );
1531
+ }
1532
+ lines.push("");
1533
+ let maxLabelLen = 0;
1534
+ let maxCountLen = 0;
1535
+ for (const group of summary.groups) {
1536
+ const visible = group.patterns.slice(0, MAX_PATTERNS_PER_GROUP);
1537
+ for (const p of visible) {
1538
+ const label = `${p.title} (${p.toolDisplayName})`;
1539
+ maxLabelLen = Math.max(maxLabelLen, label.length);
1540
+ const countStr = `${p.issueCount} issue${p.issueCount !== 1 ? "s" : ""}`;
1541
+ maxCountLen = Math.max(maxCountLen, countStr.length);
1542
+ }
1543
+ }
1544
+ for (const group of summary.groups) {
1545
+ const color = severityColorFn(group.severity);
1546
+ lines.push(color(`${group.severityLabel} \u2014 ${group.categoryLabel}`));
1547
+ const visible = group.patterns.slice(0, MAX_PATTERNS_PER_GROUP);
1548
+ const remaining = group.patterns.length - visible.length;
1549
+ for (const pattern of visible) {
1550
+ const label = `${pattern.title} (${pattern.toolDisplayName})`;
1551
+ const countStr = `${pattern.issueCount} issue${pattern.issueCount !== 1 ? "s" : ""}`;
1552
+ const fileStr = `in ${pattern.fileCount} file${pattern.fileCount !== 1 ? "s" : ""}`;
1553
+ const noisyStr = pattern.noisy ? import_ansis.default.bold.red(" NOISY") : "";
1554
+ lines.push(
1555
+ ` ${label.padEnd(maxLabelLen)} ${countStr.padStart(maxCountLen)} ${fileStr}${noisyStr}`
1556
+ );
1557
+ }
1558
+ if (remaining > 0) {
1559
+ lines.push(import_ansis.default.dim(` ... and ${remaining} other pattern${remaining !== 1 ? "s" : ""}`));
1560
+ }
1561
+ lines.push("");
1562
+ }
1563
+ return lines.join("\n");
1564
+ }
1244
1565
 
1245
1566
  // src/init.ts
1246
- var import_promises3 = __toESM(require("fs/promises"));
1247
- var import_path6 = __toESM(require("path"));
1567
+ var import_promises4 = __toESM(require("fs/promises"));
1568
+ var import_path7 = __toESM(require("path"));
1248
1569
  var import_child_process3 = require("child_process");
1249
1570
  var import_tooling5 = require("@codacy/tooling");
1250
1571
 
1251
- // src/codacy-yaml.ts
1572
+ // src/discover.ts
1252
1573
  var import_promises2 = __toESM(require("fs/promises"));
1253
1574
  var import_path5 = __toESM(require("path"));
1575
+
1576
+ // src/discover-catalog.ts
1577
+ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
1578
+ // --- JavaScript / TypeScript — Frameworks ---
1579
+ ["react", { displayName: "React", kind: "framework" }],
1580
+ ["react-dom", { displayName: "React DOM", kind: "framework" }],
1581
+ ["react-native", { displayName: "React Native", kind: "framework" }],
1582
+ ["vue", { displayName: "Vue.js", kind: "framework" }],
1583
+ ["nuxt", { displayName: "Nuxt", kind: "framework" }],
1584
+ ["@angular/core", { displayName: "Angular", kind: "framework" }],
1585
+ ["svelte", { displayName: "Svelte", kind: "framework" }],
1586
+ ["@sveltejs/kit", { displayName: "SvelteKit", kind: "framework" }],
1587
+ ["next", { displayName: "Next.js", kind: "framework" }],
1588
+ ["ember-source", { displayName: "Ember.js", kind: "framework" }],
1589
+ ["lit", { displayName: "Lit", kind: "framework" }],
1590
+ ["solid-js", { displayName: "Solid", kind: "framework" }],
1591
+ ["astro", { displayName: "Astro", kind: "framework" }],
1592
+ ["@builder.io/qwik", { displayName: "Qwik", kind: "framework" }],
1593
+ ["@builder.io/qwik-city", { displayName: "Qwik", kind: "framework" }],
1594
+ ["@remix-run/react", { displayName: "Remix", kind: "framework" }],
1595
+ ["gatsby", { displayName: "Gatsby", kind: "framework" }],
1596
+ ["preact", { displayName: "Preact", kind: "framework" }],
1597
+ ["express", { displayName: "Express", kind: "framework" }],
1598
+ ["@nestjs/core", { displayName: "NestJS", kind: "framework" }],
1599
+ ["fastify", { displayName: "Fastify", kind: "framework" }],
1600
+ ["koa", { displayName: "Koa", kind: "framework" }],
1601
+ ["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
1602
+ ["hono", { displayName: "Hono", kind: "framework" }],
1603
+ ["electron", { displayName: "Electron", kind: "framework" }],
1604
+ // --- JavaScript / TypeScript — Libraries ---
1605
+ ["lodash", { displayName: "Lodash", kind: "library" }],
1606
+ ["underscore", { displayName: "Underscore.js", kind: "library" }],
1607
+ ["ramda", { displayName: "Ramda", kind: "library" }],
1608
+ ["jest", { displayName: "Jest", kind: "library" }],
1609
+ ["vitest", { displayName: "Vitest", kind: "library" }],
1610
+ ["@playwright/test", { displayName: "Playwright", kind: "library" }],
1611
+ ["playwright", { displayName: "Playwright", kind: "library" }],
1612
+ ["cypress", { displayName: "Cypress", kind: "library" }],
1613
+ ["mocha", { displayName: "Mocha", kind: "library" }],
1614
+ ["ava", { displayName: "AVA", kind: "library" }],
1615
+ ["jasmine", { displayName: "Jasmine", kind: "library" }],
1616
+ ["jasmine-core", { displayName: "Jasmine", kind: "library" }],
1617
+ ["@testing-library/react", { displayName: "Testing Library", kind: "library" }],
1618
+ ["@testing-library/vue", { displayName: "Testing Library", kind: "library" }],
1619
+ ["@testing-library/dom", { displayName: "Testing Library", kind: "library" }],
1620
+ ["tailwindcss", { displayName: "Tailwind CSS", kind: "library" }],
1621
+ ["@storybook/react", { displayName: "Storybook", kind: "library" }],
1622
+ ["@storybook/vue3", { displayName: "Storybook", kind: "library" }],
1623
+ ["@storybook/svelte", { displayName: "Storybook", kind: "library" }],
1624
+ ["redux", { displayName: "Redux", kind: "library" }],
1625
+ ["@reduxjs/toolkit", { displayName: "Redux Toolkit", kind: "library" }],
1626
+ ["mobx", { displayName: "MobX", kind: "library" }],
1627
+ ["zustand", { displayName: "Zustand", kind: "library" }],
1628
+ ["axios", { displayName: "Axios", kind: "library" }],
1629
+ ["date-fns", { displayName: "date-fns", kind: "library" }],
1630
+ ["dayjs", { displayName: "Day.js", kind: "library" }],
1631
+ ["moment", { displayName: "Moment.js", kind: "library" }],
1632
+ ["rxjs", { displayName: "RxJS", kind: "library" }],
1633
+ ["zod", { displayName: "Zod", kind: "library" }],
1634
+ ["joi", { displayName: "Joi", kind: "library" }],
1635
+ ["yup", { displayName: "Yup", kind: "library" }],
1636
+ ["prisma", { displayName: "Prisma", kind: "library" }],
1637
+ ["@prisma/client", { displayName: "Prisma", kind: "library" }],
1638
+ ["typeorm", { displayName: "TypeORM", kind: "library" }],
1639
+ ["sequelize", { displayName: "Sequelize", kind: "library" }],
1640
+ ["drizzle-orm", { displayName: "Drizzle ORM", kind: "library" }],
1641
+ ["mongoose", { displayName: "Mongoose", kind: "library" }],
1642
+ ["graphql", { displayName: "GraphQL", kind: "library" }],
1643
+ ["@apollo/client", { displayName: "Apollo Client", kind: "library" }],
1644
+ ["socket.io", { displayName: "Socket.IO", kind: "library" }],
1645
+ ["three", { displayName: "Three.js", kind: "library" }],
1646
+ ["d3", { displayName: "D3.js", kind: "library" }],
1647
+ ["chart.js", { displayName: "Chart.js", kind: "library" }],
1648
+ ["sass", { displayName: "Sass", kind: "library" }],
1649
+ ["node-sass", { displayName: "Sass", kind: "library" }],
1650
+ ["less", { displayName: "Less", kind: "library" }],
1651
+ ["styled-components", { displayName: "styled-components", kind: "library" }],
1652
+ ["@emotion/react", { displayName: "Emotion", kind: "library" }],
1653
+ ["pino", { displayName: "Pino", kind: "library" }],
1654
+ ["winston", { displayName: "Winston", kind: "library" }],
1655
+ ["i18next", { displayName: "i18next", kind: "library" }],
1656
+ ["react-intl", { displayName: "react-intl", kind: "library" }],
1657
+ ["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
1658
+ ["next-intl", { displayName: "next-intl", kind: "library" }],
1659
+ // --- JavaScript / TypeScript — Tools ---
1660
+ ["typescript", { displayName: "TypeScript", kind: "tool" }],
1661
+ ["eslint", { displayName: "ESLint", kind: "tool" }],
1662
+ ["prettier", { displayName: "Prettier", kind: "tool" }],
1663
+ ["webpack", { displayName: "Webpack", kind: "tool" }],
1664
+ ["vite", { displayName: "Vite", kind: "tool" }],
1665
+ ["esbuild", { displayName: "esbuild", kind: "tool" }],
1666
+ ["rollup", { displayName: "Rollup", kind: "tool" }],
1667
+ ["turbo", { displayName: "Turborepo", kind: "tool" }],
1668
+ ["tsup", { displayName: "tsup", kind: "tool" }],
1669
+ ["@biomejs/biome", { displayName: "Biome", kind: "tool" }],
1670
+ ["stylelint", { displayName: "Stylelint", kind: "tool" }],
1671
+ // --- JavaScript / TypeScript — Platforms ---
1672
+ ["@lwc/engine-dom", { displayName: "Lightning Web Components", kind: "platform" }],
1673
+ ["@salesforce/core", { displayName: "Salesforce", kind: "platform" }],
1674
+ ["@shopify/polaris", { displayName: "Shopify Polaris", kind: "platform" }],
1675
+ // --- Python — Frameworks ---
1676
+ ["django", { displayName: "Django", kind: "framework" }],
1677
+ ["flask", { displayName: "Flask", kind: "framework" }],
1678
+ ["fastapi", { displayName: "FastAPI", kind: "framework" }],
1679
+ ["tornado", { displayName: "Tornado", kind: "framework" }],
1680
+ ["pyramid", { displayName: "Pyramid", kind: "framework" }],
1681
+ ["starlette", { displayName: "Starlette", kind: "framework" }],
1682
+ ["sanic", { displayName: "Sanic", kind: "framework" }],
1683
+ ["bottle", { displayName: "Bottle", kind: "framework" }],
1684
+ ["streamlit", { displayName: "Streamlit", kind: "framework" }],
1685
+ ["gradio", { displayName: "Gradio", kind: "framework" }],
1686
+ ["apache-airflow", { displayName: "Airflow", kind: "framework" }],
1687
+ // --- Python — Libraries ---
1688
+ ["flask-babel", { displayName: "Flask-Babel", kind: "library" }],
1689
+ ["pytest", { displayName: "pytest", kind: "library" }],
1690
+ ["numpy", { displayName: "NumPy", kind: "library" }],
1691
+ ["pandas", { displayName: "pandas", kind: "library" }],
1692
+ ["requests", { displayName: "Requests", kind: "library" }],
1693
+ ["sqlalchemy", { displayName: "SQLAlchemy", kind: "library" }],
1694
+ ["celery", { displayName: "Celery", kind: "library" }],
1695
+ ["pydantic", { displayName: "Pydantic", kind: "library" }],
1696
+ ["boto3", { displayName: "Boto3 (AWS SDK)", kind: "library" }],
1697
+ ["scipy", { displayName: "SciPy", kind: "library" }],
1698
+ ["scikit-learn", { displayName: "scikit-learn", kind: "library" }],
1699
+ ["tensorflow", { displayName: "TensorFlow", kind: "library" }],
1700
+ ["torch", { displayName: "PyTorch", kind: "library" }],
1701
+ ["httpx", { displayName: "HTTPX", kind: "library" }],
1702
+ ["aiohttp", { displayName: "aiohttp", kind: "library" }],
1703
+ ["pillow", { displayName: "Pillow", kind: "library" }],
1704
+ ["beautifulsoup4", { displayName: "Beautiful Soup", kind: "library" }],
1705
+ ["scrapy", { displayName: "Scrapy", kind: "library" }],
1706
+ ["alembic", { displayName: "Alembic", kind: "library" }],
1707
+ ["marshmallow", { displayName: "Marshmallow", kind: "library" }],
1708
+ ["click", { displayName: "Click", kind: "library" }],
1709
+ ["typer", { displayName: "Typer", kind: "library" }],
1710
+ ["rich", { displayName: "Rich", kind: "library" }],
1711
+ ["polars", { displayName: "Polars", kind: "library" }],
1712
+ ["dask", { displayName: "Dask", kind: "library" }],
1713
+ ["matplotlib", { displayName: "Matplotlib", kind: "library" }],
1714
+ ["seaborn", { displayName: "Seaborn", kind: "library" }],
1715
+ ["plotly", { displayName: "Plotly", kind: "library" }],
1716
+ // --- Python — Tools ---
1717
+ ["black", { displayName: "Black", kind: "tool" }],
1718
+ ["ruff", { displayName: "Ruff", kind: "tool" }],
1719
+ ["mypy", { displayName: "mypy", kind: "tool" }],
1720
+ ["pylint", { displayName: "Pylint", kind: "tool" }],
1721
+ ["flake8", { displayName: "Flake8", kind: "tool" }],
1722
+ ["bandit", { displayName: "Bandit", kind: "tool" }],
1723
+ ["isort", { displayName: "isort", kind: "tool" }],
1724
+ // --- Ruby — Frameworks ---
1725
+ ["rails", { displayName: "Ruby on Rails", kind: "framework" }],
1726
+ ["sinatra", { displayName: "Sinatra", kind: "framework" }],
1727
+ ["hanami", { displayName: "Hanami", kind: "framework" }],
1728
+ ["grape", { displayName: "Grape", kind: "framework" }],
1729
+ ["padrino", { displayName: "Padrino", kind: "framework" }],
1730
+ // --- Ruby — Libraries ---
1731
+ ["rspec", { displayName: "RSpec", kind: "library" }],
1732
+ ["rspec-core", { displayName: "RSpec", kind: "library" }],
1733
+ ["rspec-rails", { displayName: "RSpec Rails", kind: "library" }],
1734
+ ["capybara", { displayName: "Capybara", kind: "library" }],
1735
+ ["sidekiq", { displayName: "Sidekiq", kind: "library" }],
1736
+ ["devise", { displayName: "Devise", kind: "library" }],
1737
+ ["pundit", { displayName: "Pundit", kind: "library" }],
1738
+ ["activeadmin", { displayName: "ActiveAdmin", kind: "library" }],
1739
+ ["puma", { displayName: "Puma", kind: "library" }],
1740
+ ["nokogiri", { displayName: "Nokogiri", kind: "library" }],
1741
+ ["faraday", { displayName: "Faraday", kind: "library" }],
1742
+ ["httparty", { displayName: "HTTParty", kind: "library" }],
1743
+ ["minitest", { displayName: "Minitest", kind: "library" }],
1744
+ ["factory_bot", { displayName: "FactoryBot", kind: "library" }],
1745
+ ["dry-validation", { displayName: "dry-validation", kind: "library" }],
1746
+ // --- Ruby — Tools ---
1747
+ ["rubocop", { displayName: "RuboCop", kind: "tool" }],
1748
+ ["bundler", { displayName: "Bundler", kind: "tool" }],
1749
+ ["solargraph", { displayName: "Solargraph", kind: "tool" }],
1750
+ // --- Java / Kotlin — Frameworks ---
1751
+ ["spring-boot-starter", { displayName: "Spring Boot", kind: "framework" }],
1752
+ ["spring-boot-starter-web", { displayName: "Spring Boot", kind: "framework" }],
1753
+ ["spring-core", { displayName: "Spring Framework", kind: "framework" }],
1754
+ ["quarkus-core", { displayName: "Quarkus", kind: "framework" }],
1755
+ ["micronaut-core", { displayName: "Micronaut", kind: "framework" }],
1756
+ ["ktor-server-core", { displayName: "Ktor", kind: "framework" }],
1757
+ ["android", { displayName: "Android SDK", kind: "framework" }],
1758
+ // --- Java / Kotlin — Libraries ---
1759
+ ["junit-jupiter", { displayName: "JUnit 5", kind: "library" }],
1760
+ ["junit", { displayName: "JUnit", kind: "library" }],
1761
+ ["junit-jupiter-api", { displayName: "JUnit 5", kind: "library" }],
1762
+ ["mockito-core", { displayName: "Mockito", kind: "library" }],
1763
+ ["lombok", { displayName: "Lombok", kind: "library" }],
1764
+ ["jackson-databind", { displayName: "Jackson", kind: "library" }],
1765
+ ["jackson-core", { displayName: "Jackson", kind: "library" }],
1766
+ ["hibernate-core", { displayName: "Hibernate", kind: "library" }],
1767
+ ["guava", { displayName: "Guava", kind: "library" }],
1768
+ ["commons-lang3", { displayName: "Apache Commons Lang", kind: "library" }],
1769
+ ["commons-io", { displayName: "Apache Commons IO", kind: "library" }],
1770
+ ["slf4j-api", { displayName: "SLF4J", kind: "library" }],
1771
+ ["log4j-core", { displayName: "Log4j", kind: "library" }],
1772
+ ["logback-classic", { displayName: "Logback", kind: "library" }],
1773
+ ["testng", { displayName: "TestNG", kind: "library" }],
1774
+ ["assertj-core", { displayName: "AssertJ", kind: "library" }],
1775
+ ["gson", { displayName: "Gson", kind: "library" }],
1776
+ ["okhttp", { displayName: "OkHttp", kind: "library" }],
1777
+ ["retrofit", { displayName: "Retrofit", kind: "library" }],
1778
+ // --- Go — Frameworks ---
1779
+ ["github.com/gin-gonic/gin", { displayName: "Gin", kind: "framework" }],
1780
+ ["github.com/labstack/echo", { displayName: "Echo", kind: "framework" }],
1781
+ ["github.com/gofiber/fiber", { displayName: "Fiber", kind: "framework" }],
1782
+ ["github.com/go-chi/chi", { displayName: "Chi", kind: "framework" }],
1783
+ ["github.com/gorilla/mux", { displayName: "Gorilla Mux", kind: "framework" }],
1784
+ ["github.com/beego/beego", { displayName: "Beego", kind: "framework" }],
1785
+ // --- Go — Libraries ---
1786
+ ["github.com/stretchr/testify", { displayName: "Testify", kind: "library" }],
1787
+ ["github.com/spf13/cobra", { displayName: "Cobra", kind: "library" }],
1788
+ ["github.com/spf13/viper", { displayName: "Viper", kind: "library" }],
1789
+ ["go.uber.org/zap", { displayName: "Zap", kind: "library" }],
1790
+ ["github.com/sirupsen/logrus", { displayName: "Logrus", kind: "library" }],
1791
+ ["gorm.io/gorm", { displayName: "GORM", kind: "library" }],
1792
+ ["github.com/go-kit/kit", { displayName: "Go Kit", kind: "library" }],
1793
+ ["google.golang.org/protobuf", { displayName: "Protocol Buffers", kind: "library" }],
1794
+ ["google.golang.org/grpc", { displayName: "gRPC", kind: "library" }],
1795
+ ["github.com/jackc/pgx", { displayName: "pgx", kind: "library" }],
1796
+ ["github.com/redis/go-redis", { displayName: "go-redis", kind: "library" }],
1797
+ ["github.com/aws/aws-sdk-go-v2", { displayName: "AWS SDK for Go", kind: "library" }],
1798
+ ["github.com/prometheus/client_golang", { displayName: "Prometheus Client", kind: "library" }],
1799
+ // --- Rust — Frameworks ---
1800
+ ["actix-web", { displayName: "Actix Web", kind: "framework" }],
1801
+ ["axum", { displayName: "Axum", kind: "framework" }],
1802
+ ["rocket", { displayName: "Rocket", kind: "framework" }],
1803
+ ["warp", { displayName: "Warp", kind: "framework" }],
1804
+ ["tauri", { displayName: "Tauri", kind: "framework" }],
1805
+ // --- Rust — Libraries ---
1806
+ ["tokio", { displayName: "Tokio", kind: "library" }],
1807
+ ["serde", { displayName: "Serde", kind: "library" }],
1808
+ ["clap", { displayName: "clap", kind: "library" }],
1809
+ ["reqwest", { displayName: "reqwest", kind: "library" }],
1810
+ ["diesel", { displayName: "Diesel", kind: "library" }],
1811
+ ["sqlx", { displayName: "SQLx", kind: "library" }],
1812
+ ["tracing", { displayName: "Tracing", kind: "library" }],
1813
+ ["anyhow", { displayName: "anyhow", kind: "library" }],
1814
+ ["thiserror", { displayName: "thiserror", kind: "library" }],
1815
+ ["rayon", { displayName: "Rayon", kind: "library" }],
1816
+ // --- PHP — Frameworks ---
1817
+ ["laravel/framework", { displayName: "Laravel", kind: "framework" }],
1818
+ ["symfony/framework-bundle", { displayName: "Symfony", kind: "framework" }],
1819
+ ["codeigniter4/framework", { displayName: "CodeIgniter", kind: "framework" }],
1820
+ ["slim/slim", { displayName: "Slim", kind: "framework" }],
1821
+ ["cakephp/cakephp", { displayName: "CakePHP", kind: "framework" }],
1822
+ // --- PHP — Libraries ---
1823
+ ["phpunit/phpunit", { displayName: "PHPUnit", kind: "library" }],
1824
+ ["guzzlehttp/guzzle", { displayName: "Guzzle", kind: "library" }],
1825
+ ["monolog/monolog", { displayName: "Monolog", kind: "library" }],
1826
+ ["nesbot/carbon", { displayName: "Carbon", kind: "library" }],
1827
+ ["doctrine/orm", { displayName: "Doctrine ORM", kind: "library" }],
1828
+ ["doctrine/dbal", { displayName: "Doctrine DBAL", kind: "library" }],
1829
+ ["league/flysystem", { displayName: "Flysystem", kind: "library" }],
1830
+ ["php-amqplib/php-amqplib", { displayName: "php-amqplib", kind: "library" }],
1831
+ ["predis/predis", { displayName: "Predis", kind: "library" }],
1832
+ // --- PHP — Tools ---
1833
+ ["phpstan/phpstan", { displayName: "PHPStan", kind: "tool" }],
1834
+ ["squizlabs/php_codesniffer", { displayName: "PHP_CodeSniffer", kind: "tool" }],
1835
+ // --- Swift — Frameworks ---
1836
+ ["vapor", { displayName: "Vapor", kind: "framework" }],
1837
+ // --- Swift — Libraries ---
1838
+ ["Alamofire", { displayName: "Alamofire", kind: "library" }],
1839
+ ["SnapKit", { displayName: "SnapKit", kind: "library" }],
1840
+ ["Kingfisher", { displayName: "Kingfisher", kind: "library" }],
1841
+ ["Moya", { displayName: "Moya", kind: "library" }],
1842
+ ["RxSwift", { displayName: "RxSwift", kind: "library" }],
1843
+ ["RealmSwift", { displayName: "Realm", kind: "library" }],
1844
+ // --- Scala — Frameworks ---
1845
+ ["play-server", { displayName: "Play Framework", kind: "framework" }],
1846
+ ["akka-actor", { displayName: "Akka", kind: "framework" }],
1847
+ // --- Scala — Libraries ---
1848
+ ["scalatest", { displayName: "ScalaTest", kind: "library" }],
1849
+ ["specs2-core", { displayName: "Specs2", kind: "library" }],
1850
+ ["cats-core", { displayName: "Cats", kind: "library" }],
1851
+ ["circe-core", { displayName: "Circe", kind: "library" }]
1852
+ ]);
1853
+ var DEPENDENCY_PREFIX_RULES = [
1854
+ { prefix: "@angular/", classification: { displayName: "Angular", kind: "framework" } },
1855
+ { prefix: "@nestjs/", classification: { displayName: "NestJS", kind: "framework" } },
1856
+ { prefix: "@remix-run/", classification: { displayName: "Remix", kind: "framework" } },
1857
+ { prefix: "@sveltejs/", classification: { displayName: "SvelteKit", kind: "framework" } },
1858
+ { prefix: "@nuxt/", classification: { displayName: "Nuxt", kind: "framework" } },
1859
+ { prefix: "@storybook/", classification: { displayName: "Storybook", kind: "library" } },
1860
+ {
1861
+ prefix: "@testing-library/",
1862
+ classification: { displayName: "Testing Library", kind: "library" }
1863
+ },
1864
+ { prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
1865
+ { prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
1866
+ { prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
1867
+ { prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
1868
+ { prefix: "@formatjs/", classification: { displayName: "react-intl", kind: "library" } },
1869
+ { prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
1870
+ { prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
1871
+ { prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
1872
+ { prefix: "@chakra-ui/", classification: { displayName: "Chakra UI", kind: "library" } },
1873
+ {
1874
+ prefix: "@lwc/",
1875
+ classification: { displayName: "Lightning Web Components", kind: "platform" }
1876
+ },
1877
+ { prefix: "@salesforce/", classification: { displayName: "Salesforce", kind: "platform" } },
1878
+ { prefix: "@shopify/", classification: { displayName: "Shopify", kind: "platform" } },
1879
+ { prefix: "@aws-sdk/", classification: { displayName: "AWS SDK", kind: "library" } },
1880
+ { prefix: "@google-cloud/", classification: { displayName: "Google Cloud", kind: "library" } },
1881
+ { prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } }
1882
+ ];
1883
+ function classifyDependency(name) {
1884
+ const exact = DEPENDENCY_CATALOG.get(name);
1885
+ if (exact) return exact;
1886
+ for (const rule of DEPENDENCY_PREFIX_RULES) {
1887
+ if (name.startsWith(rule.prefix)) return rule.classification;
1888
+ }
1889
+ return void 0;
1890
+ }
1891
+ var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
1892
+ "Dockerfile",
1893
+ "docker-compose.yml",
1894
+ "docker-compose.yaml",
1895
+ "compose.yml",
1896
+ "compose.yaml",
1897
+ "Makefile",
1898
+ "Justfile",
1899
+ "pom.xml",
1900
+ "build.gradle",
1901
+ "build.gradle.kts",
1902
+ "build.sbt",
1903
+ "go.mod",
1904
+ "Cargo.toml",
1905
+ "Gemfile",
1906
+ "Podfile",
1907
+ "Package.swift",
1908
+ "Jenkinsfile",
1909
+ "Vagrantfile",
1910
+ "Procfile"
1911
+ ]);
1912
+ var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
1913
+ ".dockerignore",
1914
+ "Taskfile.yml",
1915
+ "Rakefile",
1916
+ "Gruntfile.js",
1917
+ "Gulpfile.js",
1918
+ "settings.gradle",
1919
+ "settings.gradle.kts",
1920
+ "go.sum",
1921
+ "Cargo.lock",
1922
+ "CLAUDE.md",
1923
+ "AGENTS.md",
1924
+ "agents.md",
1925
+ ".cursorrules",
1926
+ "copilot-instructions.md",
1927
+ ".travis.yml",
1928
+ ".gitlab-ci.yml",
1929
+ "azure-pipelines.yml",
1930
+ "bitbucket-pipelines.yml",
1931
+ "appveyor.yml",
1932
+ "package.json",
1933
+ "requirements.txt",
1934
+ "pyproject.toml",
1935
+ "Pipfile",
1936
+ "composer.json",
1937
+ "tsconfig.json",
1938
+ "jsconfig.json",
1939
+ "serverless.yml",
1940
+ "serverless.yaml",
1941
+ "cdk.json",
1942
+ "pulumi.yaml",
1943
+ "README.md",
1944
+ "CHANGELOG.md",
1945
+ "CONTRIBUTING.md",
1946
+ "LICENSE",
1947
+ "renovate.json",
1948
+ ".nvmrc",
1949
+ ".node-version",
1950
+ ".python-version",
1951
+ ".ruby-version",
1952
+ ".tool-versions"
1953
+ ]);
1954
+ var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
1955
+ var CONFIG_HEURISTIC_PATTERNS = [
1956
+ /^\..*rc$/,
1957
+ // .eslintrc, .prettierrc, etc.
1958
+ /^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
1959
+ // .eslintrc.json, etc.
1960
+ /^.*\.config\.(js|ts|mjs|cjs)$/,
1961
+ // vite.config.ts, jest.config.js, etc.
1962
+ /^\.env(\..+)?$/
1963
+ // .env, .env.local, .env.production
1964
+ ];
1965
+ function isNotableFile(filePath) {
1966
+ const basename = filePath.split("/").pop() ?? filePath;
1967
+ const isRoot = !filePath.includes("/");
1968
+ if (NOTABLE_ANY_DEPTH.has(basename)) return true;
1969
+ if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
1970
+ for (const prefix of NOTABLE_PATH_PREFIXES) {
1971
+ if (filePath.startsWith(prefix)) return true;
1972
+ }
1973
+ if (!filePath.includes("/")) {
1974
+ for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
1975
+ if (pattern.test(basename)) return true;
1976
+ }
1977
+ }
1978
+ return false;
1979
+ }
1980
+ function parsePackageJson(content) {
1981
+ try {
1982
+ const pkg = JSON.parse(content);
1983
+ const deps = [];
1984
+ for (const section of ["dependencies", "devDependencies", "peerDependencies"]) {
1985
+ const entries2 = pkg[section];
1986
+ if (entries2 && typeof entries2 === "object") {
1987
+ for (const [name, version] of Object.entries(entries2)) {
1988
+ deps.push({ name, version: typeof version === "string" ? version : void 0 });
1989
+ }
1990
+ }
1991
+ }
1992
+ return deps;
1993
+ } catch {
1994
+ return [];
1995
+ }
1996
+ }
1997
+ function parseRequirementsTxt(content) {
1998
+ const deps = [];
1999
+ for (const rawLine of content.split("\n")) {
2000
+ const line = rawLine.trim();
2001
+ if (!line || line.startsWith("#") || line.startsWith("-")) continue;
2002
+ const noComment = line.split("#")[0].trim();
2003
+ if (!noComment) continue;
2004
+ const match = noComment.match(/^([a-zA-Z0-9_.-]+(?:\[[^\]]*\])?)\s*([<>=!~@;].*)?$/);
2005
+ if (match) {
2006
+ const rawName = match[1].replace(/\[.*\]$/, "");
2007
+ const normalized = rawName.toLowerCase().replace(/_/g, "-");
2008
+ deps.push({ name: normalized, version: match[2]?.trim() });
2009
+ }
2010
+ }
2011
+ return deps;
2012
+ }
2013
+ function parsePyprojectToml(content) {
2014
+ const deps = [];
2015
+ const projectDepsMatch = content.match(/\[project\][\s\S]*?dependencies\s*=\s*\[([\s\S]*?)\]/);
2016
+ if (projectDepsMatch) {
2017
+ const items = projectDepsMatch[1].match(/"([^"]+)"|'([^']+)'/g) ?? [];
2018
+ for (const item of items) {
2019
+ const raw = item.replace(/["']/g, "");
2020
+ const name = raw.split(/[<>=!~;\s]/)[0].replace(/\[.*\]$/, "");
2021
+ if (name) deps.push({ name: name.toLowerCase().replace(/_/g, "-") });
2022
+ }
2023
+ }
2024
+ const poetryMatch = content.match(/\[tool\.poetry\.dependencies\]([\s\S]*?)(?:\n\[|\n$)/);
2025
+ if (poetryMatch) {
2026
+ const lines = poetryMatch[1].split("\n");
2027
+ for (const line of lines) {
2028
+ const m = line.match(/^([a-zA-Z0-9_.-]+)\s*=/);
2029
+ if (m && m[1] !== "python") {
2030
+ deps.push({ name: m[1].toLowerCase().replace(/_/g, "-") });
2031
+ }
2032
+ }
2033
+ }
2034
+ const poetryGroupRegex = /\[tool\.poetry\.group\.\w+\.dependencies\]([\s\S]*?)(?:\n\[|\n$)/g;
2035
+ let groupMatch;
2036
+ while ((groupMatch = poetryGroupRegex.exec(content)) !== null) {
2037
+ const lines = groupMatch[1].split("\n");
2038
+ for (const line of lines) {
2039
+ const m = line.match(/^([a-zA-Z0-9_.-]+)\s*=/);
2040
+ if (m) {
2041
+ deps.push({ name: m[1].toLowerCase().replace(/_/g, "-") });
2042
+ }
2043
+ }
2044
+ }
2045
+ return deps;
2046
+ }
2047
+ function parsePipfile(content) {
2048
+ const deps = [];
2049
+ let inDepsSection = false;
2050
+ for (const rawLine of content.split("\n")) {
2051
+ const line = rawLine.trim();
2052
+ if (/^\[(packages|dev-packages)\]/.test(line)) {
2053
+ inDepsSection = true;
2054
+ continue;
2055
+ }
2056
+ if (line.startsWith("[")) {
2057
+ inDepsSection = false;
2058
+ continue;
2059
+ }
2060
+ if (inDepsSection) {
2061
+ const m = line.match(/^([a-zA-Z0-9_.-]+)\s*=/);
2062
+ if (m) {
2063
+ deps.push({ name: m[1].toLowerCase().replace(/_/g, "-") });
2064
+ }
2065
+ }
2066
+ }
2067
+ return deps;
2068
+ }
2069
+ function parseGemfile(content) {
2070
+ const deps = [];
2071
+ const gemRegex = /^\s*gem\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
2072
+ let match;
2073
+ while ((match = gemRegex.exec(content)) !== null) {
2074
+ deps.push({ name: match[1], version: match[2] });
2075
+ }
2076
+ return deps;
2077
+ }
2078
+ function parseGoMod(content) {
2079
+ const deps = [];
2080
+ const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
2081
+ let match;
2082
+ while ((match = singleRegex.exec(content)) !== null) {
2083
+ if (!match[1].startsWith("(")) {
2084
+ deps.push({ name: match[1], version: match[2] });
2085
+ }
2086
+ }
2087
+ const blockRegex = /require\s*\(([\s\S]*?)\)/g;
2088
+ while ((match = blockRegex.exec(content)) !== null) {
2089
+ const lines = match[1].split("\n");
2090
+ for (const line of lines) {
2091
+ const m = line.trim().match(/^(\S+)\s+(\S+)/);
2092
+ if (m && !m[1].startsWith("//")) {
2093
+ deps.push({ name: m[1], version: m[2] });
2094
+ }
2095
+ }
2096
+ }
2097
+ return deps;
2098
+ }
2099
+ function parsePomXml(content) {
2100
+ const deps = [];
2101
+ const depRegex = /<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g;
2102
+ let match;
2103
+ while ((match = depRegex.exec(content)) !== null) {
2104
+ deps.push({ name: match[1].trim() });
2105
+ }
2106
+ return deps;
2107
+ }
2108
+ function parseBuildGradle(content) {
2109
+ const deps = [];
2110
+ const depRegex = /(?:implementation|testImplementation|api|compileOnly|runtimeOnly|kapt|annotationProcessor)\s*(?:\(\s*)?["']([^"':]+):([^"':]+)(?::([^"']+))?["']/g;
2111
+ let match;
2112
+ while ((match = depRegex.exec(content)) !== null) {
2113
+ deps.push({ name: match[2].trim(), version: match[3]?.trim() });
2114
+ }
2115
+ return deps;
2116
+ }
2117
+ function parseCargoToml(content) {
2118
+ const deps = [];
2119
+ let inDepsSection = false;
2120
+ for (const rawLine of content.split("\n")) {
2121
+ const line = rawLine.trim();
2122
+ if (/^\[((?:dev-|build-)?dependencies)\]/.test(line)) {
2123
+ inDepsSection = true;
2124
+ continue;
2125
+ }
2126
+ if (line.startsWith("[") && !line.includes("dependencies.")) {
2127
+ inDepsSection = false;
2128
+ continue;
2129
+ }
2130
+ if (inDepsSection) {
2131
+ const m = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
2132
+ if (m) {
2133
+ const versionMatch = line.match(/["']([^"']+)["']/);
2134
+ deps.push({ name: m[1], version: versionMatch?.[1] });
2135
+ }
2136
+ }
2137
+ }
2138
+ return deps;
2139
+ }
2140
+ function parseComposerJson(content) {
2141
+ try {
2142
+ const pkg = JSON.parse(content);
2143
+ const deps = [];
2144
+ for (const section of ["require", "require-dev"]) {
2145
+ const entries2 = pkg[section];
2146
+ if (entries2 && typeof entries2 === "object") {
2147
+ for (const [name, version] of Object.entries(entries2)) {
2148
+ if (name === "php" || name.startsWith("ext-")) continue;
2149
+ deps.push({ name, version: typeof version === "string" ? version : void 0 });
2150
+ }
2151
+ }
2152
+ }
2153
+ return deps;
2154
+ } catch {
2155
+ return [];
2156
+ }
2157
+ }
2158
+ function parsePodfile(content) {
2159
+ const deps = [];
2160
+ const podRegex = /^\s*pod\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
2161
+ let match;
2162
+ while ((match = podRegex.exec(content)) !== null) {
2163
+ deps.push({ name: match[1], version: match[2] });
2164
+ }
2165
+ return deps;
2166
+ }
2167
+ function parsePackageSwift(content) {
2168
+ const deps = [];
2169
+ const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
2170
+ let match;
2171
+ while ((match = pkgRegex.exec(content)) !== null) {
2172
+ deps.push({ name: match[1] });
2173
+ }
2174
+ return deps;
2175
+ }
2176
+ var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
2177
+ ["package.json", parsePackageJson],
2178
+ ["requirements.txt", parseRequirementsTxt],
2179
+ ["pyproject.toml", parsePyprojectToml],
2180
+ ["Pipfile", parsePipfile],
2181
+ ["Gemfile", parseGemfile],
2182
+ ["go.mod", parseGoMod],
2183
+ ["pom.xml", parsePomXml],
2184
+ ["build.gradle", parseBuildGradle],
2185
+ ["build.gradle.kts", parseBuildGradle],
2186
+ ["Cargo.toml", parseCargoToml],
2187
+ ["composer.json", parseComposerJson],
2188
+ ["Podfile", parsePodfile],
2189
+ ["Package.swift", parsePackageSwift]
2190
+ ]);
2191
+
2192
+ // src/discover.ts
2193
+ async function discover(repositoryRoot, options) {
2194
+ const files = listRepoFiles(repositoryRoot);
2195
+ const filePatterns = extractFilePatterns(files);
2196
+ const notableFiles = findNotableFiles(files);
2197
+ const languages = await detectLanguages(repositoryRoot, files);
2198
+ let frameworks = [];
2199
+ let libraries = [];
2200
+ if (!options?.skipDependencies) {
2201
+ const allDeps = await extractDependencies(repositoryRoot, files);
2202
+ frameworks = deduplicateDeps(allDeps.filter((d) => d.kind === "framework"));
2203
+ libraries = deduplicateDeps(
2204
+ allDeps.filter((d) => d.kind === "library" || d.kind === "platform" || d.kind === "tool")
2205
+ );
2206
+ }
2207
+ return {
2208
+ filePatterns,
2209
+ notableFiles,
2210
+ languages,
2211
+ frameworks,
2212
+ libraries,
2213
+ metadata: {
2214
+ repositoryRoot: import_path5.default.resolve(repositoryRoot),
2215
+ totalFiles: files.length,
2216
+ scannedAt: (/* @__PURE__ */ new Date()).toISOString()
2217
+ }
2218
+ };
2219
+ }
2220
+ function extractFilePatterns(files) {
2221
+ const extensions = /* @__PURE__ */ new Set();
2222
+ for (const file of files) {
2223
+ const ext = import_path5.default.extname(file).toLowerCase();
2224
+ if (ext) {
2225
+ extensions.add(`*${ext}`);
2226
+ }
2227
+ }
2228
+ return Array.from(extensions).sort();
2229
+ }
2230
+ function findNotableFiles(files) {
2231
+ const notable = /* @__PURE__ */ new Set();
2232
+ for (const file of files) {
2233
+ if (isNotableFile(file)) {
2234
+ notable.add(file);
2235
+ }
2236
+ }
2237
+ return Array.from(notable).sort();
2238
+ }
2239
+ async function extractDependencies(repositoryRoot, files) {
2240
+ const manifestFiles = /* @__PURE__ */ new Map();
2241
+ for (const file of files) {
2242
+ const basename = file.split("/").pop() ?? file;
2243
+ if (MANIFEST_PARSERS.has(basename)) {
2244
+ const existing = manifestFiles.get(basename) ?? [];
2245
+ existing.push(file);
2246
+ manifestFiles.set(basename, existing);
2247
+ }
2248
+ }
2249
+ const allDeps = [];
2250
+ for (const [manifestName, paths] of manifestFiles) {
2251
+ const parser = MANIFEST_PARSERS.get(manifestName);
2252
+ for (const filePath of paths) {
2253
+ let content;
2254
+ try {
2255
+ content = await import_promises2.default.readFile(import_path5.default.join(repositoryRoot, filePath), "utf-8");
2256
+ } catch {
2257
+ continue;
2258
+ }
2259
+ const rawDeps = parser(content);
2260
+ for (const raw of rawDeps) {
2261
+ const classification = classifyDependency(raw.name);
2262
+ if (classification) {
2263
+ allDeps.push({
2264
+ name: raw.name,
2265
+ displayName: classification.displayName,
2266
+ kind: classification.kind,
2267
+ source: "manifest",
2268
+ manifestFile: filePath,
2269
+ version: raw.version
2270
+ });
2271
+ }
2272
+ }
2273
+ }
2274
+ }
2275
+ return allDeps;
2276
+ }
2277
+ function deduplicateDeps(deps) {
2278
+ const seen = /* @__PURE__ */ new Set();
2279
+ const result = [];
2280
+ for (const dep of deps) {
2281
+ if (!seen.has(dep.displayName)) {
2282
+ seen.add(dep.displayName);
2283
+ result.push(dep);
2284
+ }
2285
+ }
2286
+ return result.sort((a, b) => a.displayName.localeCompare(b.displayName));
2287
+ }
2288
+
2289
+ // src/codacy-yaml.ts
2290
+ var import_promises3 = __toESM(require("fs/promises"));
2291
+ var import_path6 = __toESM(require("path"));
1254
2292
  var import_yaml = require("yaml");
1255
2293
  async function readCodacyYaml(repositoryRoot) {
1256
2294
  const candidates = [".codacy.yaml", ".codacy.yml"];
1257
2295
  let raw = null;
1258
2296
  for (const filename of candidates) {
1259
2297
  try {
1260
- raw = await import_promises2.default.readFile(import_path5.default.join(repositoryRoot, filename), "utf-8");
2298
+ raw = await import_promises3.default.readFile(import_path6.default.join(repositoryRoot, filename), "utf-8");
1261
2299
  break;
1262
2300
  } catch {
1263
2301
  }
@@ -1515,13 +2553,13 @@ var getQueryString = (params) => {
1515
2553
  };
1516
2554
  var getUrl = (config, options) => {
1517
2555
  const encoder = config.ENCODE_PATH || encodeURI;
1518
- const path8 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
2556
+ const path9 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
1519
2557
  if (options.path?.hasOwnProperty(group)) {
1520
2558
  return encoder(String(options.path[group]));
1521
2559
  }
1522
2560
  return substring;
1523
2561
  });
1524
- const url = `${config.BASE}${path8}`;
2562
+ const url = `${config.BASE}${path9}`;
1525
2563
  if (options.query) {
1526
2564
  return `${url}${getQueryString(options.query)}`;
1527
2565
  }
@@ -4865,22 +5903,26 @@ async function listIgnoredFiles(provider, org, repo) {
4865
5903
  }
4866
5904
 
4867
5905
  // src/init.ts
4868
- async function detectLanguages(repositoryRoot) {
4869
- let files = [];
4870
- try {
4871
- const output = (0, import_child_process3.execSync)("git ls-files", {
4872
- cwd: repositoryRoot,
4873
- encoding: "utf-8",
4874
- maxBuffer: 50 * 1024 * 1024
4875
- }).trim();
4876
- files = output ? output.split("\n") : [];
4877
- } catch {
4878
- return [];
5906
+ async function detectLanguages(repositoryRoot, precomputedFiles) {
5907
+ let files;
5908
+ if (precomputedFiles) {
5909
+ files = precomputedFiles;
5910
+ } else {
5911
+ try {
5912
+ const output = (0, import_child_process3.execSync)("git ls-files", {
5913
+ cwd: repositoryRoot,
5914
+ encoding: "utf-8",
5915
+ maxBuffer: 50 * 1024 * 1024
5916
+ }).trim();
5917
+ files = output ? output.split("\n") : [];
5918
+ } catch {
5919
+ return [];
5920
+ }
4879
5921
  }
4880
5922
  const detected = /* @__PURE__ */ new Set();
4881
5923
  for (const file of files) {
4882
- const basename = import_path6.default.basename(file);
4883
- const ext = import_path6.default.extname(file).toLowerCase();
5924
+ const basename = import_path7.default.basename(file);
5925
+ const ext = import_path7.default.extname(file).toLowerCase();
4884
5926
  for (const [lang, config] of Object.entries(import_tooling5.LANGUAGE_CONFIG)) {
4885
5927
  if (config.extensions.includes(ext)) {
4886
5928
  detected.add(lang);
@@ -4897,9 +5939,9 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
4897
5939
  adapters.map(async (adapter) => {
4898
5940
  const result = await adapter.checkLocalConfigurationFile({
4899
5941
  repositoryRoot,
4900
- generatedConfigDir: import_path6.default.join(repositoryRoot, ".codacy", "generated"),
4901
- globalDir: import_path6.default.join(process.env.HOME ?? "~", ".codacy"),
4902
- toolInstallDir: import_path6.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
5942
+ generatedConfigDir: import_path7.default.join(repositoryRoot, ".codacy", "generated"),
5943
+ globalDir: import_path7.default.join(process.env.HOME ?? "~", ".codacy"),
5944
+ toolInstallDir: import_path7.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
4903
5945
  targetFiles: [],
4904
5946
  platform: { os: process.platform, arch: process.arch },
4905
5947
  logger: silentLogger,
@@ -5096,10 +6138,10 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
5096
6138
  };
5097
6139
  }
5098
6140
  async function initContainerConfig(repositoryRoot, adapters) {
5099
- const codacyrcPath = import_path6.default.join(repositoryRoot, ".codacyrc");
6141
+ const codacyrcPath = import_path7.default.join(repositoryRoot, ".codacyrc");
5100
6142
  let raw;
5101
6143
  try {
5102
- raw = await import_promises3.default.readFile(codacyrcPath, "utf-8");
6144
+ raw = await import_promises4.default.readFile(codacyrcPath, "utf-8");
5103
6145
  } catch {
5104
6146
  throw new Error(`.codacyrc not found at ${codacyrcPath}`);
5105
6147
  }
@@ -5150,10 +6192,257 @@ async function initContainerConfig(repositoryRoot, adapters) {
5150
6192
  }
5151
6193
  return createCodacyConfig({ source: "container", tools, includeFiles });
5152
6194
  }
6195
+ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString) {
6196
+ const discoveryResult = await discover(repositoryRoot);
6197
+ onDiscoveryComplete?.(discoveryResult);
6198
+ const detectedKeys = /* @__PURE__ */ new Set();
6199
+ for (const lang of discoveryResult.languages) detectedKeys.add(lang);
6200
+ for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.displayName);
6201
+ for (const lib of discoveryResult.libraries) detectedKeys.add(lib.displayName);
6202
+ const repoLanguages = new Set(discoveryResult.languages);
6203
+ const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
6204
+ const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
6205
+ const candidates = [];
6206
+ for (const adapter of adapters) {
6207
+ if (!adapter.languages.includes("*") && adapter.languages.length > 0 && repoLanguages.size > 0) {
6208
+ const hasOverlap = adapter.languages.some((lang) => repoLanguages.has(lang));
6209
+ if (!hasOverlap) continue;
6210
+ }
6211
+ candidates.push(adapter);
6212
+ }
6213
+ const deduped = deduplicateToolFamilies(candidates, localConfigs);
6214
+ const tools = [];
6215
+ for (const adapter of deduped) {
6216
+ const localConfigPath = localConfigs.get(adapter.id);
6217
+ if (localConfigPath) {
6218
+ tools.push({
6219
+ toolId: adapter.id,
6220
+ localConfigurationFile: localConfigPath,
6221
+ useLocalConfigurationFile: true,
6222
+ patterns: []
6223
+ });
6224
+ continue;
6225
+ }
6226
+ const patternsMetadata = adapter.patternsMetadata;
6227
+ if (!patternsMetadata || patternsMetadata.length === 0) continue;
6228
+ const desc = descriptorMap.get(adapter.id);
6229
+ const mappings = desc?.frameworkMappings;
6230
+ const selectedPatterns = filterAutoPatterns(
6231
+ patternsMetadata,
6232
+ mappings,
6233
+ detectedKeys,
6234
+ repoLanguages,
6235
+ filters
6236
+ );
6237
+ if (selectedPatterns.length > 0) {
6238
+ tools.push({
6239
+ toolId: adapter.id,
6240
+ patterns: selectedPatterns.map((p) => ({ patternId: p.id }))
6241
+ });
6242
+ }
6243
+ }
6244
+ const baseConfig = createCodacyConfig({
6245
+ source: "auto",
6246
+ languages: discoveryResult.languages,
6247
+ tools,
6248
+ autoFilters: rawFilterString || void 0
6249
+ });
6250
+ const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
6251
+ baseConfig,
6252
+ repositoryRoot,
6253
+ adapters
6254
+ );
6255
+ return {
6256
+ config,
6257
+ stats: {
6258
+ detectedLanguages: discoveryResult.languages.length,
6259
+ detectedFrameworks: discoveryResult.frameworks.length,
6260
+ detectedLibraries: discoveryResult.libraries.length,
6261
+ selectedTools: tools.length,
6262
+ codacyYamlApplied
6263
+ }
6264
+ };
6265
+ }
6266
+ function toolFamily(id) {
6267
+ return id.replace(/\d+$/, "");
6268
+ }
6269
+ function deduplicateToolFamilies(candidates, localConfigs) {
6270
+ const families = /* @__PURE__ */ new Map();
6271
+ for (const adapter of candidates) {
6272
+ const family = toolFamily(adapter.id);
6273
+ const group = families.get(family) ?? [];
6274
+ group.push(adapter);
6275
+ families.set(family, group);
6276
+ }
6277
+ const result = [];
6278
+ for (const group of families.values()) {
6279
+ if (group.length === 1) {
6280
+ result.push(group[0]);
6281
+ continue;
6282
+ }
6283
+ const withConfig = group.filter((a) => localConfigs.has(a.id));
6284
+ if (withConfig.length === 1) {
6285
+ result.push(withConfig[0]);
6286
+ continue;
6287
+ }
6288
+ group.sort((a, b) => {
6289
+ const majorA = parseInt(a.preferredVersion.split(".")[0], 10) || 0;
6290
+ const majorB = parseInt(b.preferredVersion.split(".")[0], 10) || 0;
6291
+ return majorB - majorA;
6292
+ });
6293
+ result.push(group[0]);
6294
+ }
6295
+ return result;
6296
+ }
6297
+ function filterAutoPatterns(patterns, mappings, detectedKeys, repoLanguages, filters) {
6298
+ const allMappedPrefixes = [];
6299
+ const prefixToKeys = /* @__PURE__ */ new Map();
6300
+ const allMappedTags = [];
6301
+ const tagToKeys = /* @__PURE__ */ new Map();
6302
+ if (mappings) {
6303
+ for (const [key, entry] of Object.entries(mappings)) {
6304
+ if (entry.prefixes) {
6305
+ for (const prefix of entry.prefixes) {
6306
+ allMappedPrefixes.push(prefix);
6307
+ const existing = prefixToKeys.get(prefix) ?? [];
6308
+ existing.push(key);
6309
+ prefixToKeys.set(prefix, existing);
6310
+ }
6311
+ }
6312
+ if (entry.tags) {
6313
+ for (const tag of entry.tags) {
6314
+ if (!allMappedTags.includes(tag)) allMappedTags.push(tag);
6315
+ const existing = tagToKeys.get(tag) ?? [];
6316
+ existing.push(key);
6317
+ tagToKeys.set(tag, existing);
6318
+ }
6319
+ }
6320
+ }
6321
+ }
6322
+ const severityFilter = filters?.severities.length ? new Map(filters.severities.map((s) => [s.level, s.all])) : void 0;
6323
+ const categoryFilter = filters?.categories.length ? new Map(filters.categories.map((c) => [c.category, c.all])) : void 0;
6324
+ const result = [];
6325
+ for (const pattern of patterns) {
6326
+ const matchedKeys = findMappedKeys(
6327
+ pattern.id,
6328
+ pattern.tags,
6329
+ allMappedPrefixes,
6330
+ prefixToKeys,
6331
+ allMappedTags,
6332
+ tagToKeys
6333
+ );
6334
+ if (matchedKeys.length > 0) {
6335
+ if (!matchedKeys.some((k) => detectedKeys.has(k))) continue;
6336
+ } else {
6337
+ if (!pattern.enabled) {
6338
+ if (!isOverriddenByAllPrefix(pattern, severityFilter, categoryFilter)) {
6339
+ continue;
6340
+ }
6341
+ }
6342
+ }
6343
+ if (pattern.languages.length > 0 && repoLanguages.size > 0) {
6344
+ if (!pattern.languages.some((lang) => repoLanguages.has(lang))) continue;
6345
+ }
6346
+ if (severityFilter && !severityFilter.has(pattern.severityLevel)) continue;
6347
+ if (categoryFilter && !categoryFilter.has(pattern.category)) continue;
6348
+ result.push(pattern);
6349
+ }
6350
+ return result;
6351
+ }
6352
+ function findMappedKeys(patternId, patternTags, allPrefixes, prefixToKeys, allTags, tagToKeys) {
6353
+ const matched = /* @__PURE__ */ new Set();
6354
+ let longestPrefix = "";
6355
+ for (const prefix of allPrefixes) {
6356
+ if (patternId.startsWith(prefix) && prefix.length > longestPrefix.length) {
6357
+ longestPrefix = prefix;
6358
+ }
6359
+ }
6360
+ if (longestPrefix) {
6361
+ const keys = prefixToKeys.get(longestPrefix);
6362
+ if (keys) for (const k of keys) matched.add(k);
6363
+ }
6364
+ if (patternTags && patternTags.length > 0 && allTags.length > 0) {
6365
+ for (const tag of patternTags) {
6366
+ const keys = tagToKeys.get(tag);
6367
+ if (keys) for (const k of keys) matched.add(k);
6368
+ }
6369
+ }
6370
+ return Array.from(matched);
6371
+ }
6372
+ function isOverriddenByAllPrefix(pattern, severityFilter, categoryFilter) {
6373
+ if (severityFilter) {
6374
+ const allFlag = severityFilter.get(pattern.severityLevel);
6375
+ if (allFlag === true) return true;
6376
+ }
6377
+ if (categoryFilter) {
6378
+ const allFlag = categoryFilter.get(pattern.category);
6379
+ if (allFlag === true) return true;
6380
+ }
6381
+ return false;
6382
+ }
6383
+
6384
+ // src/auto-filters.ts
6385
+ var SEVERITY_ALIASES = {
6386
+ info: "Info",
6387
+ minor: "Info",
6388
+ warning: "Warning",
6389
+ medium: "Warning",
6390
+ high: "High",
6391
+ error: "Error",
6392
+ critical: "Error"
6393
+ };
6394
+ var CATEGORY_NAMES = {
6395
+ security: "Security",
6396
+ errorprone: "ErrorProne",
6397
+ codestyle: "CodeStyle",
6398
+ compatibility: "Compatibility",
6399
+ performance: "Performance",
6400
+ unusedcode: "UnusedCode",
6401
+ complexity: "Complexity",
6402
+ comprehensibility: "Comprehensibility",
6403
+ bestpractice: "BestPractice",
6404
+ documentation: "Documentation"
6405
+ };
6406
+ function parseAutoFilters(raw) {
6407
+ const severities = [];
6408
+ const categories = [];
6409
+ const unknown = [];
6410
+ const tokens = raw.split(",").map((t) => t.trim()).filter(Boolean);
6411
+ for (const token of tokens) {
6412
+ let name = token;
6413
+ let all = false;
6414
+ if (name.toLowerCase().startsWith("all")) {
6415
+ all = true;
6416
+ name = name.slice(3);
6417
+ }
6418
+ const severityMatch = SEVERITY_ALIASES[name.toLowerCase()];
6419
+ if (severityMatch) {
6420
+ if (!severities.some((s) => s.level === severityMatch)) {
6421
+ severities.push({ level: severityMatch, all });
6422
+ } else {
6423
+ const existing = severities.find((s) => s.level === severityMatch);
6424
+ if (all) existing.all = true;
6425
+ }
6426
+ continue;
6427
+ }
6428
+ const categoryMatch = CATEGORY_NAMES[name.toLowerCase()];
6429
+ if (categoryMatch) {
6430
+ if (!categories.some((c) => c.category === categoryMatch)) {
6431
+ categories.push({ category: categoryMatch, all });
6432
+ } else {
6433
+ const existing = categories.find((c) => c.category === categoryMatch);
6434
+ if (all) existing.all = true;
6435
+ }
6436
+ continue;
6437
+ }
6438
+ unknown.push(token);
6439
+ }
6440
+ return { filters: { severities, categories }, unknown };
6441
+ }
5153
6442
 
5154
6443
  // src/logger.ts
5155
- var import_fs = __toESM(require("fs"));
5156
- var import_path7 = __toESM(require("path"));
6444
+ var import_fs2 = __toESM(require("fs"));
6445
+ var import_path8 = __toESM(require("path"));
5157
6446
  var import_os2 = __toESM(require("os"));
5158
6447
  var LEVEL_ORDER = {
5159
6448
  debug: 0,
@@ -5164,7 +6453,7 @@ var LEVEL_ORDER = {
5164
6453
  function rotateLogFiles(logsDir, maxFiles) {
5165
6454
  let entries2;
5166
6455
  try {
5167
- entries2 = import_fs.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
6456
+ entries2 = import_fs2.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
5168
6457
  } catch {
5169
6458
  return;
5170
6459
  }
@@ -5173,7 +6462,7 @@ function rotateLogFiles(logsDir, maxFiles) {
5173
6462
  const toRemove = entries2.slice(0, entries2.length - maxFiles);
5174
6463
  for (const f of toRemove) {
5175
6464
  try {
5176
- import_fs.default.unlinkSync(import_path7.default.join(logsDir, f));
6465
+ import_fs2.default.unlinkSync(import_path8.default.join(logsDir, f));
5177
6466
  } catch {
5178
6467
  }
5179
6468
  }
@@ -5208,17 +6497,17 @@ function enrichMessage(message, fields) {
5208
6497
  function createLogger(options = {}) {
5209
6498
  const minLevel = LEVEL_ORDER[options.level ?? "info"];
5210
6499
  const fileEnabled = options.fileEnabled ?? true;
5211
- const logsDir = options.logsDir ?? import_path7.default.join(import_os2.default.homedir(), ".codacy", "logs");
6500
+ const logsDir = options.logsDir ?? import_path8.default.join(import_os2.default.homedir(), ".codacy", "logs");
5212
6501
  const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;
5213
6502
  const maxFiles = options.maxFiles ?? 5;
5214
6503
  const onStderrMessage = options.onStderrMessage;
5215
6504
  let logFd = null;
5216
6505
  let bytesWritten = 0;
5217
6506
  if (fileEnabled) {
5218
- import_fs.default.mkdirSync(logsDir, { recursive: true });
6507
+ import_fs2.default.mkdirSync(logsDir, { recursive: true });
5219
6508
  rotateLogFiles(logsDir, maxFiles);
5220
- const logFilePath = import_path7.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
5221
- logFd = import_fs.default.openSync(logFilePath, "a");
6509
+ const logFilePath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
6510
+ logFd = import_fs2.default.openSync(logFilePath, "a");
5222
6511
  }
5223
6512
  function log(level, message, fields) {
5224
6513
  const numLevel = LEVEL_ORDER[level];
@@ -5231,13 +6520,13 @@ function createLogger(options = {}) {
5231
6520
  ...fields
5232
6521
  });
5233
6522
  const buf = Buffer.from(entry + "\n", "utf-8");
5234
- import_fs.default.writeSync(logFd, buf);
6523
+ import_fs2.default.writeSync(logFd, buf);
5235
6524
  bytesWritten += buf.length;
5236
6525
  if (bytesWritten >= maxFileSize) {
5237
- import_fs.default.closeSync(logFd);
6526
+ import_fs2.default.closeSync(logFd);
5238
6527
  rotateLogFiles(logsDir, maxFiles);
5239
- const newPath = import_path7.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
5240
- logFd = import_fs.default.openSync(newPath, "a");
6528
+ const newPath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
6529
+ logFd = import_fs2.default.openSync(newPath, "a");
5241
6530
  bytesWritten = 0;
5242
6531
  }
5243
6532
  }
@@ -5262,15 +6551,18 @@ function createLogger(options = {}) {
5262
6551
  applyCodacyYamlExcludes,
5263
6552
  buildToolConfig,
5264
6553
  clearAdapters,
6554
+ computePatternSummary,
5265
6555
  configureApiToken,
5266
6556
  createCodacyConfig,
5267
6557
  createLogger,
5268
6558
  detectLanguages,
6559
+ discover,
5269
6560
  formatOutput,
5270
6561
  formatTextSummary,
5271
6562
  getAdapter,
5272
6563
  getRegisteredAdapters,
5273
6564
  getRegisteredDescriptors,
6565
+ initAutoConfig,
5274
6566
  initContainerConfig,
5275
6567
  initDefaultConfig,
5276
6568
  initLocalConfig,
@@ -5279,6 +6571,7 @@ function createLogger(options = {}) {
5279
6571
  listIgnoredFiles,
5280
6572
  listPrFiles,
5281
6573
  listStagedFiles,
6574
+ parseAutoFilters,
5282
6575
  readCodacyConfig,
5283
6576
  readCodacyYaml,
5284
6577
  registerAdapter,