@aisdlc/core 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7484,6 +7484,31 @@ function inferHumanTrailers(trailers, linesAdded, linesDeleted) {
7484
7484
  function inferMissingTrailers(trailers, linesAdded, linesDeleted) {
7485
7485
  return trailers.authorType === "agent" ? inferAgentTrailers(trailers, linesAdded, linesDeleted) : inferHumanTrailers(trailers, linesAdded, linesDeleted);
7486
7486
  }
7487
+ function resolveGitRef(cwd, ref) {
7488
+ if (!ref || ref === "HEAD") return ref;
7489
+ const candidates = [
7490
+ ref,
7491
+ `feat/${ref}`,
7492
+ `release/${ref}`,
7493
+ `bug/${ref}`,
7494
+ `task/${ref}`,
7495
+ `origin/${ref}`,
7496
+ `origin/feat/${ref}`,
7497
+ `origin/release/${ref}`,
7498
+ `origin/bug/${ref}`
7499
+ ];
7500
+ for (const cand of candidates) {
7501
+ try {
7502
+ execFileSync("git", ["rev-parse", "--verify", "--quiet", cand], {
7503
+ cwd,
7504
+ stdio: ["pipe", "pipe", "pipe"]
7505
+ });
7506
+ return cand;
7507
+ } catch {
7508
+ }
7509
+ }
7510
+ return ref;
7511
+ }
7487
7512
  function fetchGitLogOutput(cwd, baseRef, headRef, format) {
7488
7513
  const rangeArg = baseRef ? `${baseRef}..${headRef}` : headRef;
7489
7514
  try {
@@ -7527,8 +7552,10 @@ function buildCommitKpiRecord(chunk) {
7527
7552
  }
7528
7553
  function extractCommitKpisFromRange(baseRef, headRef = "HEAD", options = {}) {
7529
7554
  const cwd = options.cwd || process.cwd();
7555
+ const resolvedBase = resolveGitRef(cwd, baseRef);
7556
+ const resolvedHead = resolveGitRef(cwd, headRef);
7530
7557
  const format = "---AI_SDLC_COMMIT---%n%H%x1f%an%x1f%ae%x1f%aI%x1f%s%n%B%n---AI_SDLC_BODY_END---";
7531
- const rawOutput = fetchGitLogOutput(cwd, baseRef, headRef, format);
7558
+ const rawOutput = fetchGitLogOutput(cwd, resolvedBase, resolvedHead, format);
7532
7559
  if (!rawOutput) return [];
7533
7560
  const chunks = parseRawLogChunks(rawOutput);
7534
7561
  const records = [];
@@ -7852,8 +7879,7 @@ function accumulateRecord(target, record) {
7852
7879
  target.completionTokens += record.trailers.completionTokens;
7853
7880
  target.estimatedCostUsd = calculateEstimatedCost(target.promptTokens, target.completionTokens);
7854
7881
  }
7855
- function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
7856
- const records = extractCommitKpisFromRange(baseRef, headRef, options);
7882
+ function aggregateCommitKpiRecords(records, baseRef = "main", headRef = "HEAD") {
7857
7883
  const humanSummary = createEmptySummary();
7858
7884
  const byModel = {};
7859
7885
  let totalLinesAdded = 0;
@@ -7879,6 +7905,8 @@ function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
7879
7905
  accumulateRecord(humanSummary, record);
7880
7906
  }
7881
7907
  }
7908
+ const totalTokens = totalPrompt + totalCompletion;
7909
+ const estimatedCostUsd = calculateEstimatedCost(totalPrompt, totalCompletion);
7882
7910
  return {
7883
7911
  baseRef,
7884
7912
  headRef,
@@ -7888,14 +7916,18 @@ function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
7888
7916
  totalActiveTimeSeconds: totalActiveTime,
7889
7917
  totalPromptTokens: totalPrompt,
7890
7918
  totalCompletionTokens: totalCompletion,
7891
- totalTokens: totalPrompt + totalCompletion,
7892
- estimatedCostUsd: calculateEstimatedCost(totalPrompt, totalCompletion),
7919
+ totalTokens,
7920
+ estimatedCostUsd,
7893
7921
  humanSummary,
7894
7922
  byModel,
7895
7923
  isBugPr,
7896
7924
  commitRecords: records
7897
7925
  };
7898
7926
  }
7927
+ function aggregatePrKpis(baseRef, headRef = "HEAD", options = {}) {
7928
+ const records = extractCommitKpisFromRange(baseRef, headRef, options);
7929
+ return aggregateCommitKpiRecords(records, baseRef, headRef);
7930
+ }
7899
7931
  function formatMinutes(seconds) {
7900
7932
  const mins = Math.round(seconds / 60);
7901
7933
  return mins > 0 ? `${mins} min` : `${seconds}s`;
@@ -8031,7 +8063,10 @@ function computeReworkRatios(totalTime, totalTokens, totalCost, reworkTime, rewo
8031
8063
  };
8032
8064
  }
8033
8065
  function aggregateReleaseKpis(releaseBranch, baseBranch = "main", options = {}) {
8034
- const records = extractCommitKpisFromRange(baseBranch, releaseBranch, options);
8066
+ const cwd = options.cwd || process.cwd();
8067
+ const resolvedRelease = resolveGitRef(cwd, releaseBranch);
8068
+ const resolvedBase = resolveGitRef(cwd, baseBranch);
8069
+ const records = extractCommitKpisFromRange(resolvedBase, resolvedRelease, options);
8035
8070
  const statsMap = {};
8036
8071
  const bugList = [];
8037
8072
  const totals = { linesAdded: 0, linesDeleted: 0, time: 0, tokens: 0, reworkTime: 0, reworkTokens: 0 };
@@ -8046,8 +8081,8 @@ function aggregateReleaseKpis(releaseBranch, baseBranch = "main", options = {})
8046
8081
  const reworkCostUsd = calculateCost(totals.reworkTokens * 0.8, totals.reworkTokens * 0.2);
8047
8082
  const ratios = computeReworkRatios(totals.time, totals.tokens, totalCostUsd, totals.reworkTime, totals.reworkTokens, reworkCostUsd);
8048
8083
  return {
8049
- releaseBranch,
8050
- baseBranch,
8084
+ releaseBranch: resolvedRelease,
8085
+ baseBranch: resolvedBase,
8051
8086
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8052
8087
  totalCommits: records.length,
8053
8088
  totalLinesAdded: totals.linesAdded,
@@ -8114,7 +8149,7 @@ function formatReleaseKpiMarkdown(report) {
8114
8149
  ].join("\n");
8115
8150
  }
8116
8151
  function writeReleaseKpiReport(report, options = {}) {
8117
- const root = options.outputDir || path5.join(process.cwd(), "reports", "releases");
8152
+ const root = options.outputDir || (options.rootDir ? path5.join(options.rootDir, "reports", "releases") : path5.join(process.cwd(), "reports", "releases"));
8118
8153
  if (!fs6.existsSync(root)) {
8119
8154
  fs6.mkdirSync(root, { recursive: true });
8120
8155
  }
@@ -9768,6 +9803,86 @@ var DASHBOARD_CLIENT_SCRIPT = `
9768
9803
  if (backdrop) backdrop.style.display = 'block';
9769
9804
  }
9770
9805
 
9806
+ function filterTelemetryByBranch(branchKey) {
9807
+ if (typeof telemetryData === 'undefined' || !telemetryData) return;
9808
+ var data = telemetryData[branchKey] || null;
9809
+ if (!data) return;
9810
+
9811
+ var isTotal = branchKey === '__total__';
9812
+ var label = isTotal ? 'Totalizador' : branchKey;
9813
+ var badgeText = isTotal ? 'Vista: Totalizador Global' : ('Rama: ' + branchKey);
9814
+
9815
+ var cardBranchLabel = document.getElementById('card-branch-label');
9816
+ if (cardBranchLabel) cardBranchLabel.textContent = label;
9817
+
9818
+ var tableBranchLabel = document.getElementById('table-branch-label');
9819
+ if (tableBranchLabel) tableBranchLabel.textContent = isTotal ? 'Totalizador (Todas las ramas)' : branchKey;
9820
+
9821
+ var badgeElem = document.getElementById('telemetry-badge-container');
9822
+ if (badgeElem) {
9823
+ 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>';
9824
+ }
9825
+
9826
+ var elCommits = document.getElementById('card-commits');
9827
+ if (elCommits) elCommits.textContent = data.totalCommits;
9828
+ var elLines = document.getElementById('card-lines');
9829
+ if (elLines) elLines.textContent = '+' + data.linesAdded + ' / -' + data.linesDeleted + ' lines';
9830
+
9831
+ var elTime = document.getElementById('card-time');
9832
+ if (elTime) elTime.textContent = Math.round(data.totalActiveTimeSeconds / 60) + 'm';
9833
+ var elTimeSec = document.getElementById('card-time-seconds');
9834
+ if (elTimeSec) elTimeSec.textContent = data.totalActiveTimeSeconds + 's accumulated';
9835
+
9836
+ var elTokens = document.getElementById('card-tokens');
9837
+ if (elTokens) elTokens.textContent = Number(data.totalTokens).toLocaleString('en-US');
9838
+ var elTokensDetail = document.getElementById('card-tokens-detail');
9839
+ if (elTokensDetail) elTokensDetail.textContent = 'In: ' + Number(data.totalPromptTokens).toLocaleString('en-US') + ' | Out: ' + Number(data.totalCompletionTokens).toLocaleString('en-US');
9840
+
9841
+ var elCost = document.getElementById('card-cost');
9842
+ if (elCost) elCost.textContent = '$' + Number(data.estimatedCostUsd).toFixed(2);
9843
+
9844
+ var tbody = document.getElementById('telemetry-table-tbody');
9845
+ if (tbody) {
9846
+ var html = '';
9847
+ if (data.humanSummary && data.humanSummary.commits > 0) {
9848
+ html += '<tr>' +
9849
+ '<td><strong>Human (Developer)</strong></td>' +
9850
+ '<td><span class="badge" style="background:#334155;">HUMAN</span></td>' +
9851
+ '<td>' + data.humanSummary.commits + '</td>' +
9852
+ '<td>+' + data.humanSummary.linesAdded + ' / -' + data.humanSummary.linesDeleted + '</td>' +
9853
+ '<td>' + Math.round(data.humanSummary.activeTimeSeconds / 60) + 'm</td>' +
9854
+ '<td>0</td><td>\u2014</td>' +
9855
+ '</tr>';
9856
+ }
9857
+ if (data.byModel) {
9858
+ var models = Object.keys(data.byModel);
9859
+ for (var i = 0; i < models.length; i++) {
9860
+ var m = models[i];
9861
+ var summary = data.byModel[m];
9862
+ var safeModel = String(m).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
9863
+ html += '<tr>' +
9864
+ '<td><strong><code>' + safeModel + '</code></strong></td>' +
9865
+ '<td><span class="badge badge-green">AGENT</span></td>' +
9866
+ '<td>' + summary.commits + '</td>' +
9867
+ '<td>+' + summary.linesAdded + ' / -' + summary.linesDeleted + '</td>' +
9868
+ '<td>' + Math.round(summary.activeTimeSeconds / 60) + 'm</td>' +
9869
+ '<td>' + (summary.promptTokens + summary.completionTokens).toLocaleString('en-US') + '</td>' +
9870
+ '<td>$' + Number(summary.estimatedCostUsd).toFixed(2) + '</td>' +
9871
+ '</tr>';
9872
+ }
9873
+ }
9874
+ html += '<tr style="font-weight: bold; background: rgba(255,255,255,0.05);">' +
9875
+ '<td>' + (isTotal ? 'TOTAL REPOSITORIO' : 'TOTAL RAMA') + '</td><td>\u2014</td>' +
9876
+ '<td>' + data.totalCommits + '</td>' +
9877
+ '<td>+' + data.linesAdded + ' / -' + data.linesDeleted + '</td>' +
9878
+ '<td>' + Math.round(data.totalActiveTimeSeconds / 60) + 'm</td>' +
9879
+ '<td>' + Number(data.totalTokens).toLocaleString('en-US') + '</td>' +
9880
+ '<td>$' + Number(data.estimatedCostUsd).toFixed(2) + ' USD</td>' +
9881
+ '</tr>';
9882
+ tbody.innerHTML = html;
9883
+ }
9884
+ }
9885
+
9771
9886
  function closeTaskSidebar() {
9772
9887
  var sidebar = document.getElementById('task-sidebar');
9773
9888
  var backdrop = document.getElementById('task-backdrop');
@@ -10082,13 +10197,39 @@ function renderManualsTab(manuals) {
10082
10197
  `<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>`
10083
10198
  ].join("\n");
10084
10199
  }
10085
- function renderTelemetryCardsHtml(kpi) {
10200
+ function renderBranchOption(b, activeBranch) {
10201
+ const isAct = b === activeBranch ? " (Rama activa)" : "";
10202
+ return ` <option value="${escapeHtml(b)}">${escapeHtml(b)}${isAct}</option>`;
10203
+ }
10204
+ function renderTelemetrySelect(options) {
10205
+ return [
10206
+ ` <div style="display: flex; align-items: center; gap: 0.6rem;">`,
10207
+ ` <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>`,
10208
+ ` <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;">`,
10209
+ options,
10210
+ ` </select>`,
10211
+ ` </div>`
10212
+ ].join("\n");
10213
+ }
10214
+ function renderTelemetryFilterBarHtml(branches, activeBranch) {
10215
+ const options = [
10216
+ ` <option value="__total__" selected>\u{1F31F} Totalizador (Todas las ramas)</option>`,
10217
+ ...branches.map((b) => renderBranchOption(b, activeBranch))
10218
+ ].join("\n");
10219
+ const selectHtml = renderTelemetrySelect(options);
10220
+ 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>`;
10221
+ 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;">
10222
+ ${selectHtml}
10223
+ ${badgeHtml}
10224
+ </div>`;
10225
+ }
10226
+ function renderTelemetryCardsHtml(kpi, displayLabel = "Totalizador") {
10086
10227
  return [
10087
10228
  `<div class="grid-cards">`,
10088
- ` <div class="metric-card"><span class="title">Commits in Active Branch</span><span class="value">${kpi.totalCommits}</span><span class="subtitle">+${kpi.linesAdded} / -${kpi.linesDeleted} lines</span></div>`,
10089
- ` <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>`,
10090
- ` <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>`,
10091
- ` <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>`,
10229
+ ` <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>`,
10230
+ ` <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>`,
10231
+ ` <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>`,
10232
+ ` <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>`,
10092
10233
  `</div>`
10093
10234
  ].join("\n");
10094
10235
  }
@@ -10118,27 +10259,27 @@ function renderHumanTelemetryRow(human) {
10118
10259
  `</tr>`
10119
10260
  ].join("");
10120
10261
  }
10121
- function renderTotalBranchRow(kpi) {
10262
+ function renderTotalBranchRow(kpi, isTotal = false) {
10122
10263
  const mins = Math.round(kpi.totalActiveTimeSeconds / 60);
10123
10264
  const tokens = kpi.totalTokens.toLocaleString("en-US");
10124
10265
  const cost = kpi.estimatedCostUsd.toFixed(2);
10266
+ const label = isTotal ? "TOTAL REPOSITORIO" : "TOTAL RAMA";
10125
10267
  return [
10126
10268
  `<tr style="font-weight: bold; background: rgba(255,255,255,0.05);">`,
10127
- ` <td>TOTAL BRANCH</td><td>\u2014</td><td>${kpi.totalCommits}</td><td>+${kpi.linesAdded} / -${kpi.linesDeleted}</td>`,
10269
+ ` <td>${label}</td><td>\u2014</td><td>${kpi.totalCommits}</td><td>+${kpi.linesAdded} / -${kpi.linesDeleted}</td>`,
10128
10270
  ` <td>${mins}m</td><td>${tokens}</td><td>$${cost} USD</td>`,
10129
10271
  `</tr>`
10130
10272
  ].join("");
10131
10273
  }
10132
- function renderActiveTelemetrySection(kpi, branch) {
10133
- const cardsHtml = renderTelemetryCardsHtml(kpi);
10274
+ function renderTelemetryTable(kpi, displayLabel) {
10134
10275
  const humanRow = renderHumanTelemetryRow(kpi.humanSummary);
10135
10276
  const modelRows = Object.entries(kpi.byModel).map(([m, s]) => renderModelRow(m, s)).join("\n");
10136
- const totalRow = renderTotalBranchRow(kpi);
10277
+ const totalRow = renderTotalBranchRow(kpi, displayLabel === "Totalizador");
10278
+ const titleLabel = "Totalizador (Todas las ramas)" ;
10137
10279
  return [
10138
- cardsHtml,
10139
10280
  `<div class="chart-container">`,
10140
- ` <h3>Telemetry Breakdown by Author and Model (${branch || "HEAD"})</h3>`,
10141
- ` <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>`,
10281
+ ` <h3>Telemetry Breakdown by Author and Model (<span id="table-branch-label">${escapeHtml(titleLabel)}</span>)</h3>`,
10282
+ ` <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">`,
10142
10283
  humanRow,
10143
10284
  modelRows,
10144
10285
  totalRow,
@@ -10146,6 +10287,14 @@ function renderActiveTelemetrySection(kpi, branch) {
10146
10287
  `</div>`
10147
10288
  ].join("\n");
10148
10289
  }
10290
+ function renderActiveTelemetrySection(kpi, displayLabel, branches, activeBranch) {
10291
+ const filterBarHtml = renderTelemetryFilterBarHtml(branches, activeBranch);
10292
+ const cardsHtml = renderTelemetryCardsHtml(kpi, displayLabel);
10293
+ const tableHtml = renderTelemetryTable(kpi, displayLabel);
10294
+ return `${filterBarHtml}
10295
+ ${cardsHtml}
10296
+ ${tableHtml}`;
10297
+ }
10149
10298
  function renderReleaseHistoryRow(h) {
10150
10299
  return [
10151
10300
  `<tr>`,
@@ -10154,10 +10303,25 @@ function renderReleaseHistoryRow(h) {
10154
10303
  `</tr>`
10155
10304
  ].join("");
10156
10305
  }
10306
+ function renderHistoricalKpisTable(history) {
10307
+ if (history.length === 0) {
10308
+ 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>`;
10309
+ }
10310
+ const rows = history.map(renderReleaseHistoryRow).join("\n");
10311
+ 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>`;
10312
+ }
10313
+ function resolveBranchFilterKeys(metricsData) {
10314
+ const keys = Object.keys(metricsData.branchTelemetries || {});
10315
+ if (metricsData.activeBranch && metricsData.activeBranch !== "HEAD" && !keys.includes(metricsData.activeBranch)) {
10316
+ keys.unshift(metricsData.activeBranch);
10317
+ }
10318
+ return keys;
10319
+ }
10157
10320
  function renderKpisTabHtml(metricsData) {
10158
- const telemetryHtml = metricsData.activePrKpi ? renderActiveTelemetrySection(metricsData.activePrKpi, metricsData.activeBranch) : "";
10159
- const historyRows = metricsData.history.map(renderReleaseHistoryRow).join("\n");
10160
- const historyContent = metricsData.history.length > 0 ? `<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>${historyRows}</tbody></table>` : `<p style="color: var(--text-muted); font-size: 0.85rem;">No consolidated release reports recorded yet in <code>reports/releases/*.kpis.json</code>.</p>`;
10321
+ const displayKpi = metricsData.totalTelemetry || metricsData.activePrKpi;
10322
+ const branchKeys = resolveBranchFilterKeys(metricsData);
10323
+ 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>';
10324
+ const historyContent = renderHistoricalKpisTable(metricsData.history);
10161
10325
  return [
10162
10326
  ` <div id="tab-kpis" class="tab-panel">`,
10163
10327
  ` <div class="content-container">`,
@@ -10178,18 +10342,29 @@ function renderTaskSidebarHtml() {
10178
10342
  ` </aside>`
10179
10343
  ].join("\n");
10180
10344
  }
10345
+ function buildTelemetryMap(metricsData) {
10346
+ const map = {};
10347
+ const total = metricsData.totalTelemetry || metricsData.activePrKpi;
10348
+ if (total) map["__total__"] = total;
10349
+ Object.assign(map, metricsData.branchTelemetries || {});
10350
+ if (metricsData.activePrKpi && metricsData.activeBranch && !map[metricsData.activeBranch]) {
10351
+ map[metricsData.activeBranch] = metricsData.activePrKpi;
10352
+ }
10353
+ return map;
10354
+ }
10181
10355
  function renderDashboardScripts(graphData, metricsData) {
10182
- const cytoscapeScript = getCytoscapeScript();
10183
10356
  const elementsJson = safeJsonForScript(graphData.elements);
10184
10357
  const govTasksJson = safeJsonForScript(metricsData.gov.tasks || []);
10185
10358
  const allManuals = [...metricsData.manuals.userManuals, ...metricsData.manuals.productionManuals];
10186
10359
  const manualsJson = safeJsonForScript(allManuals);
10360
+ const telemetryJson = safeJsonForScript(buildTelemetryMap(metricsData));
10187
10361
  return [
10188
- ` <script>${cytoscapeScript}</script>`,
10362
+ ` <script>${getCytoscapeScript()}</script>`,
10189
10363
  ` <script>`,
10190
10364
  ` var elements = ${elementsJson};`,
10191
10365
  ` var govTasks = ${govTasksJson};`,
10192
10366
  ` var manualsData = ${manualsJson};`,
10367
+ ` var telemetryData = ${telemetryJson};`,
10193
10368
  DASHBOARD_CLIENT_SCRIPT,
10194
10369
  ` </script>`
10195
10370
  ].join("\n");
@@ -10232,16 +10407,55 @@ ${scripts}
10232
10407
  }
10233
10408
 
10234
10409
  // src/reporters/dashboard.ts
10410
+ var IGNORED_SCAN_DIRS = /* @__PURE__ */ new Set([
10411
+ "node_modules",
10412
+ ".git",
10413
+ "dist",
10414
+ "coverage",
10415
+ "bin",
10416
+ "obj",
10417
+ ".gemini",
10418
+ ".agent"
10419
+ ]);
10420
+ function readKpiFilesInDir(dir) {
10421
+ if (!fs6.existsSync(dir)) return [];
10422
+ try {
10423
+ return fs6.readdirSync(dir).filter((f) => f.endsWith(".kpis.json")).map((f) => path5.join(dir, f));
10424
+ } catch {
10425
+ return [];
10426
+ }
10427
+ }
10428
+ function scanSubdirKpiFiles(dir, depth, acc) {
10429
+ if (depth > 4) return;
10430
+ try {
10431
+ const entries = fs6.readdirSync(dir, { withFileTypes: true });
10432
+ for (const entry of entries) {
10433
+ if (!entry.isDirectory() || IGNORED_SCAN_DIRS.has(entry.name)) continue;
10434
+ const sub = path5.join(dir, entry.name);
10435
+ if (entry.name === "releases" && path5.basename(dir) === "reports") {
10436
+ acc.push(...readKpiFilesInDir(sub));
10437
+ } else {
10438
+ scanSubdirKpiFiles(sub, depth + 1, acc);
10439
+ }
10440
+ }
10441
+ } catch {
10442
+ }
10443
+ }
10444
+ function findKpiJsonFiles(rootDir) {
10445
+ const primaryFiles = readKpiFilesInDir(path5.join(rootDir, "reports", "releases"));
10446
+ if (primaryFiles.length > 0) return primaryFiles;
10447
+ const found = [];
10448
+ scanSubdirKpiFiles(rootDir, 0, found);
10449
+ return found;
10450
+ }
10235
10451
  function collectHistoricalKpis(rootDir) {
10236
- const releasesDir = path5.join(rootDir, "reports", "releases");
10237
10452
  const items = [];
10238
- if (fs6.existsSync(releasesDir)) {
10453
+ const kpiFiles = findKpiJsonFiles(rootDir);
10454
+ for (const fullPath of kpiFiles) {
10239
10455
  try {
10240
- const files = fs6.readdirSync(releasesDir).filter((f) => f.endsWith(".kpis.json"));
10241
- for (const file of files) {
10242
- const fullPath = path5.join(releasesDir, file);
10243
- const content = fs6.readFileSync(fullPath, "utf-8");
10244
- const report = JSON.parse(content);
10456
+ const content = fs6.readFileSync(fullPath, "utf-8");
10457
+ const report = JSON.parse(content);
10458
+ if (!items.some((it) => it.id === report.releaseBranch && it.timestamp === report.generatedAt)) {
10245
10459
  items.push({
10246
10460
  id: report.releaseBranch,
10247
10461
  label: report.releaseBranch,
@@ -10310,6 +10524,82 @@ function resolveActivePrKpi(rootDir, activeBranch) {
10310
10524
  return null;
10311
10525
  }
10312
10526
  }
10527
+ function collectGitUnmergedBranches(rootDir) {
10528
+ for (const base of ["main", "master"]) {
10529
+ try {
10530
+ const output = execFileSync("git", ["branch", "--no-merged", base], {
10531
+ cwd: rootDir,
10532
+ encoding: "utf-8",
10533
+ stdio: ["pipe", "pipe", "pipe"]
10534
+ });
10535
+ return output.split("\n").map((l) => l.replace("*", "").trim()).filter((b) => b && !b.includes("detached"));
10536
+ } catch {
10537
+ }
10538
+ }
10539
+ return [];
10540
+ }
10541
+ function collectGitPrefixedBranches(rootDir) {
10542
+ try {
10543
+ const raw = execFileSync("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
10544
+ cwd: rootDir,
10545
+ encoding: "utf-8",
10546
+ stdio: ["pipe", "pipe", "pipe"]
10547
+ });
10548
+ return raw.split("\n").map((s) => s.trim()).filter((b) => b.startsWith("feat/") || b.startsWith("bug/") || b.startsWith("task/") || b.startsWith("release/"));
10549
+ } catch {
10550
+ return [];
10551
+ }
10552
+ }
10553
+ function resolveCandidateBranches(rootDir, activeBranch, history) {
10554
+ const branchSet = /* @__PURE__ */ new Set();
10555
+ if (activeBranch && activeBranch !== "HEAD") {
10556
+ branchSet.add(activeBranch);
10557
+ }
10558
+ for (const h of history) {
10559
+ if (h.id && h.id !== "HEAD") branchSet.add(h.id);
10560
+ }
10561
+ for (const b of collectGitUnmergedBranches(rootDir)) branchSet.add(b);
10562
+ for (const b of collectGitPrefixedBranches(rootDir)) branchSet.add(b);
10563
+ return Array.from(branchSet);
10564
+ }
10565
+ function fetchBranchKpi(rootDir, branch) {
10566
+ try {
10567
+ const isDefault = branch === "main" || branch === "master";
10568
+ const base = isDefault ? resolveDefaultBranchBase(rootDir) : "main";
10569
+ const kpi = aggregatePrKpis(base, branch, { cwd: rootDir });
10570
+ return kpi && kpi.totalCommits > 0 ? kpi : null;
10571
+ } catch {
10572
+ return null;
10573
+ }
10574
+ }
10575
+ function recordBranchKpi(telemetries, commitMap, branch, kpi) {
10576
+ telemetries[branch] = kpi;
10577
+ for (const rec of kpi.commitRecords || []) commitMap.set(rec.sha, rec);
10578
+ }
10579
+ function populateCandidateTelemetries(rootDir, candidateBranches, telemetries, commitMap) {
10580
+ for (const branch of candidateBranches) {
10581
+ if (telemetries[branch]) continue;
10582
+ const kpi = fetchBranchKpi(rootDir, branch);
10583
+ if (kpi) recordBranchKpi(telemetries, commitMap, branch, kpi);
10584
+ }
10585
+ }
10586
+ function computeConsolidatedTelemetry(uniqueCommits, fallback) {
10587
+ if (uniqueCommits.length > 0) {
10588
+ return aggregateCommitKpiRecords(uniqueCommits, "all", "Totalizador");
10589
+ }
10590
+ return fallback;
10591
+ }
10592
+ function collectAllBranchTelemetries(rootDir, activeBranch, history, activePrKpi) {
10593
+ const branchTelemetries = {};
10594
+ const uniqueCommits = /* @__PURE__ */ new Map();
10595
+ if (activePrKpi && activeBranch && activeBranch !== "HEAD") {
10596
+ recordBranchKpi(branchTelemetries, uniqueCommits, activeBranch, activePrKpi);
10597
+ }
10598
+ const candidateBranches = resolveCandidateBranches(rootDir, activeBranch, history);
10599
+ populateCandidateTelemetries(rootDir, candidateBranches, branchTelemetries, uniqueCommits);
10600
+ const totalTelemetry = computeConsolidatedTelemetry(Array.from(uniqueCommits.values()), activePrKpi);
10601
+ return { branchTelemetries, totalTelemetry };
10602
+ }
10313
10603
  function collectDashboardMetrics(rootDir) {
10314
10604
  const gate = verifyQualityGate({ rootDir });
10315
10605
  const gov = verifyTasksGovernance({ rootDir });
@@ -10320,6 +10610,12 @@ function collectDashboardMetrics(rootDir) {
10320
10610
  const activeBranch = resolveActiveBranch(rootDir);
10321
10611
  const activePrKpi = resolveActivePrKpi(rootDir, activeBranch);
10322
10612
  const codeCoverage = readCodeCoverageSummary(rootDir);
10613
+ const { branchTelemetries, totalTelemetry } = collectAllBranchTelemetries(
10614
+ rootDir,
10615
+ activeBranch,
10616
+ history,
10617
+ activePrKpi
10618
+ );
10323
10619
  return {
10324
10620
  gate,
10325
10621
  gov,
@@ -10329,7 +10625,9 @@ function collectDashboardMetrics(rootDir) {
10329
10625
  manuals,
10330
10626
  history,
10331
10627
  activePrKpi,
10332
- activeBranch
10628
+ activeBranch,
10629
+ branchTelemetries,
10630
+ totalTelemetry
10333
10631
  };
10334
10632
  }
10335
10633
  function computeSummaryRatios(metricsData) {
@@ -12674,6 +12972,6 @@ function initProject(options = {}) {
12674
12972
  );
12675
12973
  }
12676
12974
 
12677
- export { ALL_ARCHITECTURE_TEMPLATES, BRANCH_PATTERNS, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_EXCLUDES, DEFAULT_EXTENSIONS, DEFAULT_SAST_EXCLUDES, GENERAL_SOURCE_EXTENSIONS, KPI_SUMMARY_END_TAG, KPI_SUMMARY_START_TAG, OpenSpecAdapter, PREPARE_COMMIT_MSG_SCRIPT, PROMPT_INJECTION_SIGNATURES, PROTECTED_BYPASS_PATTERNS, SAST_RULES, SECRET_RULES, SPECIALIZED_AGENT_TEMPLATES, STARTER_ANTIGRAVITY_MCP, STARTER_ANTIGRAVITY_RULES, STARTER_ARCH_ADR, STARTER_ARCH_ARCHITECTURE_CONSTRAINTS, STARTER_ARCH_COMPONENT, STARTER_ARCH_CONTEXT_AND_SCOPE, STARTER_ARCH_CROSS_CUTTING_CONCEPTS, STARTER_ARCH_DEPLOYMENT_VIEW, STARTER_ARCH_GLOSSARY, STARTER_ARCH_INTRODUCTION_AND_GOALS, STARTER_ARCH_LEVEL_1_WHITEBOX, STARTER_ARCH_QUALITY_REQUIREMENTS, STARTER_ARCH_RISKS_AND_TECHNICAL_DEBT, STARTER_ARCH_RUNTIME_VIEW, STARTER_ARCH_SOLUTION_STRATEGY, STARTER_AZURE_PIPELINES, STARTER_BITBUCKET_PIPELINES, STARTER_CLAUDE_RULES, STARTER_COPILOT_RULES, STARTER_CURSOR_CORE_RULES, STARTER_CURSOR_MCP, STARTER_CURSOR_PRODUCT_RULES, STARTER_CURSOR_QUALITY_RULES, STARTER_GITHUB_CI, STARTER_GITHUB_SDD_INTEGRATE, STARTER_GITLAB_CI, STARTER_LICENSE_POLICY, STARTER_QUALITY_POLICY, STARTER_REQUIREMENT_TEMPLATE, STARTER_TASK_TEMPLATE, STARTER_VSCODE_MCP, STARTER_WORKFLOW_HANDOFF_TEMPLATE, SpecKitAdapter, TYPE_TO_SCHEMA_MAP, VALID_AGENT_TARGETS, VALID_ARCHITECTURE_GRANULARITIES, VALID_AUTONOMY_MODES, VALID_CHANGE_PROFILES, VALID_CI_PROVIDERS, aggregatePrKpis, aggregateReleaseKpis, appendRevisionHistoryRow, buildCycloneDxComponent, buildFeatureFileContent, buildGraphElements, calculateJaccardSimilarity, calculateMetrics, calculateRating, calculateShannonEntropy, checkGherkinInSync, checkoutTaskBranch, classifyBranch, collectAgentFiles, collectDashboardMetrics, collectHistoricalKpis, computeCanonicalSha256, computeSha256, createDefaultQualityPolicy, depositProductHandoffSidecar, detectActiveChangeForIntegration, detectAuthorIdentity, detectLanguage, detectProfileFromChange, detectPromptInjection, emitCiOutput, evaluateCompoundLicense, evaluateSingleDependency, extractCitations, extractCommitKpisFromRange, extractDirectDepNames, extractFeatureTags, extractFrontmatter, extractFunctions, extractFunctionsBraceLanguages, extractFunctionsPolyglot, extractFunctionsPython, extractFunctionsTypeScriptAst, extractGherkinBlock, extractGherkinFeatures, extractNormativeText, findChangeWorkspaceDir, findLicenseFile, formatCommitTrailers, formatPrKpiMarkdown, formatReleaseKpiMarkdown, generateBranchHierarchyPlan, generateCycloneDxSbom, generateDashboardReport, generateDuplicatesReportMarkdown, generateGovernanceReportMarkdown, generateLicenseReportMarkdown, generatePdacReportMarkdown, generateQualityReport, generateSastReportMarkdown, generateSecretsReportMarkdown, generateTestingCoverageReportMarkdown, generateThirdPartyNotices, generateTraceabilityReportMarkdown, getArchitectureTemplates, getCytoscapeScript, getGitDiffLines, getNextCorrelativeNumber, getSddAdapter, inferArtifactTypeFromId, inferMissingTrailers, initProject, injectKpisIntoPrBody, installGitHooks, integrateSddChange, isGitleaksInstalled, isPathProtectedFromPatch, isPlaceholderOrIgnored, isRequirementArtifact2 as isRequirementArtifact, isSemgrepInstalled, isToolAvailable, loadProductHandoffSidecar, locateTaskInActiveChanges, maskSecret, normalizeSpdxLicense, parseCommitTrailers, parseFrontmatter, parseFrontmatterId, parseGenericDoc, parseLicensePolicy, parseMarkdownFrontmatter, parseQualityPolicy, parseRequirementFile, parseSpecCatalogScope, parseTasksDoc, parseTasksFromContent, processPackageDir, readCodeCoverageSummary, readPackageJson, renderAntigravityAgentFile, renderAutonomyModeSection, renderClaudeAgentFile, renderCopilotAgentFile, renderCursorAgentFile, renderDashboardHtml, renderManualsTab, resolveAgentTargets, resolveBranchHierarchy, resolveCiEnvironment, resolveCytoscapePath, resolveFeatureFilePath, resolveLicenseFromFile, resolveSchemaForArtifact, resolveTargetFiles, runGitleaksCli, runSemgrepCli, scaffoldSddChange, scanAllProductHandoffs, scanAllTestArtifacts, scanContentForSecrets, scanFileForSast, scanInstalledLicenses, scanManualsInWorkspace, scanPnpmStore, scanSubdirectories, scanWithNativeFs, scanWithSyft, scanWithTrivy, slugify, syncPdacDigests, tokenizeTitle, updateCitationsInContent, validateAgainstSchema, validateArtifactSchema, verifyArtifactDuplicates, verifyArtifactsSchemas, verifyLicenses, verifyManuals, verifyPdacGraph, verifyProgressiveFriction, verifyQualityGate, verifySast, verifySddIntegration, verifySecrets, verifyTasksGovernance, verifyTestingCoverage, verifyTraceability, walkDir, walkMdFiles, walkProjectFiles, walkSourceFiles, walkTaskFiles, writeCycloneDxSbom, writeReleaseKpiReport, writeThirdPartyNotices };
12975
+ export { ALL_ARCHITECTURE_TEMPLATES, BRANCH_PATTERNS, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_EXCLUDES, DEFAULT_EXTENSIONS, DEFAULT_SAST_EXCLUDES, GENERAL_SOURCE_EXTENSIONS, KPI_SUMMARY_END_TAG, KPI_SUMMARY_START_TAG, OpenSpecAdapter, PREPARE_COMMIT_MSG_SCRIPT, PROMPT_INJECTION_SIGNATURES, PROTECTED_BYPASS_PATTERNS, SAST_RULES, SECRET_RULES, SPECIALIZED_AGENT_TEMPLATES, STARTER_ANTIGRAVITY_MCP, STARTER_ANTIGRAVITY_RULES, STARTER_ARCH_ADR, STARTER_ARCH_ARCHITECTURE_CONSTRAINTS, STARTER_ARCH_COMPONENT, STARTER_ARCH_CONTEXT_AND_SCOPE, STARTER_ARCH_CROSS_CUTTING_CONCEPTS, STARTER_ARCH_DEPLOYMENT_VIEW, STARTER_ARCH_GLOSSARY, STARTER_ARCH_INTRODUCTION_AND_GOALS, STARTER_ARCH_LEVEL_1_WHITEBOX, STARTER_ARCH_QUALITY_REQUIREMENTS, STARTER_ARCH_RISKS_AND_TECHNICAL_DEBT, STARTER_ARCH_RUNTIME_VIEW, STARTER_ARCH_SOLUTION_STRATEGY, STARTER_AZURE_PIPELINES, STARTER_BITBUCKET_PIPELINES, STARTER_CLAUDE_RULES, STARTER_COPILOT_RULES, STARTER_CURSOR_CORE_RULES, STARTER_CURSOR_MCP, STARTER_CURSOR_PRODUCT_RULES, STARTER_CURSOR_QUALITY_RULES, STARTER_GITHUB_CI, STARTER_GITHUB_SDD_INTEGRATE, STARTER_GITLAB_CI, STARTER_LICENSE_POLICY, STARTER_QUALITY_POLICY, STARTER_REQUIREMENT_TEMPLATE, STARTER_TASK_TEMPLATE, STARTER_VSCODE_MCP, STARTER_WORKFLOW_HANDOFF_TEMPLATE, SpecKitAdapter, TYPE_TO_SCHEMA_MAP, VALID_AGENT_TARGETS, VALID_ARCHITECTURE_GRANULARITIES, VALID_AUTONOMY_MODES, VALID_CHANGE_PROFILES, VALID_CI_PROVIDERS, aggregateCommitKpiRecords, aggregatePrKpis, aggregateReleaseKpis, appendRevisionHistoryRow, buildCycloneDxComponent, buildFeatureFileContent, buildGraphElements, calculateJaccardSimilarity, calculateMetrics, calculateRating, calculateShannonEntropy, checkGherkinInSync, checkoutTaskBranch, classifyBranch, collectAgentFiles, collectDashboardMetrics, collectHistoricalKpis, computeCanonicalSha256, computeSha256, createDefaultQualityPolicy, depositProductHandoffSidecar, detectActiveChangeForIntegration, detectAuthorIdentity, detectLanguage, detectProfileFromChange, detectPromptInjection, emitCiOutput, evaluateCompoundLicense, evaluateSingleDependency, extractCitations, extractCommitKpisFromRange, extractDirectDepNames, extractFeatureTags, extractFrontmatter, extractFunctions, extractFunctionsBraceLanguages, extractFunctionsPolyglot, extractFunctionsPython, extractFunctionsTypeScriptAst, extractGherkinBlock, extractGherkinFeatures, extractNormativeText, findChangeWorkspaceDir, findLicenseFile, formatCommitTrailers, formatPrKpiMarkdown, formatReleaseKpiMarkdown, generateBranchHierarchyPlan, generateCycloneDxSbom, generateDashboardReport, generateDuplicatesReportMarkdown, generateGovernanceReportMarkdown, generateLicenseReportMarkdown, generatePdacReportMarkdown, generateQualityReport, generateSastReportMarkdown, generateSecretsReportMarkdown, generateTestingCoverageReportMarkdown, generateThirdPartyNotices, generateTraceabilityReportMarkdown, getArchitectureTemplates, getCytoscapeScript, getGitDiffLines, getNextCorrelativeNumber, getSddAdapter, inferArtifactTypeFromId, inferMissingTrailers, initProject, injectKpisIntoPrBody, installGitHooks, integrateSddChange, isGitleaksInstalled, isPathProtectedFromPatch, isPlaceholderOrIgnored, isRequirementArtifact2 as isRequirementArtifact, isSemgrepInstalled, isToolAvailable, loadProductHandoffSidecar, locateTaskInActiveChanges, maskSecret, normalizeSpdxLicense, parseCommitTrailers, parseFrontmatter, parseFrontmatterId, parseGenericDoc, parseLicensePolicy, parseMarkdownFrontmatter, parseQualityPolicy, parseRequirementFile, parseSpecCatalogScope, parseTasksDoc, parseTasksFromContent, processPackageDir, readCodeCoverageSummary, readPackageJson, renderAntigravityAgentFile, renderAutonomyModeSection, renderClaudeAgentFile, renderCopilotAgentFile, renderCursorAgentFile, renderDashboardHtml, renderManualsTab, resolveAgentTargets, resolveBranchHierarchy, resolveCiEnvironment, resolveCytoscapePath, resolveFeatureFilePath, resolveGitRef, resolveLicenseFromFile, resolveSchemaForArtifact, resolveTargetFiles, runGitleaksCli, runSemgrepCli, scaffoldSddChange, scanAllProductHandoffs, scanAllTestArtifacts, scanContentForSecrets, scanFileForSast, scanInstalledLicenses, scanManualsInWorkspace, scanPnpmStore, scanSubdirectories, scanWithNativeFs, scanWithSyft, scanWithTrivy, slugify, syncPdacDigests, tokenizeTitle, updateCitationsInContent, validateAgainstSchema, validateArtifactSchema, verifyArtifactDuplicates, verifyArtifactsSchemas, verifyLicenses, verifyManuals, verifyPdacGraph, verifyProgressiveFriction, verifyQualityGate, verifySast, verifySddIntegration, verifySecrets, verifyTasksGovernance, verifyTestingCoverage, verifyTraceability, walkDir, walkMdFiles, walkProjectFiles, walkSourceFiles, walkTaskFiles, writeCycloneDxSbom, writeReleaseKpiReport, writeThirdPartyNotices };
12678
12976
  //# sourceMappingURL=index.js.map
12679
12977
  //# sourceMappingURL=index.js.map