devcompass 2.6.0 → 2.7.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.
@@ -26,6 +26,17 @@ const { loadConfig, filterAlerts } = require('../config/loader');
26
26
  const { getCached, setCache } = require('../cache/manager');
27
27
  const { formatAsJson } = require('../utils/json-formatter');
28
28
  const { handleCiMode } = require('../utils/ci-handler');
29
+
30
+ // NEW v2.7.0 imports
31
+ const { analyzeSupplyChain, getSupplyChainStats } = require('../analyzers/supply-chain');
32
+ const { analyzeLicenseRisks, getLicenseRiskScore } = require('../analyzers/license-risk');
33
+ const { analyzePackageQuality } = require('../analyzers/package-quality');
34
+ const {
35
+ generateSecurityRecommendations,
36
+ groupByPriority,
37
+ calculateExpectedImpact
38
+ } = require('../analyzers/security-recommendations');
39
+
29
40
  const packageJson = require('../../package.json');
30
41
 
31
42
  async function analyze(options) {
@@ -184,7 +195,7 @@ async function analyze(options) {
184
195
  }
185
196
  }
186
197
 
187
- // Check for predictive warnings (GitHub Issues) - ENHANCED v2.6.0
198
+ // Check for predictive warnings (GitHub Issues) - v2.6.0
188
199
  const { getTrackedPackageCount, TRACKED_REPOS } = require('../alerts/github-tracker');
189
200
  const totalTracked = getTrackedPackageCount();
190
201
 
@@ -199,6 +210,7 @@ async function analyze(options) {
199
210
 
200
211
  let predictiveWarnings = [];
201
212
  let githubCheckTime = 0;
213
+ let githubData = [];
202
214
 
203
215
  if (config.cache) {
204
216
  predictiveWarnings = getCached(projectPath, 'predictive');
@@ -207,11 +219,22 @@ async function analyze(options) {
207
219
  if (!predictiveWarnings) {
208
220
  try {
209
221
  const { generatePredictiveWarnings } = require('../alerts/predictive');
222
+ const { checkGitHubIssues } = require('../alerts/github-tracker');
210
223
 
211
- // Track performance - NEW in v2.6.0
224
+ // Track performance
212
225
  const startTime = Date.now();
213
226
 
214
- // Pass progress callback - NEW in v2.6.0
227
+ // Get raw GitHub data for quality analysis
228
+ githubData = await checkGitHubIssues(dependencies, {
229
+ concurrency: 5,
230
+ onProgress: (current, total, packageName) => {
231
+ if (outputMode === 'normal') {
232
+ spinner.text = `Checking GitHub activity (${current}/${total} packages) - ${packageName}`;
233
+ }
234
+ }
235
+ });
236
+
237
+ // Generate predictive warnings
215
238
  predictiveWarnings = await generatePredictiveWarnings(dependencies, {
216
239
  onProgress: (current, total, packageName) => {
217
240
  if (outputMode === 'normal') {
@@ -274,6 +297,75 @@ async function analyze(options) {
274
297
  }
275
298
  }
276
299
 
300
+ // NEW v2.7.0 - Supply Chain Analysis
301
+ spinner.text = 'Analyzing supply chain security...';
302
+ let supplyChainWarnings = [];
303
+
304
+ if (config.cache) {
305
+ supplyChainWarnings = getCached(projectPath, 'supplyChain');
306
+ }
307
+
308
+ if (!supplyChainWarnings || supplyChainWarnings.length === 0) {
309
+ try {
310
+ supplyChainWarnings = await analyzeSupplyChain(projectPath, dependencies);
311
+ if (config.cache) {
312
+ setCache(projectPath, 'supplyChain', supplyChainWarnings);
313
+ }
314
+ } catch (error) {
315
+ if (outputMode !== 'silent') {
316
+ console.log(chalk.yellow('\n⚠️ Could not analyze supply chain'));
317
+ console.log(chalk.gray(` Error: ${error.message}\n`));
318
+ }
319
+ supplyChainWarnings = [];
320
+ }
321
+ }
322
+
323
+ // NEW v2.7.0 - Enhanced License Risk Analysis
324
+ spinner.text = 'Analyzing license risks...';
325
+ let licenseRiskData = { warnings: [], stats: {}, projectLicense: 'MIT' };
326
+
327
+ if (config.cache) {
328
+ const cached = getCached(projectPath, 'licenseRisk');
329
+ if (cached) licenseRiskData = cached;
330
+ }
331
+
332
+ if (!licenseRiskData.warnings || licenseRiskData.warnings.length === 0) {
333
+ try {
334
+ licenseRiskData = await analyzeLicenseRisks(projectPath, licenses);
335
+ if (config.cache) {
336
+ setCache(projectPath, 'licenseRisk', licenseRiskData);
337
+ }
338
+ } catch (error) {
339
+ if (outputMode !== 'silent') {
340
+ console.log(chalk.yellow('\n⚠️ Could not analyze license risks'));
341
+ console.log(chalk.gray(` Error: ${error.message}\n`));
342
+ }
343
+ }
344
+ }
345
+
346
+ // NEW v2.7.0 - Package Quality Analysis
347
+ spinner.text = 'Analyzing package quality...';
348
+ let qualityData = { results: [], stats: {} };
349
+
350
+ if (config.cache) {
351
+ const cached = getCached(projectPath, 'quality');
352
+ if (cached) qualityData = cached;
353
+ }
354
+
355
+ if (!qualityData.results || qualityData.results.length === 0) {
356
+ try {
357
+ qualityData = await analyzePackageQuality(dependencies, githubData);
358
+ if (config.cache) {
359
+ setCache(projectPath, 'quality', qualityData);
360
+ }
361
+ } catch (error) {
362
+ if (outputMode !== 'silent') {
363
+ console.log(chalk.yellow('\n⚠️ Could not analyze package quality'));
364
+ console.log(chalk.gray(` Error: ${error.message}\n`));
365
+ }
366
+ }
367
+ }
368
+
277
369
  // Calculate score
278
370
  const alertPenalty = calculateAlertPenalty(alerts);
279
371
  const securityPenalty = calculateSecurityPenalty(securityData.metadata);
@@ -289,23 +381,76 @@ async function analyze(options) {
289
381
 
290
382
  spinner.succeed(chalk.green(`Scanned ${totalDeps} dependencies in project`));
291
383
 
292
- // Show performance info if GitHub check was performed - NEW in v2.6.0
384
+ // Show performance info if GitHub check was performed
293
385
  if (githubCheckTime > 0 && outputMode === 'normal' && installedTrackedCount > 0) {
294
386
  const timeInSeconds = (githubCheckTime / 1000).toFixed(2);
295
387
  console.log(chalk.gray(`⚡ GitHub check completed in ${timeInSeconds}s (parallel processing)`));
296
388
  }
297
389
 
390
+ // NEW v2.7.0 - Generate Security Recommendations
391
+ const recommendations = generateSecurityRecommendations({
392
+ supplyChainWarnings,
393
+ licenseWarnings: licenseRiskData.warnings,
394
+ qualityResults: qualityData.results,
395
+ securityVulnerabilities: securityData.metadata,
396
+ ecosystemAlerts: alerts,
397
+ unusedDeps,
398
+ outdatedPackages: outdatedDeps
399
+ });
400
+
298
401
  // Handle different output modes
299
402
  if (outputMode === 'json') {
300
- const jsonOutput = formatAsJson(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses, predictiveWarnings);
403
+ const jsonOutput = formatAsJson(
404
+ alerts,
405
+ unusedDeps,
406
+ outdatedDeps,
407
+ score,
408
+ totalDeps,
409
+ securityData,
410
+ bundleSizes,
411
+ licenses,
412
+ predictiveWarnings,
413
+ supplyChainWarnings,
414
+ licenseRiskData,
415
+ qualityData,
416
+ recommendations
417
+ );
301
418
  console.log(jsonOutput);
302
419
  } else if (outputMode === 'ci') {
303
- displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses, predictiveWarnings);
420
+ displayResults(
421
+ alerts,
422
+ unusedDeps,
423
+ outdatedDeps,
424
+ score,
425
+ totalDeps,
426
+ securityData,
427
+ bundleSizes,
428
+ licenses,
429
+ predictiveWarnings,
430
+ supplyChainWarnings,
431
+ licenseRiskData,
432
+ qualityData,
433
+ recommendations
434
+ );
304
435
  handleCiMode(score, config, alerts, unusedDeps);
305
436
  } else if (outputMode === 'silent') {
306
437
  // Silent mode - no output
307
438
  } else {
308
- displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses, predictiveWarnings);
439
+ displayResults(
440
+ alerts,
441
+ unusedDeps,
442
+ outdatedDeps,
443
+ score,
444
+ totalDeps,
445
+ securityData,
446
+ bundleSizes,
447
+ licenses,
448
+ predictiveWarnings,
449
+ supplyChainWarnings,
450
+ licenseRiskData,
451
+ qualityData,
452
+ recommendations
453
+ );
309
454
  }
310
455
 
311
456
  } catch (error) {
@@ -318,7 +463,21 @@ async function analyze(options) {
318
463
  }
319
464
  }
320
465
 
321
- function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData, bundleSizes, licenses, predictiveWarnings) {
466
+ function displayResults(
467
+ alerts,
468
+ unusedDeps,
469
+ outdatedDeps,
470
+ score,
471
+ totalDeps,
472
+ securityData,
473
+ bundleSizes,
474
+ licenses,
475
+ predictiveWarnings,
476
+ supplyChainWarnings = [],
477
+ licenseRiskData = {},
478
+ qualityData = {},
479
+ recommendations = []
480
+ ) {
322
481
  logDivider();
323
482
 
324
483
  // SECURITY VULNERABILITIES
@@ -351,6 +510,59 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
351
510
 
352
511
  logDivider();
353
512
 
513
+ // NEW v2.7.0 - SUPPLY CHAIN SECURITY
514
+ if (supplyChainWarnings.length > 0) {
515
+ const stats = getSupplyChainStats(supplyChainWarnings);
516
+
517
+ logSection('🛡️ SUPPLY CHAIN SECURITY', supplyChainWarnings.length);
518
+
519
+ // Group by type
520
+ const malicious = supplyChainWarnings.filter(w => w.type === 'malicious');
521
+ const typosquat = supplyChainWarnings.filter(w => w.type === 'typosquatting' || w.type === 'typosquatting_suspected');
522
+ const scripts = supplyChainWarnings.filter(w => w.type === 'install_script');
523
+
524
+ // Malicious packages (CRITICAL)
525
+ if (malicious.length > 0) {
526
+ log(chalk.red.bold('\n🔴 MALICIOUS PACKAGES DETECTED\n'));
527
+ malicious.forEach(w => {
528
+ log(` ${chalk.red.bold(w.package)}`);
529
+ log(` ${chalk.red(w.message)}`);
530
+ log(` ${chalk.yellow('→')} ${w.recommendation}\n`);
531
+ });
532
+ }
533
+
534
+ // Typosquatting (HIGH)
535
+ if (typosquat.length > 0) {
536
+ log(chalk.red('\n🟠 TYPOSQUATTING RISK\n'));
537
+ typosquat.forEach(w => {
538
+ const display = getSeverityDisplay(w.severity);
539
+ log(` ${display.emoji} ${chalk.bold(w.package)}`);
540
+ log(` Similar to: ${chalk.green(w.official)} (official package)`);
541
+ log(` ${chalk.yellow('→')} ${w.recommendation}\n`);
542
+ });
543
+ }
544
+
545
+ // Install scripts (MEDIUM/HIGH)
546
+ if (scripts.length > 0) {
547
+ log(chalk.yellow('\n🟡 INSTALL SCRIPT WARNINGS\n'));
548
+ scripts.slice(0, 3).forEach(w => {
549
+ log(` ${chalk.bold(w.package)}`);
550
+ log(` Script: ${chalk.gray(w.script)}`);
551
+ log(` Patterns: ${chalk.yellow(w.patterns.join(', '))}`);
552
+ log(` ${chalk.yellow('→')} ${w.recommendation}\n`);
553
+ });
554
+
555
+ if (scripts.length > 3) {
556
+ log(chalk.gray(` ... and ${scripts.length - 3} more install script warnings\n`));
557
+ }
558
+ }
559
+ } else {
560
+ logSection('✅ SUPPLY CHAIN SECURITY');
561
+ log(chalk.green(' No supply chain risks detected!\n'));
562
+ }
563
+
564
+ logDivider();
565
+
354
566
  // ECOSYSTEM ALERTS
355
567
  if (alerts.length > 0) {
356
568
  logSection('🚨 ECOSYSTEM ALERTS', alerts.length);
@@ -391,7 +603,7 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
391
603
 
392
604
  logDivider();
393
605
 
394
- // PREDICTIVE WARNINGS (v2.5.0+)
606
+ // PREDICTIVE WARNINGS
395
607
  if (predictiveWarnings.length > 0) {
396
608
  const { getTrackedPackageCount } = require('../alerts/github-tracker');
397
609
  const totalTracked = getTrackedPackageCount();
@@ -424,6 +636,124 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
424
636
 
425
637
  logDivider();
426
638
 
639
+ // NEW v2.7.0 - LICENSE RISK ANALYSIS
640
+ if (licenseRiskData.warnings && licenseRiskData.warnings.length > 0) {
641
+ logSection('⚖️ LICENSE RISK ANALYSIS', licenseRiskData.warnings.length);
642
+
643
+ const { warnings, projectLicense } = licenseRiskData;
644
+
645
+ log(chalk.gray(` Project License: ${projectLicense}\n`));
646
+
647
+ // Group by severity
648
+ const critical = warnings.filter(w => w.severity === 'critical');
649
+ const high = warnings.filter(w => w.severity === 'high');
650
+ const medium = warnings.filter(w => w.severity === 'medium');
651
+
652
+ if (critical.length > 0) {
653
+ log(chalk.red.bold('🔴 CRITICAL LICENSE RISKS\n'));
654
+ critical.forEach(w => {
655
+ log(` ${chalk.red.bold(w.package)}`);
656
+ log(` License: ${chalk.red(w.license)}`);
657
+ log(` ${chalk.yellow(w.message)}`);
658
+ log(` ${chalk.cyan('→')} ${w.recommendation}\n`);
659
+ });
660
+ }
661
+
662
+ if (high.length > 0) {
663
+ log(chalk.red('\n🟠 HIGH RISK LICENSES\n'));
664
+ high.slice(0, 3).forEach(w => {
665
+ log(` ${chalk.bold(w.package)}`);
666
+ log(` License: ${chalk.yellow(w.license)}`);
667
+ log(` ${chalk.gray(w.message)}`);
668
+ log(` ${chalk.cyan('→')} ${w.recommendation}\n`);
669
+ });
670
+
671
+ if (high.length > 3) {
672
+ log(chalk.gray(` ... and ${high.length - 3} more high-risk licenses\n`));
673
+ }
674
+ }
675
+
676
+ if (medium.length > 0) {
677
+ log(chalk.gray(`\n ${medium.length} medium-risk licenses detected\n`));
678
+ }
679
+ } else {
680
+ logSection('✅ LICENSE COMPLIANCE');
681
+ log(chalk.green(' All licenses are compliant!\n'));
682
+ }
683
+
684
+ logDivider();
685
+
686
+ // NEW v2.7.0 - PACKAGE QUALITY METRICS
687
+ if (qualityData.results && qualityData.results.length > 0) {
688
+ const { results, stats } = qualityData;
689
+
690
+ logSection('📊 PACKAGE QUALITY METRICS', results.length);
691
+
692
+ // Healthy packages
693
+ if (stats.healthy > 0) {
694
+ log(chalk.green(`\n✅ HEALTHY PACKAGES (${stats.healthy})\n`));
695
+ const healthy = results.filter(r => r.status === 'healthy');
696
+ const display = healthy.slice(0, 10).map(r => r.package).join(', ');
697
+ log(chalk.gray(` ${display}${healthy.length > 10 ? '...' : ''}\n`));
698
+ }
699
+
700
+ // Needs attention
701
+ if (stats.needsAttention > 0) {
702
+ log(chalk.yellow(`\n🟡 NEEDS ATTENTION (${stats.needsAttention})\n`));
703
+ const attention = results.filter(r => r.status === 'needs_attention');
704
+ attention.slice(0, 3).forEach(r => {
705
+ log(` ${chalk.bold(r.package)}`);
706
+ log(` Health Score: ${chalk.yellow(r.healthScore + '/10')}`);
707
+ log(` Last Update: ${chalk.gray(r.daysSincePublish)} days ago`);
708
+ if (r.githubMetrics) {
709
+ log(` Open Issues: ${chalk.gray(r.githubMetrics.totalIssues)}`);
710
+ }
711
+ log('');
712
+ });
713
+ }
714
+
715
+ // Stale packages
716
+ if (stats.stale > 0) {
717
+ log(chalk.red(`\n🟠 STALE PACKAGES (${stats.stale})\n`));
718
+ const stale = results.filter(r => r.status === 'stale');
719
+ stale.slice(0, 3).forEach(r => {
720
+ log(` ${chalk.bold(r.package)}`);
721
+ log(` Health Score: ${chalk.red(r.healthScore + '/10')}`);
722
+ log(` Last Update: ${chalk.red(Math.floor(r.daysSincePublish / 30))} months ago`);
723
+ log(` ${chalk.cyan('→')} Consider finding actively maintained alternative\n`);
724
+ });
725
+ }
726
+
727
+ // Abandoned packages
728
+ if (stats.abandoned > 0) {
729
+ log(chalk.red.bold(`\n🔴 ABANDONED PACKAGES (${stats.abandoned})\n`));
730
+ const abandoned = results.filter(r => r.status === 'abandoned');
731
+ abandoned.forEach(r => {
732
+ log(` ${chalk.red.bold(r.package)}`);
733
+ log(` Health Score: ${chalk.red(r.healthScore + '/10')}`);
734
+ log(` Last Update: ${chalk.red(Math.floor(r.daysSincePublish / 365))} years ago`);
735
+ log(` Maintainer: ${chalk.red('Inactive')}`);
736
+ log(` ${chalk.cyan('→')} Migrate to actively maintained alternative\n`);
737
+ });
738
+ }
739
+
740
+ // Deprecated packages
741
+ if (stats.deprecated > 0) {
742
+ log(chalk.red.bold(`\n🔴 DEPRECATED PACKAGES (${stats.deprecated})\n`));
743
+ const deprecated = results.filter(r => r.status === 'deprecated');
744
+ deprecated.forEach(r => {
745
+ log(` ${chalk.red.bold(r.package)}`);
746
+ log(` ${chalk.red('Package is officially deprecated')}`);
747
+ log(` ${chalk.cyan('→')} Find alternative immediately\n`);
748
+ });
749
+ }
750
+ } else {
751
+ logSection('📊 PACKAGE QUALITY');
752
+ log(chalk.green(' Quality analysis in progress...\n'));
753
+ }
754
+
755
+ logDivider();
756
+
427
757
  // UNUSED DEPENDENCIES
428
758
  if (unusedDeps.length > 0) {
429
759
  logSection('🔴 UNUSED DEPENDENCIES', unusedDeps.length);
@@ -485,9 +815,9 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
485
815
  logDivider();
486
816
  }
487
817
 
488
- // LICENSE WARNINGS
818
+ // LICENSE WARNINGS (legacy - for packages not caught by license risk)
489
819
  const problematicLicenses = findProblematicLicenses(licenses);
490
- if (problematicLicenses.length > 0) {
820
+ if (problematicLicenses.length > 0 && (!licenseRiskData.warnings || licenseRiskData.warnings.length === 0)) {
491
821
  logSection('⚖️ LICENSE WARNINGS', problematicLicenses.length);
492
822
 
493
823
  problematicLicenses.forEach(pkg => {
@@ -498,13 +828,9 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
498
828
  });
499
829
 
500
830
  log(chalk.gray('\n Note: Restrictive licenses may require legal review\n'));
501
- } else {
502
- logSection('✅ LICENSE COMPLIANCE');
503
- log(chalk.green(' All licenses are permissive!\n'));
831
+ logDivider();
504
832
  }
505
833
 
506
- logDivider();
507
-
508
834
  // PROJECT HEALTH
509
835
  logSection('📊 PROJECT HEALTH');
510
836
 
@@ -516,6 +842,10 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
516
842
  log(` Security Vulnerabilities: ${chalk.red(securityData.metadata.total)}`);
517
843
  }
518
844
 
845
+ if (supplyChainWarnings.length > 0) {
846
+ log(` Supply Chain Warnings: ${chalk.red(supplyChainWarnings.length)}`);
847
+ }
848
+
519
849
  if (alerts.length > 0) {
520
850
  log(` Ecosystem Alerts: ${chalk.red(alerts.length)}`);
521
851
  }
@@ -524,15 +854,114 @@ function displayResults(alerts, unusedDeps, outdatedDeps, score, totalDeps, secu
524
854
  log(` Predictive Warnings: ${chalk.yellow(predictiveWarnings.length)}`);
525
855
  }
526
856
 
857
+ if (licenseRiskData.warnings && licenseRiskData.warnings.length > 0) {
858
+ log(` License Risks: ${chalk.yellow(licenseRiskData.warnings.length)}`);
859
+ }
860
+
861
+ if (qualityData.stats) {
862
+ const { abandoned, deprecated, stale } = qualityData.stats;
863
+ const total = (abandoned || 0) + (deprecated || 0) + (stale || 0);
864
+ if (total > 0) {
865
+ log(` Quality Issues: ${chalk.yellow(total)}`);
866
+ }
867
+ }
868
+
527
869
  log(` Unused: ${chalk.red(unusedDeps.length)}`);
528
870
  log(` Outdated: ${chalk.yellow(outdatedDeps.length)}\n`);
529
871
 
530
872
  logDivider();
531
873
 
532
- // QUICK WINS
533
- displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData);
874
+ // NEW v2.7.0 - SECURITY RECOMMENDATIONS
875
+ if (recommendations.length > 0) {
876
+ displaySecurityRecommendations(recommendations, score.total);
877
+ } else {
878
+ displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData);
879
+ }
880
+ }
881
+
882
+ // NEW v2.7.0 - Display Security Recommendations
883
+ function displaySecurityRecommendations(recommendations, currentScore) {
884
+ const grouped = groupByPriority(recommendations);
885
+ const impact = calculateExpectedImpact(recommendations, currentScore);
886
+
887
+ logSection('💡 SECURITY RECOMMENDATIONS (Prioritized)');
888
+
889
+ // CRITICAL
890
+ if (grouped.critical.length > 0) {
891
+ log(chalk.red.bold('\n🔴 CRITICAL (Fix Immediately)\n'));
892
+ grouped.critical.forEach((rec, index) => {
893
+ log(` ${index + 1}. ${chalk.bold(rec.issue)}`);
894
+ if (rec.package) {
895
+ log(` Package: ${chalk.red(rec.package)}`);
896
+ }
897
+ log(` ${chalk.cyan('Action:')} ${rec.action}`);
898
+ if (rec.command) {
899
+ log(chalk.gray(` $ ${rec.command}`));
900
+ }
901
+ if (rec.alternative) {
902
+ log(chalk.gray(` ${rec.alternative}`));
903
+ }
904
+ log('');
905
+ });
906
+ }
907
+
908
+ // HIGH
909
+ if (grouped.high.length > 0) {
910
+ log(chalk.red('\n🟠 HIGH (Fix Soon)\n'));
911
+ grouped.high.slice(0, 5).forEach((rec, index) => {
912
+ log(` ${index + 1}. ${rec.issue}`);
913
+ if (rec.package) {
914
+ log(` Package: ${chalk.yellow(rec.package)}`);
915
+ }
916
+ log(` ${chalk.cyan('Action:')} ${rec.action}`);
917
+ if (rec.command) {
918
+ log(chalk.gray(` $ ${rec.command}`));
919
+ }
920
+ log('');
921
+ });
922
+
923
+ if (grouped.high.length > 5) {
924
+ log(chalk.gray(` ... and ${grouped.high.length - 5} more high-priority items\n`));
925
+ }
926
+ }
927
+
928
+ // MEDIUM
929
+ if (grouped.medium.length > 0) {
930
+ log(chalk.yellow('\n🟡 MEDIUM (Plan to Fix)\n'));
931
+ grouped.medium.slice(0, 3).forEach((rec, index) => {
932
+ log(` ${index + 1}. ${rec.issue}`);
933
+ if (rec.package) {
934
+ log(` Package: ${chalk.gray(rec.package)}`);
935
+ }
936
+ log(` ${chalk.cyan('Action:')} ${rec.action}`);
937
+ log('');
938
+ });
939
+
940
+ if (grouped.medium.length > 3) {
941
+ log(chalk.gray(` ... and ${grouped.medium.length - 3} more medium-priority items\n`));
942
+ }
943
+ }
944
+
945
+ // Expected Impact
946
+ log(chalk.cyan.bold('\n📈 Expected Impact:\n'));
947
+ log(` ${chalk.green('✓')} Current Health Score: ${chalk.yellow(impact.currentScore + '/10')}`);
948
+ log(` ${chalk.green('✓')} Expected Score: ${chalk.green(impact.expectedScore + '/10')}`);
949
+ log(` ${chalk.green('✓')} Improvement: ${chalk.cyan('+' + impact.improvement)} points (${impact.percentageIncrease}% increase)`);
950
+ log(` ${chalk.green('✓')} Issues Resolved: ${impact.critical + impact.high + impact.medium} critical/high/medium`);
951
+
952
+ if (grouped.critical.length > 0) {
953
+ log(` ${chalk.green('✓')} Eliminate ${impact.critical} critical security risks`);
954
+ }
955
+ if (grouped.high.length > 0) {
956
+ log(` ${chalk.green('✓')} Resolve ${impact.high} high-priority issues`);
957
+ }
958
+
959
+ log(chalk.cyan('\n💡 TIP: Run') + chalk.bold(' devcompass fix ') + chalk.cyan('to apply automated fixes!\n'));
960
+
961
+ logDivider();
534
962
  }
535
963
 
964
+ // Original Quick Wins (fallback)
536
965
  function displayQuickWins(alerts, unusedDeps, outdatedDeps, score, totalDeps, securityData) {
537
966
  const hasCriticalAlerts = alerts.some(a => a.severity === 'critical' || a.severity === 'high');
538
967
  const hasCriticalSecurity = securityData.metadata.critical > 0 || securityData.metadata.high > 0;