@aisdlc/core 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +335 -35
- 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 +334 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7512,6 +7512,31 @@ function inferHumanTrailers(trailers, linesAdded, linesDeleted) {
|
|
|
7512
7512
|
function inferMissingTrailers(trailers, linesAdded, linesDeleted) {
|
|
7513
7513
|
return trailers.authorType === "agent" ? inferAgentTrailers(trailers, linesAdded, linesDeleted) : inferHumanTrailers(trailers, linesAdded, linesDeleted);
|
|
7514
7514
|
}
|
|
7515
|
+
function resolveGitRef(cwd, ref) {
|
|
7516
|
+
if (!ref || ref === "HEAD") return ref;
|
|
7517
|
+
const candidates = [
|
|
7518
|
+
ref,
|
|
7519
|
+
`feat/${ref}`,
|
|
7520
|
+
`release/${ref}`,
|
|
7521
|
+
`bug/${ref}`,
|
|
7522
|
+
`task/${ref}`,
|
|
7523
|
+
`origin/${ref}`,
|
|
7524
|
+
`origin/feat/${ref}`,
|
|
7525
|
+
`origin/release/${ref}`,
|
|
7526
|
+
`origin/bug/${ref}`
|
|
7527
|
+
];
|
|
7528
|
+
for (const cand of candidates) {
|
|
7529
|
+
try {
|
|
7530
|
+
child_process.execFileSync("git", ["rev-parse", "--verify", "--quiet", cand], {
|
|
7531
|
+
cwd,
|
|
7532
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7533
|
+
});
|
|
7534
|
+
return cand;
|
|
7535
|
+
} catch {
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
return ref;
|
|
7539
|
+
}
|
|
7515
7540
|
function fetchGitLogOutput(cwd, baseRef, headRef, format) {
|
|
7516
7541
|
const rangeArg = baseRef ? `${baseRef}..${headRef}` : headRef;
|
|
7517
7542
|
try {
|
|
@@ -7555,8 +7580,10 @@ function buildCommitKpiRecord(chunk) {
|
|
|
7555
7580
|
}
|
|
7556
7581
|
function extractCommitKpisFromRange(baseRef, headRef = "HEAD", options = {}) {
|
|
7557
7582
|
const cwd = options.cwd || process.cwd();
|
|
7583
|
+
const resolvedBase = resolveGitRef(cwd, baseRef);
|
|
7584
|
+
const resolvedHead = resolveGitRef(cwd, headRef);
|
|
7558
7585
|
const format = "---AI_SDLC_COMMIT---%n%H%x1f%an%x1f%ae%x1f%aI%x1f%s%n%B%n---AI_SDLC_BODY_END---";
|
|
7559
|
-
const rawOutput = fetchGitLogOutput(cwd,
|
|
7586
|
+
const rawOutput = fetchGitLogOutput(cwd, resolvedBase, resolvedHead, format);
|
|
7560
7587
|
if (!rawOutput) return [];
|
|
7561
7588
|
const chunks = parseRawLogChunks(rawOutput);
|
|
7562
7589
|
const records = [];
|
|
@@ -7880,8 +7907,7 @@ function accumulateRecord(target, record) {
|
|
|
7880
7907
|
target.completionTokens += record.trailers.completionTokens;
|
|
7881
7908
|
target.estimatedCostUsd = calculateEstimatedCost(target.promptTokens, target.completionTokens);
|
|
7882
7909
|
}
|
|
7883
|
-
function
|
|
7884
|
-
const records = extractCommitKpisFromRange(baseRef, headRef, options);
|
|
7910
|
+
function aggregateCommitKpiRecords(records, baseRef = "main", headRef = "HEAD") {
|
|
7885
7911
|
const humanSummary = createEmptySummary();
|
|
7886
7912
|
const byModel = {};
|
|
7887
7913
|
let totalLinesAdded = 0;
|
|
@@ -7907,6 +7933,8 @@ function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
|
|
|
7907
7933
|
accumulateRecord(humanSummary, record);
|
|
7908
7934
|
}
|
|
7909
7935
|
}
|
|
7936
|
+
const totalTokens = totalPrompt + totalCompletion;
|
|
7937
|
+
const estimatedCostUsd = calculateEstimatedCost(totalPrompt, totalCompletion);
|
|
7910
7938
|
return {
|
|
7911
7939
|
baseRef,
|
|
7912
7940
|
headRef,
|
|
@@ -7916,14 +7944,18 @@ function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
|
|
|
7916
7944
|
totalActiveTimeSeconds: totalActiveTime,
|
|
7917
7945
|
totalPromptTokens: totalPrompt,
|
|
7918
7946
|
totalCompletionTokens: totalCompletion,
|
|
7919
|
-
totalTokens
|
|
7920
|
-
estimatedCostUsd
|
|
7947
|
+
totalTokens,
|
|
7948
|
+
estimatedCostUsd,
|
|
7921
7949
|
humanSummary,
|
|
7922
7950
|
byModel,
|
|
7923
7951
|
isBugPr,
|
|
7924
7952
|
commitRecords: records
|
|
7925
7953
|
};
|
|
7926
7954
|
}
|
|
7955
|
+
function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
|
|
7956
|
+
const records = extractCommitKpisFromRange(baseRef, headRef, options);
|
|
7957
|
+
return aggregateCommitKpiRecords(records, baseRef, headRef);
|
|
7958
|
+
}
|
|
7927
7959
|
function formatMinutes(seconds) {
|
|
7928
7960
|
const mins = Math.round(seconds / 60);
|
|
7929
7961
|
return mins > 0 ? `${mins} min` : `${seconds}s`;
|
|
@@ -8059,7 +8091,10 @@ function computeReworkRatios(totalTime, totalTokens, totalCost, reworkTime, rewo
|
|
|
8059
8091
|
};
|
|
8060
8092
|
}
|
|
8061
8093
|
function aggregateReleaseKpis(releaseBranch, baseBranch = "main", options = {}) {
|
|
8062
|
-
const
|
|
8094
|
+
const cwd = options.cwd || process.cwd();
|
|
8095
|
+
const resolvedRelease = resolveGitRef(cwd, releaseBranch);
|
|
8096
|
+
const resolvedBase = resolveGitRef(cwd, baseBranch);
|
|
8097
|
+
const records = extractCommitKpisFromRange(resolvedBase, resolvedRelease, options);
|
|
8063
8098
|
const statsMap = {};
|
|
8064
8099
|
const bugList = [];
|
|
8065
8100
|
const totals = { linesAdded: 0, linesDeleted: 0, time: 0, tokens: 0, reworkTime: 0, reworkTokens: 0 };
|
|
@@ -8074,8 +8109,8 @@ function aggregateReleaseKpis(releaseBranch, baseBranch = "main", options = {})
|
|
|
8074
8109
|
const reworkCostUsd = calculateCost(totals.reworkTokens * 0.8, totals.reworkTokens * 0.2);
|
|
8075
8110
|
const ratios = computeReworkRatios(totals.time, totals.tokens, totalCostUsd, totals.reworkTime, totals.reworkTokens, reworkCostUsd);
|
|
8076
8111
|
return {
|
|
8077
|
-
releaseBranch,
|
|
8078
|
-
baseBranch,
|
|
8112
|
+
releaseBranch: resolvedRelease,
|
|
8113
|
+
baseBranch: resolvedBase,
|
|
8079
8114
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8080
8115
|
totalCommits: records.length,
|
|
8081
8116
|
totalLinesAdded: totals.linesAdded,
|
|
@@ -8142,7 +8177,7 @@ function formatReleaseKpiMarkdown(report) {
|
|
|
8142
8177
|
].join("\n");
|
|
8143
8178
|
}
|
|
8144
8179
|
function writeReleaseKpiReport(report, options = {}) {
|
|
8145
|
-
const root = options.outputDir || path5__namespace.join(process.cwd(), "reports", "releases");
|
|
8180
|
+
const root = options.outputDir || (options.rootDir ? path5__namespace.join(options.rootDir, "reports", "releases") : path5__namespace.join(process.cwd(), "reports", "releases"));
|
|
8146
8181
|
if (!fs6__namespace.existsSync(root)) {
|
|
8147
8182
|
fs6__namespace.mkdirSync(root, { recursive: true });
|
|
8148
8183
|
}
|
|
@@ -9796,6 +9831,86 @@ var DASHBOARD_CLIENT_SCRIPT = `
|
|
|
9796
9831
|
if (backdrop) backdrop.style.display = 'block';
|
|
9797
9832
|
}
|
|
9798
9833
|
|
|
9834
|
+
function filterTelemetryByBranch(branchKey) {
|
|
9835
|
+
if (typeof telemetryData === 'undefined' || !telemetryData) return;
|
|
9836
|
+
var data = telemetryData[branchKey] || null;
|
|
9837
|
+
if (!data) return;
|
|
9838
|
+
|
|
9839
|
+
var isTotal = branchKey === '__total__';
|
|
9840
|
+
var label = isTotal ? 'Totalizador' : branchKey;
|
|
9841
|
+
var badgeText = isTotal ? 'Vista: Totalizador Global' : ('Rama: ' + branchKey);
|
|
9842
|
+
|
|
9843
|
+
var cardBranchLabel = document.getElementById('card-branch-label');
|
|
9844
|
+
if (cardBranchLabel) cardBranchLabel.textContent = label;
|
|
9845
|
+
|
|
9846
|
+
var tableBranchLabel = document.getElementById('table-branch-label');
|
|
9847
|
+
if (tableBranchLabel) tableBranchLabel.textContent = isTotal ? 'Totalizador (Todas las ramas)' : branchKey;
|
|
9848
|
+
|
|
9849
|
+
var badgeElem = document.getElementById('telemetry-badge-container');
|
|
9850
|
+
if (badgeElem) {
|
|
9851
|
+
badgeElem.innerHTML = '<span class="badge" style="background: rgba(56, 189, 248, 0.2); color: #38bdf8; border: 1px solid #38bdf8; font-size: 0.75rem; padding: 0.25rem 0.6rem; border-radius: 4px;">' + badgeText + '</span>';
|
|
9852
|
+
}
|
|
9853
|
+
|
|
9854
|
+
var elCommits = document.getElementById('card-commits');
|
|
9855
|
+
if (elCommits) elCommits.textContent = data.totalCommits;
|
|
9856
|
+
var elLines = document.getElementById('card-lines');
|
|
9857
|
+
if (elLines) elLines.textContent = '+' + data.linesAdded + ' / -' + data.linesDeleted + ' lines';
|
|
9858
|
+
|
|
9859
|
+
var elTime = document.getElementById('card-time');
|
|
9860
|
+
if (elTime) elTime.textContent = Math.round(data.totalActiveTimeSeconds / 60) + 'm';
|
|
9861
|
+
var elTimeSec = document.getElementById('card-time-seconds');
|
|
9862
|
+
if (elTimeSec) elTimeSec.textContent = data.totalActiveTimeSeconds + 's accumulated';
|
|
9863
|
+
|
|
9864
|
+
var elTokens = document.getElementById('card-tokens');
|
|
9865
|
+
if (elTokens) elTokens.textContent = Number(data.totalTokens).toLocaleString('en-US');
|
|
9866
|
+
var elTokensDetail = document.getElementById('card-tokens-detail');
|
|
9867
|
+
if (elTokensDetail) elTokensDetail.textContent = 'In: ' + Number(data.totalPromptTokens).toLocaleString('en-US') + ' | Out: ' + Number(data.totalCompletionTokens).toLocaleString('en-US');
|
|
9868
|
+
|
|
9869
|
+
var elCost = document.getElementById('card-cost');
|
|
9870
|
+
if (elCost) elCost.textContent = '$' + Number(data.estimatedCostUsd).toFixed(2);
|
|
9871
|
+
|
|
9872
|
+
var tbody = document.getElementById('telemetry-table-tbody');
|
|
9873
|
+
if (tbody) {
|
|
9874
|
+
var html = '';
|
|
9875
|
+
if (data.humanSummary && data.humanSummary.commits > 0) {
|
|
9876
|
+
html += '<tr>' +
|
|
9877
|
+
'<td><strong>Human (Developer)</strong></td>' +
|
|
9878
|
+
'<td><span class="badge" style="background:#334155;">HUMAN</span></td>' +
|
|
9879
|
+
'<td>' + data.humanSummary.commits + '</td>' +
|
|
9880
|
+
'<td>+' + data.humanSummary.linesAdded + ' / -' + data.humanSummary.linesDeleted + '</td>' +
|
|
9881
|
+
'<td>' + Math.round(data.humanSummary.activeTimeSeconds / 60) + 'm</td>' +
|
|
9882
|
+
'<td>0</td><td>\u2014</td>' +
|
|
9883
|
+
'</tr>';
|
|
9884
|
+
}
|
|
9885
|
+
if (data.byModel) {
|
|
9886
|
+
var models = Object.keys(data.byModel);
|
|
9887
|
+
for (var i = 0; i < models.length; i++) {
|
|
9888
|
+
var m = models[i];
|
|
9889
|
+
var summary = data.byModel[m];
|
|
9890
|
+
var safeModel = String(m).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
9891
|
+
html += '<tr>' +
|
|
9892
|
+
'<td><strong><code>' + safeModel + '</code></strong></td>' +
|
|
9893
|
+
'<td><span class="badge badge-green">AGENT</span></td>' +
|
|
9894
|
+
'<td>' + summary.commits + '</td>' +
|
|
9895
|
+
'<td>+' + summary.linesAdded + ' / -' + summary.linesDeleted + '</td>' +
|
|
9896
|
+
'<td>' + Math.round(summary.activeTimeSeconds / 60) + 'm</td>' +
|
|
9897
|
+
'<td>' + (summary.promptTokens + summary.completionTokens).toLocaleString('en-US') + '</td>' +
|
|
9898
|
+
'<td>$' + Number(summary.estimatedCostUsd).toFixed(2) + '</td>' +
|
|
9899
|
+
'</tr>';
|
|
9900
|
+
}
|
|
9901
|
+
}
|
|
9902
|
+
html += '<tr style="font-weight: bold; background: rgba(255,255,255,0.05);">' +
|
|
9903
|
+
'<td>' + (isTotal ? 'TOTAL REPOSITORIO' : 'TOTAL RAMA') + '</td><td>\u2014</td>' +
|
|
9904
|
+
'<td>' + data.totalCommits + '</td>' +
|
|
9905
|
+
'<td>+' + data.linesAdded + ' / -' + data.linesDeleted + '</td>' +
|
|
9906
|
+
'<td>' + Math.round(data.totalActiveTimeSeconds / 60) + 'm</td>' +
|
|
9907
|
+
'<td>' + Number(data.totalTokens).toLocaleString('en-US') + '</td>' +
|
|
9908
|
+
'<td>$' + Number(data.estimatedCostUsd).toFixed(2) + ' USD</td>' +
|
|
9909
|
+
'</tr>';
|
|
9910
|
+
tbody.innerHTML = html;
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
|
|
9799
9914
|
function closeTaskSidebar() {
|
|
9800
9915
|
var sidebar = document.getElementById('task-sidebar');
|
|
9801
9916
|
var backdrop = document.getElementById('task-backdrop');
|
|
@@ -10110,13 +10225,39 @@ function renderManualsTab(manuals) {
|
|
|
10110
10225
|
`<div class="chart-container" id="manual-viewer-container" style="display: none;"><h3 id="manual-viewer-title"></h3><div id="manual-viewer" style="font-size: 0.82rem; line-height: 1.5; color: #cbd5e1;"></div></div>`
|
|
10111
10226
|
].join("\n");
|
|
10112
10227
|
}
|
|
10113
|
-
function
|
|
10228
|
+
function renderBranchOption(b, activeBranch) {
|
|
10229
|
+
const isAct = b === activeBranch ? " (Rama activa)" : "";
|
|
10230
|
+
return ` <option value="${escapeHtml(b)}">${escapeHtml(b)}${isAct}</option>`;
|
|
10231
|
+
}
|
|
10232
|
+
function renderTelemetrySelect(options) {
|
|
10233
|
+
return [
|
|
10234
|
+
` <div style="display: flex; align-items: center; gap: 0.6rem;">`,
|
|
10235
|
+
` <label for="telemetry-branch-select" style="font-size: 0.85rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;">Filtrar por Rama:</label>`,
|
|
10236
|
+
` <select id="telemetry-branch-select" onchange="filterTelemetryByBranch(this.value)" class="form-select" style="background: var(--panel-bg, #1e293b); color: var(--text-main, #f8fafc); border: 1px solid var(--border-color, #475569); border-radius: 6px; padding: 0.4rem 0.8rem; font-size: 0.85rem; cursor: pointer; outline: none; font-weight: 500;">`,
|
|
10237
|
+
options,
|
|
10238
|
+
` </select>`,
|
|
10239
|
+
` </div>`
|
|
10240
|
+
].join("\n");
|
|
10241
|
+
}
|
|
10242
|
+
function renderTelemetryFilterBarHtml(branches, activeBranch) {
|
|
10243
|
+
const options = [
|
|
10244
|
+
` <option value="__total__" selected>\u{1F31F} Totalizador (Todas las ramas)</option>`,
|
|
10245
|
+
...branches.map((b) => renderBranchOption(b, activeBranch))
|
|
10246
|
+
].join("\n");
|
|
10247
|
+
const selectHtml = renderTelemetrySelect(options);
|
|
10248
|
+
const badgeHtml = ` <div id="telemetry-badge-container"><span class="badge" style="background: rgba(56, 189, 248, 0.2); color: #38bdf8; border: 1px solid #38bdf8; font-size: 0.75rem; padding: 0.25rem 0.6rem; border-radius: 4px;">Vista: Totalizador Global</span></div>`;
|
|
10249
|
+
return `<div class="telemetry-filter-bar" style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; flex-wrap: wrap; gap: 0.75rem;">
|
|
10250
|
+
${selectHtml}
|
|
10251
|
+
${badgeHtml}
|
|
10252
|
+
</div>`;
|
|
10253
|
+
}
|
|
10254
|
+
function renderTelemetryCardsHtml(kpi, displayLabel = "Totalizador") {
|
|
10114
10255
|
return [
|
|
10115
10256
|
`<div class="grid-cards">`,
|
|
10116
|
-
` <div class="metric-card"><span class="title">Commits
|
|
10117
|
-
` <div class="metric-card"><span class="title">Active Development Time</span><span class="value">${Math.round(kpi.totalActiveTimeSeconds / 60)}m</span><span class="subtitle">${kpi.totalActiveTimeSeconds}s accumulated</span></div>`,
|
|
10118
|
-
` <div class="metric-card"><span class="title">Consumed Tokens</span><span class="value">${kpi.totalTokens.toLocaleString("en-US")}</span><span class="subtitle">In: ${kpi.totalPromptTokens.toLocaleString()} | Out: ${kpi.totalCompletionTokens.toLocaleString()}</span></div>`,
|
|
10119
|
-
` <div class="metric-card"><span class="title">Estimated Compute Cost</span><span class="value">$${kpi.estimatedCostUsd.toFixed(2)}</span><span class="subtitle">Weighted USD</span></div>`,
|
|
10257
|
+
` <div class="metric-card"><span class="title">Commits (<span id="card-branch-label">${escapeHtml(displayLabel)}</span>)</span><span class="value" id="card-commits">${kpi.totalCommits}</span><span class="subtitle" id="card-lines">+${kpi.linesAdded} / -${kpi.linesDeleted} lines</span></div>`,
|
|
10258
|
+
` <div class="metric-card"><span class="title">Active Development Time</span><span class="value" id="card-time">${Math.round(kpi.totalActiveTimeSeconds / 60)}m</span><span class="subtitle" id="card-time-seconds">${kpi.totalActiveTimeSeconds}s accumulated</span></div>`,
|
|
10259
|
+
` <div class="metric-card"><span class="title">Consumed Tokens</span><span class="value" id="card-tokens">${kpi.totalTokens.toLocaleString("en-US")}</span><span class="subtitle" id="card-tokens-detail">In: ${kpi.totalPromptTokens.toLocaleString()} | Out: ${kpi.totalCompletionTokens.toLocaleString()}</span></div>`,
|
|
10260
|
+
` <div class="metric-card"><span class="title">Estimated Compute Cost</span><span class="value" id="card-cost">$${kpi.estimatedCostUsd.toFixed(2)}</span><span class="subtitle">Weighted USD</span></div>`,
|
|
10120
10261
|
`</div>`
|
|
10121
10262
|
].join("\n");
|
|
10122
10263
|
}
|
|
@@ -10146,27 +10287,27 @@ function renderHumanTelemetryRow(human) {
|
|
|
10146
10287
|
`</tr>`
|
|
10147
10288
|
].join("");
|
|
10148
10289
|
}
|
|
10149
|
-
function renderTotalBranchRow(kpi) {
|
|
10290
|
+
function renderTotalBranchRow(kpi, isTotal = false) {
|
|
10150
10291
|
const mins = Math.round(kpi.totalActiveTimeSeconds / 60);
|
|
10151
10292
|
const tokens = kpi.totalTokens.toLocaleString("en-US");
|
|
10152
10293
|
const cost = kpi.estimatedCostUsd.toFixed(2);
|
|
10294
|
+
const label = isTotal ? "TOTAL REPOSITORIO" : "TOTAL RAMA";
|
|
10153
10295
|
return [
|
|
10154
10296
|
`<tr style="font-weight: bold; background: rgba(255,255,255,0.05);">`,
|
|
10155
|
-
` <td
|
|
10297
|
+
` <td>${label}</td><td>\u2014</td><td>${kpi.totalCommits}</td><td>+${kpi.linesAdded} / -${kpi.linesDeleted}</td>`,
|
|
10156
10298
|
` <td>${mins}m</td><td>${tokens}</td><td>$${cost} USD</td>`,
|
|
10157
10299
|
`</tr>`
|
|
10158
10300
|
].join("");
|
|
10159
10301
|
}
|
|
10160
|
-
function
|
|
10161
|
-
const cardsHtml = renderTelemetryCardsHtml(kpi);
|
|
10302
|
+
function renderTelemetryTable(kpi, displayLabel) {
|
|
10162
10303
|
const humanRow = renderHumanTelemetryRow(kpi.humanSummary);
|
|
10163
10304
|
const modelRows = Object.entries(kpi.byModel).map(([m, s]) => renderModelRow(m, s)).join("\n");
|
|
10164
|
-
const totalRow = renderTotalBranchRow(kpi);
|
|
10305
|
+
const totalRow = renderTotalBranchRow(kpi, displayLabel === "Totalizador");
|
|
10306
|
+
const titleLabel = "Totalizador (Todas las ramas)" ;
|
|
10165
10307
|
return [
|
|
10166
|
-
cardsHtml,
|
|
10167
10308
|
`<div class="chart-container">`,
|
|
10168
|
-
` <h3>Telemetry Breakdown by Author and Model (
|
|
10169
|
-
` <table class="data-table"><thead><tr><th>Entity / Model</th><th>Type</th><th>Commits</th><th>Lines (+/-)</th><th>Active Time</th><th>Consumed Tokens</th><th>Estimated Cost ($ USD)</th></tr></thead><tbody>`,
|
|
10309
|
+
` <h3>Telemetry Breakdown by Author and Model (<span id="table-branch-label">${escapeHtml(titleLabel)}</span>)</h3>`,
|
|
10310
|
+
` <table class="data-table"><thead><tr><th>Entity / Model</th><th>Type</th><th>Commits</th><th>Lines (+/-)</th><th>Active Time</th><th>Consumed Tokens</th><th>Estimated Cost ($ USD)</th></tr></thead><tbody id="telemetry-table-tbody">`,
|
|
10170
10311
|
humanRow,
|
|
10171
10312
|
modelRows,
|
|
10172
10313
|
totalRow,
|
|
@@ -10174,6 +10315,14 @@ function renderActiveTelemetrySection(kpi, branch) {
|
|
|
10174
10315
|
`</div>`
|
|
10175
10316
|
].join("\n");
|
|
10176
10317
|
}
|
|
10318
|
+
function renderActiveTelemetrySection(kpi, displayLabel, branches, activeBranch) {
|
|
10319
|
+
const filterBarHtml = renderTelemetryFilterBarHtml(branches, activeBranch);
|
|
10320
|
+
const cardsHtml = renderTelemetryCardsHtml(kpi, displayLabel);
|
|
10321
|
+
const tableHtml = renderTelemetryTable(kpi, displayLabel);
|
|
10322
|
+
return `${filterBarHtml}
|
|
10323
|
+
${cardsHtml}
|
|
10324
|
+
${tableHtml}`;
|
|
10325
|
+
}
|
|
10177
10326
|
function renderReleaseHistoryRow(h) {
|
|
10178
10327
|
return [
|
|
10179
10328
|
`<tr>`,
|
|
@@ -10182,10 +10331,25 @@ function renderReleaseHistoryRow(h) {
|
|
|
10182
10331
|
`</tr>`
|
|
10183
10332
|
].join("");
|
|
10184
10333
|
}
|
|
10334
|
+
function renderHistoricalKpisTable(history) {
|
|
10335
|
+
if (history.length === 0) {
|
|
10336
|
+
return `<p style="color: var(--text-muted); font-size: 0.85rem;">No consolidated release reports recorded yet in <code>reports/releases/*.kpis.json</code>.</p>`;
|
|
10337
|
+
}
|
|
10338
|
+
const rows = history.map(renderReleaseHistoryRow).join("\n");
|
|
10339
|
+
return `<table class="data-table"><thead><tr><th>Release</th><th>Date</th><th>KLoC</th><th>Commits</th><th>Confirmed Bugs</th><th>DIR (Bugs/KLoC)</th><th>Rework (%)</th><th>Cost ($ USD)</th></tr></thead><tbody>${rows}</tbody></table>`;
|
|
10340
|
+
}
|
|
10341
|
+
function resolveBranchFilterKeys(metricsData) {
|
|
10342
|
+
const keys = Object.keys(metricsData.branchTelemetries || {});
|
|
10343
|
+
if (metricsData.activeBranch && metricsData.activeBranch !== "HEAD" && !keys.includes(metricsData.activeBranch)) {
|
|
10344
|
+
keys.unshift(metricsData.activeBranch);
|
|
10345
|
+
}
|
|
10346
|
+
return keys;
|
|
10347
|
+
}
|
|
10185
10348
|
function renderKpisTabHtml(metricsData) {
|
|
10186
|
-
const
|
|
10187
|
-
const
|
|
10188
|
-
const
|
|
10349
|
+
const displayKpi = metricsData.totalTelemetry || metricsData.activePrKpi;
|
|
10350
|
+
const branchKeys = resolveBranchFilterKeys(metricsData);
|
|
10351
|
+
const telemetryHtml = displayKpi ? renderActiveTelemetrySection(displayKpi, "Totalizador", branchKeys, metricsData.activeBranch) : '<p style="color: var(--text-muted); font-size: 0.85rem;">No telemetry recorded yet in this workspace.</p>';
|
|
10352
|
+
const historyContent = renderHistoricalKpisTable(metricsData.history);
|
|
10189
10353
|
return [
|
|
10190
10354
|
` <div id="tab-kpis" class="tab-panel">`,
|
|
10191
10355
|
` <div class="content-container">`,
|
|
@@ -10206,18 +10370,29 @@ function renderTaskSidebarHtml() {
|
|
|
10206
10370
|
` </aside>`
|
|
10207
10371
|
].join("\n");
|
|
10208
10372
|
}
|
|
10373
|
+
function buildTelemetryMap(metricsData) {
|
|
10374
|
+
const map = {};
|
|
10375
|
+
const total = metricsData.totalTelemetry || metricsData.activePrKpi;
|
|
10376
|
+
if (total) map["__total__"] = total;
|
|
10377
|
+
Object.assign(map, metricsData.branchTelemetries || {});
|
|
10378
|
+
if (metricsData.activePrKpi && metricsData.activeBranch && !map[metricsData.activeBranch]) {
|
|
10379
|
+
map[metricsData.activeBranch] = metricsData.activePrKpi;
|
|
10380
|
+
}
|
|
10381
|
+
return map;
|
|
10382
|
+
}
|
|
10209
10383
|
function renderDashboardScripts(graphData, metricsData) {
|
|
10210
|
-
const cytoscapeScript = getCytoscapeScript();
|
|
10211
10384
|
const elementsJson = safeJsonForScript(graphData.elements);
|
|
10212
10385
|
const govTasksJson = safeJsonForScript(metricsData.gov.tasks || []);
|
|
10213
10386
|
const allManuals = [...metricsData.manuals.userManuals, ...metricsData.manuals.productionManuals];
|
|
10214
10387
|
const manualsJson = safeJsonForScript(allManuals);
|
|
10388
|
+
const telemetryJson = safeJsonForScript(buildTelemetryMap(metricsData));
|
|
10215
10389
|
return [
|
|
10216
|
-
` <script>${
|
|
10390
|
+
` <script>${getCytoscapeScript()}</script>`,
|
|
10217
10391
|
` <script>`,
|
|
10218
10392
|
` var elements = ${elementsJson};`,
|
|
10219
10393
|
` var govTasks = ${govTasksJson};`,
|
|
10220
10394
|
` var manualsData = ${manualsJson};`,
|
|
10395
|
+
` var telemetryData = ${telemetryJson};`,
|
|
10221
10396
|
DASHBOARD_CLIENT_SCRIPT,
|
|
10222
10397
|
` </script>`
|
|
10223
10398
|
].join("\n");
|
|
@@ -10260,16 +10435,55 @@ ${scripts}
|
|
|
10260
10435
|
}
|
|
10261
10436
|
|
|
10262
10437
|
// src/reporters/dashboard.ts
|
|
10438
|
+
var IGNORED_SCAN_DIRS = /* @__PURE__ */ new Set([
|
|
10439
|
+
"node_modules",
|
|
10440
|
+
".git",
|
|
10441
|
+
"dist",
|
|
10442
|
+
"coverage",
|
|
10443
|
+
"bin",
|
|
10444
|
+
"obj",
|
|
10445
|
+
".gemini",
|
|
10446
|
+
".agent"
|
|
10447
|
+
]);
|
|
10448
|
+
function readKpiFilesInDir(dir) {
|
|
10449
|
+
if (!fs6__namespace.existsSync(dir)) return [];
|
|
10450
|
+
try {
|
|
10451
|
+
return fs6__namespace.readdirSync(dir).filter((f) => f.endsWith(".kpis.json")).map((f) => path5__namespace.join(dir, f));
|
|
10452
|
+
} catch {
|
|
10453
|
+
return [];
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
function scanSubdirKpiFiles(dir, depth, acc) {
|
|
10457
|
+
if (depth > 4) return;
|
|
10458
|
+
try {
|
|
10459
|
+
const entries = fs6__namespace.readdirSync(dir, { withFileTypes: true });
|
|
10460
|
+
for (const entry of entries) {
|
|
10461
|
+
if (!entry.isDirectory() || IGNORED_SCAN_DIRS.has(entry.name)) continue;
|
|
10462
|
+
const sub = path5__namespace.join(dir, entry.name);
|
|
10463
|
+
if (entry.name === "releases" && path5__namespace.basename(dir) === "reports") {
|
|
10464
|
+
acc.push(...readKpiFilesInDir(sub));
|
|
10465
|
+
} else {
|
|
10466
|
+
scanSubdirKpiFiles(sub, depth + 1, acc);
|
|
10467
|
+
}
|
|
10468
|
+
}
|
|
10469
|
+
} catch {
|
|
10470
|
+
}
|
|
10471
|
+
}
|
|
10472
|
+
function findKpiJsonFiles(rootDir) {
|
|
10473
|
+
const primaryFiles = readKpiFilesInDir(path5__namespace.join(rootDir, "reports", "releases"));
|
|
10474
|
+
if (primaryFiles.length > 0) return primaryFiles;
|
|
10475
|
+
const found = [];
|
|
10476
|
+
scanSubdirKpiFiles(rootDir, 0, found);
|
|
10477
|
+
return found;
|
|
10478
|
+
}
|
|
10263
10479
|
function collectHistoricalKpis(rootDir) {
|
|
10264
|
-
const releasesDir = path5__namespace.join(rootDir, "reports", "releases");
|
|
10265
10480
|
const items = [];
|
|
10266
|
-
|
|
10481
|
+
const kpiFiles = findKpiJsonFiles(rootDir);
|
|
10482
|
+
for (const fullPath of kpiFiles) {
|
|
10267
10483
|
try {
|
|
10268
|
-
const
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
const content = fs6__namespace.readFileSync(fullPath, "utf-8");
|
|
10272
|
-
const report = JSON.parse(content);
|
|
10484
|
+
const content = fs6__namespace.readFileSync(fullPath, "utf-8");
|
|
10485
|
+
const report = JSON.parse(content);
|
|
10486
|
+
if (!items.some((it) => it.id === report.releaseBranch && it.timestamp === report.generatedAt)) {
|
|
10273
10487
|
items.push({
|
|
10274
10488
|
id: report.releaseBranch,
|
|
10275
10489
|
label: report.releaseBranch,
|
|
@@ -10338,6 +10552,82 @@ function resolveActivePrKpi(rootDir, activeBranch) {
|
|
|
10338
10552
|
return null;
|
|
10339
10553
|
}
|
|
10340
10554
|
}
|
|
10555
|
+
function collectGitUnmergedBranches(rootDir) {
|
|
10556
|
+
for (const base of ["main", "master"]) {
|
|
10557
|
+
try {
|
|
10558
|
+
const output = child_process.execFileSync("git", ["branch", "--no-merged", base], {
|
|
10559
|
+
cwd: rootDir,
|
|
10560
|
+
encoding: "utf-8",
|
|
10561
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10562
|
+
});
|
|
10563
|
+
return output.split("\n").map((l) => l.replace("*", "").trim()).filter((b) => b && !b.includes("detached"));
|
|
10564
|
+
} catch {
|
|
10565
|
+
}
|
|
10566
|
+
}
|
|
10567
|
+
return [];
|
|
10568
|
+
}
|
|
10569
|
+
function collectGitPrefixedBranches(rootDir) {
|
|
10570
|
+
try {
|
|
10571
|
+
const raw = child_process.execFileSync("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
|
|
10572
|
+
cwd: rootDir,
|
|
10573
|
+
encoding: "utf-8",
|
|
10574
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10575
|
+
});
|
|
10576
|
+
return raw.split("\n").map((s) => s.trim()).filter((b) => b.startsWith("feat/") || b.startsWith("bug/") || b.startsWith("task/") || b.startsWith("release/"));
|
|
10577
|
+
} catch {
|
|
10578
|
+
return [];
|
|
10579
|
+
}
|
|
10580
|
+
}
|
|
10581
|
+
function resolveCandidateBranches(rootDir, activeBranch, history) {
|
|
10582
|
+
const branchSet = /* @__PURE__ */ new Set();
|
|
10583
|
+
if (activeBranch && activeBranch !== "HEAD") {
|
|
10584
|
+
branchSet.add(activeBranch);
|
|
10585
|
+
}
|
|
10586
|
+
for (const h of history) {
|
|
10587
|
+
if (h.id && h.id !== "HEAD") branchSet.add(h.id);
|
|
10588
|
+
}
|
|
10589
|
+
for (const b of collectGitUnmergedBranches(rootDir)) branchSet.add(b);
|
|
10590
|
+
for (const b of collectGitPrefixedBranches(rootDir)) branchSet.add(b);
|
|
10591
|
+
return Array.from(branchSet);
|
|
10592
|
+
}
|
|
10593
|
+
function fetchBranchKpi(rootDir, branch) {
|
|
10594
|
+
try {
|
|
10595
|
+
const isDefault = branch === "main" || branch === "master";
|
|
10596
|
+
const base = isDefault ? resolveDefaultBranchBase(rootDir) : "main";
|
|
10597
|
+
const kpi = aggregatePrKpis(base, branch, { cwd: rootDir });
|
|
10598
|
+
return kpi && kpi.totalCommits > 0 ? kpi : null;
|
|
10599
|
+
} catch {
|
|
10600
|
+
return null;
|
|
10601
|
+
}
|
|
10602
|
+
}
|
|
10603
|
+
function recordBranchKpi(telemetries, commitMap, branch, kpi) {
|
|
10604
|
+
telemetries[branch] = kpi;
|
|
10605
|
+
for (const rec of kpi.commitRecords || []) commitMap.set(rec.sha, rec);
|
|
10606
|
+
}
|
|
10607
|
+
function populateCandidateTelemetries(rootDir, candidateBranches, telemetries, commitMap) {
|
|
10608
|
+
for (const branch of candidateBranches) {
|
|
10609
|
+
if (telemetries[branch]) continue;
|
|
10610
|
+
const kpi = fetchBranchKpi(rootDir, branch);
|
|
10611
|
+
if (kpi) recordBranchKpi(telemetries, commitMap, branch, kpi);
|
|
10612
|
+
}
|
|
10613
|
+
}
|
|
10614
|
+
function computeConsolidatedTelemetry(uniqueCommits, fallback) {
|
|
10615
|
+
if (uniqueCommits.length > 0) {
|
|
10616
|
+
return aggregateCommitKpiRecords(uniqueCommits, "all", "Totalizador");
|
|
10617
|
+
}
|
|
10618
|
+
return fallback;
|
|
10619
|
+
}
|
|
10620
|
+
function collectAllBranchTelemetries(rootDir, activeBranch, history, activePrKpi) {
|
|
10621
|
+
const branchTelemetries = {};
|
|
10622
|
+
const uniqueCommits = /* @__PURE__ */ new Map();
|
|
10623
|
+
if (activePrKpi && activeBranch && activeBranch !== "HEAD") {
|
|
10624
|
+
recordBranchKpi(branchTelemetries, uniqueCommits, activeBranch, activePrKpi);
|
|
10625
|
+
}
|
|
10626
|
+
const candidateBranches = resolveCandidateBranches(rootDir, activeBranch, history);
|
|
10627
|
+
populateCandidateTelemetries(rootDir, candidateBranches, branchTelemetries, uniqueCommits);
|
|
10628
|
+
const totalTelemetry = computeConsolidatedTelemetry(Array.from(uniqueCommits.values()), activePrKpi);
|
|
10629
|
+
return { branchTelemetries, totalTelemetry };
|
|
10630
|
+
}
|
|
10341
10631
|
function collectDashboardMetrics(rootDir) {
|
|
10342
10632
|
const gate = verifyQualityGate({ rootDir });
|
|
10343
10633
|
const gov = verifyTasksGovernance({ rootDir });
|
|
@@ -10348,6 +10638,12 @@ function collectDashboardMetrics(rootDir) {
|
|
|
10348
10638
|
const activeBranch = resolveActiveBranch(rootDir);
|
|
10349
10639
|
const activePrKpi = resolveActivePrKpi(rootDir, activeBranch);
|
|
10350
10640
|
const codeCoverage = readCodeCoverageSummary(rootDir);
|
|
10641
|
+
const { branchTelemetries, totalTelemetry } = collectAllBranchTelemetries(
|
|
10642
|
+
rootDir,
|
|
10643
|
+
activeBranch,
|
|
10644
|
+
history,
|
|
10645
|
+
activePrKpi
|
|
10646
|
+
);
|
|
10351
10647
|
return {
|
|
10352
10648
|
gate,
|
|
10353
10649
|
gov,
|
|
@@ -10357,7 +10653,9 @@ function collectDashboardMetrics(rootDir) {
|
|
|
10357
10653
|
manuals,
|
|
10358
10654
|
history,
|
|
10359
10655
|
activePrKpi,
|
|
10360
|
-
activeBranch
|
|
10656
|
+
activeBranch,
|
|
10657
|
+
branchTelemetries,
|
|
10658
|
+
totalTelemetry
|
|
10361
10659
|
};
|
|
10362
10660
|
}
|
|
10363
10661
|
function computeSummaryRatios(metricsData) {
|
|
@@ -12757,6 +13055,7 @@ exports.VALID_ARCHITECTURE_GRANULARITIES = VALID_ARCHITECTURE_GRANULARITIES;
|
|
|
12757
13055
|
exports.VALID_AUTONOMY_MODES = VALID_AUTONOMY_MODES;
|
|
12758
13056
|
exports.VALID_CHANGE_PROFILES = VALID_CHANGE_PROFILES;
|
|
12759
13057
|
exports.VALID_CI_PROVIDERS = VALID_CI_PROVIDERS;
|
|
13058
|
+
exports.aggregateCommitKpiRecords = aggregateCommitKpiRecords;
|
|
12760
13059
|
exports.aggregatePrKpis = aggregatePrKpis;
|
|
12761
13060
|
exports.aggregateReleaseKpis = aggregateReleaseKpis;
|
|
12762
13061
|
exports.appendRevisionHistoryRow = appendRevisionHistoryRow;
|
|
@@ -12863,6 +13162,7 @@ exports.resolveBranchHierarchy = resolveBranchHierarchy;
|
|
|
12863
13162
|
exports.resolveCiEnvironment = resolveCiEnvironment;
|
|
12864
13163
|
exports.resolveCytoscapePath = resolveCytoscapePath;
|
|
12865
13164
|
exports.resolveFeatureFilePath = resolveFeatureFilePath;
|
|
13165
|
+
exports.resolveGitRef = resolveGitRef;
|
|
12866
13166
|
exports.resolveLicenseFromFile = resolveLicenseFromFile;
|
|
12867
13167
|
exports.resolveSchemaForArtifact = resolveSchemaForArtifact;
|
|
12868
13168
|
exports.resolveTargetFiles = resolveTargetFiles;
|