@getcodesentinel/codesentinel 1.10.1 → 1.12.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 +1 -0
- package/dist/index.js +622 -103
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -127,7 +127,12 @@ var buildExternalAnalysisSummary = (targetPath, extraction, metadataByKey, confi
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
const { depthByName, maxDepth } = computeDepths(nodeByName, directNames);
|
|
130
|
-
const centralityRanking = rankCentrality(
|
|
130
|
+
const centralityRanking = rankCentrality(
|
|
131
|
+
nodes,
|
|
132
|
+
dependentsByName,
|
|
133
|
+
directNames,
|
|
134
|
+
config.centralityTopN
|
|
135
|
+
);
|
|
131
136
|
const topCentralNames = new Set(
|
|
132
137
|
centralityRanking.slice(0, Math.max(1, Math.ceil(centralityRanking.length * 0.25))).map((entry) => entry.name)
|
|
133
138
|
);
|
|
@@ -230,8 +235,12 @@ var buildExternalAnalysisSummary = (targetPath, extraction, metadataByKey, confi
|
|
|
230
235
|
metrics: {
|
|
231
236
|
totalDependencies: allDependencies.length,
|
|
232
237
|
directDependencies: dependencies.length,
|
|
233
|
-
directProductionDependencies: dependencies.filter(
|
|
234
|
-
|
|
238
|
+
directProductionDependencies: dependencies.filter(
|
|
239
|
+
(dependency) => dependency.dependencyScope === "prod"
|
|
240
|
+
).length,
|
|
241
|
+
directDevelopmentDependencies: dependencies.filter(
|
|
242
|
+
(dependency) => dependency.dependencyScope === "dev"
|
|
243
|
+
).length,
|
|
235
244
|
transitiveDependencies: allDependencies.length - dependencies.length,
|
|
236
245
|
dependencyDepth: maxDepth,
|
|
237
246
|
lockfileKind: extraction.kind,
|
|
@@ -257,7 +266,8 @@ var DEFAULT_EXTERNAL_ANALYSIS_CONFIG = {
|
|
|
257
266
|
var mapWithConcurrency = async (values, limit, handler) => {
|
|
258
267
|
const effectiveLimit = Math.max(1, limit);
|
|
259
268
|
const workerCount = Math.min(effectiveLimit, values.length);
|
|
260
|
-
const
|
|
269
|
+
const UNSET = /* @__PURE__ */ Symbol("map_with_concurrency_unset");
|
|
270
|
+
const results = new Array(values.length).fill(UNSET);
|
|
261
271
|
let index = 0;
|
|
262
272
|
const workers = Array.from({ length: workerCount }, async () => {
|
|
263
273
|
while (true) {
|
|
@@ -267,12 +277,13 @@ var mapWithConcurrency = async (values, limit, handler) => {
|
|
|
267
277
|
return;
|
|
268
278
|
}
|
|
269
279
|
const value = values[current];
|
|
270
|
-
|
|
271
|
-
results[current] = await handler(value);
|
|
272
|
-
}
|
|
280
|
+
results[current] = await handler(value);
|
|
273
281
|
}
|
|
274
282
|
});
|
|
275
283
|
await Promise.all(workers);
|
|
284
|
+
if (results.some((value) => value === UNSET)) {
|
|
285
|
+
throw new Error("map_with_concurrency_incomplete_results");
|
|
286
|
+
}
|
|
276
287
|
return results;
|
|
277
288
|
};
|
|
278
289
|
var collectDependencyMetadata = async (extraction, metadataProvider, concurrency, onProgress) => {
|
|
@@ -473,9 +484,7 @@ var parsePnpmLockfile = (raw, directSpecs) => {
|
|
|
473
484
|
version: nodeId.slice(at + 1),
|
|
474
485
|
dependencies: [...deps].sort((a, b) => a.localeCompare(b))
|
|
475
486
|
};
|
|
476
|
-
}).sort(
|
|
477
|
-
(a, b) => a.name.localeCompare(b.name) || a.version.localeCompare(b.version)
|
|
478
|
-
);
|
|
487
|
+
}).sort((a, b) => a.name.localeCompare(b.name) || a.version.localeCompare(b.version));
|
|
479
488
|
return {
|
|
480
489
|
kind: "pnpm",
|
|
481
490
|
directDependencies: directSpecs,
|
|
@@ -579,7 +588,9 @@ var parseYarnLock = (raw, directSpecs) => {
|
|
|
579
588
|
return {
|
|
580
589
|
kind: "yarn",
|
|
581
590
|
directDependencies: directSpecs,
|
|
582
|
-
nodes: [...deduped.values()].sort(
|
|
591
|
+
nodes: [...deduped.values()].sort(
|
|
592
|
+
(a, b) => a.name.localeCompare(b.name) || a.version.localeCompare(b.version)
|
|
593
|
+
)
|
|
583
594
|
};
|
|
584
595
|
};
|
|
585
596
|
var parseBunLock = (_raw, _directSpecs) => {
|
|
@@ -855,7 +866,9 @@ var resolveRangeVersion = (versions, requested) => {
|
|
|
855
866
|
if (clauses.length === 0) {
|
|
856
867
|
return null;
|
|
857
868
|
}
|
|
858
|
-
const parsedVersions = versions.map((version2) => ({ version: version2, parsed: parseSemver(version2) })).filter(
|
|
869
|
+
const parsedVersions = versions.map((version2) => ({ version: version2, parsed: parseSemver(version2) })).filter(
|
|
870
|
+
(candidate) => candidate.parsed !== null
|
|
871
|
+
).sort((a, b) => compareSemver(b.parsed, a.parsed));
|
|
859
872
|
for (const candidate of parsedVersions) {
|
|
860
873
|
let clauseMatched = false;
|
|
861
874
|
let clauseUnsupported = false;
|
|
@@ -929,7 +942,9 @@ var resolveRequestedVersion = (packument, requested) => {
|
|
|
929
942
|
fallbackUsed: requested !== null
|
|
930
943
|
};
|
|
931
944
|
}
|
|
932
|
-
const semverSorted = versionKeys.map((version2) => ({ version: version2, parsed: parseSemver(version2) })).filter(
|
|
945
|
+
const semverSorted = versionKeys.map((version2) => ({ version: version2, parsed: parseSemver(version2) })).filter(
|
|
946
|
+
(candidate) => candidate.parsed !== null
|
|
947
|
+
).sort((a, b) => compareSemver(b.parsed, a.parsed)).map((candidate) => candidate.version);
|
|
933
948
|
const fallbackVersion = semverSorted[0] ?? versionKeys.sort((a, b) => b.localeCompare(a))[0];
|
|
934
949
|
if (fallbackVersion === void 0) {
|
|
935
950
|
return null;
|
|
@@ -1002,11 +1017,15 @@ var resolveRegistryGraphFromDirectSpecs = async (directSpecs, options) => {
|
|
|
1002
1017
|
continue;
|
|
1003
1018
|
}
|
|
1004
1019
|
const manifest = (packument.versions ?? {})[resolved.version] ?? {};
|
|
1005
|
-
const dependencies = Object.entries(manifest.dependencies ?? {}).filter(
|
|
1020
|
+
const dependencies = Object.entries(manifest.dependencies ?? {}).filter(
|
|
1021
|
+
([dependencyName, dependencyRange]) => dependencyName.length > 0 && dependencyRange.length > 0
|
|
1022
|
+
).sort((a, b) => a[0].localeCompare(b[0]));
|
|
1006
1023
|
nodesByKey.set(nodeKey, {
|
|
1007
1024
|
name: item.name,
|
|
1008
1025
|
version: resolved.version,
|
|
1009
|
-
dependencies: dependencies.map(
|
|
1026
|
+
dependencies: dependencies.map(
|
|
1027
|
+
([dependencyName, dependencyRange]) => `${dependencyName}@${dependencyRange}`
|
|
1028
|
+
)
|
|
1010
1029
|
});
|
|
1011
1030
|
if (item.depth >= maxDepth && dependencies.length > 0) {
|
|
1012
1031
|
truncated = true;
|
|
@@ -1183,7 +1202,8 @@ var parseDependencySpec = (value) => {
|
|
|
1183
1202
|
var mapWithConcurrency2 = async (values, limit, handler) => {
|
|
1184
1203
|
const effectiveLimit = Math.max(1, limit);
|
|
1185
1204
|
const workerCount = Math.min(effectiveLimit, values.length);
|
|
1186
|
-
const
|
|
1205
|
+
const UNSET = /* @__PURE__ */ Symbol("map_with_concurrency_unset");
|
|
1206
|
+
const results = new Array(values.length).fill(UNSET);
|
|
1187
1207
|
let index = 0;
|
|
1188
1208
|
const workers = Array.from({ length: workerCount }, async () => {
|
|
1189
1209
|
while (true) {
|
|
@@ -1193,12 +1213,13 @@ var mapWithConcurrency2 = async (values, limit, handler) => {
|
|
|
1193
1213
|
return;
|
|
1194
1214
|
}
|
|
1195
1215
|
const value = values[current];
|
|
1196
|
-
|
|
1197
|
-
results[current] = await handler(value);
|
|
1198
|
-
}
|
|
1216
|
+
results[current] = await handler(value);
|
|
1199
1217
|
}
|
|
1200
1218
|
});
|
|
1201
1219
|
await Promise.all(workers);
|
|
1220
|
+
if (results.some((value) => value === UNSET)) {
|
|
1221
|
+
throw new Error("map_with_concurrency_incomplete_results");
|
|
1222
|
+
}
|
|
1202
1223
|
return results;
|
|
1203
1224
|
};
|
|
1204
1225
|
var analyzeDependencyCandidate = async (input, metadataProvider) => {
|
|
@@ -1363,8 +1384,8 @@ var NpmRegistryMetadataProvider = class {
|
|
|
1363
1384
|
}
|
|
1364
1385
|
};
|
|
1365
1386
|
var NoopMetadataProvider = class {
|
|
1366
|
-
|
|
1367
|
-
return null;
|
|
1387
|
+
getMetadata(_name, _version, _context) {
|
|
1388
|
+
return Promise.resolve(null);
|
|
1368
1389
|
}
|
|
1369
1390
|
};
|
|
1370
1391
|
var analyzeDependencyExposureFromProject = async (input, onProgress) => {
|
|
@@ -1445,7 +1466,9 @@ var diffSets = (current, baseline) => {
|
|
|
1445
1466
|
return { added, removed };
|
|
1446
1467
|
};
|
|
1447
1468
|
var diffScoreMap = (current, baseline) => {
|
|
1448
|
-
const keys = [.../* @__PURE__ */ new Set([...current.keys(), ...baseline.keys()])].sort(
|
|
1469
|
+
const keys = [.../* @__PURE__ */ new Set([...current.keys(), ...baseline.keys()])].sort(
|
|
1470
|
+
(a, b) => a.localeCompare(b)
|
|
1471
|
+
);
|
|
1449
1472
|
return keys.map((key) => {
|
|
1450
1473
|
const before = baseline.get(key) ?? 0;
|
|
1451
1474
|
const after = current.get(key) ?? 0;
|
|
@@ -1486,17 +1509,27 @@ var compareSnapshots = (current, baseline) => {
|
|
|
1486
1509
|
singleMaintainerDependencies: [],
|
|
1487
1510
|
abandonedDependencies: []
|
|
1488
1511
|
};
|
|
1489
|
-
const highRisk = diffSets(
|
|
1512
|
+
const highRisk = diffSets(
|
|
1513
|
+
currentExternal.highRiskDependencies,
|
|
1514
|
+
baselineExternal.highRiskDependencies
|
|
1515
|
+
);
|
|
1490
1516
|
const singleMaintainer = diffSets(
|
|
1491
1517
|
currentExternal.singleMaintainerDependencies,
|
|
1492
1518
|
baselineExternal.singleMaintainerDependencies
|
|
1493
1519
|
);
|
|
1494
|
-
const abandoned = diffSets(
|
|
1520
|
+
const abandoned = diffSets(
|
|
1521
|
+
currentExternal.abandonedDependencies,
|
|
1522
|
+
baselineExternal.abandonedDependencies
|
|
1523
|
+
);
|
|
1495
1524
|
const hotspots = diffSets(currentHotspots, baselineHotspots);
|
|
1496
1525
|
const cycles = diffSets(currentCycles, baselineCycles);
|
|
1497
1526
|
return {
|
|
1498
|
-
repositoryScoreDelta: round43(
|
|
1499
|
-
|
|
1527
|
+
repositoryScoreDelta: round43(
|
|
1528
|
+
current.analysis.risk.repositoryScore - baseline.analysis.risk.repositoryScore
|
|
1529
|
+
),
|
|
1530
|
+
normalizedScoreDelta: round43(
|
|
1531
|
+
current.analysis.risk.normalizedScore - baseline.analysis.risk.normalizedScore
|
|
1532
|
+
),
|
|
1500
1533
|
fileRiskChanges: diffScoreMap(currentFileScores, baselineFileScores),
|
|
1501
1534
|
moduleRiskChanges: diffScoreMap(currentModuleScores, baselineModuleScores),
|
|
1502
1535
|
newHotspots: hotspots.added,
|
|
@@ -1586,6 +1619,32 @@ var repositoryConfidence = (snapshot) => {
|
|
|
1586
1619
|
);
|
|
1587
1620
|
return round43(weighted / weight);
|
|
1588
1621
|
};
|
|
1622
|
+
var repositoryDimensionScores = (snapshot) => {
|
|
1623
|
+
const target = findTraceTarget(snapshot, "repository", snapshot.analysis.structural.targetPath);
|
|
1624
|
+
if (target === void 0) {
|
|
1625
|
+
return {
|
|
1626
|
+
structural: null,
|
|
1627
|
+
evolution: null,
|
|
1628
|
+
external: null,
|
|
1629
|
+
interactions: null
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
const structural = target.factors.find((factor) => factor.factorId === "repository.structural");
|
|
1633
|
+
const evolution = target.factors.find((factor) => factor.factorId === "repository.evolution");
|
|
1634
|
+
const external = target.factors.find((factor) => factor.factorId === "repository.external");
|
|
1635
|
+
const interactions = target.factors.find(
|
|
1636
|
+
(factor) => factor.factorId === "repository.composite.interactions"
|
|
1637
|
+
);
|
|
1638
|
+
const interactionScore = interactions === void 0 ? null : round43(
|
|
1639
|
+
((interactions.rawMetrics["structuralEvolution"] ?? 0) + (interactions.rawMetrics["centralInstability"] ?? 0) + (interactions.rawMetrics["dependencyAmplification"] ?? 0)) * 100
|
|
1640
|
+
);
|
|
1641
|
+
return {
|
|
1642
|
+
structural: structural === void 0 ? null : round43((structural.rawMetrics["structuralDimension"] ?? 0) * 100),
|
|
1643
|
+
evolution: evolution === void 0 ? null : round43((evolution.rawMetrics["evolutionDimension"] ?? 0) * 100),
|
|
1644
|
+
external: external === void 0 ? null : round43((external.rawMetrics["externalDimension"] ?? 0) * 100),
|
|
1645
|
+
interactions: interactionScore
|
|
1646
|
+
};
|
|
1647
|
+
};
|
|
1589
1648
|
var createReport = (snapshot, diff) => {
|
|
1590
1649
|
const external = snapshot.analysis.external;
|
|
1591
1650
|
return {
|
|
@@ -1596,7 +1655,8 @@ var createReport = (snapshot, diff) => {
|
|
|
1596
1655
|
repositoryScore: snapshot.analysis.risk.repositoryScore,
|
|
1597
1656
|
normalizedScore: snapshot.analysis.risk.normalizedScore,
|
|
1598
1657
|
riskTier: toRiskTier(snapshot.analysis.risk.repositoryScore),
|
|
1599
|
-
confidence: repositoryConfidence(snapshot)
|
|
1658
|
+
confidence: repositoryConfidence(snapshot),
|
|
1659
|
+
dimensionScores: repositoryDimensionScores(snapshot)
|
|
1600
1660
|
},
|
|
1601
1661
|
hotspots: hotspotItems(snapshot),
|
|
1602
1662
|
structural: {
|
|
@@ -1616,10 +1676,18 @@ var createReport = (snapshot, diff) => {
|
|
|
1616
1676
|
reason: external.reason
|
|
1617
1677
|
} : {
|
|
1618
1678
|
available: true,
|
|
1619
|
-
highRiskDependencies: [...external.highRiskDependencies].sort(
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1679
|
+
highRiskDependencies: [...external.highRiskDependencies].sort(
|
|
1680
|
+
(a, b) => a.localeCompare(b)
|
|
1681
|
+
),
|
|
1682
|
+
highRiskDevelopmentDependencies: [...external.highRiskDevelopmentDependencies].sort(
|
|
1683
|
+
(a, b) => a.localeCompare(b)
|
|
1684
|
+
),
|
|
1685
|
+
singleMaintainerDependencies: [...external.singleMaintainerDependencies].sort(
|
|
1686
|
+
(a, b) => a.localeCompare(b)
|
|
1687
|
+
),
|
|
1688
|
+
abandonedDependencies: [...external.abandonedDependencies].sort(
|
|
1689
|
+
(a, b) => a.localeCompare(b)
|
|
1690
|
+
)
|
|
1623
1691
|
},
|
|
1624
1692
|
appendix: {
|
|
1625
1693
|
snapshotSchemaVersion: snapshot.schemaVersion,
|
|
@@ -1655,6 +1723,12 @@ var renderTextReport = (report) => {
|
|
|
1655
1723
|
lines.push(` riskTier: ${report.repository.riskTier}`);
|
|
1656
1724
|
lines.push(` confidence: ${report.repository.confidence ?? "n/a"}`);
|
|
1657
1725
|
lines.push("");
|
|
1726
|
+
lines.push("Dimension Scores (0-100)");
|
|
1727
|
+
lines.push(` structural: ${report.repository.dimensionScores.structural ?? "n/a"}`);
|
|
1728
|
+
lines.push(` evolution: ${report.repository.dimensionScores.evolution ?? "n/a"}`);
|
|
1729
|
+
lines.push(` external: ${report.repository.dimensionScores.external ?? "n/a"}`);
|
|
1730
|
+
lines.push(` interactions: ${report.repository.dimensionScores.interactions ?? "n/a"}`);
|
|
1731
|
+
lines.push("");
|
|
1658
1732
|
lines.push("Top Hotspots");
|
|
1659
1733
|
for (const hotspot of report.hotspots) {
|
|
1660
1734
|
lines.push(` - ${hotspot.target} | score=${hotspot.score}`);
|
|
@@ -1677,14 +1751,18 @@ var renderTextReport = (report) => {
|
|
|
1677
1751
|
if (!report.external.available) {
|
|
1678
1752
|
lines.push(` unavailable: ${report.external.reason}`);
|
|
1679
1753
|
} else {
|
|
1680
|
-
lines.push(
|
|
1754
|
+
lines.push(
|
|
1755
|
+
` highRiskDependencies: ${report.external.highRiskDependencies.join(", ") || "none"}`
|
|
1756
|
+
);
|
|
1681
1757
|
lines.push(
|
|
1682
1758
|
` highRiskDevelopmentDependencies: ${report.external.highRiskDevelopmentDependencies.join(", ") || "none"}`
|
|
1683
1759
|
);
|
|
1684
1760
|
lines.push(
|
|
1685
1761
|
` singleMaintainerDependencies: ${report.external.singleMaintainerDependencies.join(", ") || "none"}`
|
|
1686
1762
|
);
|
|
1687
|
-
lines.push(
|
|
1763
|
+
lines.push(
|
|
1764
|
+
` abandonedDependencies: ${report.external.abandonedDependencies.join(", ") || "none"}`
|
|
1765
|
+
);
|
|
1688
1766
|
}
|
|
1689
1767
|
lines.push("");
|
|
1690
1768
|
lines.push("Appendix");
|
|
@@ -1721,6 +1799,12 @@ var renderMarkdownReport = (report) => {
|
|
|
1721
1799
|
lines.push(`- riskTier: \`${report.repository.riskTier}\``);
|
|
1722
1800
|
lines.push(`- confidence: \`${report.repository.confidence ?? "n/a"}\``);
|
|
1723
1801
|
lines.push("");
|
|
1802
|
+
lines.push("## Dimension Scores (0-100)");
|
|
1803
|
+
lines.push(`- structural: \`${report.repository.dimensionScores.structural ?? "n/a"}\``);
|
|
1804
|
+
lines.push(`- evolution: \`${report.repository.dimensionScores.evolution ?? "n/a"}\``);
|
|
1805
|
+
lines.push(`- external: \`${report.repository.dimensionScores.external ?? "n/a"}\``);
|
|
1806
|
+
lines.push(`- interactions: \`${report.repository.dimensionScores.interactions ?? "n/a"}\``);
|
|
1807
|
+
lines.push("");
|
|
1724
1808
|
lines.push("## Top Hotspots");
|
|
1725
1809
|
for (const hotspot of report.hotspots) {
|
|
1726
1810
|
lines.push(`- **${hotspot.target}** (score: \`${hotspot.score}\`)`);
|
|
@@ -1737,21 +1821,27 @@ var renderMarkdownReport = (report) => {
|
|
|
1737
1821
|
lines.push("");
|
|
1738
1822
|
lines.push("## Structural Observations");
|
|
1739
1823
|
lines.push(`- cycles detected: \`${report.structural.cycleCount}\``);
|
|
1740
|
-
lines.push(
|
|
1824
|
+
lines.push(
|
|
1825
|
+
`- cycles: ${report.structural.cycles.map((cycle) => `\`${cycle}\``).join(", ") || "none"}`
|
|
1826
|
+
);
|
|
1741
1827
|
lines.push(`- fragile clusters: \`${report.structural.fragileClusters.length}\``);
|
|
1742
1828
|
lines.push("");
|
|
1743
1829
|
lines.push("## External Exposure Summary");
|
|
1744
1830
|
if (!report.external.available) {
|
|
1745
1831
|
lines.push(`- unavailable: \`${report.external.reason}\``);
|
|
1746
1832
|
} else {
|
|
1747
|
-
lines.push(
|
|
1833
|
+
lines.push(
|
|
1834
|
+
`- high-risk dependencies: ${report.external.highRiskDependencies.map((item) => `\`${item}\``).join(", ") || "none"}`
|
|
1835
|
+
);
|
|
1748
1836
|
lines.push(
|
|
1749
1837
|
`- high-risk development dependencies: ${report.external.highRiskDevelopmentDependencies.map((item) => `\`${item}\``).join(", ") || "none"}`
|
|
1750
1838
|
);
|
|
1751
1839
|
lines.push(
|
|
1752
1840
|
`- single maintainer dependencies: ${report.external.singleMaintainerDependencies.map((item) => `\`${item}\``).join(", ") || "none"}`
|
|
1753
1841
|
);
|
|
1754
|
-
lines.push(
|
|
1842
|
+
lines.push(
|
|
1843
|
+
`- abandoned dependencies: ${report.external.abandonedDependencies.map((item) => `\`${item}\``).join(", ") || "none"}`
|
|
1844
|
+
);
|
|
1755
1845
|
}
|
|
1756
1846
|
lines.push("");
|
|
1757
1847
|
lines.push("## Appendix");
|
|
@@ -1871,7 +1961,9 @@ var validateGateConfig = (input) => {
|
|
|
1871
1961
|
throw new GovernanceConfigurationError("max-repo-score must be a number in [0, 100]");
|
|
1872
1962
|
}
|
|
1873
1963
|
if (config.newHotspotScoreThreshold !== void 0 && (!Number.isFinite(config.newHotspotScoreThreshold) || config.newHotspotScoreThreshold < 0 || config.newHotspotScoreThreshold > 100)) {
|
|
1874
|
-
throw new GovernanceConfigurationError(
|
|
1964
|
+
throw new GovernanceConfigurationError(
|
|
1965
|
+
"new-hotspot-score-threshold must be a number in [0, 100]"
|
|
1966
|
+
);
|
|
1875
1967
|
}
|
|
1876
1968
|
};
|
|
1877
1969
|
var evaluateGates = (input) => {
|
|
@@ -2021,7 +2113,9 @@ var renderCheckMarkdown = (snapshot, result) => {
|
|
|
2021
2113
|
lines.push("## CodeSentinel CI Summary");
|
|
2022
2114
|
lines.push(`- target: \`${snapshot.analysis.structural.targetPath}\``);
|
|
2023
2115
|
lines.push(`- repositoryScore: \`${snapshot.analysis.risk.repositoryScore}\``);
|
|
2024
|
-
lines.push(
|
|
2116
|
+
lines.push(
|
|
2117
|
+
`- evaluatedGates: ${result.evaluatedGates.map((item) => `\`${item}\``).join(", ") || "none"}`
|
|
2118
|
+
);
|
|
2025
2119
|
lines.push(`- violations: \`${result.violations.length}\``);
|
|
2026
2120
|
lines.push(`- exitCode: \`${result.exitCode}\``);
|
|
2027
2121
|
const repositoryTrace = snapshot.trace?.targets.find(
|
|
@@ -2369,7 +2463,7 @@ var resolveAutoBaselineRef = async (input) => {
|
|
|
2369
2463
|
|
|
2370
2464
|
// src/index.ts
|
|
2371
2465
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2372
|
-
import { dirname, resolve as resolve5 } from "path";
|
|
2466
|
+
import { dirname as dirname2, resolve as resolve5 } from "path";
|
|
2373
2467
|
import { fileURLToPath } from "url";
|
|
2374
2468
|
|
|
2375
2469
|
// src/application/format-analyze-output.ts
|
|
@@ -2392,7 +2486,10 @@ var createSummaryShape = (summary) => ({
|
|
|
2392
2486
|
0,
|
|
2393
2487
|
10
|
|
2394
2488
|
),
|
|
2395
|
-
transitiveExposureDependenciesTop: summary.external.transitiveExposureDependencies.slice(
|
|
2489
|
+
transitiveExposureDependenciesTop: summary.external.transitiveExposureDependencies.slice(
|
|
2490
|
+
0,
|
|
2491
|
+
10
|
|
2492
|
+
)
|
|
2396
2493
|
} : {
|
|
2397
2494
|
available: false,
|
|
2398
2495
|
reason: summary.external.reason
|
|
@@ -2413,13 +2510,16 @@ var formatAnalyzeOutput = (summary, mode) => mode === "json" ? JSON.stringify(su
|
|
|
2413
2510
|
// src/application/format-explain-output.ts
|
|
2414
2511
|
var sortFactorByContribution = (left, right) => right.contribution - left.contribution || left.factorId.localeCompare(right.factorId);
|
|
2415
2512
|
var toRiskBand = (score) => {
|
|
2416
|
-
if (score <
|
|
2513
|
+
if (score < 20) {
|
|
2417
2514
|
return "low";
|
|
2418
2515
|
}
|
|
2419
|
-
if (score <
|
|
2516
|
+
if (score < 40) {
|
|
2420
2517
|
return "moderate";
|
|
2421
2518
|
}
|
|
2422
|
-
if (score <
|
|
2519
|
+
if (score < 60) {
|
|
2520
|
+
return "elevated";
|
|
2521
|
+
}
|
|
2522
|
+
if (score < 80) {
|
|
2423
2523
|
return "high";
|
|
2424
2524
|
}
|
|
2425
2525
|
return "very_high";
|
|
@@ -2444,6 +2544,7 @@ var factorLabelById2 = {
|
|
|
2444
2544
|
};
|
|
2445
2545
|
var formatFactorLabel = (factorId) => factorLabelById2[factorId] ?? factorId;
|
|
2446
2546
|
var formatNumber = (value) => value === null || value === void 0 ? "n/a" : `${value}`;
|
|
2547
|
+
var formatDimension = (value) => value === null ? "n/a" : `${value}`;
|
|
2447
2548
|
var formatFactorSummary = (factor) => `${formatFactorLabel(factor.factorId)} (+${factor.contribution}, confidence=${factor.confidence})`;
|
|
2448
2549
|
var formatFactorEvidence = (factor) => {
|
|
2449
2550
|
if (factor.factorId === "repository.structural") {
|
|
@@ -2473,6 +2574,36 @@ var formatFactorEvidence = (factor) => {
|
|
|
2473
2574
|
return "evidence available in trace";
|
|
2474
2575
|
};
|
|
2475
2576
|
var findRepositoryTarget = (targets) => targets.find((target) => target.targetType === "repository");
|
|
2577
|
+
var repositoryDimensionScores2 = (repositoryTarget) => {
|
|
2578
|
+
if (repositoryTarget === void 0) {
|
|
2579
|
+
return {
|
|
2580
|
+
structural: null,
|
|
2581
|
+
evolution: null,
|
|
2582
|
+
external: null,
|
|
2583
|
+
interactions: null
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
const structural = repositoryTarget.factors.find(
|
|
2587
|
+
(factor) => factor.factorId === "repository.structural"
|
|
2588
|
+
);
|
|
2589
|
+
const evolution = repositoryTarget.factors.find(
|
|
2590
|
+
(factor) => factor.factorId === "repository.evolution"
|
|
2591
|
+
);
|
|
2592
|
+
const external = repositoryTarget.factors.find(
|
|
2593
|
+
(factor) => factor.factorId === "repository.external"
|
|
2594
|
+
);
|
|
2595
|
+
const interactions = repositoryTarget.factors.find(
|
|
2596
|
+
(factor) => factor.factorId === "repository.composite.interactions"
|
|
2597
|
+
);
|
|
2598
|
+
return {
|
|
2599
|
+
structural: structural === void 0 ? null : Number(((structural.rawMetrics["structuralDimension"] ?? 0) * 100).toFixed(4)),
|
|
2600
|
+
evolution: evolution === void 0 ? null : Number(((evolution.rawMetrics["evolutionDimension"] ?? 0) * 100).toFixed(4)),
|
|
2601
|
+
external: external === void 0 ? null : Number(((external.rawMetrics["externalDimension"] ?? 0) * 100).toFixed(4)),
|
|
2602
|
+
interactions: interactions === void 0 ? null : Number(
|
|
2603
|
+
(((interactions.rawMetrics["structuralEvolution"] ?? 0) + (interactions.rawMetrics["centralInstability"] ?? 0) + (interactions.rawMetrics["dependencyAmplification"] ?? 0)) * 100).toFixed(4)
|
|
2604
|
+
)
|
|
2605
|
+
};
|
|
2606
|
+
};
|
|
2476
2607
|
var buildRepositoryActions = (payload, repositoryTarget) => {
|
|
2477
2608
|
if (repositoryTarget === void 0) {
|
|
2478
2609
|
return ["No repository trace available."];
|
|
@@ -2518,12 +2649,8 @@ var renderTargetText = (target) => {
|
|
|
2518
2649
|
lines.push(" top factors:");
|
|
2519
2650
|
const topFactors = [...target.factors].sort(sortFactorByContribution).slice(0, 5);
|
|
2520
2651
|
for (const factor of topFactors) {
|
|
2521
|
-
lines.push(
|
|
2522
|
-
|
|
2523
|
-
);
|
|
2524
|
-
lines.push(
|
|
2525
|
-
` evidence: ${formatFactorEvidence(factor)}`
|
|
2526
|
-
);
|
|
2652
|
+
lines.push(` - ${formatFactorSummary(factor)}`);
|
|
2653
|
+
lines.push(` evidence: ${formatFactorEvidence(factor)}`);
|
|
2527
2654
|
}
|
|
2528
2655
|
lines.push(" reduction levers:");
|
|
2529
2656
|
for (const lever of target.reductionLevers) {
|
|
@@ -2537,11 +2664,17 @@ var renderText = (payload) => {
|
|
|
2537
2664
|
const lines = [];
|
|
2538
2665
|
const repositoryTarget = findRepositoryTarget(payload.selectedTargets) ?? findRepositoryTarget(payload.trace.targets);
|
|
2539
2666
|
const repositoryTopFactors = repositoryTarget === void 0 ? [] : [...repositoryTarget.factors].sort(sortFactorByContribution).slice(0, 3);
|
|
2667
|
+
const dimensionScores = repositoryDimensionScores2(repositoryTarget);
|
|
2540
2668
|
const compositeFactors = repositoryTopFactors.filter((factor) => factor.family === "composite");
|
|
2541
2669
|
lines.push(`target: ${payload.summary.structural.targetPath}`);
|
|
2542
2670
|
lines.push(`repositoryScore: ${payload.summary.risk.repositoryScore}`);
|
|
2543
2671
|
lines.push(`riskBand: ${toRiskBand(payload.summary.risk.repositoryScore)}`);
|
|
2544
2672
|
lines.push(`selectedTargets: ${payload.selectedTargets.length}`);
|
|
2673
|
+
lines.push("dimensionScores:");
|
|
2674
|
+
lines.push(` structural: ${formatDimension(dimensionScores.structural)}`);
|
|
2675
|
+
lines.push(` evolution: ${formatDimension(dimensionScores.evolution)}`);
|
|
2676
|
+
lines.push(` external: ${formatDimension(dimensionScores.external)}`);
|
|
2677
|
+
lines.push(` interactions: ${formatDimension(dimensionScores.interactions)}`);
|
|
2545
2678
|
lines.push("");
|
|
2546
2679
|
lines.push("explanation:");
|
|
2547
2680
|
lines.push(
|
|
@@ -2570,6 +2703,7 @@ var renderMarkdown = (payload) => {
|
|
|
2570
2703
|
const lines = [];
|
|
2571
2704
|
const repositoryTarget = findRepositoryTarget(payload.selectedTargets) ?? findRepositoryTarget(payload.trace.targets);
|
|
2572
2705
|
const repositoryTopFactors = repositoryTarget === void 0 ? [] : [...repositoryTarget.factors].sort(sortFactorByContribution).slice(0, 3);
|
|
2706
|
+
const dimensionScores = repositoryDimensionScores2(repositoryTarget);
|
|
2573
2707
|
const compositeFactors = repositoryTopFactors.filter((factor) => factor.family === "composite");
|
|
2574
2708
|
lines.push(`# CodeSentinel Explanation`);
|
|
2575
2709
|
lines.push(`- target: \`${payload.summary.structural.targetPath}\``);
|
|
@@ -2577,6 +2711,12 @@ var renderMarkdown = (payload) => {
|
|
|
2577
2711
|
lines.push(`- riskBand: \`${toRiskBand(payload.summary.risk.repositoryScore)}\``);
|
|
2578
2712
|
lines.push(`- selectedTargets: \`${payload.selectedTargets.length}\``);
|
|
2579
2713
|
lines.push("");
|
|
2714
|
+
lines.push("## Dimension Scores (0-100)");
|
|
2715
|
+
lines.push(`- structural: \`${formatDimension(dimensionScores.structural)}\``);
|
|
2716
|
+
lines.push(`- evolution: \`${formatDimension(dimensionScores.evolution)}\``);
|
|
2717
|
+
lines.push(`- external: \`${formatDimension(dimensionScores.external)}\``);
|
|
2718
|
+
lines.push(`- interactions: \`${formatDimension(dimensionScores.interactions)}\``);
|
|
2719
|
+
lines.push("");
|
|
2580
2720
|
lines.push(`## Summary`);
|
|
2581
2721
|
lines.push(
|
|
2582
2722
|
`- why risky: ${repositoryTopFactors.map(formatFactorSummary).join("; ") || "insufficient data"}`
|
|
@@ -2603,9 +2743,7 @@ var renderMarkdown = (payload) => {
|
|
|
2603
2743
|
lines.push(
|
|
2604
2744
|
` - \`${formatFactorLabel(factor.factorId)}\` contribution=\`${factor.contribution}\` confidence=\`${factor.confidence}\``
|
|
2605
2745
|
);
|
|
2606
|
-
lines.push(
|
|
2607
|
-
` - evidence: \`${formatFactorEvidence(factor)}\``
|
|
2608
|
-
);
|
|
2746
|
+
lines.push(` - evidence: \`${formatFactorEvidence(factor)}\``);
|
|
2609
2747
|
}
|
|
2610
2748
|
lines.push(`- Reduction levers:`);
|
|
2611
2749
|
for (const lever of target.reductionLevers) {
|
|
@@ -2719,6 +2857,337 @@ var parseLogLevel = (value) => {
|
|
|
2719
2857
|
}
|
|
2720
2858
|
};
|
|
2721
2859
|
|
|
2860
|
+
// src/application/check-for-updates.ts
|
|
2861
|
+
import { spawn } from "child_process";
|
|
2862
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
2863
|
+
import { homedir } from "os";
|
|
2864
|
+
import { dirname, join as join3 } from "path";
|
|
2865
|
+
import { stderr, stdin } from "process";
|
|
2866
|
+
import { clearScreenDown, cursorTo, emitKeypressEvents, moveCursor } from "readline";
|
|
2867
|
+
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2868
|
+
var UPDATE_CACHE_PATH = join3(homedir(), ".cache", "codesentinel", "update-check.json");
|
|
2869
|
+
var SEMVER_PATTERN = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:-(?<prerelease>[0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
2870
|
+
var ANSI = {
|
|
2871
|
+
reset: "\x1B[0m",
|
|
2872
|
+
bold: "\x1B[1m",
|
|
2873
|
+
dim: "\x1B[2m",
|
|
2874
|
+
cyan: "\x1B[36m",
|
|
2875
|
+
green: "\x1B[32m",
|
|
2876
|
+
yellow: "\x1B[33m"
|
|
2877
|
+
};
|
|
2878
|
+
var parsePrereleaseIdentifier = (identifier) => {
|
|
2879
|
+
if (/^\d+$/.test(identifier)) {
|
|
2880
|
+
return Number.parseInt(identifier, 10);
|
|
2881
|
+
}
|
|
2882
|
+
return identifier;
|
|
2883
|
+
};
|
|
2884
|
+
var parseSemver2 = (value) => {
|
|
2885
|
+
const match = SEMVER_PATTERN.exec(value.trim());
|
|
2886
|
+
if (match === null) {
|
|
2887
|
+
return null;
|
|
2888
|
+
}
|
|
2889
|
+
const groups = match.groups;
|
|
2890
|
+
if (groups === void 0) {
|
|
2891
|
+
return null;
|
|
2892
|
+
}
|
|
2893
|
+
const majorRaw = groups["major"];
|
|
2894
|
+
const minorRaw = groups["minor"];
|
|
2895
|
+
const patchRaw = groups["patch"];
|
|
2896
|
+
const prereleaseRaw = groups["prerelease"];
|
|
2897
|
+
if (majorRaw === void 0 || minorRaw === void 0 || patchRaw === void 0) {
|
|
2898
|
+
return null;
|
|
2899
|
+
}
|
|
2900
|
+
const prerelease = prereleaseRaw === void 0 || prereleaseRaw.length === 0 ? [] : prereleaseRaw.split(".").map(parsePrereleaseIdentifier);
|
|
2901
|
+
return {
|
|
2902
|
+
major: Number.parseInt(majorRaw, 10),
|
|
2903
|
+
minor: Number.parseInt(minorRaw, 10),
|
|
2904
|
+
patch: Number.parseInt(patchRaw, 10),
|
|
2905
|
+
prerelease
|
|
2906
|
+
};
|
|
2907
|
+
};
|
|
2908
|
+
var comparePrerelease = (left, right) => {
|
|
2909
|
+
if (left.length === 0 && right.length === 0) {
|
|
2910
|
+
return 0;
|
|
2911
|
+
}
|
|
2912
|
+
if (left.length === 0) {
|
|
2913
|
+
return 1;
|
|
2914
|
+
}
|
|
2915
|
+
if (right.length === 0) {
|
|
2916
|
+
return -1;
|
|
2917
|
+
}
|
|
2918
|
+
const length = Math.max(left.length, right.length);
|
|
2919
|
+
for (let index = 0; index < length; index += 1) {
|
|
2920
|
+
const leftValue = left[index];
|
|
2921
|
+
const rightValue = right[index];
|
|
2922
|
+
if (leftValue === void 0) {
|
|
2923
|
+
return -1;
|
|
2924
|
+
}
|
|
2925
|
+
if (rightValue === void 0) {
|
|
2926
|
+
return 1;
|
|
2927
|
+
}
|
|
2928
|
+
if (typeof leftValue === "number" && typeof rightValue === "number") {
|
|
2929
|
+
if (leftValue !== rightValue) {
|
|
2930
|
+
return leftValue > rightValue ? 1 : -1;
|
|
2931
|
+
}
|
|
2932
|
+
continue;
|
|
2933
|
+
}
|
|
2934
|
+
if (typeof leftValue === "number" && typeof rightValue === "string") {
|
|
2935
|
+
return -1;
|
|
2936
|
+
}
|
|
2937
|
+
if (typeof leftValue === "string" && typeof rightValue === "number") {
|
|
2938
|
+
return 1;
|
|
2939
|
+
}
|
|
2940
|
+
if (leftValue !== rightValue) {
|
|
2941
|
+
return leftValue > rightValue ? 1 : -1;
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
return 0;
|
|
2945
|
+
};
|
|
2946
|
+
var compareVersions = (left, right) => {
|
|
2947
|
+
const leftParsed = parseSemver2(left);
|
|
2948
|
+
const rightParsed = parseSemver2(right);
|
|
2949
|
+
if (leftParsed === null || rightParsed === null) {
|
|
2950
|
+
return null;
|
|
2951
|
+
}
|
|
2952
|
+
if (leftParsed.major !== rightParsed.major) {
|
|
2953
|
+
return leftParsed.major > rightParsed.major ? 1 : -1;
|
|
2954
|
+
}
|
|
2955
|
+
if (leftParsed.minor !== rightParsed.minor) {
|
|
2956
|
+
return leftParsed.minor > rightParsed.minor ? 1 : -1;
|
|
2957
|
+
}
|
|
2958
|
+
if (leftParsed.patch !== rightParsed.patch) {
|
|
2959
|
+
return leftParsed.patch > rightParsed.patch ? 1 : -1;
|
|
2960
|
+
}
|
|
2961
|
+
return comparePrerelease(leftParsed.prerelease, rightParsed.prerelease);
|
|
2962
|
+
};
|
|
2963
|
+
var isTruthy = (value) => {
|
|
2964
|
+
if (value === void 0) {
|
|
2965
|
+
return false;
|
|
2966
|
+
}
|
|
2967
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
2968
|
+
};
|
|
2969
|
+
var parseNpmViewVersionOutput = (output) => {
|
|
2970
|
+
const trimmed = output.trim();
|
|
2971
|
+
if (trimmed.length === 0) {
|
|
2972
|
+
return null;
|
|
2973
|
+
}
|
|
2974
|
+
try {
|
|
2975
|
+
const parsed = JSON.parse(trimmed);
|
|
2976
|
+
if (typeof parsed === "string" && parsed.trim().length > 0) {
|
|
2977
|
+
return parsed.trim();
|
|
2978
|
+
}
|
|
2979
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
2980
|
+
const latest = parsed.at(-1);
|
|
2981
|
+
if (typeof latest === "string" && latest.trim().length > 0) {
|
|
2982
|
+
return latest.trim();
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
} catch {
|
|
2986
|
+
return trimmed;
|
|
2987
|
+
}
|
|
2988
|
+
return null;
|
|
2989
|
+
};
|
|
2990
|
+
var readCache = async () => {
|
|
2991
|
+
try {
|
|
2992
|
+
const raw = await readFile(UPDATE_CACHE_PATH, "utf8");
|
|
2993
|
+
const parsed = JSON.parse(raw);
|
|
2994
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.lastCheckedAt === "string") {
|
|
2995
|
+
return { lastCheckedAt: parsed.lastCheckedAt };
|
|
2996
|
+
}
|
|
2997
|
+
} catch {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
return null;
|
|
3001
|
+
};
|
|
3002
|
+
var writeCache = async (cache) => {
|
|
3003
|
+
await mkdir(dirname(UPDATE_CACHE_PATH), { recursive: true });
|
|
3004
|
+
await writeFile(UPDATE_CACHE_PATH, JSON.stringify(cache), "utf8");
|
|
3005
|
+
};
|
|
3006
|
+
var shouldRunUpdateCheck = (input) => {
|
|
3007
|
+
if (!input.isInteractive) {
|
|
3008
|
+
return false;
|
|
3009
|
+
}
|
|
3010
|
+
if (isTruthy(input.env["CI"])) {
|
|
3011
|
+
return false;
|
|
3012
|
+
}
|
|
3013
|
+
if (isTruthy(input.env["CODESENTINEL_NO_UPDATE_NOTIFIER"])) {
|
|
3014
|
+
return false;
|
|
3015
|
+
}
|
|
3016
|
+
if (input.argv.some((argument) => argument === "--help" || argument === "-h")) {
|
|
3017
|
+
return false;
|
|
3018
|
+
}
|
|
3019
|
+
if (input.argv.some((argument) => argument === "--version" || argument === "-V")) {
|
|
3020
|
+
return false;
|
|
3021
|
+
}
|
|
3022
|
+
if (input.lastCheckedAt === null) {
|
|
3023
|
+
return true;
|
|
3024
|
+
}
|
|
3025
|
+
const lastCheckedMs = Date.parse(input.lastCheckedAt);
|
|
3026
|
+
if (!Number.isFinite(lastCheckedMs)) {
|
|
3027
|
+
return true;
|
|
3028
|
+
}
|
|
3029
|
+
return input.nowMs - lastCheckedMs >= UPDATE_CHECK_INTERVAL_MS;
|
|
3030
|
+
};
|
|
3031
|
+
var runCommand = async (command, args, mode) => {
|
|
3032
|
+
return await new Promise((resolvePromise, reject) => {
|
|
3033
|
+
const child = spawn(command, [...args], {
|
|
3034
|
+
stdio: mode === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"]
|
|
3035
|
+
});
|
|
3036
|
+
let stdoutRaw = "";
|
|
3037
|
+
if (mode === "capture" && child.stdout !== null) {
|
|
3038
|
+
child.stdout.setEncoding("utf8");
|
|
3039
|
+
child.stdout.on("data", (chunk) => {
|
|
3040
|
+
stdoutRaw += chunk;
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
child.on("error", (error) => {
|
|
3044
|
+
reject(error);
|
|
3045
|
+
});
|
|
3046
|
+
child.on("close", (code) => {
|
|
3047
|
+
resolvePromise({ code: code ?? 1, stdout: stdoutRaw });
|
|
3048
|
+
});
|
|
3049
|
+
});
|
|
3050
|
+
};
|
|
3051
|
+
var fetchLatestVersion = async (packageName) => {
|
|
3052
|
+
const result = await runCommand("npm", ["view", packageName, "version", "--json"], "capture");
|
|
3053
|
+
if (result.code !== 0) {
|
|
3054
|
+
return null;
|
|
3055
|
+
}
|
|
3056
|
+
return parseNpmViewVersionOutput(result.stdout);
|
|
3057
|
+
};
|
|
3058
|
+
var renderUpdatePrompt = (latestVersion, currentVersion, selectedIndex) => {
|
|
3059
|
+
const options = ["Install update now", "Not now (continue current command)"];
|
|
3060
|
+
const lines = [
|
|
3061
|
+
`${ANSI.cyan}${ANSI.bold}CodeSentinel Update Available${ANSI.reset}`,
|
|
3062
|
+
`${ANSI.dim}Current: ${currentVersion} Latest: ${latestVersion}${ANSI.reset}`,
|
|
3063
|
+
"",
|
|
3064
|
+
...options.map((option, index) => {
|
|
3065
|
+
const selected = index === selectedIndex;
|
|
3066
|
+
const prefix = selected ? `${ANSI.green}>${ANSI.reset}` : " ";
|
|
3067
|
+
const text = selected ? `${ANSI.bold}${option}${ANSI.reset}` : option;
|
|
3068
|
+
return `${prefix} ${text}`;
|
|
3069
|
+
}),
|
|
3070
|
+
"",
|
|
3071
|
+
`${ANSI.dim}Use \u2191/\u2193 to choose, Enter to confirm.${ANSI.reset}`
|
|
3072
|
+
];
|
|
3073
|
+
stderr.write(lines.join("\n"));
|
|
3074
|
+
return lines.length;
|
|
3075
|
+
};
|
|
3076
|
+
var promptInstall = async (latestVersion, currentVersion) => {
|
|
3077
|
+
if (!stdin.isTTY || !stderr.isTTY || typeof stdin.setRawMode !== "function") {
|
|
3078
|
+
stderr.write(
|
|
3079
|
+
`New version ${latestVersion} is available (current ${currentVersion}). Run: npm install -g @getcodesentinel/codesentinel@latest
|
|
3080
|
+
`
|
|
3081
|
+
);
|
|
3082
|
+
return "skip";
|
|
3083
|
+
}
|
|
3084
|
+
return await new Promise((resolve6) => {
|
|
3085
|
+
emitKeypressEvents(stdin);
|
|
3086
|
+
let selectedIndex = 0;
|
|
3087
|
+
let renderedLines = 0;
|
|
3088
|
+
const previousRawMode = stdin.isRaw;
|
|
3089
|
+
const clearPromptArea = () => {
|
|
3090
|
+
if (renderedLines > 0) {
|
|
3091
|
+
moveCursor(stderr, 0, -(renderedLines - 1));
|
|
3092
|
+
}
|
|
3093
|
+
cursorTo(stderr, 0);
|
|
3094
|
+
clearScreenDown(stderr);
|
|
3095
|
+
};
|
|
3096
|
+
const redraw = () => {
|
|
3097
|
+
clearPromptArea();
|
|
3098
|
+
renderedLines = renderUpdatePrompt(latestVersion, currentVersion, selectedIndex);
|
|
3099
|
+
};
|
|
3100
|
+
const cleanup = (choice) => {
|
|
3101
|
+
stdin.off("keypress", onKeypress);
|
|
3102
|
+
stdin.pause();
|
|
3103
|
+
if (typeof stdin.setRawMode === "function") {
|
|
3104
|
+
stdin.setRawMode(previousRawMode);
|
|
3105
|
+
}
|
|
3106
|
+
clearPromptArea();
|
|
3107
|
+
if (choice === "install") {
|
|
3108
|
+
stderr.write(`${ANSI.yellow}Installing latest CodeSentinel...${ANSI.reset}
|
|
3109
|
+
`);
|
|
3110
|
+
} else if (renderedLines > 0) {
|
|
3111
|
+
stderr.write("\n");
|
|
3112
|
+
}
|
|
3113
|
+
resolve6(choice);
|
|
3114
|
+
};
|
|
3115
|
+
const onKeypress = (_str, key) => {
|
|
3116
|
+
if (key.ctrl === true && key.name === "c") {
|
|
3117
|
+
cleanup("interrupt");
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
if (key.name === "up") {
|
|
3121
|
+
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : 1;
|
|
3122
|
+
redraw();
|
|
3123
|
+
return;
|
|
3124
|
+
}
|
|
3125
|
+
if (key.name === "down") {
|
|
3126
|
+
selectedIndex = selectedIndex < 1 ? selectedIndex + 1 : 0;
|
|
3127
|
+
redraw();
|
|
3128
|
+
return;
|
|
3129
|
+
}
|
|
3130
|
+
if (key.name === "return" || key.name === "enter") {
|
|
3131
|
+
cleanup(selectedIndex === 0 ? "install" : "skip");
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
stdin.on("keypress", onKeypress);
|
|
3135
|
+
if (typeof stdin.setRawMode === "function") {
|
|
3136
|
+
stdin.setRawMode(true);
|
|
3137
|
+
}
|
|
3138
|
+
stdin.resume();
|
|
3139
|
+
redraw();
|
|
3140
|
+
});
|
|
3141
|
+
};
|
|
3142
|
+
var installLatestVersion = async (packageName) => {
|
|
3143
|
+
const result = await runCommand("npm", ["install", "-g", `${packageName}@latest`], "inherit");
|
|
3144
|
+
return result.code === 0;
|
|
3145
|
+
};
|
|
3146
|
+
var checkForCliUpdates = async (input) => {
|
|
3147
|
+
try {
|
|
3148
|
+
const nowMs = Date.now();
|
|
3149
|
+
const cache = await readCache();
|
|
3150
|
+
const shouldCheck = shouldRunUpdateCheck({
|
|
3151
|
+
argv: input.argv,
|
|
3152
|
+
env: input.env,
|
|
3153
|
+
isInteractive: Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY),
|
|
3154
|
+
nowMs,
|
|
3155
|
+
lastCheckedAt: cache?.lastCheckedAt ?? null
|
|
3156
|
+
});
|
|
3157
|
+
if (!shouldCheck) {
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
await writeCache({ lastCheckedAt: new Date(nowMs).toISOString() });
|
|
3161
|
+
const latestVersion = await fetchLatestVersion(input.packageName);
|
|
3162
|
+
if (latestVersion === null) {
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
const comparison = compareVersions(latestVersion, input.currentVersion);
|
|
3166
|
+
if (comparison === null || comparison <= 0) {
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
const choice = await promptInstall(latestVersion, input.currentVersion);
|
|
3170
|
+
if (choice === "interrupt") {
|
|
3171
|
+
process.exit(130);
|
|
3172
|
+
}
|
|
3173
|
+
if (choice !== "install") {
|
|
3174
|
+
return;
|
|
3175
|
+
}
|
|
3176
|
+
const installed = await installLatestVersion(input.packageName);
|
|
3177
|
+
if (installed) {
|
|
3178
|
+
stderr.write(
|
|
3179
|
+
"CodeSentinel updated to latest version. Rerun your command to use the new version.\n"
|
|
3180
|
+
);
|
|
3181
|
+
process.exit(0);
|
|
3182
|
+
} else {
|
|
3183
|
+
stderr.write(
|
|
3184
|
+
"CodeSentinel update failed. You can retry with: npm install -g @getcodesentinel/codesentinel@latest\n"
|
|
3185
|
+
);
|
|
3186
|
+
}
|
|
3187
|
+
} catch {
|
|
3188
|
+
}
|
|
3189
|
+
};
|
|
3190
|
+
|
|
2722
3191
|
// src/application/run-analyze-command.ts
|
|
2723
3192
|
import { resolve as resolve3 } from "path";
|
|
2724
3193
|
|
|
@@ -3031,7 +3500,11 @@ var parseTsConfigFile = (configPath) => {
|
|
|
3031
3500
|
return parsedCommandLine;
|
|
3032
3501
|
};
|
|
3033
3502
|
var collectFilesFromTsConfigGraph = (projectRoot) => {
|
|
3034
|
-
const rootConfigPath = ts.findConfigFile(
|
|
3503
|
+
const rootConfigPath = ts.findConfigFile(
|
|
3504
|
+
projectRoot,
|
|
3505
|
+
(filePath) => ts.sys.fileExists(filePath),
|
|
3506
|
+
"tsconfig.json"
|
|
3507
|
+
);
|
|
3035
3508
|
if (rootConfigPath === void 0) {
|
|
3036
3509
|
return null;
|
|
3037
3510
|
}
|
|
@@ -3053,7 +3526,11 @@ var collectFilesFromTsConfigGraph = (projectRoot) => {
|
|
|
3053
3526
|
}
|
|
3054
3527
|
for (const reference of parsed.projectReferences ?? []) {
|
|
3055
3528
|
const referencePath = resolve2(reference.path);
|
|
3056
|
-
const referenceConfigPath = ts.sys.directoryExists(referencePath) ? ts.findConfigFile(
|
|
3529
|
+
const referenceConfigPath = ts.sys.directoryExists(referencePath) ? ts.findConfigFile(
|
|
3530
|
+
referencePath,
|
|
3531
|
+
(filePath) => ts.sys.fileExists(filePath),
|
|
3532
|
+
"tsconfig.json"
|
|
3533
|
+
) : referencePath;
|
|
3057
3534
|
if (referenceConfigPath !== void 0 && ts.sys.fileExists(referenceConfigPath)) {
|
|
3058
3535
|
visitConfig(referenceConfigPath);
|
|
3059
3536
|
}
|
|
@@ -3215,7 +3692,12 @@ var parseTypescriptProject = (projectPath, onProgress) => {
|
|
|
3215
3692
|
const cacheKey = `${sourcePath}\0${specifier}`;
|
|
3216
3693
|
let resolvedPath = resolverCache.get(cacheKey);
|
|
3217
3694
|
if (resolvedPath === void 0 && !resolverCache.has(cacheKey)) {
|
|
3218
|
-
const resolved = ts.resolveModuleName(
|
|
3695
|
+
const resolved = ts.resolveModuleName(
|
|
3696
|
+
specifier,
|
|
3697
|
+
sourcePath,
|
|
3698
|
+
options,
|
|
3699
|
+
ts.sys
|
|
3700
|
+
).resolvedModule;
|
|
3219
3701
|
if (resolved !== void 0) {
|
|
3220
3702
|
resolvedPath = normalizePath(resolve2(resolved.resolvedFileName));
|
|
3221
3703
|
}
|
|
@@ -3299,7 +3781,10 @@ var buildAuthorAliasMap = (commits) => {
|
|
|
3299
3781
|
const nameCountsByAuthorId = /* @__PURE__ */ new Map();
|
|
3300
3782
|
const commitCountByAuthorId = /* @__PURE__ */ new Map();
|
|
3301
3783
|
for (const commit of commits) {
|
|
3302
|
-
commitCountByAuthorId.set(
|
|
3784
|
+
commitCountByAuthorId.set(
|
|
3785
|
+
commit.authorId,
|
|
3786
|
+
(commitCountByAuthorId.get(commit.authorId) ?? 0) + 1
|
|
3787
|
+
);
|
|
3303
3788
|
const normalizedName = normalizeName(commit.authorName);
|
|
3304
3789
|
const names = nameCountsByAuthorId.get(commit.authorId) ?? /* @__PURE__ */ new Map();
|
|
3305
3790
|
if (normalizedName.length > 0) {
|
|
@@ -3307,19 +3792,21 @@ var buildAuthorAliasMap = (commits) => {
|
|
|
3307
3792
|
}
|
|
3308
3793
|
nameCountsByAuthorId.set(commit.authorId, names);
|
|
3309
3794
|
}
|
|
3310
|
-
const profiles = [...commitCountByAuthorId.entries()].map(
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3795
|
+
const profiles = [...commitCountByAuthorId.entries()].map(
|
|
3796
|
+
([authorId, commitCount]) => {
|
|
3797
|
+
const names = nameCountsByAuthorId.get(authorId);
|
|
3798
|
+
const primaryName = names === void 0 ? "" : [...names.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? "";
|
|
3799
|
+
const normalizedAuthorId = authorId.toLowerCase();
|
|
3800
|
+
const isBot = normalizedAuthorId.includes("[bot]");
|
|
3801
|
+
return {
|
|
3802
|
+
authorId,
|
|
3803
|
+
commitCount,
|
|
3804
|
+
primaryName,
|
|
3805
|
+
emailStem: isBot ? null : extractEmailStem(authorId),
|
|
3806
|
+
isBot
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3809
|
+
);
|
|
3323
3810
|
const groupsByStem = /* @__PURE__ */ new Map();
|
|
3324
3811
|
for (const profile of profiles) {
|
|
3325
3812
|
if (profile.emailStem === null || profile.emailStem.length < 4) {
|
|
@@ -3425,7 +3912,10 @@ var selectHotspots = (files, config) => {
|
|
|
3425
3912
|
const sorted = [...files].sort(
|
|
3426
3913
|
(a, b) => b.commitCount - a.commitCount || b.churnTotal - a.churnTotal || a.filePath.localeCompare(b.filePath)
|
|
3427
3914
|
);
|
|
3428
|
-
const hotspotCount = Math.max(
|
|
3915
|
+
const hotspotCount = Math.max(
|
|
3916
|
+
config.hotspotMinFiles,
|
|
3917
|
+
Math.ceil(sorted.length * config.hotspotTopPercent)
|
|
3918
|
+
);
|
|
3429
3919
|
const selected = sorted.slice(0, hotspotCount);
|
|
3430
3920
|
const hotspots = selected.map((file, index) => ({
|
|
3431
3921
|
filePath: file.filePath,
|
|
@@ -3734,7 +4224,10 @@ var GitCliHistoryProvider = class {
|
|
|
3734
4224
|
"--find-renames"
|
|
3735
4225
|
]);
|
|
3736
4226
|
onProgress?.({ stage: "git_log_received", bytes: Buffer.byteLength(output, "utf8") });
|
|
3737
|
-
const commits = parseGitLog(
|
|
4227
|
+
const commits = parseGitLog(
|
|
4228
|
+
output,
|
|
4229
|
+
(event) => onProgress?.(mapParseProgressToHistoryProgress(event))
|
|
4230
|
+
);
|
|
3738
4231
|
onProgress?.({ stage: "git_log_parsed", commits: commits.length });
|
|
3739
4232
|
return commits;
|
|
3740
4233
|
}
|
|
@@ -3920,7 +4413,10 @@ var dependencySignalWeightBudget = Object.values(dependencySignalWeights).reduce
|
|
|
3920
4413
|
0
|
|
3921
4414
|
);
|
|
3922
4415
|
var computeDependencySignalScore = (ownSignals, inheritedSignals, inheritedSignalMultiplier) => {
|
|
3923
|
-
const ownWeight = ownSignals.reduce(
|
|
4416
|
+
const ownWeight = ownSignals.reduce(
|
|
4417
|
+
(sum, signal) => sum + (dependencySignalWeights[signal] ?? 0),
|
|
4418
|
+
0
|
|
4419
|
+
);
|
|
3924
4420
|
const inheritedWeight = inheritedSignals.reduce(
|
|
3925
4421
|
(sum, signal) => sum + (dependencySignalWeights[signal] ?? 0),
|
|
3926
4422
|
0
|
|
@@ -3986,16 +4482,12 @@ var buildFactorTraces = (totalScore, inputs) => {
|
|
|
3986
4482
|
}
|
|
3987
4483
|
return traces;
|
|
3988
4484
|
};
|
|
3989
|
-
var buildReductionLevers = (factors) => factors.filter((factor) => factor.contribution > 0).sort(
|
|
3990
|
-
(a, b) => b.contribution - a.contribution || a.factorId.localeCompare(b.factorId)
|
|
3991
|
-
).slice(0, 3).map((factor) => ({
|
|
4485
|
+
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) => ({
|
|
3992
4486
|
factorId: factor.factorId,
|
|
3993
4487
|
estimatedImpact: round45(factor.contribution)
|
|
3994
4488
|
}));
|
|
3995
4489
|
var buildTargetTrace = (targetType, targetId, totalScore, normalizedScore, factors) => {
|
|
3996
|
-
const dominantFactors = [...factors].filter((factor) => factor.contribution > 0).sort(
|
|
3997
|
-
(a, b) => b.contribution - a.contribution || a.factorId.localeCompare(b.factorId)
|
|
3998
|
-
).slice(0, 3).map((factor) => factor.factorId);
|
|
4490
|
+
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);
|
|
3999
4491
|
return {
|
|
4000
4492
|
targetType,
|
|
4001
4493
|
targetId,
|
|
@@ -4017,7 +4509,9 @@ var computeDependencyScores = (external, config) => {
|
|
|
4017
4509
|
const transitiveCounts = external.dependencies.map(
|
|
4018
4510
|
(dependency) => logScale(dependency.transitiveDependencies.length)
|
|
4019
4511
|
);
|
|
4020
|
-
const dependentCounts = external.dependencies.map(
|
|
4512
|
+
const dependentCounts = external.dependencies.map(
|
|
4513
|
+
(dependency) => logScale(dependency.dependents)
|
|
4514
|
+
);
|
|
4021
4515
|
const chainDepths = external.dependencies.map((dependency) => dependency.dependencyDepth);
|
|
4022
4516
|
const transitiveScale = buildQuantileScale(
|
|
4023
4517
|
transitiveCounts,
|
|
@@ -4312,8 +4806,14 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4312
4806
|
let busFactorRisk = 0;
|
|
4313
4807
|
const evolutionMetrics = evolutionByFile.get(filePath);
|
|
4314
4808
|
if (evolution.available && evolutionMetrics !== void 0) {
|
|
4315
|
-
frequencyRisk = normalizeWithScale(
|
|
4316
|
-
|
|
4809
|
+
frequencyRisk = normalizeWithScale(
|
|
4810
|
+
logScale(evolutionMetrics.commitCount),
|
|
4811
|
+
evolutionScales.commitCount
|
|
4812
|
+
);
|
|
4813
|
+
churnRisk = normalizeWithScale(
|
|
4814
|
+
logScale(evolutionMetrics.churnTotal),
|
|
4815
|
+
evolutionScales.churnTotal
|
|
4816
|
+
);
|
|
4317
4817
|
volatilityRisk = toUnitInterval(evolutionMetrics.recentVolatility);
|
|
4318
4818
|
ownershipConcentrationRisk = toUnitInterval(evolutionMetrics.topAuthorShare);
|
|
4319
4819
|
busFactorRisk = toUnitInterval(
|
|
@@ -4567,7 +5067,9 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4567
5067
|
externalPressure: round45(zone.externalPressure)
|
|
4568
5068
|
}));
|
|
4569
5069
|
if (collector !== void 0 && external.available) {
|
|
4570
|
-
const dependencyByName = new Map(
|
|
5070
|
+
const dependencyByName = new Map(
|
|
5071
|
+
external.dependencies.map((dependency) => [dependency.name, dependency])
|
|
5072
|
+
);
|
|
4571
5073
|
for (const dependencyScore of dependencyComputation.dependencyScores) {
|
|
4572
5074
|
const dependency = dependencyByName.get(dependencyScore.dependency);
|
|
4573
5075
|
const context = dependencyComputation.dependencyContexts.get(dependencyScore.dependency);
|
|
@@ -4598,7 +5100,9 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4598
5100
|
normalizedMetrics: { stalenessRisk: context.stalenessRisk },
|
|
4599
5101
|
weight: config.dependencyFactorWeights.staleness,
|
|
4600
5102
|
amplification: null,
|
|
4601
|
-
evidence: [
|
|
5103
|
+
evidence: [
|
|
5104
|
+
{ kind: "dependency_metric", target: dependency.name, metric: "daysSinceLastRelease" }
|
|
5105
|
+
],
|
|
4602
5106
|
confidence: hasMetadata ? 0.9 : 0.5
|
|
4603
5107
|
},
|
|
4604
5108
|
{
|
|
@@ -4611,7 +5115,9 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4611
5115
|
},
|
|
4612
5116
|
weight: config.dependencyFactorWeights.maintainerConcentration,
|
|
4613
5117
|
amplification: null,
|
|
4614
|
-
evidence: [
|
|
5118
|
+
evidence: [
|
|
5119
|
+
{ kind: "dependency_metric", target: dependency.name, metric: "maintainerCount" }
|
|
5120
|
+
],
|
|
4615
5121
|
confidence: hasMetadata ? 0.9 : 0.5
|
|
4616
5122
|
},
|
|
4617
5123
|
{
|
|
@@ -4630,7 +5136,9 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4630
5136
|
},
|
|
4631
5137
|
weight: config.dependencyFactorWeights.transitiveBurden + config.dependencyFactorWeights.centrality + config.dependencyFactorWeights.chainDepth,
|
|
4632
5138
|
amplification: null,
|
|
4633
|
-
evidence: [
|
|
5139
|
+
evidence: [
|
|
5140
|
+
{ kind: "dependency_metric", target: dependency.name, metric: "dependencyDepth" }
|
|
5141
|
+
],
|
|
4634
5142
|
confidence: 1
|
|
4635
5143
|
},
|
|
4636
5144
|
{
|
|
@@ -4652,7 +5160,9 @@ var computeRiskSummary = (structural, evolution, external, config, traceCollecto
|
|
|
4652
5160
|
normalizedMetrics: { popularityDampener: context.popularityDampener },
|
|
4653
5161
|
weight: config.dependencySignals.popularityMaxDampening,
|
|
4654
5162
|
amplification: null,
|
|
4655
|
-
evidence: [
|
|
5163
|
+
evidence: [
|
|
5164
|
+
{ kind: "dependency_metric", target: dependency.name, metric: "weeklyDownloads" }
|
|
5165
|
+
],
|
|
4656
5166
|
confidence: context.rawMetrics.weeklyDownloads === null ? 0.4 : 0.9
|
|
4657
5167
|
}
|
|
4658
5168
|
]);
|
|
@@ -5041,7 +5551,12 @@ var collectAnalysisInputs = async (inputPath, authorIdentityMode, options = {},
|
|
|
5041
5551
|
};
|
|
5042
5552
|
};
|
|
5043
5553
|
var runAnalyzeCommand = async (inputPath, authorIdentityMode, options = {}, logger = createSilentLogger()) => {
|
|
5044
|
-
const analysisInputs = await collectAnalysisInputs(
|
|
5554
|
+
const analysisInputs = await collectAnalysisInputs(
|
|
5555
|
+
inputPath,
|
|
5556
|
+
authorIdentityMode,
|
|
5557
|
+
options,
|
|
5558
|
+
logger
|
|
5559
|
+
);
|
|
5045
5560
|
logger.info("computing risk summary");
|
|
5046
5561
|
const risk = computeRepositoryRiskSummary(analysisInputs);
|
|
5047
5562
|
logger.info(`analysis completed (repositoryScore=${risk.repositoryScore})`);
|
|
@@ -5052,7 +5567,7 @@ var runAnalyzeCommand = async (inputPath, authorIdentityMode, options = {}, logg
|
|
|
5052
5567
|
};
|
|
5053
5568
|
|
|
5054
5569
|
// src/application/run-check-command.ts
|
|
5055
|
-
import { readFile, writeFile } from "fs/promises";
|
|
5570
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
5056
5571
|
|
|
5057
5572
|
// src/application/build-analysis-snapshot.ts
|
|
5058
5573
|
var buildAnalysisSnapshot = async (inputPath, authorIdentityMode, options, logger) => {
|
|
@@ -5117,7 +5632,7 @@ var runCheckCommand = async (inputPath, authorIdentityMode, options, logger = cr
|
|
|
5117
5632
|
let diff;
|
|
5118
5633
|
if (options.baselinePath !== void 0) {
|
|
5119
5634
|
logger.info(`loading baseline snapshot: ${options.baselinePath}`);
|
|
5120
|
-
const baselineRaw = await
|
|
5635
|
+
const baselineRaw = await readFile2(options.baselinePath, "utf8");
|
|
5121
5636
|
try {
|
|
5122
5637
|
baseline = parseSnapshot(baselineRaw);
|
|
5123
5638
|
} catch (error) {
|
|
@@ -5143,7 +5658,7 @@ var runCheckCommand = async (inputPath, authorIdentityMode, options, logger = cr
|
|
|
5143
5658
|
options.outputFormat
|
|
5144
5659
|
);
|
|
5145
5660
|
if (options.outputPath !== void 0) {
|
|
5146
|
-
await
|
|
5661
|
+
await writeFile2(options.outputPath, rendered, "utf8");
|
|
5147
5662
|
logger.info(`check output written: ${options.outputPath}`);
|
|
5148
5663
|
}
|
|
5149
5664
|
return {
|
|
@@ -5156,7 +5671,7 @@ var runCheckCommand = async (inputPath, authorIdentityMode, options, logger = cr
|
|
|
5156
5671
|
};
|
|
5157
5672
|
|
|
5158
5673
|
// src/application/run-ci-command.ts
|
|
5159
|
-
import { readFile as
|
|
5674
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
5160
5675
|
import { relative as relative2, resolve as resolve4 } from "path";
|
|
5161
5676
|
var isPathOutsideBase = (value) => {
|
|
5162
5677
|
return value === ".." || value.startsWith("../") || value.startsWith("..\\");
|
|
@@ -5168,9 +5683,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
|
|
|
5168
5683
|
);
|
|
5169
5684
|
}
|
|
5170
5685
|
if (options.baselineSha !== void 0 && options.baselineRef !== "auto") {
|
|
5171
|
-
throw new GovernanceConfigurationError(
|
|
5172
|
-
"baseline-sha requires --baseline-ref auto"
|
|
5173
|
-
);
|
|
5686
|
+
throw new GovernanceConfigurationError("baseline-sha requires --baseline-ref auto");
|
|
5174
5687
|
}
|
|
5175
5688
|
const resolvedTargetPath = resolve4(inputPath ?? process.cwd());
|
|
5176
5689
|
logger.info("building current snapshot");
|
|
@@ -5184,7 +5697,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
|
|
|
5184
5697
|
logger
|
|
5185
5698
|
);
|
|
5186
5699
|
if (options.snapshotPath !== void 0) {
|
|
5187
|
-
await
|
|
5700
|
+
await writeFile3(options.snapshotPath, JSON.stringify(current, null, 2), "utf8");
|
|
5188
5701
|
logger.info(`snapshot written: ${options.snapshotPath}`);
|
|
5189
5702
|
}
|
|
5190
5703
|
let baseline;
|
|
@@ -5256,7 +5769,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
|
|
|
5256
5769
|
diff = compareSnapshots(current, baseline);
|
|
5257
5770
|
} else if (options.baselinePath !== void 0) {
|
|
5258
5771
|
logger.info(`loading baseline snapshot: ${options.baselinePath}`);
|
|
5259
|
-
const baselineRaw = await
|
|
5772
|
+
const baselineRaw = await readFile3(options.baselinePath, "utf8");
|
|
5260
5773
|
try {
|
|
5261
5774
|
baseline = parseSnapshot(baselineRaw);
|
|
5262
5775
|
} catch (error) {
|
|
@@ -5278,7 +5791,7 @@ var runCiCommand = async (inputPath, authorIdentityMode, options, logger = creat
|
|
|
5278
5791
|
|
|
5279
5792
|
${ciMarkdown}`;
|
|
5280
5793
|
if (options.reportPath !== void 0) {
|
|
5281
|
-
await
|
|
5794
|
+
await writeFile3(options.reportPath, markdownSummary, "utf8");
|
|
5282
5795
|
logger.info(`report written: ${options.reportPath}`);
|
|
5283
5796
|
}
|
|
5284
5797
|
const machineReadable = {
|
|
@@ -5290,7 +5803,7 @@ ${ciMarkdown}`;
|
|
|
5290
5803
|
exitCode: gateResult.exitCode
|
|
5291
5804
|
};
|
|
5292
5805
|
if (options.jsonOutputPath !== void 0) {
|
|
5293
|
-
await
|
|
5806
|
+
await writeFile3(options.jsonOutputPath, JSON.stringify(machineReadable, null, 2), "utf8");
|
|
5294
5807
|
logger.info(`ci machine output written: ${options.jsonOutputPath}`);
|
|
5295
5808
|
}
|
|
5296
5809
|
return {
|
|
@@ -5304,7 +5817,7 @@ ${ciMarkdown}`;
|
|
|
5304
5817
|
};
|
|
5305
5818
|
|
|
5306
5819
|
// src/application/run-report-command.ts
|
|
5307
|
-
import { readFile as
|
|
5820
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
5308
5821
|
var runReportCommand = async (inputPath, authorIdentityMode, options, logger = createSilentLogger()) => {
|
|
5309
5822
|
logger.info("building analysis snapshot");
|
|
5310
5823
|
const current = await buildAnalysisSnapshot(
|
|
@@ -5317,7 +5830,7 @@ var runReportCommand = async (inputPath, authorIdentityMode, options, logger = c
|
|
|
5317
5830
|
logger
|
|
5318
5831
|
);
|
|
5319
5832
|
if (options.snapshotPath !== void 0) {
|
|
5320
|
-
await
|
|
5833
|
+
await writeFile4(options.snapshotPath, JSON.stringify(current, null, 2), "utf8");
|
|
5321
5834
|
logger.info(`snapshot written: ${options.snapshotPath}`);
|
|
5322
5835
|
}
|
|
5323
5836
|
let report;
|
|
@@ -5325,14 +5838,14 @@ var runReportCommand = async (inputPath, authorIdentityMode, options, logger = c
|
|
|
5325
5838
|
report = createReport(current);
|
|
5326
5839
|
} else {
|
|
5327
5840
|
logger.info(`loading baseline snapshot: ${options.comparePath}`);
|
|
5328
|
-
const baselineRaw = await
|
|
5841
|
+
const baselineRaw = await readFile4(options.comparePath, "utf8");
|
|
5329
5842
|
const baseline = parseSnapshot(baselineRaw);
|
|
5330
5843
|
const diff = compareSnapshots(current, baseline);
|
|
5331
5844
|
report = createReport(current, diff);
|
|
5332
5845
|
}
|
|
5333
5846
|
const rendered = formatReport(report, options.format);
|
|
5334
5847
|
if (options.outputPath !== void 0) {
|
|
5335
|
-
await
|
|
5848
|
+
await writeFile4(options.outputPath, rendered, "utf8");
|
|
5336
5849
|
logger.info(`report written: ${options.outputPath}`);
|
|
5337
5850
|
}
|
|
5338
5851
|
return { report, rendered };
|
|
@@ -5386,7 +5899,7 @@ var runExplainCommand = async (inputPath, authorIdentityMode, options, logger =
|
|
|
5386
5899
|
|
|
5387
5900
|
// src/index.ts
|
|
5388
5901
|
var program = new Command();
|
|
5389
|
-
var packageJsonPath = resolve5(
|
|
5902
|
+
var packageJsonPath = resolve5(dirname2(fileURLToPath(import.meta.url)), "../package.json");
|
|
5390
5903
|
var { version } = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
5391
5904
|
var parseRecentWindowDays = (value) => {
|
|
5392
5905
|
const parsed = Number.parseInt(value, 10);
|
|
@@ -5704,5 +6217,11 @@ if (argv.length <= 2) {
|
|
|
5704
6217
|
program.outputHelp();
|
|
5705
6218
|
process.exit(0);
|
|
5706
6219
|
}
|
|
6220
|
+
await checkForCliUpdates({
|
|
6221
|
+
packageName: "@getcodesentinel/codesentinel",
|
|
6222
|
+
currentVersion: version,
|
|
6223
|
+
argv: process.argv,
|
|
6224
|
+
env: process.env
|
|
6225
|
+
});
|
|
5707
6226
|
await program.parseAsync(argv);
|
|
5708
6227
|
//# sourceMappingURL=index.js.map
|