@aisdlc/core 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +98 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +98 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -513,6 +513,9 @@ function createDefaultQualityPolicy() {
|
|
|
513
513
|
max_cognitive: 15,
|
|
514
514
|
min_maintainability: 50,
|
|
515
515
|
max_function_lines: 40,
|
|
516
|
+
min_line_coverage: 85,
|
|
517
|
+
min_branch_coverage: 80,
|
|
518
|
+
min_function_coverage: 90,
|
|
516
519
|
enforce_mode: "STRICT",
|
|
517
520
|
target_directories: ["src", "lib", "examples", "tests"],
|
|
518
521
|
supported_extensions: [...DEFAULT_EXTENSIONS],
|
|
@@ -536,6 +539,15 @@ function applyDocScope(policy, doc) {
|
|
|
536
539
|
}
|
|
537
540
|
}
|
|
538
541
|
}
|
|
542
|
+
function applyCoverageThresholds(policy, covRaw) {
|
|
543
|
+
if (!covRaw || typeof covRaw !== "object") return;
|
|
544
|
+
const cov = covRaw;
|
|
545
|
+
if (cov.min_line_coverage_percent !== void 0) policy.min_line_coverage = Number(cov.min_line_coverage_percent);
|
|
546
|
+
if (cov.min_branch_coverage_percent !== void 0) policy.min_branch_coverage = Number(cov.min_branch_coverage_percent);
|
|
547
|
+
if (cov.min_function_coverage_percent !== void 0) {
|
|
548
|
+
policy.min_function_coverage = Number(cov.min_function_coverage_percent);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
539
551
|
function applyDocThresholds(policy, doc) {
|
|
540
552
|
const sections = [doc.release_thresholds, doc.thresholds].filter(
|
|
541
553
|
(s) => Boolean(s && typeof s === "object")
|
|
@@ -549,6 +561,7 @@ function applyDocThresholds(policy, doc) {
|
|
|
549
561
|
if (cog !== void 0) policy.max_cognitive = Number(cog);
|
|
550
562
|
if (mi !== void 0) policy.min_maintainability = Number(mi);
|
|
551
563
|
if (loc !== void 0) policy.max_function_lines = Number(loc);
|
|
564
|
+
applyCoverageThresholds(policy, s.test_coverage);
|
|
552
565
|
}
|
|
553
566
|
const codingRules = doc.coding_rules;
|
|
554
567
|
const rules = codingRules?.rules;
|
|
@@ -5815,6 +5828,78 @@ function detectLanguage(filePath) {
|
|
|
5815
5828
|
return "Unknown";
|
|
5816
5829
|
}
|
|
5817
5830
|
}
|
|
5831
|
+
function parseJsonSummaryFile(filePath, relSource) {
|
|
5832
|
+
try {
|
|
5833
|
+
const raw = JSON.parse(fs6__namespace.readFileSync(filePath, "utf-8"));
|
|
5834
|
+
const total = raw.total;
|
|
5835
|
+
if (!total || typeof total !== "object") return null;
|
|
5836
|
+
const linesPct = Number(total.lines?.pct);
|
|
5837
|
+
const branchesPct = Number(total.branches?.pct);
|
|
5838
|
+
const functionsPct = Number(total.functions?.pct);
|
|
5839
|
+
if (Number.isNaN(linesPct) || Number.isNaN(branchesPct) || Number.isNaN(functionsPct)) return null;
|
|
5840
|
+
const statementsPct = total.statements?.pct !== void 0 ? Number(total.statements.pct) : void 0;
|
|
5841
|
+
return { linesPct, branchesPct, functionsPct, statementsPct, source: relSource };
|
|
5842
|
+
} catch {
|
|
5843
|
+
return null;
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
function parseLcovFile(filePath, relSource) {
|
|
5847
|
+
try {
|
|
5848
|
+
const lines = fs6__namespace.readFileSync(filePath, "utf-8").split(/\r?\n/);
|
|
5849
|
+
let lf = 0, lh = 0, brf = 0, brh = 0, fnf = 0, fnh = 0;
|
|
5850
|
+
for (const line of lines) {
|
|
5851
|
+
if (line.startsWith("LF:")) lf += parseInt(line.slice(3), 10) || 0;
|
|
5852
|
+
else if (line.startsWith("LH:")) lh += parseInt(line.slice(3), 10) || 0;
|
|
5853
|
+
else if (line.startsWith("BRF:")) brf += parseInt(line.slice(4), 10) || 0;
|
|
5854
|
+
else if (line.startsWith("BRH:")) brh += parseInt(line.slice(4), 10) || 0;
|
|
5855
|
+
else if (line.startsWith("FNF:")) fnf += parseInt(line.slice(4), 10) || 0;
|
|
5856
|
+
else if (line.startsWith("FNH:")) fnh += parseInt(line.slice(4), 10) || 0;
|
|
5857
|
+
}
|
|
5858
|
+
if (lf === 0 && fnf === 0 && brf === 0) return null;
|
|
5859
|
+
const pct = (hit, found) => found > 0 ? Math.round(hit / found * 1e3) / 10 : 100;
|
|
5860
|
+
return {
|
|
5861
|
+
linesPct: pct(lh, lf),
|
|
5862
|
+
branchesPct: pct(brh, brf),
|
|
5863
|
+
functionsPct: pct(fnh, fnf),
|
|
5864
|
+
source: relSource
|
|
5865
|
+
};
|
|
5866
|
+
} catch {
|
|
5867
|
+
return null;
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
function readCodeCoverageSummary(rootDir = process.cwd()) {
|
|
5871
|
+
const jsonCandidates = ["coverage/coverage-summary.json", "reports/coverage-summary.json"];
|
|
5872
|
+
for (const rel of jsonCandidates) {
|
|
5873
|
+
const full = path8__namespace.join(rootDir, rel);
|
|
5874
|
+
if (fs6__namespace.existsSync(full)) {
|
|
5875
|
+
const parsed = parseJsonSummaryFile(full, rel);
|
|
5876
|
+
if (parsed) return parsed;
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
const lcovCandidates = ["coverage/lcov.info", "lcov.info", "reports/lcov.info"];
|
|
5880
|
+
for (const rel of lcovCandidates) {
|
|
5881
|
+
const full = path8__namespace.join(rootDir, rel);
|
|
5882
|
+
if (fs6__namespace.existsSync(full)) {
|
|
5883
|
+
const parsed = parseLcovFile(full, rel);
|
|
5884
|
+
if (parsed) return parsed;
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
return null;
|
|
5888
|
+
}
|
|
5889
|
+
function formatCoverageRow(label, measured, minThreshold) {
|
|
5890
|
+
if (measured === void 0) {
|
|
5891
|
+
return `| **${label}** | \`N/A (Run test:coverage)\` | $\\ge ${minThreshold}\\%$ | \u2139\uFE0F NOT MEASURED |`;
|
|
5892
|
+
}
|
|
5893
|
+
const status = measured >= minThreshold ? "\u2705 COMPLIANT" : "\u26A0\uFE0F BELOW THRESHOLD";
|
|
5894
|
+
return `| **${label}** | \`${measured}%\` | $\\ge ${minThreshold}\\%$ | ${status} |`;
|
|
5895
|
+
}
|
|
5896
|
+
function buildCoverageRows(coverage, policy) {
|
|
5897
|
+
return [
|
|
5898
|
+
formatCoverageRow("Line Coverage", coverage?.linesPct, policy.min_line_coverage),
|
|
5899
|
+
formatCoverageRow("Branch Coverage", coverage?.branchesPct, policy.min_branch_coverage),
|
|
5900
|
+
formatCoverageRow("Function Coverage", coverage?.functionsPct, policy.min_function_coverage)
|
|
5901
|
+
];
|
|
5902
|
+
}
|
|
5818
5903
|
function generateQualityReport(options = {}) {
|
|
5819
5904
|
const rootDir = options.rootDir || process.cwd();
|
|
5820
5905
|
const gateResult = verifyQualityGate({
|
|
@@ -5822,6 +5907,7 @@ function generateQualityReport(options = {}) {
|
|
|
5822
5907
|
policyPath: options.policyPath,
|
|
5823
5908
|
thresholds: options.thresholds
|
|
5824
5909
|
});
|
|
5910
|
+
const coverage = readCodeCoverageSummary(rootDir);
|
|
5825
5911
|
const totalFiles = gateResult.totalFiles;
|
|
5826
5912
|
const totalFunctions = gateResult.totalFunctions;
|
|
5827
5913
|
let totalMi = 0;
|
|
@@ -5870,6 +5956,7 @@ function generateQualityReport(options = {}) {
|
|
|
5870
5956
|
`| **Cyclomatic Complexity (Average)** | \`${avgCyclomatic}\` | $\\le ${gateResult.policy.max_cyclomatic}$ | ${avgCyclomatic <= gateResult.policy.max_cyclomatic ? "\u2705 COMPLIANT" : "\u274C EXCEEDED"} |`,
|
|
5871
5957
|
`| **Cognitive Complexity (Average)** | \`${avgCognitive}\` | $\\le ${gateResult.policy.max_cognitive}$ | ${avgCognitive <= gateResult.policy.max_cognitive ? "\u2705 COMPLIANT" : "\u274C EXCEEDED"} |`,
|
|
5872
5958
|
`| **Maintainability Index (SEI MI)** | \`${avgMaintainability} / 100\` | $\\ge ${gateResult.policy.min_maintainability}$ | ${avgMaintainability >= gateResult.policy.min_maintainability ? "\u2705 COMPLIANT" : "\u274C INSUFFICIENT"} |`,
|
|
5959
|
+
...buildCoverageRows(coverage, gateResult.policy),
|
|
5873
5960
|
`| **Functions in Violation** | \`${gateResult.failCount}\` | $0$ (Mode ${gateResult.policy.enforce_mode}) | ${gateResult.failCount === 0 ? "\u2705 0 VIOLATIONS" : "\u274C BLOCKED"} |`,
|
|
5874
5961
|
``,
|
|
5875
5962
|
`---`,
|
|
@@ -5901,6 +5988,7 @@ function generateQualityReport(options = {}) {
|
|
|
5901
5988
|
lines.push("## 4. Evaluation Criteria and Standards");
|
|
5902
5989
|
lines.push("- **McCabe Cyclomatic Complexity (CC)**: Number of linearly independent paths.");
|
|
5903
5990
|
lines.push("- **Maintainability Index (SEI MI)**: Normalized formula [0 - 100] combining Halstead Volume, CC, and LOC.");
|
|
5991
|
+
lines.push("- **Automated Test Coverage**: Passively ingested from `coverage/coverage-summary.json` or `lcov.info` when present.");
|
|
5904
5992
|
lines.push("- **Clean Code Guardrails**: Prohibition of implicit `any` typing, function length limits ($le 40$ lines), and zero unjustified suppressions.");
|
|
5905
5993
|
const markdown = lines.join("\n");
|
|
5906
5994
|
const reportsDir = path8__namespace.join(rootDir, "reports");
|
|
@@ -5912,7 +6000,8 @@ function generateQualityReport(options = {}) {
|
|
|
5912
6000
|
totalFunctions,
|
|
5913
6001
|
avgMaintainability,
|
|
5914
6002
|
avgCyclomatic,
|
|
5915
|
-
verdict
|
|
6003
|
+
verdict,
|
|
6004
|
+
coverage
|
|
5916
6005
|
};
|
|
5917
6006
|
}
|
|
5918
6007
|
|
|
@@ -7601,10 +7690,12 @@ function collectDashboardMetrics(rootDir) {
|
|
|
7601
7690
|
}
|
|
7602
7691
|
} catch {
|
|
7603
7692
|
}
|
|
7693
|
+
const codeCoverage = readCodeCoverageSummary(rootDir);
|
|
7604
7694
|
return {
|
|
7605
7695
|
gate,
|
|
7606
7696
|
gov,
|
|
7607
7697
|
cov,
|
|
7698
|
+
codeCoverage,
|
|
7608
7699
|
trace,
|
|
7609
7700
|
history,
|
|
7610
7701
|
activePrKpi,
|
|
@@ -8146,6 +8237,11 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
|
|
|
8146
8237
|
<span class="value">${metricsData.cov.totalRequirements > 0 ? Math.round(metricsData.cov.passedRequirements / metricsData.cov.totalRequirements * 100) : 100}%</span>
|
|
8147
8238
|
<span class="subtitle">${metricsData.cov.passedRequirements} / ${metricsData.cov.totalRequirements} verified</span>
|
|
8148
8239
|
</div>
|
|
8240
|
+
<div class="metric-card">
|
|
8241
|
+
<span class="title">Code Coverage (Lines)</span>
|
|
8242
|
+
<span class="value">${metricsData.codeCoverage ? metricsData.codeCoverage.linesPct + "%" : "N/A"}</span>
|
|
8243
|
+
<span class="subtitle">${metricsData.codeCoverage ? `Branches: ${metricsData.codeCoverage.branchesPct}% | Funcs: ${metricsData.codeCoverage.functionsPct}%` : `Min target: ${metricsData.gate.policy.min_line_coverage}% (Run test:coverage)`}</span>
|
|
8244
|
+
</div>
|
|
8149
8245
|
</div>
|
|
8150
8246
|
|
|
8151
8247
|
<div class="chart-container">
|
|
@@ -11558,6 +11654,7 @@ exports.parseSpecCatalogScope = parseSpecCatalogScope;
|
|
|
11558
11654
|
exports.parseTasksDoc = parseTasksDoc;
|
|
11559
11655
|
exports.parseTasksFromContent = parseTasksFromContent;
|
|
11560
11656
|
exports.processPackageDir = processPackageDir;
|
|
11657
|
+
exports.readCodeCoverageSummary = readCodeCoverageSummary;
|
|
11561
11658
|
exports.readPackageJson = readPackageJson;
|
|
11562
11659
|
exports.renderAntigravityAgentFile = renderAntigravityAgentFile;
|
|
11563
11660
|
exports.renderClaudeAgentFile = renderClaudeAgentFile;
|