@getcodesentinel/codesentinel 1.14.0 → 1.15.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/README.md +20 -1
- package/dist/index.js +420 -102
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1656,6 +1656,7 @@ var createReport = (snapshot, diff) => {
|
|
|
1656
1656
|
confidence: repositoryConfidence(snapshot),
|
|
1657
1657
|
dimensionScores: repositoryDimensionScores(snapshot)
|
|
1658
1658
|
},
|
|
1659
|
+
quality: snapshot.analysis.quality,
|
|
1659
1660
|
hotspots: hotspotItems(snapshot),
|
|
1660
1661
|
structural: {
|
|
1661
1662
|
cycleCount: snapshot.analysis.structural.metrics.cycleCount,
|
|
@@ -1727,6 +1728,22 @@ var renderTextReport = (report) => {
|
|
|
1727
1728
|
lines.push(` external: ${report.repository.dimensionScores.external ?? "n/a"}`);
|
|
1728
1729
|
lines.push(` interactions: ${report.repository.dimensionScores.interactions ?? "n/a"}`);
|
|
1729
1730
|
lines.push("");
|
|
1731
|
+
lines.push("Quality Summary");
|
|
1732
|
+
lines.push(` qualityScore: ${report.quality.qualityScore}`);
|
|
1733
|
+
lines.push(` normalizedScore: ${report.quality.normalizedScore}`);
|
|
1734
|
+
lines.push(` modularity: ${report.quality.dimensions.modularity}`);
|
|
1735
|
+
lines.push(` changeHygiene: ${report.quality.dimensions.changeHygiene}`);
|
|
1736
|
+
lines.push(` testHealth: ${report.quality.dimensions.testHealth}`);
|
|
1737
|
+
lines.push(" topIssues:");
|
|
1738
|
+
for (const issue of report.quality.topIssues.slice(0, 5)) {
|
|
1739
|
+
lines.push(
|
|
1740
|
+
` - [${issue.severity}] (${issue.dimension}) ${issue.id} @ ${issue.target}: ${issue.message}`
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
if (report.quality.topIssues.length === 0) {
|
|
1744
|
+
lines.push(" - none");
|
|
1745
|
+
}
|
|
1746
|
+
lines.push("");
|
|
1730
1747
|
lines.push("Top Hotspots");
|
|
1731
1748
|
for (const hotspot of report.hotspots) {
|
|
1732
1749
|
lines.push(` - ${hotspot.target} | score=${hotspot.score}`);
|
|
@@ -1802,6 +1819,23 @@ var renderMarkdownReport = (report) => {
|
|
|
1802
1819
|
lines.push(`- external: \`${report.repository.dimensionScores.external ?? "n/a"}\``);
|
|
1803
1820
|
lines.push(`- interactions: \`${report.repository.dimensionScores.interactions ?? "n/a"}\``);
|
|
1804
1821
|
lines.push("");
|
|
1822
|
+
lines.push("## Quality Summary");
|
|
1823
|
+
lines.push(`- qualityScore: \`${report.quality.qualityScore}\``);
|
|
1824
|
+
lines.push(`- normalizedScore: \`${report.quality.normalizedScore}\``);
|
|
1825
|
+
lines.push(`- modularity: \`${report.quality.dimensions.modularity}\``);
|
|
1826
|
+
lines.push(`- changeHygiene: \`${report.quality.dimensions.changeHygiene}\``);
|
|
1827
|
+
lines.push(`- testHealth: \`${report.quality.dimensions.testHealth}\``);
|
|
1828
|
+
if (report.quality.topIssues.length === 0) {
|
|
1829
|
+
lines.push("- top issues: none");
|
|
1830
|
+
} else {
|
|
1831
|
+
lines.push("- top issues:");
|
|
1832
|
+
for (const issue of report.quality.topIssues.slice(0, 5)) {
|
|
1833
|
+
lines.push(
|
|
1834
|
+
` - [${issue.severity}] \`${issue.id}\` (\`${issue.dimension}\`) @ \`${issue.target}\`: ${issue.message}`
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
lines.push("");
|
|
1805
1839
|
lines.push("## Top Hotspots");
|
|
1806
1840
|
for (const hotspot of report.hotspots) {
|
|
1807
1841
|
lines.push(`- **${hotspot.target}** (score: \`${hotspot.score}\`)`);
|
|
@@ -2459,7 +2493,7 @@ var resolveAutoBaselineRef = async (input) => {
|
|
|
2459
2493
|
|
|
2460
2494
|
// src/index.ts
|
|
2461
2495
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2462
|
-
import { readFile as
|
|
2496
|
+
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
2463
2497
|
import { dirname as dirname2, resolve as resolve5 } from "path";
|
|
2464
2498
|
import { fileURLToPath } from "url";
|
|
2465
2499
|
|
|
@@ -2500,6 +2534,12 @@ var createSummaryShape = (summary) => ({
|
|
|
2500
2534
|
})),
|
|
2501
2535
|
fragileClusterCount: summary.risk.fragileClusters.length,
|
|
2502
2536
|
dependencyAmplificationZoneCount: summary.risk.dependencyAmplificationZones.length
|
|
2537
|
+
},
|
|
2538
|
+
quality: {
|
|
2539
|
+
qualityScore: summary.quality.qualityScore,
|
|
2540
|
+
normalizedScore: summary.quality.normalizedScore,
|
|
2541
|
+
dimensions: summary.quality.dimensions,
|
|
2542
|
+
topIssues: summary.quality.topIssues.slice(0, 5)
|
|
2503
2543
|
}
|
|
2504
2544
|
});
|
|
2505
2545
|
var formatAnalyzeOutput = (summary, mode) => mode === "json" ? JSON.stringify(summary, null, 2) : JSON.stringify(createSummaryShape(summary), null, 2);
|
|
@@ -3176,7 +3216,8 @@ var checkForCliUpdates = async (input) => {
|
|
|
3176
3216
|
};
|
|
3177
3217
|
|
|
3178
3218
|
// src/application/run-analyze-command.ts
|
|
3179
|
-
import {
|
|
3219
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
3220
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
3180
3221
|
|
|
3181
3222
|
// ../code-graph/dist/index.js
|
|
3182
3223
|
import { extname, isAbsolute, relative, resolve as resolve2 } from "path";
|
|
@@ -4224,6 +4265,208 @@ var analyzeRepositoryEvolutionFromGit = (input, onProgress) => {
|
|
|
4224
4265
|
return analyzeRepositoryEvolution(input, historyProvider, onProgress);
|
|
4225
4266
|
};
|
|
4226
4267
|
|
|
4268
|
+
// ../quality-engine/dist/index.js
|
|
4269
|
+
var clamp01 = (value) => {
|
|
4270
|
+
if (!Number.isFinite(value)) {
|
|
4271
|
+
return 0;
|
|
4272
|
+
}
|
|
4273
|
+
if (value <= 0) {
|
|
4274
|
+
return 0;
|
|
4275
|
+
}
|
|
4276
|
+
if (value >= 1) {
|
|
4277
|
+
return 1;
|
|
4278
|
+
}
|
|
4279
|
+
return value;
|
|
4280
|
+
};
|
|
4281
|
+
var round45 = (value) => Number(value.toFixed(4));
|
|
4282
|
+
var average = (values) => {
|
|
4283
|
+
if (values.length === 0) {
|
|
4284
|
+
return 0;
|
|
4285
|
+
}
|
|
4286
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
4287
|
+
return total / values.length;
|
|
4288
|
+
};
|
|
4289
|
+
var concentration = (rawValues) => {
|
|
4290
|
+
const values = rawValues.filter((value) => value > 0);
|
|
4291
|
+
const count = values.length;
|
|
4292
|
+
if (count <= 1) {
|
|
4293
|
+
return 0;
|
|
4294
|
+
}
|
|
4295
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
4296
|
+
if (total <= 0) {
|
|
4297
|
+
return 0;
|
|
4298
|
+
}
|
|
4299
|
+
const hhi = values.reduce((sum, value) => {
|
|
4300
|
+
const share = value / total;
|
|
4301
|
+
return sum + share * share;
|
|
4302
|
+
}, 0);
|
|
4303
|
+
const minHhi = 1 / count;
|
|
4304
|
+
const normalized = (hhi - minHhi) / (1 - minHhi);
|
|
4305
|
+
return clamp01(normalized);
|
|
4306
|
+
};
|
|
4307
|
+
var DIMENSION_WEIGHTS = {
|
|
4308
|
+
modularity: 0.45,
|
|
4309
|
+
changeHygiene: 0.35,
|
|
4310
|
+
testHealth: 0.2
|
|
4311
|
+
};
|
|
4312
|
+
var TODO_FIXME_MAX_IMPACT = 0.08;
|
|
4313
|
+
var toPercentage = (normalizedQuality) => round45(clamp01(normalizedQuality) * 100);
|
|
4314
|
+
var filePaths = (structural) => structural.files.map((file) => file.relativePath);
|
|
4315
|
+
var isTestPath = (path) => {
|
|
4316
|
+
const normalized = path.toLowerCase();
|
|
4317
|
+
return normalized.includes("/__tests__/") || normalized.includes("\\__tests__\\") || normalized.includes(".test.") || normalized.includes(".spec.");
|
|
4318
|
+
};
|
|
4319
|
+
var isSourcePath = (path) => {
|
|
4320
|
+
if (path.endsWith(".d.ts")) {
|
|
4321
|
+
return false;
|
|
4322
|
+
}
|
|
4323
|
+
return !isTestPath(path);
|
|
4324
|
+
};
|
|
4325
|
+
var pushIssue = (issues, issue) => {
|
|
4326
|
+
issues.push({
|
|
4327
|
+
...issue,
|
|
4328
|
+
severity: issue.severity ?? "warn"
|
|
4329
|
+
});
|
|
4330
|
+
};
|
|
4331
|
+
var computeRepositoryQualitySummary = (input) => {
|
|
4332
|
+
const issues = [];
|
|
4333
|
+
const sourceFileSet = new Set(input.structural.files.map((file) => file.relativePath));
|
|
4334
|
+
const cycleCount = input.structural.metrics.cycleCount;
|
|
4335
|
+
const cycleSizeAverage = input.structural.cycles.length === 0 ? 0 : average(input.structural.cycles.map((cycle) => cycle.nodes.length));
|
|
4336
|
+
const cyclePenalty = clamp01(cycleCount / 6) * 0.7 + clamp01((cycleSizeAverage - 2) / 8) * 0.3;
|
|
4337
|
+
if (cycleCount > 0) {
|
|
4338
|
+
pushIssue(issues, {
|
|
4339
|
+
id: "quality.modularity.structural_cycles",
|
|
4340
|
+
dimension: "modularity",
|
|
4341
|
+
target: input.structural.cycles[0]?.nodes.slice().sort((a, b) => a.localeCompare(b)).join(" -> ") ?? input.structural.targetPath,
|
|
4342
|
+
message: `${cycleCount} structural cycle(s) increase coupling and refactor cost.`,
|
|
4343
|
+
severity: cycleCount >= 3 ? "error" : "warn",
|
|
4344
|
+
impact: round45(cyclePenalty * 0.6)
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
const fanInConcentration = concentration(input.structural.files.map((file) => file.fanIn));
|
|
4348
|
+
const fanOutConcentration = concentration(input.structural.files.map((file) => file.fanOut));
|
|
4349
|
+
const centralityConcentration = average([fanInConcentration, fanOutConcentration]);
|
|
4350
|
+
if (centralityConcentration >= 0.5) {
|
|
4351
|
+
const hottest = [...input.structural.files].map((file) => ({
|
|
4352
|
+
path: file.relativePath,
|
|
4353
|
+
pressure: file.fanIn + file.fanOut
|
|
4354
|
+
})).sort((a, b) => b.pressure - a.pressure || a.path.localeCompare(b.path))[0];
|
|
4355
|
+
pushIssue(issues, {
|
|
4356
|
+
id: "quality.modularity.centrality_concentration",
|
|
4357
|
+
dimension: "modularity",
|
|
4358
|
+
target: hottest?.path ?? input.structural.targetPath,
|
|
4359
|
+
message: "Fan-in/fan-out pressure is concentrated in a small set of files.",
|
|
4360
|
+
impact: round45(centralityConcentration * 0.5)
|
|
4361
|
+
});
|
|
4362
|
+
}
|
|
4363
|
+
let churnConcentration = 0;
|
|
4364
|
+
let volatilityConcentration = 0;
|
|
4365
|
+
let couplingDensity = 0;
|
|
4366
|
+
let couplingIntensity = 0;
|
|
4367
|
+
if (input.evolution.available) {
|
|
4368
|
+
const evolutionSourceFiles = input.evolution.files.filter(
|
|
4369
|
+
(file) => sourceFileSet.has(file.filePath)
|
|
4370
|
+
);
|
|
4371
|
+
churnConcentration = concentration(evolutionSourceFiles.map((file) => file.churnTotal));
|
|
4372
|
+
volatilityConcentration = concentration(
|
|
4373
|
+
evolutionSourceFiles.map((file) => file.recentVolatility)
|
|
4374
|
+
);
|
|
4375
|
+
const fileCount = Math.max(1, evolutionSourceFiles.length);
|
|
4376
|
+
const maxPairs = fileCount * (fileCount - 1) / 2;
|
|
4377
|
+
const sourcePairs = input.evolution.coupling.pairs.filter(
|
|
4378
|
+
(pair) => sourceFileSet.has(pair.fileA) && sourceFileSet.has(pair.fileB)
|
|
4379
|
+
);
|
|
4380
|
+
couplingDensity = maxPairs <= 0 ? 0 : clamp01(sourcePairs.length / maxPairs);
|
|
4381
|
+
couplingIntensity = average(sourcePairs.map((pair) => pair.couplingScore));
|
|
4382
|
+
if (churnConcentration >= 0.45) {
|
|
4383
|
+
const mostChurn = [...evolutionSourceFiles].sort(
|
|
4384
|
+
(a, b) => b.churnTotal - a.churnTotal || a.filePath.localeCompare(b.filePath)
|
|
4385
|
+
)[0];
|
|
4386
|
+
pushIssue(issues, {
|
|
4387
|
+
id: "quality.change_hygiene.churn_concentration",
|
|
4388
|
+
dimension: "changeHygiene",
|
|
4389
|
+
target: mostChurn?.filePath ?? input.structural.targetPath,
|
|
4390
|
+
message: "Churn is concentrated in a narrow part of the codebase.",
|
|
4391
|
+
impact: round45(churnConcentration * 0.45)
|
|
4392
|
+
});
|
|
4393
|
+
}
|
|
4394
|
+
if (volatilityConcentration >= 0.45) {
|
|
4395
|
+
const volatileFile = [...evolutionSourceFiles].sort(
|
|
4396
|
+
(a, b) => b.recentVolatility - a.recentVolatility || a.filePath.localeCompare(b.filePath)
|
|
4397
|
+
)[0];
|
|
4398
|
+
pushIssue(issues, {
|
|
4399
|
+
id: "quality.change_hygiene.volatility_concentration",
|
|
4400
|
+
dimension: "changeHygiene",
|
|
4401
|
+
target: volatileFile?.filePath ?? input.structural.targetPath,
|
|
4402
|
+
message: "Recent volatility is concentrated in files that change frequently.",
|
|
4403
|
+
impact: round45(volatilityConcentration * 0.4)
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
if (couplingDensity >= 0.35 || couplingIntensity >= 0.45) {
|
|
4407
|
+
const strongestPair = [...sourcePairs].sort(
|
|
4408
|
+
(a, b) => b.couplingScore - a.couplingScore || `${a.fileA}|${a.fileB}`.localeCompare(`${b.fileA}|${b.fileB}`)
|
|
4409
|
+
)[0];
|
|
4410
|
+
pushIssue(issues, {
|
|
4411
|
+
id: "quality.change_hygiene.coupling_density",
|
|
4412
|
+
dimension: "changeHygiene",
|
|
4413
|
+
target: strongestPair === void 0 ? input.structural.targetPath : `${strongestPair.fileA}<->${strongestPair.fileB}`,
|
|
4414
|
+
message: "Co-change relationships are dense, increasing coordination overhead.",
|
|
4415
|
+
impact: round45(average([couplingDensity, couplingIntensity]) * 0.35)
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
const modularityPenalty = clamp01(cyclePenalty * 0.55 + centralityConcentration * 0.45);
|
|
4420
|
+
const changeHygienePenalty = input.evolution.available ? clamp01(
|
|
4421
|
+
churnConcentration * 0.4 + volatilityConcentration * 0.35 + couplingDensity * 0.15 + couplingIntensity * 0.1
|
|
4422
|
+
) : 0.25;
|
|
4423
|
+
const paths = filePaths(input.structural);
|
|
4424
|
+
const testFiles = paths.filter((path) => isTestPath(path)).length;
|
|
4425
|
+
const sourceFiles = paths.filter((path) => isSourcePath(path)).length;
|
|
4426
|
+
const testRatio = sourceFiles <= 0 ? 1 : testFiles / sourceFiles;
|
|
4427
|
+
const testPresencePenalty = sourceFiles <= 0 ? 0 : 1 - clamp01(testRatio / 0.3);
|
|
4428
|
+
if (sourceFiles > 0 && testRatio < 0.2) {
|
|
4429
|
+
pushIssue(issues, {
|
|
4430
|
+
id: "quality.test_health.low_test_presence",
|
|
4431
|
+
dimension: "testHealth",
|
|
4432
|
+
target: input.structural.targetPath,
|
|
4433
|
+
message: `Detected ${testFiles} test file(s) for ${sourceFiles} source file(s).`,
|
|
4434
|
+
severity: testRatio === 0 ? "error" : "warn",
|
|
4435
|
+
impact: round45(testPresencePenalty * 0.5)
|
|
4436
|
+
});
|
|
4437
|
+
}
|
|
4438
|
+
const todoFixmeCount = Math.max(0, input.todoFixmeCount ?? 0);
|
|
4439
|
+
const todoFixmePenalty = clamp01(todoFixmeCount / 120) * TODO_FIXME_MAX_IMPACT;
|
|
4440
|
+
if (todoFixmeCount > 0) {
|
|
4441
|
+
pushIssue(issues, {
|
|
4442
|
+
id: "quality.change_hygiene.todo_fixme_load",
|
|
4443
|
+
dimension: "changeHygiene",
|
|
4444
|
+
target: input.structural.targetPath,
|
|
4445
|
+
message: `Found ${todoFixmeCount} TODO/FIXME marker(s); cleanup debt is accumulating.`,
|
|
4446
|
+
impact: round45(todoFixmePenalty * 0.2)
|
|
4447
|
+
});
|
|
4448
|
+
}
|
|
4449
|
+
const modularityQuality = clamp01(1 - modularityPenalty);
|
|
4450
|
+
const changeHygieneQuality = clamp01(1 - clamp01(changeHygienePenalty + todoFixmePenalty));
|
|
4451
|
+
const testHealthQuality = clamp01(1 - testPresencePenalty);
|
|
4452
|
+
const normalizedScore = clamp01(
|
|
4453
|
+
modularityQuality * DIMENSION_WEIGHTS.modularity + changeHygieneQuality * DIMENSION_WEIGHTS.changeHygiene + testHealthQuality * DIMENSION_WEIGHTS.testHealth
|
|
4454
|
+
);
|
|
4455
|
+
const topIssues = [...issues].sort(
|
|
4456
|
+
(a, b) => b.impact - a.impact || a.id.localeCompare(b.id) || a.target.localeCompare(b.target)
|
|
4457
|
+
).slice(0, 8).map(({ impact: _impact, ...issue }) => issue);
|
|
4458
|
+
return {
|
|
4459
|
+
qualityScore: toPercentage(normalizedScore),
|
|
4460
|
+
normalizedScore: round45(normalizedScore),
|
|
4461
|
+
dimensions: {
|
|
4462
|
+
modularity: toPercentage(modularityQuality),
|
|
4463
|
+
changeHygiene: toPercentage(changeHygieneQuality),
|
|
4464
|
+
testHealth: toPercentage(testHealthQuality)
|
|
4465
|
+
},
|
|
4466
|
+
topIssues
|
|
4467
|
+
};
|
|
4468
|
+
};
|
|
4469
|
+
|
|
4227
4470
|
// ../risk-engine/dist/index.js
|
|
4228
4471
|
var DEFAULT_RISK_ENGINE_CONFIG = {
|
|
4229
4472
|
// Base dimensional influence. Risk is never dominated by a single dimension by default.
|
|
@@ -4309,8 +4552,8 @@ var DEFAULT_RISK_ENGINE_CONFIG = {
|
|
|
4309
4552
|
}
|
|
4310
4553
|
};
|
|
4311
4554
|
var toUnitInterval = (value) => Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0;
|
|
4312
|
-
var
|
|
4313
|
-
var
|
|
4555
|
+
var round46 = (value) => Number(value.toFixed(4));
|
|
4556
|
+
var average2 = (values) => {
|
|
4314
4557
|
if (values.length === 0) {
|
|
4315
4558
|
return 0;
|
|
4316
4559
|
}
|
|
@@ -4418,14 +4661,14 @@ var computeAggregatorAttenuation = (input) => {
|
|
|
4418
4661
|
const fanOutSignal = toUnitInterval((fanOut - config.minFanOut) / Math.max(1, config.minFanOut));
|
|
4419
4662
|
const lowChurnPerCommitSignal = 1 - toUnitInterval(churnPerCommit / config.maxChurnPerCommit);
|
|
4420
4663
|
const lowChurnPerDependencySignal = 1 - toUnitInterval(churnPerDependency / config.maxChurnPerDependency);
|
|
4421
|
-
const attenuationConfidence =
|
|
4664
|
+
const attenuationConfidence = average2([
|
|
4422
4665
|
fanInSignal,
|
|
4423
4666
|
fanOutSignal,
|
|
4424
4667
|
lowChurnPerCommitSignal,
|
|
4425
4668
|
lowChurnPerDependencySignal
|
|
4426
4669
|
]);
|
|
4427
4670
|
const reduction = toUnitInterval(config.maxStructuralReduction) * attenuationConfidence;
|
|
4428
|
-
return
|
|
4671
|
+
return round46(toUnitInterval(1 - reduction));
|
|
4429
4672
|
};
|
|
4430
4673
|
var dependencySignalWeights = {
|
|
4431
4674
|
single_maintainer: 0.3,
|
|
@@ -4455,12 +4698,12 @@ var computeDependencySignalScore = (ownSignals, inheritedSignals, inheritedSigna
|
|
|
4455
4698
|
}
|
|
4456
4699
|
return toUnitInterval(weightedTotal / maxWeightedTotal);
|
|
4457
4700
|
};
|
|
4458
|
-
var clampConfidence = (value) =>
|
|
4701
|
+
var clampConfidence = (value) => round46(toUnitInterval(value));
|
|
4459
4702
|
var computeExternalMetadataConfidence = (external) => {
|
|
4460
4703
|
if (!external.available) {
|
|
4461
4704
|
return 0;
|
|
4462
4705
|
}
|
|
4463
|
-
return
|
|
4706
|
+
return round46(toUnitInterval(0.35 + external.metrics.metadataCoverage * 0.65));
|
|
4464
4707
|
};
|
|
4465
4708
|
var computeEvolutionHistoryConfidence = (structural, evolution, evolutionByFile) => {
|
|
4466
4709
|
if (!evolution.available) {
|
|
@@ -4477,7 +4720,7 @@ var computeEvolutionHistoryConfidence = (structural, evolution, evolutionByFile)
|
|
|
4477
4720
|
}
|
|
4478
4721
|
}
|
|
4479
4722
|
const coverage = coveredFiles / totalFiles;
|
|
4480
|
-
return
|
|
4723
|
+
return round46(toUnitInterval(0.3 + coverage * 0.7));
|
|
4481
4724
|
};
|
|
4482
4725
|
var buildFactorTraces = (totalScore, inputs) => {
|
|
4483
4726
|
const positiveInputs = inputs.filter((input) => input.strength > 0);
|
|
@@ -4515,7 +4758,7 @@ var buildFactorTraces = (totalScore, inputs) => {
|
|
|
4515
4758
|
continue;
|
|
4516
4759
|
}
|
|
4517
4760
|
if (index === scored.length - 1) {
|
|
4518
|
-
const remaining =
|
|
4761
|
+
const remaining = round46(totalScore - distributed);
|
|
4519
4762
|
traces[traceIndex] = {
|
|
4520
4763
|
...existing,
|
|
4521
4764
|
contribution: Math.max(0, remaining)
|
|
@@ -4523,7 +4766,7 @@ var buildFactorTraces = (totalScore, inputs) => {
|
|
|
4523
4766
|
distributed += Math.max(0, remaining);
|
|
4524
4767
|
continue;
|
|
4525
4768
|
}
|
|
4526
|
-
const rounded =
|
|
4769
|
+
const rounded = round46(current.contribution);
|
|
4527
4770
|
traces[traceIndex] = {
|
|
4528
4771
|
...existing,
|
|
4529
4772
|
contribution: rounded
|
|
@@ -4534,15 +4777,15 @@ var buildFactorTraces = (totalScore, inputs) => {
|
|
|
4534
4777
|
};
|
|
4535
4778
|
var buildReductionLevers = (factors) => factors.filter((factor) => factor.contribution > 0).sort((a, b) => b.contribution - a.contribution || a.factorId.localeCompare(b.factorId)).slice(0, 3).map((factor) => ({
|
|
4536
4779
|
factorId: factor.factorId,
|
|
4537
|
-
estimatedImpact:
|
|
4780
|
+
estimatedImpact: round46(factor.contribution)
|
|
4538
4781
|
}));
|
|
4539
4782
|
var buildTargetTrace = (targetType, targetId, totalScore, normalizedScore, factors) => {
|
|
4540
4783
|
const dominantFactors = [...factors].filter((factor) => factor.contribution > 0).sort((a, b) => b.contribution - a.contribution || a.factorId.localeCompare(b.factorId)).slice(0, 3).map((factor) => factor.factorId);
|
|
4541
4784
|
return {
|
|
4542
4785
|
targetType,
|
|
4543
4786
|
targetId,
|
|
4544
|
-
totalScore:
|
|
4545
|
-
normalizedScore:
|
|
4787
|
+
totalScore: round46(totalScore),
|
|
4788
|
+
normalizedScore: round46(normalizedScore),
|
|
4546
4789
|
factors,
|
|
4547
4790
|
dominantFactors,
|
|
4548
4791
|
reductionLevers: buildReductionLevers(factors)
|
|
@@ -4616,14 +4859,14 @@ var computeDependencyScores = (external, config) => {
|
|
|
4616
4859
|
].filter((value) => value !== null).length;
|
|
4617
4860
|
const confidence = toUnitInterval((0.5 + availableMetricCount * 0.125) * metadataConfidence);
|
|
4618
4861
|
dependencyContexts.set(dependency.name, {
|
|
4619
|
-
signalScore:
|
|
4620
|
-
stalenessRisk:
|
|
4621
|
-
maintainerConcentrationRisk:
|
|
4622
|
-
transitiveBurdenRisk:
|
|
4623
|
-
centralityRisk:
|
|
4624
|
-
chainDepthRisk:
|
|
4625
|
-
busFactorRisk:
|
|
4626
|
-
popularityDampener:
|
|
4862
|
+
signalScore: round46(signalScore),
|
|
4863
|
+
stalenessRisk: round46(stalenessRisk),
|
|
4864
|
+
maintainerConcentrationRisk: round46(maintainerConcentrationRisk),
|
|
4865
|
+
transitiveBurdenRisk: round46(transitiveBurdenRisk),
|
|
4866
|
+
centralityRisk: round46(centralityRisk),
|
|
4867
|
+
chainDepthRisk: round46(chainDepthRisk),
|
|
4868
|
+
busFactorRisk: round46(busFactorRisk),
|
|
4869
|
+
popularityDampener: round46(popularityDampener),
|
|
4627
4870
|
rawMetrics: {
|
|
4628
4871
|
daysSinceLastRelease: dependency.daysSinceLastRelease,
|
|
4629
4872
|
maintainerCount: dependency.maintainerCount,
|
|
@@ -4633,12 +4876,12 @@ var computeDependencyScores = (external, config) => {
|
|
|
4633
4876
|
busFactor: dependency.busFactor,
|
|
4634
4877
|
weeklyDownloads: dependency.weeklyDownloads
|
|
4635
4878
|
},
|
|
4636
|
-
confidence:
|
|
4879
|
+
confidence: round46(confidence)
|
|
4637
4880
|
});
|
|
4638
4881
|
return {
|
|
4639
4882
|
dependency: dependency.name,
|
|
4640
|
-
score:
|
|
4641
|
-
normalizedScore:
|
|
4883
|
+
score: round46(normalizedScore * 100),
|
|
4884
|
+
normalizedScore: round46(normalizedScore),
|
|
4642
4885
|
ownRiskSignals: dependency.ownRiskSignals,
|
|
4643
4886
|
inheritedRiskSignals: dependency.inheritedRiskSignals
|
|
4644
4887
|
};
|
|
@@ -4647,7 +4890,7 @@ var computeDependencyScores = (external, config) => {
|
|
|
4647
4890
|
);
|
|
4648
4891
|
const normalizedValues = dependencyScores.map((score) => score.normalizedScore);
|
|
4649
4892
|
const highDependencyRisk = dependencyScores.length === 0 ? 0 : percentile(normalizedValues, config.externalDimension.topDependencyPercentile);
|
|
4650
|
-
const averageDependencyRisk =
|
|
4893
|
+
const averageDependencyRisk = average2(normalizedValues);
|
|
4651
4894
|
const depthRisk = halfLifeRisk(
|
|
4652
4895
|
external.metrics.dependencyDepth,
|
|
4653
4896
|
config.externalDimension.dependencyDepthHalfLife
|
|
@@ -4657,7 +4900,7 @@ var computeDependencyScores = (external, config) => {
|
|
|
4657
4900
|
);
|
|
4658
4901
|
return {
|
|
4659
4902
|
dependencyScores,
|
|
4660
|
-
repositoryExternalPressure:
|
|
4903
|
+
repositoryExternalPressure: round46(repositoryExternalPressure),
|
|
4661
4904
|
dependencyContexts
|
|
4662
4905
|
};
|
|
4663
4906
|
};
|
|
@@ -4718,11 +4961,11 @@ var buildFragileClusters = (structural, evolution, fileScoresByFile, config) =>
|
|
|
4718
4961
|
continue;
|
|
4719
4962
|
}
|
|
4720
4963
|
files.sort((a, b) => a.localeCompare(b));
|
|
4721
|
-
const averageRisk =
|
|
4964
|
+
const averageRisk = average2(
|
|
4722
4965
|
files.map((filePath) => fileScoresByFile.get(filePath)?.normalizedScore ?? 0)
|
|
4723
4966
|
);
|
|
4724
4967
|
const cycleSizeRisk = toUnitInterval((files.length - 1) / 5);
|
|
4725
|
-
const score =
|
|
4968
|
+
const score = round46(toUnitInterval(averageRisk * 0.75 + cycleSizeRisk * 0.25) * 100);
|
|
4726
4969
|
cycleClusterCount += 1;
|
|
4727
4970
|
clusters.push({
|
|
4728
4971
|
id: `cycle:${cycleClusterCount}`,
|
|
@@ -4792,11 +5035,11 @@ var buildFragileClusters = (structural, evolution, fileScoresByFile, config) =>
|
|
|
4792
5035
|
const componentPairs = selectedPairs.filter(
|
|
4793
5036
|
(pair) => fileSet.has(pair.fileA) && fileSet.has(pair.fileB)
|
|
4794
5037
|
);
|
|
4795
|
-
const meanFileRisk =
|
|
5038
|
+
const meanFileRisk = average2(
|
|
4796
5039
|
files.map((filePath) => fileScoresByFile.get(filePath)?.normalizedScore ?? 0)
|
|
4797
5040
|
);
|
|
4798
|
-
const meanCoupling =
|
|
4799
|
-
const score =
|
|
5041
|
+
const meanCoupling = average2(componentPairs.map((pair) => pair.couplingScore));
|
|
5042
|
+
const score = round46(toUnitInterval(meanFileRisk * 0.65 + meanCoupling * 0.35) * 100);
|
|
4800
5043
|
couplingClusterCount += 1;
|
|
4801
5044
|
clusters.push({
|
|
4802
5045
|
id: `coupling:${couplingClusterCount}`,
|
|
@@ -4907,21 +5150,21 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4907
5150
|
const normalizedScore = saturatingComposite(baseline, interactions);
|
|
4908
5151
|
return {
|
|
4909
5152
|
file: filePath,
|
|
4910
|
-
score:
|
|
4911
|
-
normalizedScore:
|
|
5153
|
+
score: round46(normalizedScore * 100),
|
|
5154
|
+
normalizedScore: round46(normalizedScore),
|
|
4912
5155
|
factors: {
|
|
4913
|
-
structural:
|
|
4914
|
-
evolution:
|
|
4915
|
-
external:
|
|
5156
|
+
structural: round46(structuralFactor),
|
|
5157
|
+
evolution: round46(evolutionFactor),
|
|
5158
|
+
external: round46(externalFactor)
|
|
4916
5159
|
},
|
|
4917
|
-
structuralCentrality:
|
|
5160
|
+
structuralCentrality: round46(structuralCentrality),
|
|
4918
5161
|
traceTerms: {
|
|
4919
|
-
structuralBase:
|
|
4920
|
-
evolutionBase:
|
|
4921
|
-
externalBase:
|
|
4922
|
-
interactionStructuralEvolution:
|
|
4923
|
-
interactionCentralInstability:
|
|
4924
|
-
interactionDependencyAmplification:
|
|
5162
|
+
structuralBase: round46(structuralBase),
|
|
5163
|
+
evolutionBase: round46(evolutionBase),
|
|
5164
|
+
externalBase: round46(externalBase),
|
|
5165
|
+
interactionStructuralEvolution: round46(interactionStructuralEvolution),
|
|
5166
|
+
interactionCentralInstability: round46(interactionCentralInstability),
|
|
5167
|
+
interactionDependencyAmplification: round46(interactionDependencyAmplification)
|
|
4925
5168
|
},
|
|
4926
5169
|
rawMetrics: {
|
|
4927
5170
|
fanIn: file.fanIn,
|
|
@@ -4933,19 +5176,19 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4933
5176
|
recentVolatility: evolutionMetrics?.recentVolatility ?? null,
|
|
4934
5177
|
topAuthorShare: evolutionMetrics?.topAuthorShare ?? null,
|
|
4935
5178
|
busFactor: evolutionMetrics?.busFactor ?? null,
|
|
4936
|
-
dependencyAffinity:
|
|
4937
|
-
repositoryExternalPressure:
|
|
4938
|
-
structuralAttenuation:
|
|
5179
|
+
dependencyAffinity: round46(dependencyAffinity),
|
|
5180
|
+
repositoryExternalPressure: round46(dependencyComputation.repositoryExternalPressure),
|
|
5181
|
+
structuralAttenuation: round46(structuralAttenuation)
|
|
4939
5182
|
},
|
|
4940
5183
|
normalizedMetrics: {
|
|
4941
|
-
fanInRisk:
|
|
4942
|
-
fanOutRisk:
|
|
4943
|
-
depthRisk:
|
|
4944
|
-
frequencyRisk:
|
|
4945
|
-
churnRisk:
|
|
4946
|
-
volatilityRisk:
|
|
4947
|
-
ownershipConcentrationRisk:
|
|
4948
|
-
busFactorRisk:
|
|
5184
|
+
fanInRisk: round46(fanInRisk),
|
|
5185
|
+
fanOutRisk: round46(fanOutRisk),
|
|
5186
|
+
depthRisk: round46(depthRisk),
|
|
5187
|
+
frequencyRisk: round46(frequencyRisk),
|
|
5188
|
+
churnRisk: round46(churnRisk),
|
|
5189
|
+
volatilityRisk: round46(volatilityRisk),
|
|
5190
|
+
ownershipConcentrationRisk: round46(ownershipConcentrationRisk),
|
|
5191
|
+
busFactorRisk: round46(busFactorRisk)
|
|
4949
5192
|
}
|
|
4950
5193
|
};
|
|
4951
5194
|
}).sort((a, b) => b.score - a.score || a.file.localeCompare(b.file));
|
|
@@ -5071,29 +5314,29 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5071
5314
|
moduleFiles.set(moduleName, values);
|
|
5072
5315
|
}
|
|
5073
5316
|
const moduleScores = [...moduleFiles.entries()].map(([module, values]) => {
|
|
5074
|
-
const averageScore =
|
|
5317
|
+
const averageScore = average2(values);
|
|
5075
5318
|
const peakScore = values.reduce((max, value) => Math.max(max, value), 0);
|
|
5076
5319
|
const normalizedScore = toUnitInterval(averageScore * 0.65 + peakScore * 0.35);
|
|
5077
5320
|
return {
|
|
5078
5321
|
module,
|
|
5079
|
-
score:
|
|
5080
|
-
normalizedScore:
|
|
5322
|
+
score: round46(normalizedScore * 100),
|
|
5323
|
+
normalizedScore: round46(normalizedScore),
|
|
5081
5324
|
fileCount: values.length
|
|
5082
5325
|
};
|
|
5083
5326
|
}).sort((a, b) => b.score - a.score || a.module.localeCompare(b.module));
|
|
5084
5327
|
if (collector !== void 0) {
|
|
5085
5328
|
for (const [module, values] of moduleFiles.entries()) {
|
|
5086
|
-
const averageScore =
|
|
5329
|
+
const averageScore = average2(values);
|
|
5087
5330
|
const peakScore = values.reduce((max, value) => Math.max(max, value), 0);
|
|
5088
5331
|
const normalizedScore = toUnitInterval(averageScore * 0.65 + peakScore * 0.35);
|
|
5089
|
-
const totalScore =
|
|
5332
|
+
const totalScore = round46(normalizedScore * 100);
|
|
5090
5333
|
const factors = buildFactorTraces(totalScore, [
|
|
5091
5334
|
{
|
|
5092
5335
|
factorId: "module.average_file_risk",
|
|
5093
5336
|
family: "composite",
|
|
5094
5337
|
strength: averageScore * 0.65,
|
|
5095
|
-
rawMetrics: { averageFileRisk:
|
|
5096
|
-
normalizedMetrics: { normalizedModuleRisk:
|
|
5338
|
+
rawMetrics: { averageFileRisk: round46(averageScore), fileCount: values.length },
|
|
5339
|
+
normalizedMetrics: { normalizedModuleRisk: round46(normalizedScore) },
|
|
5097
5340
|
weight: 0.65,
|
|
5098
5341
|
amplification: null,
|
|
5099
5342
|
evidence: [{ kind: "repository_metric", metric: "moduleAggregation.average" }],
|
|
@@ -5103,8 +5346,8 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5103
5346
|
factorId: "module.peak_file_risk",
|
|
5104
5347
|
family: "composite",
|
|
5105
5348
|
strength: peakScore * 0.35,
|
|
5106
|
-
rawMetrics: { peakFileRisk:
|
|
5107
|
-
normalizedMetrics: { normalizedModuleRisk:
|
|
5349
|
+
rawMetrics: { peakFileRisk: round46(peakScore), fileCount: values.length },
|
|
5350
|
+
normalizedMetrics: { normalizedModuleRisk: round46(normalizedScore) },
|
|
5108
5351
|
weight: 0.35,
|
|
5109
5352
|
amplification: null,
|
|
5110
5353
|
evidence: [{ kind: "repository_metric", metric: "moduleAggregation.peak" }],
|
|
@@ -5127,12 +5370,12 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5127
5370
|
const normalizedZoneScore = toUnitInterval(intensity * 0.7 + fileScore.normalizedScore * 0.3);
|
|
5128
5371
|
return {
|
|
5129
5372
|
file: fileScore.file,
|
|
5130
|
-
score:
|
|
5373
|
+
score: round46(normalizedZoneScore * 100),
|
|
5131
5374
|
externalPressure: fileScore.factors.external
|
|
5132
5375
|
};
|
|
5133
5376
|
}).filter((zone) => external.available && zone.externalPressure >= pressureThreshold).sort((a, b) => b.score - a.score || a.file.localeCompare(b.file)).slice(0, config.amplificationZone.maxZones).map((zone) => ({
|
|
5134
5377
|
...zone,
|
|
5135
|
-
externalPressure:
|
|
5378
|
+
externalPressure: round46(zone.externalPressure)
|
|
5136
5379
|
}));
|
|
5137
5380
|
if (collector !== void 0 && external.available) {
|
|
5138
5381
|
const dependencyByName = new Map(
|
|
@@ -5245,16 +5488,16 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5245
5488
|
);
|
|
5246
5489
|
}
|
|
5247
5490
|
}
|
|
5248
|
-
const structuralDimension =
|
|
5249
|
-
const evolutionDimension =
|
|
5491
|
+
const structuralDimension = average2(fileScores.map((fileScore) => fileScore.factors.structural));
|
|
5492
|
+
const evolutionDimension = average2(fileScores.map((fileScore) => fileScore.factors.evolution));
|
|
5250
5493
|
const externalDimension = dependencyComputation.repositoryExternalPressure;
|
|
5251
5494
|
const topCentralSlice = Math.max(1, Math.ceil(fileRiskContexts.length * 0.1));
|
|
5252
|
-
const criticalInstability =
|
|
5495
|
+
const criticalInstability = average2(
|
|
5253
5496
|
[...fileRiskContexts].sort(
|
|
5254
5497
|
(a, b) => b.structuralCentrality * b.factors.evolution - a.structuralCentrality * a.factors.evolution || a.file.localeCompare(b.file)
|
|
5255
5498
|
).slice(0, topCentralSlice).map((context) => context.structuralCentrality * context.factors.evolution)
|
|
5256
5499
|
);
|
|
5257
|
-
const dependencyAmplification =
|
|
5500
|
+
const dependencyAmplification = average2(
|
|
5258
5501
|
dependencyAmplificationZones.map(
|
|
5259
5502
|
(zone) => toUnitInterval(zone.externalPressure * zone.score / 100)
|
|
5260
5503
|
)
|
|
@@ -5265,15 +5508,15 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5265
5508
|
criticalInstability * config.interactionWeights.centralInstability,
|
|
5266
5509
|
dependencyAmplification * config.interactionWeights.dependencyAmplification
|
|
5267
5510
|
]);
|
|
5268
|
-
const riskScore =
|
|
5511
|
+
const riskScore = round46(repositoryNormalizedScore * 100);
|
|
5269
5512
|
if (collector !== void 0) {
|
|
5270
5513
|
const repositoryFactors = buildFactorTraces(riskScore, [
|
|
5271
5514
|
{
|
|
5272
5515
|
factorId: "repository.structural",
|
|
5273
5516
|
family: "structural",
|
|
5274
5517
|
strength: structuralDimension * dimensionWeights.structural,
|
|
5275
|
-
rawMetrics: { structuralDimension:
|
|
5276
|
-
normalizedMetrics: { dimensionWeight:
|
|
5518
|
+
rawMetrics: { structuralDimension: round46(structuralDimension) },
|
|
5519
|
+
normalizedMetrics: { dimensionWeight: round46(dimensionWeights.structural) },
|
|
5277
5520
|
weight: dimensionWeights.structural,
|
|
5278
5521
|
amplification: null,
|
|
5279
5522
|
evidence: [{ kind: "repository_metric", metric: "structuralDimension" }],
|
|
@@ -5283,8 +5526,8 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5283
5526
|
factorId: "repository.evolution",
|
|
5284
5527
|
family: "evolution",
|
|
5285
5528
|
strength: evolutionDimension * dimensionWeights.evolution,
|
|
5286
|
-
rawMetrics: { evolutionDimension:
|
|
5287
|
-
normalizedMetrics: { dimensionWeight:
|
|
5529
|
+
rawMetrics: { evolutionDimension: round46(evolutionDimension) },
|
|
5530
|
+
normalizedMetrics: { dimensionWeight: round46(dimensionWeights.evolution) },
|
|
5288
5531
|
weight: dimensionWeights.evolution,
|
|
5289
5532
|
amplification: null,
|
|
5290
5533
|
evidence: [{ kind: "repository_metric", metric: "evolutionDimension" }],
|
|
@@ -5294,8 +5537,8 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5294
5537
|
factorId: "repository.external",
|
|
5295
5538
|
family: "external",
|
|
5296
5539
|
strength: externalDimension * dimensionWeights.external,
|
|
5297
|
-
rawMetrics: { externalDimension:
|
|
5298
|
-
normalizedMetrics: { dimensionWeight:
|
|
5540
|
+
rawMetrics: { externalDimension: round46(externalDimension) },
|
|
5541
|
+
normalizedMetrics: { dimensionWeight: round46(dimensionWeights.external) },
|
|
5299
5542
|
weight: dimensionWeights.external,
|
|
5300
5543
|
amplification: null,
|
|
5301
5544
|
evidence: [{ kind: "repository_metric", metric: "externalDimension" }],
|
|
@@ -5306,19 +5549,19 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5306
5549
|
family: "composite",
|
|
5307
5550
|
strength: structuralDimension * evolutionDimension * config.interactionWeights.structuralEvolution + criticalInstability * config.interactionWeights.centralInstability + dependencyAmplification * config.interactionWeights.dependencyAmplification,
|
|
5308
5551
|
rawMetrics: {
|
|
5309
|
-
structuralEvolution:
|
|
5552
|
+
structuralEvolution: round46(
|
|
5310
5553
|
structuralDimension * evolutionDimension * config.interactionWeights.structuralEvolution
|
|
5311
5554
|
),
|
|
5312
|
-
centralInstability:
|
|
5555
|
+
centralInstability: round46(
|
|
5313
5556
|
criticalInstability * config.interactionWeights.centralInstability
|
|
5314
5557
|
),
|
|
5315
|
-
dependencyAmplification:
|
|
5558
|
+
dependencyAmplification: round46(
|
|
5316
5559
|
dependencyAmplification * config.interactionWeights.dependencyAmplification
|
|
5317
5560
|
)
|
|
5318
5561
|
},
|
|
5319
5562
|
normalizedMetrics: {
|
|
5320
|
-
criticalInstability:
|
|
5321
|
-
dependencyAmplification:
|
|
5563
|
+
criticalInstability: round46(criticalInstability),
|
|
5564
|
+
dependencyAmplification: round46(dependencyAmplification)
|
|
5322
5565
|
},
|
|
5323
5566
|
weight: null,
|
|
5324
5567
|
amplification: config.interactionWeights.structuralEvolution + config.interactionWeights.centralInstability + config.interactionWeights.dependencyAmplification,
|
|
@@ -5338,7 +5581,7 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
5338
5581
|
}
|
|
5339
5582
|
return {
|
|
5340
5583
|
riskScore,
|
|
5341
|
-
normalizedScore:
|
|
5584
|
+
normalizedScore: round46(repositoryNormalizedScore),
|
|
5342
5585
|
hotspots,
|
|
5343
5586
|
fragileClusters,
|
|
5344
5587
|
dependencyAmplificationZones,
|
|
@@ -5461,6 +5704,28 @@ var evaluateRepositoryRisk = (input, options = {}) => {
|
|
|
5461
5704
|
};
|
|
5462
5705
|
};
|
|
5463
5706
|
|
|
5707
|
+
// src/application/todo-fixme-counter.ts
|
|
5708
|
+
import * as ts2 from "typescript";
|
|
5709
|
+
var markerRegex = /\b(?:TODO|FIXME)\b/gi;
|
|
5710
|
+
var countMarkers = (text) => text.match(markerRegex)?.length ?? 0;
|
|
5711
|
+
var countTodoFixmeInComments = (content) => {
|
|
5712
|
+
const scanner = ts2.createScanner(
|
|
5713
|
+
ts2.ScriptTarget.Latest,
|
|
5714
|
+
false,
|
|
5715
|
+
ts2.LanguageVariant.Standard,
|
|
5716
|
+
content
|
|
5717
|
+
);
|
|
5718
|
+
let total = 0;
|
|
5719
|
+
let token = scanner.scan();
|
|
5720
|
+
while (token !== ts2.SyntaxKind.EndOfFileToken) {
|
|
5721
|
+
if (token === ts2.SyntaxKind.SingleLineCommentTrivia || token === ts2.SyntaxKind.MultiLineCommentTrivia) {
|
|
5722
|
+
total += countMarkers(scanner.getTokenText());
|
|
5723
|
+
}
|
|
5724
|
+
token = scanner.scan();
|
|
5725
|
+
}
|
|
5726
|
+
return total;
|
|
5727
|
+
};
|
|
5728
|
+
|
|
5464
5729
|
// src/application/run-analyze-command.ts
|
|
5465
5730
|
var resolveTargetPath = (inputPath, cwd) => resolve3(cwd, inputPath ?? ".");
|
|
5466
5731
|
var riskProfileConfig = {
|
|
@@ -5589,6 +5854,18 @@ var createEvolutionProgressReporter = (logger) => {
|
|
|
5589
5854
|
}
|
|
5590
5855
|
};
|
|
5591
5856
|
};
|
|
5857
|
+
var collectTodoFixmeCount = async (targetPath, structural) => {
|
|
5858
|
+
const filePaths2 = [...structural.files].map((file) => file.relativePath).sort((a, b) => a.localeCompare(b));
|
|
5859
|
+
let total = 0;
|
|
5860
|
+
for (const relativePath of filePaths2) {
|
|
5861
|
+
try {
|
|
5862
|
+
const content = await readFile2(join4(targetPath, relativePath), "utf8");
|
|
5863
|
+
total += countTodoFixmeInComments(content);
|
|
5864
|
+
} catch {
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
return total;
|
|
5868
|
+
};
|
|
5592
5869
|
var collectAnalysisInputs = async (inputPath, authorIdentityMode, options = {}, logger = createSilentLogger()) => {
|
|
5593
5870
|
const invocationCwd = process.env["INIT_CWD"] ?? process.cwd();
|
|
5594
5871
|
const targetPath = resolveTargetPath(inputPath, invocationCwd);
|
|
@@ -5631,10 +5908,14 @@ var collectAnalysisInputs = async (inputPath, authorIdentityMode, options = {},
|
|
|
5631
5908
|
} else {
|
|
5632
5909
|
logger.warn(`external analysis unavailable: ${external.reason}`);
|
|
5633
5910
|
}
|
|
5911
|
+
logger.info("collecting quality text signals");
|
|
5912
|
+
const todoFixmeCount = await collectTodoFixmeCount(targetPath, structural);
|
|
5913
|
+
logger.debug(`quality text signals: todoFixmeCount=${todoFixmeCount}`);
|
|
5634
5914
|
return {
|
|
5635
5915
|
structural,
|
|
5636
5916
|
evolution,
|
|
5637
|
-
external
|
|
5917
|
+
external,
|
|
5918
|
+
todoFixmeCount
|
|
5638
5919
|
};
|
|
5639
5920
|
};
|
|
5640
5921
|
var runAnalyzeCommand = async (inputPath, authorIdentityMode, options = {}, logger = createSilentLogger()) => {
|
|
@@ -5647,18 +5928,30 @@ var runAnalyzeCommand = async (inputPath, authorIdentityMode, options = {}, logg
|
|
|
5647
5928
|
logger.info("computing risk summary");
|
|
5648
5929
|
const riskConfig = resolveRiskConfigForProfile(options.riskProfile);
|
|
5649
5930
|
const risk = computeRepositoryRiskSummary({
|
|
5650
|
-
|
|
5931
|
+
structural: analysisInputs.structural,
|
|
5932
|
+
evolution: analysisInputs.evolution,
|
|
5933
|
+
external: analysisInputs.external,
|
|
5651
5934
|
...riskConfig === void 0 ? {} : { config: riskConfig }
|
|
5652
5935
|
});
|
|
5653
|
-
|
|
5936
|
+
const quality = computeRepositoryQualitySummary({
|
|
5937
|
+
structural: analysisInputs.structural,
|
|
5938
|
+
evolution: analysisInputs.evolution,
|
|
5939
|
+
todoFixmeCount: analysisInputs.todoFixmeCount
|
|
5940
|
+
});
|
|
5941
|
+
logger.info(
|
|
5942
|
+
`analysis completed (riskScore=${risk.riskScore}, qualityScore=${quality.qualityScore})`
|
|
5943
|
+
);
|
|
5654
5944
|
return {
|
|
5655
|
-
|
|
5656
|
-
|
|
5945
|
+
structural: analysisInputs.structural,
|
|
5946
|
+
evolution: analysisInputs.evolution,
|
|
5947
|
+
external: analysisInputs.external,
|
|
5948
|
+
risk,
|
|
5949
|
+
quality
|
|
5657
5950
|
};
|
|
5658
5951
|
};
|
|
5659
5952
|
|
|
5660
5953
|
// src/application/run-check-command.ts
|
|
5661
|
-
import { readFile as
|
|
5954
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
5662
5955
|
|
|
5663
5956
|
// src/application/build-analysis-snapshot.ts
|
|
5664
5957
|
var buildAnalysisSnapshot = async (inputPath, authorIdentityMode, options, logger) => {
|
|
@@ -5673,14 +5966,23 @@ var buildAnalysisSnapshot = async (inputPath, authorIdentityMode, options, logge
|
|
|
5673
5966
|
const riskConfig = resolveRiskConfigForProfile(options.riskProfile);
|
|
5674
5967
|
const evaluation = evaluateRepositoryRisk(
|
|
5675
5968
|
{
|
|
5676
|
-
|
|
5969
|
+
structural: analysisInputs.structural,
|
|
5970
|
+
evolution: analysisInputs.evolution,
|
|
5971
|
+
external: analysisInputs.external,
|
|
5677
5972
|
...riskConfig === void 0 ? {} : { config: riskConfig }
|
|
5678
5973
|
},
|
|
5679
5974
|
{ explain: options.includeTrace }
|
|
5680
5975
|
);
|
|
5681
5976
|
const summary = {
|
|
5682
|
-
|
|
5683
|
-
|
|
5977
|
+
structural: analysisInputs.structural,
|
|
5978
|
+
evolution: analysisInputs.evolution,
|
|
5979
|
+
external: analysisInputs.external,
|
|
5980
|
+
risk: evaluation.summary,
|
|
5981
|
+
quality: computeRepositoryQualitySummary({
|
|
5982
|
+
structural: analysisInputs.structural,
|
|
5983
|
+
evolution: analysisInputs.evolution,
|
|
5984
|
+
todoFixmeCount: analysisInputs.todoFixmeCount
|
|
5985
|
+
})
|
|
5684
5986
|
};
|
|
5685
5987
|
return createSnapshot({
|
|
5686
5988
|
analysis: summary,
|
|
@@ -5732,7 +6034,7 @@ var runCheckCommand = async (inputPath, authorIdentityMode, options, logger = cr
|
|
|
5732
6034
|
let diff;
|
|
5733
6035
|
if (options.baselinePath !== void 0) {
|
|
5734
6036
|
logger.info(`loading baseline snapshot: ${options.baselinePath}`);
|
|
5735
|
-
const baselineRaw = await
|
|
6037
|
+
const baselineRaw = await readFile3(options.baselinePath, "utf8");
|
|
5736
6038
|
try {
|
|
5737
6039
|
baseline = parseSnapshot(baselineRaw);
|
|
5738
6040
|
} catch (error) {
|
|
@@ -5771,7 +6073,7 @@ var runCheckCommand = async (inputPath, authorIdentityMode, options, logger = cr
|
|
|
5771
6073
|
};
|
|
5772
6074
|
|
|
5773
6075
|
// src/application/run-ci-command.ts
|
|
5774
|
-
import { readFile as
|
|
6076
|
+
import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
5775
6077
|
import { relative as relative2, resolve as resolve4 } from "path";
|
|
5776
6078
|
var isPathOutsideBase = (value) => {
|
|
5777
6079
|
return value === ".." || value.startsWith("../") || value.startsWith("..\\");
|
|
@@ -5871,7 +6173,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
|
|
|
5871
6173
|
diff = compareSnapshots(current, baseline);
|
|
5872
6174
|
} else if (options.baselinePath !== void 0) {
|
|
5873
6175
|
logger.info(`loading baseline snapshot: ${options.baselinePath}`);
|
|
5874
|
-
const baselineRaw = await
|
|
6176
|
+
const baselineRaw = await readFile4(options.baselinePath, "utf8");
|
|
5875
6177
|
try {
|
|
5876
6178
|
baseline = parseSnapshot(baselineRaw);
|
|
5877
6179
|
} catch (error) {
|
|
@@ -5919,7 +6221,7 @@ ${ciMarkdown}`;
|
|
|
5919
6221
|
};
|
|
5920
6222
|
|
|
5921
6223
|
// src/application/run-report-command.ts
|
|
5922
|
-
import { readFile as
|
|
6224
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
5923
6225
|
var runReportCommand = async (inputPath, authorIdentityMode, options, logger = createSilentLogger()) => {
|
|
5924
6226
|
logger.info("building analysis snapshot");
|
|
5925
6227
|
const current = await buildAnalysisSnapshot(
|
|
@@ -5941,7 +6243,7 @@ var runReportCommand = async (inputPath, authorIdentityMode, options, logger = c
|
|
|
5941
6243
|
report = createReport(current);
|
|
5942
6244
|
} else {
|
|
5943
6245
|
logger.info(`loading baseline snapshot: ${options.comparePath}`);
|
|
5944
|
-
const baselineRaw = await
|
|
6246
|
+
const baselineRaw = await readFile5(options.comparePath, "utf8");
|
|
5945
6247
|
const baseline = parseSnapshot(baselineRaw);
|
|
5946
6248
|
const diff = compareSnapshots(current, baseline);
|
|
5947
6249
|
report = createReport(current, diff);
|
|
@@ -5987,7 +6289,9 @@ var runExplainCommand = async (inputPath, authorIdentityMode, options, logger =
|
|
|
5987
6289
|
const riskConfig = resolveRiskConfigForProfile(options.riskProfile);
|
|
5988
6290
|
const evaluation = evaluateRepositoryRisk(
|
|
5989
6291
|
{
|
|
5990
|
-
|
|
6292
|
+
structural: analysisInputs.structural,
|
|
6293
|
+
evolution: analysisInputs.evolution,
|
|
6294
|
+
external: analysisInputs.external,
|
|
5991
6295
|
...riskConfig === void 0 ? {} : { config: riskConfig }
|
|
5992
6296
|
},
|
|
5993
6297
|
{ explain: true }
|
|
@@ -5996,10 +6300,19 @@ var runExplainCommand = async (inputPath, authorIdentityMode, options, logger =
|
|
|
5996
6300
|
throw new Error("risk trace unavailable");
|
|
5997
6301
|
}
|
|
5998
6302
|
const summary = {
|
|
5999
|
-
|
|
6000
|
-
|
|
6303
|
+
structural: analysisInputs.structural,
|
|
6304
|
+
evolution: analysisInputs.evolution,
|
|
6305
|
+
external: analysisInputs.external,
|
|
6306
|
+
risk: evaluation.summary,
|
|
6307
|
+
quality: computeRepositoryQualitySummary({
|
|
6308
|
+
structural: analysisInputs.structural,
|
|
6309
|
+
evolution: analysisInputs.evolution,
|
|
6310
|
+
todoFixmeCount: analysisInputs.todoFixmeCount
|
|
6311
|
+
})
|
|
6001
6312
|
};
|
|
6002
|
-
logger.info(
|
|
6313
|
+
logger.info(
|
|
6314
|
+
`explanation completed (riskScore=${summary.risk.riskScore}, qualityScore=${summary.quality.qualityScore})`
|
|
6315
|
+
);
|
|
6003
6316
|
return {
|
|
6004
6317
|
summary,
|
|
6005
6318
|
trace: evaluation.trace,
|
|
@@ -6055,6 +6368,7 @@ var renderReportHighlightsText = (report) => {
|
|
|
6055
6368
|
lines.push("Repository Summary");
|
|
6056
6369
|
lines.push(` target: ${report.repository.targetPath}`);
|
|
6057
6370
|
lines.push(` riskScore: ${report.repository.riskScore}`);
|
|
6371
|
+
lines.push(` qualityScore: ${report.quality.qualityScore}`);
|
|
6058
6372
|
lines.push(` normalizedScore: ${report.repository.normalizedScore}`);
|
|
6059
6373
|
lines.push(` riskTier: ${report.repository.riskTier}`);
|
|
6060
6374
|
lines.push("");
|
|
@@ -6070,6 +6384,7 @@ var renderReportHighlightsMarkdown = (report) => {
|
|
|
6070
6384
|
lines.push("## Repository Summary");
|
|
6071
6385
|
lines.push(`- target: \`${report.repository.targetPath}\``);
|
|
6072
6386
|
lines.push(`- riskScore: \`${report.repository.riskScore}\``);
|
|
6387
|
+
lines.push(`- qualityScore: \`${report.quality.qualityScore}\``);
|
|
6073
6388
|
lines.push(`- normalizedScore: \`${report.repository.normalizedScore}\``);
|
|
6074
6389
|
lines.push(`- riskTier: \`${report.repository.riskTier}\``);
|
|
6075
6390
|
lines.push("");
|
|
@@ -6087,6 +6402,7 @@ var renderCompactText = (report, explainSummary) => {
|
|
|
6087
6402
|
lines.push("Repository");
|
|
6088
6403
|
lines.push(` target: ${report.repository.targetPath}`);
|
|
6089
6404
|
lines.push(` riskScore: ${report.repository.riskScore}`);
|
|
6405
|
+
lines.push(` qualityScore: ${report.quality.qualityScore}`);
|
|
6090
6406
|
lines.push(` riskTier: ${report.repository.riskTier}`);
|
|
6091
6407
|
lines.push(
|
|
6092
6408
|
` 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"}`
|
|
@@ -6112,6 +6428,7 @@ var renderCompactMarkdown = (report, explainSummary) => {
|
|
|
6112
6428
|
lines.push("## Repository");
|
|
6113
6429
|
lines.push(`- target: \`${report.repository.targetPath}\``);
|
|
6114
6430
|
lines.push(`- riskScore: \`${report.repository.riskScore}\``);
|
|
6431
|
+
lines.push(`- qualityScore: \`${report.quality.qualityScore}\``);
|
|
6115
6432
|
lines.push(`- riskTier: \`${report.repository.riskTier}\``);
|
|
6116
6433
|
lines.push(
|
|
6117
6434
|
`- 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"}\``
|
|
@@ -6316,7 +6633,7 @@ program.command("run").argument("[path]", "path to the project to analyze").addO
|
|
|
6316
6633
|
}
|
|
6317
6634
|
const report = options.compare === void 0 ? createReport(snapshot) : createReport(
|
|
6318
6635
|
snapshot,
|
|
6319
|
-
compareSnapshots(snapshot, parseSnapshot(await
|
|
6636
|
+
compareSnapshots(snapshot, parseSnapshot(await readFile6(options.compare, "utf8")))
|
|
6320
6637
|
);
|
|
6321
6638
|
if (options.format === "json") {
|
|
6322
6639
|
const analyzeSummaryOutput = formatAnalyzeOutput(explain.summary, "summary");
|
|
@@ -6360,6 +6677,7 @@ program.command("run").argument("[path]", "path to the project to analyze").addO
|
|
|
6360
6677
|
},
|
|
6361
6678
|
report: {
|
|
6362
6679
|
repository: report.repository,
|
|
6680
|
+
quality: report.quality,
|
|
6363
6681
|
hotspots: report.hotspots.slice(0, 5),
|
|
6364
6682
|
structural: report.structural,
|
|
6365
6683
|
external: report.external
|