@getcodesentinel/codesentinel 1.12.1 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1524,9 +1524,7 @@ var compareSnapshots = (current, baseline) => {
1524
1524
  const hotspots = diffSets(currentHotspots, baselineHotspots);
1525
1525
  const cycles = diffSets(currentCycles, baselineCycles);
1526
1526
  return {
1527
- repositoryScoreDelta: round43(
1528
- current.analysis.risk.repositoryScore - baseline.analysis.risk.repositoryScore
1529
- ),
1527
+ riskScoreDelta: round43(current.analysis.risk.riskScore - baseline.analysis.risk.riskScore),
1530
1528
  normalizedScoreDelta: round43(
1531
1529
  current.analysis.risk.normalizedScore - baseline.analysis.risk.normalizedScore
1532
1530
  ),
@@ -1652,9 +1650,9 @@ var createReport = (snapshot, diff) => {
1652
1650
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1653
1651
  repository: {
1654
1652
  targetPath: snapshot.analysis.structural.targetPath,
1655
- repositoryScore: snapshot.analysis.risk.repositoryScore,
1653
+ riskScore: snapshot.analysis.risk.riskScore,
1656
1654
  normalizedScore: snapshot.analysis.risk.normalizedScore,
1657
- riskTier: toRiskTier(snapshot.analysis.risk.repositoryScore),
1655
+ riskTier: toRiskTier(snapshot.analysis.risk.riskScore),
1658
1656
  confidence: repositoryConfidence(snapshot),
1659
1657
  dimensionScores: repositoryDimensionScores(snapshot)
1660
1658
  },
@@ -1706,7 +1704,7 @@ var renderTextDiff = (report) => {
1706
1704
  return [
1707
1705
  "",
1708
1706
  "Diff",
1709
- ` repositoryScoreDelta: ${report.diff.repositoryScoreDelta}`,
1707
+ ` riskScoreDelta: ${report.diff.riskScoreDelta}`,
1710
1708
  ` normalizedScoreDelta: ${report.diff.normalizedScoreDelta}`,
1711
1709
  ` newHotspots: ${report.diff.newHotspots.join(", ") || "none"}`,
1712
1710
  ` resolvedHotspots: ${report.diff.resolvedHotspots.join(", ") || "none"}`,
@@ -1718,7 +1716,7 @@ var renderTextReport = (report) => {
1718
1716
  const lines = [];
1719
1717
  lines.push("Repository Summary");
1720
1718
  lines.push(` target: ${report.repository.targetPath}`);
1721
- lines.push(` repositoryScore: ${report.repository.repositoryScore}`);
1719
+ lines.push(` riskScore: ${report.repository.riskScore}`);
1722
1720
  lines.push(` normalizedScore: ${report.repository.normalizedScore}`);
1723
1721
  lines.push(` riskTier: ${report.repository.riskTier}`);
1724
1722
  lines.push(` confidence: ${report.repository.confidence ?? "n/a"}`);
@@ -1738,8 +1736,7 @@ var renderTextReport = (report) => {
1738
1736
  );
1739
1737
  lines.push(` evidence: ${factor.evidence}`);
1740
1738
  }
1741
- lines.push(` actions: ${hotspot.suggestedActions.join(" | ") || "none"}`);
1742
- lines.push(` levers: ${hotspot.biggestLevers.join(" | ") || "none"}`);
1739
+ lines.push(` priority actions: ${hotspot.suggestedActions.join(" | ") || "none"}`);
1743
1740
  }
1744
1741
  lines.push("");
1745
1742
  lines.push("Structural Observations");
@@ -1780,7 +1777,7 @@ var renderMarkdownDiff = (report) => {
1780
1777
  return [
1781
1778
  "",
1782
1779
  "## Diff",
1783
- `- repositoryScoreDelta: \`${report.diff.repositoryScoreDelta}\``,
1780
+ `- riskScoreDelta: \`${report.diff.riskScoreDelta}\``,
1784
1781
  `- normalizedScoreDelta: \`${report.diff.normalizedScoreDelta}\``,
1785
1782
  `- newHotspots: ${report.diff.newHotspots.map((item) => `\`${item}\``).join(", ") || "none"}`,
1786
1783
  `- resolvedHotspots: ${report.diff.resolvedHotspots.map((item) => `\`${item}\``).join(", ") || "none"}`,
@@ -1794,7 +1791,7 @@ var renderMarkdownReport = (report) => {
1794
1791
  lines.push("");
1795
1792
  lines.push("## Repository Summary");
1796
1793
  lines.push(`- target: \`${report.repository.targetPath}\``);
1797
- lines.push(`- repositoryScore: \`${report.repository.repositoryScore}\``);
1794
+ lines.push(`- riskScore: \`${report.repository.riskScore}\``);
1798
1795
  lines.push(`- normalizedScore: \`${report.repository.normalizedScore}\``);
1799
1796
  lines.push(`- riskTier: \`${report.repository.riskTier}\``);
1800
1797
  lines.push(`- confidence: \`${report.repository.confidence ?? "n/a"}\``);
@@ -1815,8 +1812,7 @@ var renderMarkdownReport = (report) => {
1815
1812
  );
1816
1813
  lines.push(` - evidence: \`${factor.evidence}\``);
1817
1814
  }
1818
- lines.push(` - Suggested actions: ${hotspot.suggestedActions.join(" | ") || "none"}`);
1819
- lines.push(` - Biggest levers: ${hotspot.biggestLevers.join(" | ") || "none"}`);
1815
+ lines.push(` - Priority actions: ${hotspot.suggestedActions.join(" | ") || "none"}`);
1820
1816
  }
1821
1817
  lines.push("");
1822
1818
  lines.push("## Structural Observations");
@@ -1973,7 +1969,7 @@ var evaluateGates = (input) => {
1973
1969
  const evaluatedGates = [];
1974
1970
  if (config.maxRepoScore !== void 0) {
1975
1971
  evaluatedGates.push("max-repo-score");
1976
- const current = input.current.analysis.risk.repositoryScore;
1972
+ const current = input.current.analysis.risk.riskScore;
1977
1973
  if (current > config.maxRepoScore) {
1978
1974
  violations.push(
1979
1975
  makeViolation(
@@ -1981,7 +1977,7 @@ var evaluateGates = (input) => {
1981
1977
  "error",
1982
1978
  `Repository score ${current} exceeds configured max ${config.maxRepoScore}.`,
1983
1979
  [input.current.analysis.structural.targetPath],
1984
- [{ kind: "repository_metric", metric: "repositoryScore" }]
1980
+ [{ kind: "repository_metric", metric: "riskScore" }]
1985
1981
  )
1986
1982
  );
1987
1983
  }
@@ -2093,7 +2089,7 @@ var renderCheckText = (snapshot, result) => {
2093
2089
  const lines = [];
2094
2090
  lines.push("CodeSentinel Check");
2095
2091
  lines.push(`target: ${snapshot.analysis.structural.targetPath}`);
2096
- lines.push(`repositoryScore: ${snapshot.analysis.risk.repositoryScore}`);
2092
+ lines.push(`riskScore: ${snapshot.analysis.risk.riskScore}`);
2097
2093
  lines.push(`evaluatedGates: ${result.evaluatedGates.join(", ") || "none"}`);
2098
2094
  lines.push(`violations: ${result.violations.length}`);
2099
2095
  lines.push(`exitCode: ${result.exitCode}`);
@@ -2112,7 +2108,7 @@ var renderCheckMarkdown = (snapshot, result) => {
2112
2108
  const lines = [];
2113
2109
  lines.push("## CodeSentinel CI Summary");
2114
2110
  lines.push(`- target: \`${snapshot.analysis.structural.targetPath}\``);
2115
- lines.push(`- repositoryScore: \`${snapshot.analysis.risk.repositoryScore}\``);
2111
+ lines.push(`- riskScore: \`${snapshot.analysis.risk.riskScore}\``);
2116
2112
  lines.push(
2117
2113
  `- evaluatedGates: ${result.evaluatedGates.map((item) => `\`${item}\``).join(", ") || "none"}`
2118
2114
  );
@@ -2463,6 +2459,7 @@ var resolveAutoBaselineRef = async (input) => {
2463
2459
 
2464
2460
  // src/index.ts
2465
2461
  import { readFileSync as readFileSync2 } from "fs";
2462
+ import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
2466
2463
  import { dirname as dirname2, resolve as resolve5 } from "path";
2467
2464
  import { fileURLToPath } from "url";
2468
2465
 
@@ -2495,7 +2492,7 @@ var createSummaryShape = (summary) => ({
2495
2492
  reason: summary.external.reason
2496
2493
  },
2497
2494
  risk: {
2498
- repositoryScore: summary.risk.repositoryScore,
2495
+ riskScore: summary.risk.riskScore,
2499
2496
  normalizedScore: summary.risk.normalizedScore,
2500
2497
  hotspotsTop: summary.risk.hotspots.slice(0, 5).map((hotspot) => ({
2501
2498
  file: hotspot.file,
@@ -2546,6 +2543,7 @@ var formatFactorLabel = (factorId) => factorLabelById2[factorId] ?? factorId;
2546
2543
  var formatNumber = (value) => value === null || value === void 0 ? "n/a" : `${value}`;
2547
2544
  var formatDimension = (value) => value === null ? "n/a" : `${value}`;
2548
2545
  var formatFactorSummary = (factor) => `${formatFactorLabel(factor.factorId)} (+${factor.contribution}, confidence=${factor.confidence})`;
2546
+ var formatFactorContribution = (factor) => `${formatFactorLabel(factor.factorId)}=${factor.contribution}`;
2549
2547
  var formatFactorEvidence = (factor) => {
2550
2548
  if (factor.factorId === "repository.structural") {
2551
2549
  return `structural dimension=${formatNumber(factor.rawMetrics["structuralDimension"])}`;
@@ -2560,7 +2558,7 @@ var formatFactorEvidence = (factor) => {
2560
2558
  return `structural\xD7evolution=${formatNumber(factor.rawMetrics["structuralEvolution"])}, central instability=${formatNumber(factor.rawMetrics["centralInstability"])}, dependency amplification=${formatNumber(factor.rawMetrics["dependencyAmplification"])}`;
2561
2559
  }
2562
2560
  if (factor.factorId === "file.structural") {
2563
- return `fanIn=${formatNumber(factor.rawMetrics["fanIn"])}, fanOut=${formatNumber(factor.rawMetrics["fanOut"])}, depth=${formatNumber(factor.rawMetrics["depth"])}, inCycle=${formatNumber(factor.rawMetrics["cycleParticipation"])}`;
2561
+ return `fanIn=${formatNumber(factor.rawMetrics["fanIn"])}, fanOut=${formatNumber(factor.rawMetrics["fanOut"])}, depth=${formatNumber(factor.rawMetrics["depth"])}, inCycle=${formatNumber(factor.rawMetrics["cycleParticipation"])}, structuralAttenuation=${formatNumber(factor.rawMetrics["structuralAttenuation"])}`;
2564
2562
  }
2565
2563
  if (factor.factorId === "file.evolution") {
2566
2564
  return `commitCount=${formatNumber(factor.rawMetrics["commitCount"])}, churnTotal=${formatNumber(factor.rawMetrics["churnTotal"])}, recentVolatility=${formatNumber(factor.rawMetrics["recentVolatility"])}`;
@@ -2667,8 +2665,8 @@ var renderText = (payload) => {
2667
2665
  const dimensionScores = repositoryDimensionScores2(repositoryTarget);
2668
2666
  const compositeFactors = repositoryTopFactors.filter((factor) => factor.family === "composite");
2669
2667
  lines.push(`target: ${payload.summary.structural.targetPath}`);
2670
- lines.push(`repositoryScore: ${payload.summary.risk.repositoryScore}`);
2671
- lines.push(`riskBand: ${toRiskBand(payload.summary.risk.repositoryScore)}`);
2668
+ lines.push(`riskScore: ${payload.summary.risk.riskScore}`);
2669
+ lines.push(`riskBand: ${toRiskBand(payload.summary.risk.riskScore)}`);
2672
2670
  lines.push(`selectedTargets: ${payload.selectedTargets.length}`);
2673
2671
  lines.push("dimensionScores:");
2674
2672
  lines.push(` structural: ${formatDimension(dimensionScores.structural)}`);
@@ -2678,20 +2676,15 @@ var renderText = (payload) => {
2678
2676
  lines.push("");
2679
2677
  lines.push("explanation:");
2680
2678
  lines.push(
2681
- ` why risky: ${repositoryTopFactors.map(formatFactorSummary).join("; ") || "insufficient data"}`
2682
- );
2683
- lines.push(
2684
- ` what specifically contributed: ${repositoryTopFactors.map((factor) => `${formatFactorLabel(factor.factorId)}=${factor.contribution}`).join(", ") || "insufficient data"}`
2679
+ ` key drivers: ${repositoryTopFactors.map(formatFactorSummary).join("; ") || "insufficient data"}`
2685
2680
  );
2686
2681
  lines.push(
2687
- ` dominant factors: ${repositoryTopFactors.map((factor) => formatFactorLabel(factor.factorId)).join(", ") || "insufficient data"}`
2682
+ ` contributions: ${repositoryTopFactors.map(formatFactorContribution).join(", ") || "insufficient data"}`
2688
2683
  );
2689
2684
  lines.push(
2690
- ` intersected signals: ${compositeFactors.map((factor) => `${formatFactorLabel(factor.factorId)} [${formatFactorEvidence(factor)}]`).join("; ") || "none"}`
2691
- );
2692
- lines.push(
2693
- ` what could reduce risk most: ${buildRepositoryActions(payload, repositoryTarget).join(" ")}`
2685
+ ` interaction effects: ${compositeFactors.map((factor) => `${formatFactorLabel(factor.factorId)} [${formatFactorEvidence(factor)}]`).join("; ") || "none"}`
2694
2686
  );
2687
+ lines.push(` priority actions: ${buildRepositoryActions(payload, repositoryTarget).join(" ")}`);
2695
2688
  lines.push("");
2696
2689
  for (const target of payload.selectedTargets) {
2697
2690
  lines.push(renderTargetText(target));
@@ -2707,8 +2700,8 @@ var renderMarkdown = (payload) => {
2707
2700
  const compositeFactors = repositoryTopFactors.filter((factor) => factor.family === "composite");
2708
2701
  lines.push(`# CodeSentinel Explanation`);
2709
2702
  lines.push(`- target: \`${payload.summary.structural.targetPath}\``);
2710
- lines.push(`- repositoryScore: \`${payload.summary.risk.repositoryScore}\``);
2711
- lines.push(`- riskBand: \`${toRiskBand(payload.summary.risk.repositoryScore)}\``);
2703
+ lines.push(`- riskScore: \`${payload.summary.risk.riskScore}\``);
2704
+ lines.push(`- riskBand: \`${toRiskBand(payload.summary.risk.riskScore)}\``);
2712
2705
  lines.push(`- selectedTargets: \`${payload.selectedTargets.length}\``);
2713
2706
  lines.push("");
2714
2707
  lines.push("## Dimension Scores (0-100)");
@@ -2719,25 +2712,19 @@ var renderMarkdown = (payload) => {
2719
2712
  lines.push("");
2720
2713
  lines.push(`## Summary`);
2721
2714
  lines.push(
2722
- `- why risky: ${repositoryTopFactors.map(formatFactorSummary).join("; ") || "insufficient data"}`
2723
- );
2724
- lines.push(
2725
- `- what specifically contributed: ${repositoryTopFactors.map((factor) => `${formatFactorLabel(factor.factorId)}=${factor.contribution}`).join(", ") || "insufficient data"}`
2726
- );
2727
- lines.push(
2728
- `- dominant factors: ${repositoryTopFactors.map((factor) => formatFactorLabel(factor.factorId)).join(", ") || "insufficient data"}`
2715
+ `- key drivers: ${repositoryTopFactors.map(formatFactorSummary).join("; ") || "insufficient data"}`
2729
2716
  );
2730
2717
  lines.push(
2731
- `- intersected signals: ${compositeFactors.map((factor) => `${formatFactorLabel(factor.factorId)} [${formatFactorEvidence(factor)}]`).join("; ") || "none"}`
2718
+ `- contributions: ${repositoryTopFactors.map(formatFactorContribution).join(", ") || "insufficient data"}`
2732
2719
  );
2733
2720
  lines.push(
2734
- `- what could reduce risk most: ${buildRepositoryActions(payload, repositoryTarget).join(" ")}`
2721
+ `- interaction effects: ${compositeFactors.map((factor) => `${formatFactorLabel(factor.factorId)} [${formatFactorEvidence(factor)}]`).join("; ") || "none"}`
2735
2722
  );
2723
+ lines.push(`- priority actions: ${buildRepositoryActions(payload, repositoryTarget).join(" ")}`);
2736
2724
  lines.push("");
2737
2725
  for (const target of payload.selectedTargets) {
2738
2726
  lines.push(`## ${target.targetType}: \`${target.targetId}\``);
2739
2727
  lines.push(`- score: \`${target.totalScore}\` (\`${target.normalizedScore}\`)`);
2740
- lines.push(`- dominantFactors: \`${target.dominantFactors.join(", ")}\``);
2741
2728
  lines.push(`- Top factors:`);
2742
2729
  for (const factor of [...target.factors].sort(sortFactorByContribution).slice(0, 5)) {
2743
2730
  lines.push(
@@ -4308,6 +4295,17 @@ var DEFAULT_RISK_ENGINE_CONFIG = {
4308
4295
  externalDimension: {
4309
4296
  topDependencyPercentile: 0.85,
4310
4297
  dependencyDepthHalfLife: 6
4298
+ },
4299
+ // Reduce false positives for thin aggregation hubs (for example, barrel/index re-export files)
4300
+ // that are structurally central but have low churn density and no cycle participation.
4301
+ aggregatorAttenuation: {
4302
+ enabled: true,
4303
+ minFanIn: 6,
4304
+ minFanOut: 4,
4305
+ minCommitCount: 4,
4306
+ maxChurnPerCommit: 24,
4307
+ maxChurnPerDependency: 10,
4308
+ maxStructuralReduction: 0.3
4311
4309
  }
4312
4310
  };
4313
4311
  var toUnitInterval = (value) => Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0;
@@ -4400,6 +4398,35 @@ var normalizeWithScale = (value, scale) => {
4400
4398
  return toUnitInterval((value - scale.lower) / (scale.upper - scale.lower));
4401
4399
  };
4402
4400
  var normalizePath2 = (path) => path.replaceAll("\\", "/");
4401
+ var computeAggregatorAttenuation = (input) => {
4402
+ const { fanIn, fanOut, inCycle, evolutionMetrics, config } = input;
4403
+ if (!config.enabled || inCycle > 0) {
4404
+ return 1;
4405
+ }
4406
+ if (fanIn < config.minFanIn || fanOut < config.minFanOut) {
4407
+ return 1;
4408
+ }
4409
+ if (evolutionMetrics === void 0 || evolutionMetrics.commitCount < config.minCommitCount) {
4410
+ return 1;
4411
+ }
4412
+ const churnPerCommit = evolutionMetrics.churnTotal / Math.max(1, evolutionMetrics.commitCount);
4413
+ const churnPerDependency = evolutionMetrics.churnTotal / Math.max(1, fanOut);
4414
+ if (churnPerCommit > config.maxChurnPerCommit || churnPerDependency > config.maxChurnPerDependency) {
4415
+ return 1;
4416
+ }
4417
+ const fanInSignal = toUnitInterval((fanIn - config.minFanIn) / Math.max(1, config.minFanIn));
4418
+ const fanOutSignal = toUnitInterval((fanOut - config.minFanOut) / Math.max(1, config.minFanOut));
4419
+ const lowChurnPerCommitSignal = 1 - toUnitInterval(churnPerCommit / config.maxChurnPerCommit);
4420
+ const lowChurnPerDependencySignal = 1 - toUnitInterval(churnPerDependency / config.maxChurnPerDependency);
4421
+ const attenuationConfidence = average([
4422
+ fanInSignal,
4423
+ fanOutSignal,
4424
+ lowChurnPerCommitSignal,
4425
+ lowChurnPerDependencySignal
4426
+ ]);
4427
+ const reduction = toUnitInterval(config.maxStructuralReduction) * attenuationConfidence;
4428
+ return round45(toUnitInterval(1 - reduction));
4429
+ };
4403
4430
  var dependencySignalWeights = {
4404
4431
  single_maintainer: 0.3,
4405
4432
  abandoned: 0.3,
@@ -4824,10 +4851,10 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
4824
4851
  const fanOutRisk = normalizeWithScale(logScale(file.fanOut), fanOutScale);
4825
4852
  const depthRisk = normalizeWithScale(file.depth, depthScale);
4826
4853
  const structuralWeights = config.structuralFactorWeights;
4827
- const structuralFactor = toUnitInterval(
4854
+ const structuralFactorRaw = toUnitInterval(
4828
4855
  fanInRisk * structuralWeights.fanIn + fanOutRisk * structuralWeights.fanOut + depthRisk * structuralWeights.depth + inCycle * structuralWeights.cycleParticipation
4829
4856
  );
4830
- const structuralCentrality = toUnitInterval((fanInRisk + fanOutRisk) / 2);
4857
+ const structuralCentralityRaw = toUnitInterval((fanInRisk + fanOutRisk) / 2);
4831
4858
  let evolutionFactor = 0;
4832
4859
  let frequencyRisk = 0;
4833
4860
  let churnRisk = 0;
@@ -4854,6 +4881,15 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
4854
4881
  frequencyRisk * evolutionWeights.frequency + churnRisk * evolutionWeights.churn + volatilityRisk * evolutionWeights.recentVolatility + ownershipConcentrationRisk * evolutionWeights.ownershipConcentration + busFactorRisk * evolutionWeights.busFactorRisk
4855
4882
  );
4856
4883
  }
4884
+ const structuralAttenuation = computeAggregatorAttenuation({
4885
+ fanIn: file.fanIn,
4886
+ fanOut: file.fanOut,
4887
+ inCycle,
4888
+ evolutionMetrics,
4889
+ config: config.aggregatorAttenuation
4890
+ });
4891
+ const structuralFactor = toUnitInterval(structuralFactorRaw * structuralAttenuation);
4892
+ const structuralCentrality = toUnitInterval(structuralCentralityRaw * structuralAttenuation);
4857
4893
  const dependencyAffinity = toUnitInterval(structuralCentrality * 0.6 + evolutionFactor * 0.4);
4858
4894
  const externalFactor = external.available ? toUnitInterval(dependencyComputation.repositoryExternalPressure * dependencyAffinity) : 0;
4859
4895
  const structuralBase = structuralFactor * dimensionWeights.structural;
@@ -4898,7 +4934,8 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
4898
4934
  topAuthorShare: evolutionMetrics?.topAuthorShare ?? null,
4899
4935
  busFactor: evolutionMetrics?.busFactor ?? null,
4900
4936
  dependencyAffinity: round45(dependencyAffinity),
4901
- repositoryExternalPressure: round45(dependencyComputation.repositoryExternalPressure)
4937
+ repositoryExternalPressure: round45(dependencyComputation.repositoryExternalPressure),
4938
+ structuralAttenuation: round45(structuralAttenuation)
4902
4939
  },
4903
4940
  normalizedMetrics: {
4904
4941
  fanInRisk: round45(fanInRisk),
@@ -4941,7 +4978,8 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
4941
4978
  fanIn: context.rawMetrics.fanIn,
4942
4979
  fanOut: context.rawMetrics.fanOut,
4943
4980
  depth: context.rawMetrics.depth,
4944
- cycleParticipation: context.rawMetrics.cycleParticipation
4981
+ cycleParticipation: context.rawMetrics.cycleParticipation,
4982
+ structuralAttenuation: context.rawMetrics.structuralAttenuation
4945
4983
  },
4946
4984
  normalizedMetrics: {
4947
4985
  fanInRisk: context.normalizedMetrics.fanInRisk,
@@ -5227,9 +5265,9 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
5227
5265
  criticalInstability * config.interactionWeights.centralInstability,
5228
5266
  dependencyAmplification * config.interactionWeights.dependencyAmplification
5229
5267
  ]);
5230
- const repositoryScore = round45(repositoryNormalizedScore * 100);
5268
+ const riskScore = round45(repositoryNormalizedScore * 100);
5231
5269
  if (collector !== void 0) {
5232
- const repositoryFactors = buildFactorTraces(repositoryScore, [
5270
+ const repositoryFactors = buildFactorTraces(riskScore, [
5233
5271
  {
5234
5272
  factorId: "repository.structural",
5235
5273
  family: "structural",
@@ -5292,14 +5330,14 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
5292
5330
  buildTargetTrace(
5293
5331
  "repository",
5294
5332
  structural.targetPath,
5295
- repositoryScore,
5333
+ riskScore,
5296
5334
  repositoryNormalizedScore,
5297
5335
  repositoryFactors
5298
5336
  )
5299
5337
  );
5300
5338
  }
5301
5339
  return {
5302
- repositoryScore,
5340
+ riskScore,
5303
5341
  normalizedScore: round45(repositoryNormalizedScore),
5304
5342
  hotspots,
5305
5343
  fragileClusters,
@@ -5390,6 +5428,10 @@ var mergeConfig = (overrides) => {
5390
5428
  externalDimension: {
5391
5429
  ...DEFAULT_RISK_ENGINE_CONFIG.externalDimension,
5392
5430
  ...overrides.externalDimension
5431
+ },
5432
+ aggregatorAttenuation: {
5433
+ ...DEFAULT_RISK_ENGINE_CONFIG.aggregatorAttenuation,
5434
+ ...overrides.aggregatorAttenuation
5393
5435
  }
5394
5436
  };
5395
5437
  };
@@ -5421,6 +5463,21 @@ var evaluateRepositoryRisk = (input, options = {}) => {
5421
5463
 
5422
5464
  // src/application/run-analyze-command.ts
5423
5465
  var resolveTargetPath = (inputPath, cwd) => resolve3(cwd, inputPath ?? ".");
5466
+ var riskProfileConfig = {
5467
+ default: void 0,
5468
+ personal: {
5469
+ evolutionFactorWeights: {
5470
+ frequency: 0.26,
5471
+ churn: 0.24,
5472
+ recentVolatility: 0.2,
5473
+ ownershipConcentration: 0.08,
5474
+ busFactorRisk: 0.04
5475
+ }
5476
+ }
5477
+ };
5478
+ var resolveRiskConfigForProfile = (riskProfile) => {
5479
+ return riskProfileConfig[riskProfile ?? "default"];
5480
+ };
5424
5481
  var createExternalProgressReporter = (logger) => {
5425
5482
  let lastLoggedProgress = 0;
5426
5483
  return (event) => {
@@ -5588,8 +5645,12 @@ var runAnalyzeCommand = async (inputPath, authorIdentityMode, options = {}, logg
5588
5645
  logger
5589
5646
  );
5590
5647
  logger.info("computing risk summary");
5591
- const risk = computeRepositoryRiskSummary(analysisInputs);
5592
- logger.info(`analysis completed (repositoryScore=${risk.repositoryScore})`);
5648
+ const riskConfig = resolveRiskConfigForProfile(options.riskProfile);
5649
+ const risk = computeRepositoryRiskSummary({
5650
+ ...analysisInputs,
5651
+ ...riskConfig === void 0 ? {} : { config: riskConfig }
5652
+ });
5653
+ logger.info(`analysis completed (riskScore=${risk.riskScore})`);
5593
5654
  return {
5594
5655
  ...analysisInputs,
5595
5656
  risk
@@ -5609,7 +5670,14 @@ var buildAnalysisSnapshot = async (inputPath, authorIdentityMode, options, logge
5609
5670
  },
5610
5671
  logger
5611
5672
  );
5612
- const evaluation = evaluateRepositoryRisk(analysisInputs, { explain: options.includeTrace });
5673
+ const riskConfig = resolveRiskConfigForProfile(options.riskProfile);
5674
+ const evaluation = evaluateRepositoryRisk(
5675
+ {
5676
+ ...analysisInputs,
5677
+ ...riskConfig === void 0 ? {} : { config: riskConfig }
5678
+ },
5679
+ { explain: options.includeTrace }
5680
+ );
5613
5681
  const summary = {
5614
5682
  ...analysisInputs,
5615
5683
  risk: evaluation.summary
@@ -5620,6 +5688,7 @@ var buildAnalysisSnapshot = async (inputPath, authorIdentityMode, options, logge
5620
5688
  analysisConfig: {
5621
5689
  authorIdentityMode,
5622
5690
  includeTrace: options.includeTrace,
5691
+ riskProfile: options.riskProfile ?? "default",
5623
5692
  recentWindowDays: analysisInputs.evolution.available ? analysisInputs.evolution.metrics.recentWindowDays : options.recentWindowDays ?? null
5624
5693
  }
5625
5694
  });
@@ -5654,6 +5723,7 @@ var runCheckCommand = async (inputPath, authorIdentityMode, options, logger = cr
5654
5723
  authorIdentityMode,
5655
5724
  {
5656
5725
  includeTrace: options.includeTrace,
5726
+ ...options.riskProfile === void 0 ? {} : { riskProfile: options.riskProfile },
5657
5727
  ...options.recentWindowDays === void 0 ? {} : { recentWindowDays: options.recentWindowDays }
5658
5728
  },
5659
5729
  logger
@@ -5722,6 +5792,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
5722
5792
  authorIdentityMode,
5723
5793
  {
5724
5794
  includeTrace: options.includeTrace,
5795
+ ...options.riskProfile === void 0 ? {} : { riskProfile: options.riskProfile },
5725
5796
  ...options.recentWindowDays === void 0 ? {} : { recentWindowDays: options.recentWindowDays }
5726
5797
  },
5727
5798
  logger
@@ -5780,6 +5851,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
5780
5851
  authorIdentityMode,
5781
5852
  {
5782
5853
  includeTrace: options.includeTrace,
5854
+ ...options.riskProfile === void 0 ? {} : { riskProfile: options.riskProfile },
5783
5855
  ...options.recentWindowDays === void 0 ? {} : { recentWindowDays: options.recentWindowDays }
5784
5856
  },
5785
5857
  logger
@@ -5855,6 +5927,7 @@ var runReportCommand = async (inputPath, authorIdentityMode, options, logger = c
5855
5927
  authorIdentityMode,
5856
5928
  {
5857
5929
  includeTrace: options.includeTrace,
5930
+ ...options.riskProfile === void 0 ? {} : { riskProfile: options.riskProfile },
5858
5931
  ...options.recentWindowDays === void 0 ? {} : { recentWindowDays: options.recentWindowDays }
5859
5932
  },
5860
5933
  logger
@@ -5911,7 +5984,14 @@ var runExplainCommand = async (inputPath, authorIdentityMode, options, logger =
5911
5984
  logger
5912
5985
  );
5913
5986
  logger.info("computing explainable risk summary");
5914
- const evaluation = evaluateRepositoryRisk(analysisInputs, { explain: true });
5987
+ const riskConfig = resolveRiskConfigForProfile(options.riskProfile);
5988
+ const evaluation = evaluateRepositoryRisk(
5989
+ {
5990
+ ...analysisInputs,
5991
+ ...riskConfig === void 0 ? {} : { config: riskConfig }
5992
+ },
5993
+ { explain: true }
5994
+ );
5915
5995
  if (evaluation.trace === void 0) {
5916
5996
  throw new Error("risk trace unavailable");
5917
5997
  }
@@ -5919,7 +5999,7 @@ var runExplainCommand = async (inputPath, authorIdentityMode, options, logger =
5919
5999
  ...analysisInputs,
5920
6000
  risk: evaluation.summary
5921
6001
  };
5922
- logger.info(`explanation completed (repositoryScore=${summary.risk.repositoryScore})`);
6002
+ logger.info(`explanation completed (riskScore=${summary.risk.riskScore})`);
5923
6003
  return {
5924
6004
  summary,
5925
6005
  trace: evaluation.trace,
@@ -5938,8 +6018,124 @@ var parseRecentWindowDays = (value) => {
5938
6018
  }
5939
6019
  return parsed;
5940
6020
  };
6021
+ var stripLeadingMarkdownHeading = (value, heading) => {
6022
+ const prefix = `${heading}
6023
+ `;
6024
+ if (value.startsWith(prefix)) {
6025
+ return value.slice(prefix.length).trimStart();
6026
+ }
6027
+ return value;
6028
+ };
6029
+ var extractExplainTextSummary = (text) => {
6030
+ const splitIndex = text.search(/\n\n(?:file|module|dependency|repository): /);
6031
+ if (splitIndex < 0) {
6032
+ return text.trim();
6033
+ }
6034
+ return text.slice(0, splitIndex).trim();
6035
+ };
6036
+ var extractExplainMarkdownSummary = (markdown) => {
6037
+ const splitIndex = markdown.search(/\n\n## (?:file|module|dependency|repository): /);
6038
+ if (splitIndex < 0) {
6039
+ return markdown.trim();
6040
+ }
6041
+ return markdown.slice(0, splitIndex).trim();
6042
+ };
6043
+ var extractSummaryValue = (summary, key) => {
6044
+ const prefix = `${key}: `;
6045
+ for (const rawLine of summary.split("\n")) {
6046
+ const line = rawLine.trimStart();
6047
+ if (line.startsWith(prefix)) {
6048
+ return line.slice(prefix.length).trim();
6049
+ }
6050
+ }
6051
+ return void 0;
6052
+ };
6053
+ var renderReportHighlightsText = (report) => {
6054
+ const lines = [];
6055
+ lines.push("Repository Summary");
6056
+ lines.push(` target: ${report.repository.targetPath}`);
6057
+ lines.push(` riskScore: ${report.repository.riskScore}`);
6058
+ lines.push(` normalizedScore: ${report.repository.normalizedScore}`);
6059
+ lines.push(` riskTier: ${report.repository.riskTier}`);
6060
+ lines.push("");
6061
+ lines.push("Top Hotspots");
6062
+ for (const hotspot of report.hotspots.slice(0, 5)) {
6063
+ lines.push(` - ${hotspot.target} | score=${hotspot.score}`);
6064
+ lines.push(` priority actions: ${hotspot.suggestedActions.join(" | ") || "none"}`);
6065
+ }
6066
+ return lines.join("\n");
6067
+ };
6068
+ var renderReportHighlightsMarkdown = (report) => {
6069
+ const lines = [];
6070
+ lines.push("## Repository Summary");
6071
+ lines.push(`- target: \`${report.repository.targetPath}\``);
6072
+ lines.push(`- riskScore: \`${report.repository.riskScore}\``);
6073
+ lines.push(`- normalizedScore: \`${report.repository.normalizedScore}\``);
6074
+ lines.push(`- riskTier: \`${report.repository.riskTier}\``);
6075
+ lines.push("");
6076
+ lines.push("## Top Hotspots");
6077
+ for (const hotspot of report.hotspots.slice(0, 5)) {
6078
+ lines.push(`- **${hotspot.target}** (score: \`${hotspot.score}\`)`);
6079
+ lines.push(` - priority actions: ${hotspot.suggestedActions.join(" | ") || "none"}`);
6080
+ }
6081
+ return lines.join("\n");
6082
+ };
6083
+ var renderCompactText = (report, explainSummary) => {
6084
+ const lines = [];
6085
+ lines.push("CodeSentinel Run (compact)");
6086
+ lines.push("");
6087
+ lines.push("Repository");
6088
+ lines.push(` target: ${report.repository.targetPath}`);
6089
+ lines.push(` riskScore: ${report.repository.riskScore}`);
6090
+ lines.push(` riskTier: ${report.repository.riskTier}`);
6091
+ lines.push(
6092
+ ` dimensions: structural=${report.repository.dimensionScores.structural ?? "n/a"}, evolution=${report.repository.dimensionScores.evolution ?? "n/a"}, external=${report.repository.dimensionScores.external ?? "n/a"}, interactions=${report.repository.dimensionScores.interactions ?? "n/a"}`
6093
+ );
6094
+ lines.push("");
6095
+ lines.push(
6096
+ `Key Drivers: ${extractSummaryValue(explainSummary, "key drivers") ?? "insufficient data"}`
6097
+ );
6098
+ lines.push(
6099
+ `Priority Actions: ${extractSummaryValue(explainSummary, "priority actions") ?? "insufficient data"}`
6100
+ );
6101
+ lines.push("");
6102
+ lines.push("Top Hotspots");
6103
+ for (const hotspot of report.hotspots.slice(0, 3)) {
6104
+ lines.push(` - ${hotspot.target} | score=${hotspot.score}`);
6105
+ }
6106
+ return lines.join("\n");
6107
+ };
6108
+ var renderCompactMarkdown = (report, explainSummary) => {
6109
+ const lines = [];
6110
+ lines.push("# CodeSentinel Run (compact)");
6111
+ lines.push("");
6112
+ lines.push("## Repository");
6113
+ lines.push(`- target: \`${report.repository.targetPath}\``);
6114
+ lines.push(`- riskScore: \`${report.repository.riskScore}\``);
6115
+ lines.push(`- riskTier: \`${report.repository.riskTier}\``);
6116
+ lines.push(
6117
+ `- dimensions: structural=\`${report.repository.dimensionScores.structural ?? "n/a"}\`, evolution=\`${report.repository.dimensionScores.evolution ?? "n/a"}\`, external=\`${report.repository.dimensionScores.external ?? "n/a"}\`, interactions=\`${report.repository.dimensionScores.interactions ?? "n/a"}\``
6118
+ );
6119
+ lines.push("");
6120
+ lines.push(
6121
+ `- key drivers: ${extractSummaryValue(explainSummary, "- key drivers") ?? "insufficient data"}`
6122
+ );
6123
+ lines.push(
6124
+ `- priority actions: ${extractSummaryValue(explainSummary, "- priority actions") ?? "insufficient data"}`
6125
+ );
6126
+ lines.push("");
6127
+ lines.push("## Top Hotspots");
6128
+ for (const hotspot of report.hotspots.slice(0, 3)) {
6129
+ lines.push(`- \`${hotspot.target}\` (score: \`${hotspot.score}\`)`);
6130
+ }
6131
+ return lines.join("\n");
6132
+ };
6133
+ var riskProfileOption = () => new Option(
6134
+ "--risk-profile <profile>",
6135
+ "risk profile: default (balanced) or personal (down-weights single-maintainer ownership penalties)"
6136
+ ).choices(["default", "personal"]).default("default");
5941
6137
  program.name("codesentinel").description("Structural and evolutionary risk analysis for TypeScript/JavaScript codebases").version(version);
5942
- program.command("analyze").argument("[path]", "path to the project to analyze").addOption(
6138
+ program.command("analyze").argument("[path]", "path to the project to analyze").addOption(riskProfileOption()).addOption(
5943
6139
  new Option(
5944
6140
  "--author-identity <mode>",
5945
6141
  "author identity mode: likely_merge (heuristic) or strict_email (deterministic)"
@@ -5962,7 +6158,7 @@ program.command("analyze").argument("[path]", "path to the project to analyze").
5962
6158
  const summary = await runAnalyzeCommand(
5963
6159
  path,
5964
6160
  options.authorIdentity,
5965
- { recentWindowDays: options.recentWindowDays },
6161
+ { recentWindowDays: options.recentWindowDays, riskProfile: options.riskProfile },
5966
6162
  logger
5967
6163
  );
5968
6164
  const outputMode = options.json === true ? "json" : options.output;
@@ -5970,7 +6166,7 @@ program.command("analyze").argument("[path]", "path to the project to analyze").
5970
6166
  `);
5971
6167
  }
5972
6168
  );
5973
- program.command("explain").argument("[path]", "path to the project to analyze").addOption(
6169
+ program.command("explain").argument("[path]", "path to the project to analyze").addOption(riskProfileOption()).addOption(
5974
6170
  new Option(
5975
6171
  "--author-identity <mode>",
5976
6172
  "author identity mode: likely_merge (heuristic) or strict_email (deterministic)"
@@ -5996,6 +6192,7 @@ program.command("explain").argument("[path]", "path to the project to analyze").
5996
6192
  ...options.module === void 0 ? {} : { module: options.module },
5997
6193
  top: Number.isFinite(top) ? top : 5,
5998
6194
  recentWindowDays: options.recentWindowDays,
6195
+ riskProfile: options.riskProfile,
5999
6196
  format: options.format
6000
6197
  };
6001
6198
  const result = await runExplainCommand(path, options.authorIdentity, explainOptions, logger);
@@ -6033,7 +6230,7 @@ program.command("dependency-risk").argument("<dependency>", "dependency spec to
6033
6230
  `);
6034
6231
  }
6035
6232
  );
6036
- program.command("report").argument("[path]", "path to the project to analyze").addOption(
6233
+ program.command("report").argument("[path]", "path to the project to analyze").addOption(riskProfileOption()).addOption(
6037
6234
  new Option(
6038
6235
  "--author-identity <mode>",
6039
6236
  "author identity mode: likely_merge (heuristic) or strict_email (deterministic)"
@@ -6062,6 +6259,7 @@ program.command("report").argument("[path]", "path to the project to analyze").a
6062
6259
  ...options.compare === void 0 ? {} : { comparePath: options.compare },
6063
6260
  ...options.snapshot === void 0 ? {} : { snapshotPath: options.snapshot },
6064
6261
  includeTrace: options.trace,
6262
+ riskProfile: options.riskProfile,
6065
6263
  recentWindowDays: options.recentWindowDays
6066
6264
  },
6067
6265
  logger
@@ -6072,6 +6270,219 @@ program.command("report").argument("[path]", "path to the project to analyze").a
6072
6270
  }
6073
6271
  }
6074
6272
  );
6273
+ program.command("run").argument("[path]", "path to the project to analyze").addOption(riskProfileOption()).addOption(
6274
+ new Option(
6275
+ "--author-identity <mode>",
6276
+ "author identity mode: likely_merge (heuristic) or strict_email (deterministic)"
6277
+ ).choices(["likely_merge", "strict_email"]).default("likely_merge")
6278
+ ).addOption(
6279
+ new Option(
6280
+ "--log-level <level>",
6281
+ "log verbosity: silent, error, warn, info, debug (logs are written to stderr)"
6282
+ ).choices(["silent", "error", "warn", "info", "debug"]).default(parseLogLevel(process.env["CODESENTINEL_LOG_LEVEL"]))
6283
+ ).addOption(
6284
+ new Option("--format <mode>", "combined output format: text, md, json").choices(["text", "md", "json"]).default("text")
6285
+ ).addOption(
6286
+ new Option("--detail <level>", "run detail level: compact (default), standard, full").choices(["compact", "standard", "full"]).default("compact")
6287
+ ).option("--file <path>", "explain a specific file target").option("--module <name>", "explain a specific module target").option("--top <count>", "number of top hotspots to explain when no target is selected", "5").option("--compare <baseline>", "compare against a baseline snapshot JSON file").option("--snapshot <path>", "write current snapshot JSON artifact").option("--no-trace", "disable trace embedding in generated snapshot").addOption(
6288
+ new Option(
6289
+ "--recent-window-days <days>",
6290
+ "git recency window in days used for evolution volatility metrics"
6291
+ ).argParser(parseRecentWindowDays).default(30)
6292
+ ).action(
6293
+ async (path, options) => {
6294
+ const logger = createStderrLogger(options.logLevel);
6295
+ const top = Number.parseInt(options.top, 10);
6296
+ const explain = await runExplainCommand(
6297
+ path,
6298
+ options.authorIdentity,
6299
+ {
6300
+ ...options.file === void 0 ? {} : { file: options.file },
6301
+ ...options.module === void 0 ? {} : { module: options.module },
6302
+ top: Number.isFinite(top) ? top : 5,
6303
+ format: options.format,
6304
+ recentWindowDays: options.recentWindowDays,
6305
+ riskProfile: options.riskProfile
6306
+ },
6307
+ logger
6308
+ );
6309
+ const snapshot = createSnapshot({
6310
+ analysis: explain.summary,
6311
+ ...options.trace === true ? { trace: explain.trace } : {}
6312
+ });
6313
+ if (options.snapshot !== void 0) {
6314
+ await writeFile5(options.snapshot, JSON.stringify(snapshot, null, 2), "utf8");
6315
+ logger.info(`snapshot written: ${options.snapshot}`);
6316
+ }
6317
+ const report = options.compare === void 0 ? createReport(snapshot) : createReport(
6318
+ snapshot,
6319
+ compareSnapshots(snapshot, parseSnapshot(await readFile5(options.compare, "utf8")))
6320
+ );
6321
+ if (options.format === "json") {
6322
+ const analyzeSummaryOutput = formatAnalyzeOutput(explain.summary, "summary");
6323
+ if (options.detail === "compact") {
6324
+ process.stdout.write(
6325
+ `${JSON.stringify(
6326
+ {
6327
+ schemaVersion: "codesentinel.run.v1",
6328
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6329
+ repository: report.repository,
6330
+ keyDrivers: extractSummaryValue(
6331
+ extractExplainTextSummary(formatExplainOutput(explain, "text")),
6332
+ "key drivers"
6333
+ ),
6334
+ priorityActions: extractSummaryValue(
6335
+ extractExplainTextSummary(formatExplainOutput(explain, "text")),
6336
+ "priority actions"
6337
+ ),
6338
+ topHotspots: report.hotspots.slice(0, 3).map((hotspot) => ({
6339
+ target: hotspot.target,
6340
+ score: hotspot.score
6341
+ }))
6342
+ },
6343
+ null,
6344
+ 2
6345
+ )}
6346
+ `
6347
+ );
6348
+ return;
6349
+ }
6350
+ if (options.detail === "standard") {
6351
+ const analyzeSummaryPayload = JSON.parse(analyzeSummaryOutput);
6352
+ process.stdout.write(
6353
+ `${JSON.stringify(
6354
+ {
6355
+ schemaVersion: "codesentinel.run.v1",
6356
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6357
+ analyze: analyzeSummaryPayload,
6358
+ explain: {
6359
+ summary: extractExplainTextSummary(formatExplainOutput(explain, "text"))
6360
+ },
6361
+ report: {
6362
+ repository: report.repository,
6363
+ hotspots: report.hotspots.slice(0, 5),
6364
+ structural: report.structural,
6365
+ external: report.external
6366
+ }
6367
+ },
6368
+ null,
6369
+ 2
6370
+ )}
6371
+ `
6372
+ );
6373
+ return;
6374
+ }
6375
+ process.stdout.write(
6376
+ `${JSON.stringify(
6377
+ {
6378
+ schemaVersion: "codesentinel.run.v1",
6379
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6380
+ analyze: explain.summary,
6381
+ explain,
6382
+ report
6383
+ },
6384
+ null,
6385
+ 2
6386
+ )}
6387
+ `
6388
+ );
6389
+ return;
6390
+ }
6391
+ const analyzeRendered = formatAnalyzeOutput(explain.summary, "summary");
6392
+ const explainRendered = formatExplainOutput(explain, options.format);
6393
+ if (options.detail === "compact") {
6394
+ if (options.format === "md") {
6395
+ process.stdout.write(
6396
+ `${renderCompactMarkdown(
6397
+ report,
6398
+ extractExplainMarkdownSummary(formatExplainOutput(explain, "md"))
6399
+ )}
6400
+ `
6401
+ );
6402
+ return;
6403
+ }
6404
+ process.stdout.write(
6405
+ `${renderCompactText(report, extractExplainTextSummary(formatExplainOutput(explain, "text")))}
6406
+ `
6407
+ );
6408
+ return;
6409
+ }
6410
+ if (options.detail === "standard") {
6411
+ if (options.format === "md") {
6412
+ process.stdout.write(
6413
+ `# CodeSentinel Run
6414
+
6415
+ ## Analyze
6416
+ \`\`\`json
6417
+ ${analyzeRendered}
6418
+ \`\`\`
6419
+
6420
+ ## Explain
6421
+ ${extractExplainMarkdownSummary(
6422
+ formatExplainOutput(explain, "md")
6423
+ )}
6424
+
6425
+ ${renderReportHighlightsMarkdown(report)}
6426
+ `
6427
+ );
6428
+ return;
6429
+ }
6430
+ process.stdout.write(
6431
+ `CodeSentinel Run
6432
+
6433
+ Analyze
6434
+ ${analyzeRendered}
6435
+
6436
+ Explain
6437
+ ${extractExplainTextSummary(
6438
+ formatExplainOutput(explain, "text")
6439
+ )}
6440
+
6441
+ Report
6442
+ ${renderReportHighlightsText(report)}
6443
+ `
6444
+ );
6445
+ return;
6446
+ }
6447
+ const reportRendered = formatReport(report, options.format);
6448
+ if (options.format === "md") {
6449
+ const explainSection = stripLeadingMarkdownHeading(
6450
+ explainRendered,
6451
+ "# CodeSentinel Explanation"
6452
+ );
6453
+ const reportSection = stripLeadingMarkdownHeading(reportRendered, "# CodeSentinel Report");
6454
+ process.stdout.write(
6455
+ `# CodeSentinel Run
6456
+
6457
+ ## Analyze
6458
+ \`\`\`json
6459
+ ${analyzeRendered}
6460
+ \`\`\`
6461
+
6462
+ ## Explain
6463
+ ${explainSection}
6464
+
6465
+ ## Report
6466
+ ${reportSection}
6467
+ `
6468
+ );
6469
+ return;
6470
+ }
6471
+ process.stdout.write(
6472
+ `CodeSentinel Run
6473
+
6474
+ Analyze
6475
+ ${analyzeRendered}
6476
+
6477
+ Explain
6478
+ ${explainRendered}
6479
+
6480
+ Report
6481
+ ${reportRendered}
6482
+ `
6483
+ );
6484
+ }
6485
+ );
6075
6486
  var parseGateNumber = (value, optionName) => {
6076
6487
  if (value === void 0) {
6077
6488
  return void 0;
@@ -6113,7 +6524,7 @@ var buildGateConfigFromOptions = (options) => {
6113
6524
  failOn: options.failOn
6114
6525
  };
6115
6526
  };
6116
- program.command("check").argument("[path]", "path to the project to analyze").addOption(
6527
+ program.command("check").argument("[path]", "path to the project to analyze").addOption(riskProfileOption()).addOption(
6117
6528
  new Option(
6118
6529
  "--author-identity <mode>",
6119
6530
  "author identity mode: likely_merge (heuristic) or strict_email (deterministic)"
@@ -6143,6 +6554,7 @@ program.command("check").argument("[path]", "path to the project to analyze").ad
6143
6554
  {
6144
6555
  ...options.compare === void 0 ? {} : { baselinePath: options.compare },
6145
6556
  includeTrace: options.trace,
6557
+ riskProfile: options.riskProfile,
6146
6558
  recentWindowDays: options.recentWindowDays,
6147
6559
  gateConfig,
6148
6560
  outputFormat: options.format,
@@ -6166,7 +6578,7 @@ program.command("check").argument("[path]", "path to the project to analyze").ad
6166
6578
  }
6167
6579
  }
6168
6580
  );
6169
- program.command("ci").argument("[path]", "path to the project to analyze").addOption(
6581
+ program.command("ci").argument("[path]", "path to the project to analyze").addOption(riskProfileOption()).addOption(
6170
6582
  new Option(
6171
6583
  "--author-identity <mode>",
6172
6584
  "author identity mode: likely_merge (heuristic) or strict_email (deterministic)"
@@ -6215,6 +6627,7 @@ program.command("ci").argument("[path]", "path to the project to analyze").addOp
6215
6627
  ...options.report === void 0 ? {} : { reportPath: options.report },
6216
6628
  ...options.jsonOutput === void 0 ? {} : { jsonOutputPath: options.jsonOutput },
6217
6629
  includeTrace: options.trace,
6630
+ riskProfile: options.riskProfile,
6218
6631
  recentWindowDays: options.recentWindowDays,
6219
6632
  gateConfig
6220
6633
  },