nairon-bench 0.3.14 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +459 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15226,21 +15226,28 @@ function aggregateRecommendations(frustrations) {
15226
15226
  for (const f3 of frustrations) {
15227
15227
  const key = f3.recommendedFix.tool;
15228
15228
  if (!byTool[key]) {
15229
- byTool[key] = { count: 0, timeSaved: 0, costSaved: 0, fix: f3.recommendedFix };
15229
+ byTool[key] = { count: 0, timeSaved: 0, costSaved: 0, fix: f3.recommendedFix, frustrations: [] };
15230
15230
  }
15231
15231
  byTool[key].count++;
15232
15232
  byTool[key].timeSaved += f3.estimatedTimeWastedMinutes * (f3.recommendedFix.estimatedImprovementPercent / 100);
15233
15233
  byTool[key].costSaved += f3.estimatedCostWasted * (f3.recommendedFix.estimatedImprovementPercent / 100);
15234
+ byTool[key].frustrations.push(f3);
15234
15235
  }
15235
15236
  const recommendations = [];
15236
15237
  for (const [tool, data] of Object.entries(byTool)) {
15238
+ const preventedFrustrations = data.frustrations.sort((a2, b2) => b2.estimatedTimeWastedMinutes - a2.estimatedTimeWastedMinutes).slice(0, 3).map((f3) => ({
15239
+ patternType: f3.patternDescription,
15240
+ promptSnippet: f3.exactPrompt.slice(0, 80) + (f3.exactPrompt.length > 80 ? "..." : ""),
15241
+ timeWasted: f3.estimatedTimeWastedMinutes
15242
+ }));
15237
15243
  recommendations.push({
15238
15244
  tool,
15239
15245
  description: data.fix.description,
15240
15246
  installCommand: data.fix.installCommand,
15241
15247
  wouldHavePreventedCount: data.count,
15242
15248
  estimatedTimeSavedMinutes: Math.round(data.timeSaved),
15243
- estimatedCostSaved: Math.round(data.costSaved * 100) / 100
15249
+ estimatedCostSaved: Math.round(data.costSaved * 100) / 100,
15250
+ preventedFrustrations
15244
15251
  });
15245
15252
  }
15246
15253
  recommendations.sort((a2, b2) => b2.estimatedTimeSavedMinutes - a2.estimatedTimeSavedMinutes);
@@ -15280,13 +15287,24 @@ function formatFrustrationSummary(summary) {
15280
15287
  }
15281
15288
  }
15282
15289
  if (summary.recommendations.length > 0) {
15283
- lines.push(` ${colors2.bold(colors2.primary("Recommended Fixes:"))}`);
15290
+ lines.push(` ${colors2.bold(colors2.primary("Contextual Recommendations:"))}`);
15291
+ lines.push("");
15292
+ const totalTimeSavings = summary.recommendations.reduce((a2, r3) => a2 + r3.estimatedTimeSavedMinutes, 0);
15293
+ const totalCostSavings = summary.recommendations.reduce((a2, r3) => a2 + r3.estimatedCostSaved, 0);
15294
+ lines.push(` ${colors2.dim("Total potential savings:")} ${colors2.success(`~${totalTimeSavings} min`)} | ${colors2.success(`$${totalCostSavings.toFixed(2)}`)}`);
15284
15295
  lines.push("");
15285
- for (const rec of summary.recommendations.slice(0, 3)) {
15286
- lines.push(` ${colors2.success("+")} ${colors2.bold(rec.tool)}`);
15287
- lines.push(` Would have prevented ${rec.wouldHavePreventedCount} issue${rec.wouldHavePreventedCount > 1 ? "s" : ""}`);
15288
- lines.push(` Est. time saved: ${colors2.success(`~${rec.estimatedTimeSavedMinutes} min`)}`);
15289
- lines.push(` ${colors2.dim("$")} ${colors2.primary(rec.installCommand)}`);
15296
+ for (let i3 = 0;i3 < Math.min(3, summary.recommendations.length); i3++) {
15297
+ const rec = summary.recommendations[i3];
15298
+ lines.push(` ${colors2.success(`${i3 + 1}.`)} ${colors2.bold(rec.tool)}`);
15299
+ lines.push(` ${rec.description}`);
15300
+ lines.push(` ${colors2.dim("Prevented:")} ${rec.wouldHavePreventedCount} issue${rec.wouldHavePreventedCount > 1 ? "s" : ""} ${colors2.dim("|")} ${colors2.success(`~${rec.estimatedTimeSavedMinutes} min saved`)}`);
15301
+ if (rec.preventedFrustrations.length > 0) {
15302
+ lines.push(` ${colors2.dim("Would have fixed:")}`);
15303
+ for (const pf of rec.preventedFrustrations.slice(0, 2)) {
15304
+ lines.push(` ${colors2.dim("•")} ${pf.patternType} ${colors2.dim(`(${pf.timeWasted} min)`)}`);
15305
+ }
15306
+ }
15307
+ lines.push(` ${colors2.dim("Install:")} ${colors2.primary(rec.installCommand)}`);
15290
15308
  lines.push("");
15291
15309
  }
15292
15310
  }
@@ -15346,6 +15364,362 @@ function wrapText(text, width) {
15346
15364
  return lines;
15347
15365
  }
15348
15366
 
15367
+ // src/lib/effort-analyzer.ts
15368
+ function analyzeEffort(sessions, git) {
15369
+ const sessionEfforts = [];
15370
+ let totalGeneration = 0;
15371
+ let totalReview = 0;
15372
+ let totalCorrection = 0;
15373
+ let totalIntegration = 0;
15374
+ let totalCorrectionLoops = 0;
15375
+ let totalContextChurn = 0;
15376
+ let totalRegenerations = 0;
15377
+ for (const session of sessions) {
15378
+ const effort = analyzeSessionEffort(session);
15379
+ sessionEfforts.push(effort);
15380
+ totalGeneration += effort.generationMinutes;
15381
+ totalReview += effort.reviewMinutes;
15382
+ totalCorrection += effort.correctionMinutes;
15383
+ totalIntegration += effort.integrationMinutes;
15384
+ for (const pattern of session.patterns) {
15385
+ if (pattern.type === "undo_loop") {
15386
+ totalCorrectionLoops += pattern.count;
15387
+ }
15388
+ if (pattern.type === "context_compaction" || pattern.type === "memory_loss") {
15389
+ totalContextChurn += pattern.count;
15390
+ }
15391
+ if (pattern.type === "repeated_rephrasing") {
15392
+ totalRegenerations += pattern.count;
15393
+ }
15394
+ }
15395
+ }
15396
+ const totalMinutes = totalGeneration + totalReview + totalCorrection + totalIntegration;
15397
+ const safeTotal = totalMinutes || 1;
15398
+ const generationPercent = Math.round(totalGeneration / safeTotal * 100);
15399
+ const reviewPercent = Math.round(totalReview / safeTotal * 100);
15400
+ const correctionPercent = Math.round(totalCorrection / safeTotal * 100);
15401
+ const integrationPercent = Math.round(totalIntegration / safeTotal * 100);
15402
+ const safeGeneration = totalGeneration || 1;
15403
+ const correctionRatio = totalCorrection / safeGeneration;
15404
+ const reviewOverhead = totalReview / safeGeneration;
15405
+ const trueEffortMultiplier = totalMinutes / safeGeneration;
15406
+ return {
15407
+ generationMinutes: totalGeneration,
15408
+ reviewMinutes: totalReview,
15409
+ correctionMinutes: totalCorrection,
15410
+ integrationMinutes: totalIntegration,
15411
+ totalMinutes,
15412
+ generationPercent,
15413
+ reviewPercent,
15414
+ correctionPercent,
15415
+ integrationPercent,
15416
+ totalSessions: sessions.length,
15417
+ correctionLoops: totalCorrectionLoops,
15418
+ contextChurnEvents: totalContextChurn,
15419
+ regenerationCycles: totalRegenerations,
15420
+ correctionRatio: Math.round(correctionRatio * 100) / 100,
15421
+ reviewOverhead: Math.round(reviewOverhead * 100) / 100,
15422
+ trueEffortMultiplier: Math.round(trueEffortMultiplier * 100) / 100,
15423
+ sessions: sessionEfforts
15424
+ };
15425
+ }
15426
+ function analyzeSessionEffort(session) {
15427
+ const totalMinutes = session.durationMinutes;
15428
+ let generationRatio = 0.4;
15429
+ let reviewRatio = 0.3;
15430
+ let correctionRatio = 0.2;
15431
+ let integrationRatio = 0.1;
15432
+ const patterns = [];
15433
+ let contextChurn = "low";
15434
+ let correctionIntensity = "low";
15435
+ for (const pattern of session.patterns) {
15436
+ patterns.push(pattern.type);
15437
+ if (pattern.type === "undo_loop") {
15438
+ const undoImpact = Math.min(pattern.count * 0.05, 0.25);
15439
+ correctionRatio += undoImpact;
15440
+ generationRatio -= undoImpact / 2;
15441
+ reviewRatio -= undoImpact / 2;
15442
+ correctionIntensity = pattern.count >= 5 ? "high" : pattern.count >= 2 ? "medium" : "low";
15443
+ }
15444
+ if (pattern.type === "memory_loss" || pattern.type === "context_compaction") {
15445
+ const contextImpact = Math.min(pattern.count * 0.08, 0.2);
15446
+ correctionRatio += contextImpact / 2;
15447
+ reviewRatio += contextImpact / 2;
15448
+ generationRatio -= contextImpact;
15449
+ contextChurn = pattern.count >= 3 ? "high" : pattern.count >= 1 ? "medium" : "low";
15450
+ }
15451
+ if (pattern.type === "repeated_rephrasing") {
15452
+ const rephrasingImpact = Math.min(pattern.count * 0.03, 0.15);
15453
+ generationRatio += rephrasingImpact;
15454
+ reviewRatio -= rephrasingImpact / 2;
15455
+ integrationRatio -= rephrasingImpact / 2;
15456
+ }
15457
+ if (pattern.type === "frustration_caps") {
15458
+ correctionRatio += 0.05;
15459
+ reviewRatio -= 0.05;
15460
+ }
15461
+ if (pattern.type === "smooth_flow") {
15462
+ generationRatio += 0.1;
15463
+ correctionRatio -= 0.1;
15464
+ }
15465
+ }
15466
+ const totalRatio = generationRatio + reviewRatio + correctionRatio + integrationRatio;
15467
+ generationRatio /= totalRatio;
15468
+ reviewRatio /= totalRatio;
15469
+ correctionRatio /= totalRatio;
15470
+ integrationRatio /= totalRatio;
15471
+ const generationMinutes = Math.round(totalMinutes * generationRatio);
15472
+ const reviewMinutes = Math.round(totalMinutes * reviewRatio);
15473
+ const correctionMinutes = Math.round(totalMinutes * correctionRatio);
15474
+ const integrationMinutes = totalMinutes - generationMinutes - reviewMinutes - correctionMinutes;
15475
+ return {
15476
+ sessionId: session.sessionId,
15477
+ agent: session.agent,
15478
+ startedAt: session.startedAt,
15479
+ generationMinutes,
15480
+ reviewMinutes,
15481
+ correctionMinutes,
15482
+ integrationMinutes,
15483
+ totalMinutes,
15484
+ patterns,
15485
+ contextChurn,
15486
+ correctionIntensity
15487
+ };
15488
+ }
15489
+ function generateLeadershipReport(effort, periodLabel = "Last 7 days") {
15490
+ const totalHours = effort.totalMinutes / 60;
15491
+ const generationHours = effort.generationMinutes / 60;
15492
+ const reviewHours = effort.reviewMinutes / 60;
15493
+ const correctionHours = effort.correctionMinutes / 60;
15494
+ const integrationHours = effort.integrationMinutes / 60;
15495
+ const perceivedEffort = generationHours;
15496
+ const hiddenWork = totalHours - perceivedEffort;
15497
+ const hiddenWorkPercent = hiddenWork / totalHours * 100;
15498
+ const contextChurnRate = effort.contextChurnEvents / Math.max(effort.totalSessions, 1);
15499
+ const repeatedCorrectionRate = effort.correctionLoops / Math.max(effort.totalSessions, 1);
15500
+ let burnoutRisk = "low";
15501
+ if (effort.correctionRatio > 0.8 || hiddenWorkPercent > 60) {
15502
+ burnoutRisk = "high";
15503
+ } else if (effort.correctionRatio > 0.5 || hiddenWorkPercent > 40) {
15504
+ burnoutRisk = "medium";
15505
+ }
15506
+ const insights = [];
15507
+ if (hiddenWorkPercent > 50) {
15508
+ insights.push(`${Math.round(hiddenWorkPercent)}% of effort is invisible (review, correction, integration) - leadership may underestimate true workload`);
15509
+ }
15510
+ if (effort.correctionRatio > 0.5) {
15511
+ insights.push(`High correction ratio (${Math.round(effort.correctionRatio * 100)}%) - AI output requires significant human oversight`);
15512
+ }
15513
+ if (contextChurnRate > 0.5) {
15514
+ insights.push(`Frequent context loss (${Math.round(contextChurnRate * 100)}% of sessions) - engineers repeatedly re-explaining requirements`);
15515
+ }
15516
+ if (effort.trueEffortMultiplier > 2) {
15517
+ insights.push(`True effort is ${effort.trueEffortMultiplier.toFixed(1)}x perceived effort - AI accelerates generation but not total delivery`);
15518
+ }
15519
+ if (burnoutRisk === "high") {
15520
+ insights.push("High burnout risk: excessive correction cycles indicate AI output quality issues");
15521
+ }
15522
+ if (insights.length === 0) {
15523
+ insights.push("AI workflow appears efficient - correction overhead is within healthy limits");
15524
+ }
15525
+ return {
15526
+ period: periodLabel,
15527
+ aiAssistedTasksCount: effort.totalSessions,
15528
+ totalAIGenerationHours: Math.round(generationHours * 10) / 10,
15529
+ totalReviewHours: Math.round(reviewHours * 10) / 10,
15530
+ totalCorrectionHours: Math.round(correctionHours * 10) / 10,
15531
+ totalIntegrationHours: Math.round(integrationHours * 10) / 10,
15532
+ totalEffortHours: Math.round(totalHours * 10) / 10,
15533
+ averageCorrectionRatio: Math.round(effort.correctionRatio * 100) / 100,
15534
+ integrationOverheadPercent: effort.integrationPercent,
15535
+ contextChurnRate: Math.round(contextChurnRate * 100) / 100,
15536
+ perceivedEffortHours: Math.round(perceivedEffort * 10) / 10,
15537
+ trueEffortHours: Math.round(totalHours * 10) / 10,
15538
+ hiddenWorkHours: Math.round(hiddenWork * 10) / 10,
15539
+ hiddenWorkPercent: Math.round(hiddenWorkPercent),
15540
+ burnoutRisk,
15541
+ contextLossFrequency: effort.contextChurnEvents,
15542
+ repeatedCorrectionRate: Math.round(repeatedCorrectionRate * 100) / 100,
15543
+ insights
15544
+ };
15545
+ }
15546
+ function formatEffortBreakdown(effort) {
15547
+ const lines = [];
15548
+ lines.push("");
15549
+ lines.push(colors2.dim(" " + "═".repeat(50)));
15550
+ lines.push(` ${icons.chart} ${colors2.bold("Effort Breakdown")} ${colors2.dim("(AI Work Visibility)")}`);
15551
+ lines.push(colors2.dim(" " + "═".repeat(50)));
15552
+ lines.push("");
15553
+ const total = effort.totalMinutes || 1;
15554
+ const genBar = "█".repeat(Math.round(effort.generationPercent / 5));
15555
+ const revBar = "█".repeat(Math.round(effort.reviewPercent / 5));
15556
+ const corBar = "█".repeat(Math.round(effort.correctionPercent / 5));
15557
+ const intBar = "█".repeat(Math.round(effort.integrationPercent / 5));
15558
+ lines.push(` ${colors2.bold("Time Breakdown:")}`);
15559
+ lines.push(` ${colors2.success("Generation")} ${formatTime(effort.generationMinutes).padEnd(10)} ${colors2.success(genBar)} ${effort.generationPercent}%`);
15560
+ lines.push(` ${colors2.primary("Review")} ${formatTime(effort.reviewMinutes).padEnd(10)} ${colors2.primary(revBar)} ${effort.reviewPercent}%`);
15561
+ lines.push(` ${colors2.warning("Correction")} ${formatTime(effort.correctionMinutes).padEnd(10)} ${colors2.warning(corBar)} ${effort.correctionPercent}%`);
15562
+ lines.push(` ${colors2.dim("Integration")} ${formatTime(effort.integrationMinutes).padEnd(10)} ${colors2.dim(intBar)} ${effort.integrationPercent}%`);
15563
+ lines.push("");
15564
+ lines.push(` ${colors2.bold("Total Active Effort:")} ${formatTime(effort.totalMinutes)}`);
15565
+ lines.push("");
15566
+ const hiddenWork = effort.reviewMinutes + effort.correctionMinutes + effort.integrationMinutes;
15567
+ const hiddenPercent = Math.round(hiddenWork / total * 100);
15568
+ if (hiddenPercent > 30) {
15569
+ lines.push(` ${colors2.warning("!")} ${colors2.bold("Hidden Work Alert:")}`);
15570
+ lines.push(` ${hiddenPercent}% of effort is invisible (review + correction + integration)`);
15571
+ lines.push(` ${colors2.dim("This is work leadership may not see")}`);
15572
+ lines.push("");
15573
+ }
15574
+ lines.push(` ${colors2.bold("Key Metrics:")}`);
15575
+ lines.push(` Correction ratio: ${formatRatio(effort.correctionRatio)} ${getRatioIndicator(effort.correctionRatio)}`);
15576
+ lines.push(` Review overhead: ${formatRatio(effort.reviewOverhead)}`);
15577
+ lines.push(` True effort factor: ${effort.trueEffortMultiplier}x ${colors2.dim("(total / generation)")}`);
15578
+ lines.push("");
15579
+ if (effort.correctionLoops > 0 || effort.contextChurnEvents > 0) {
15580
+ lines.push(` ${colors2.bold("Detected Issues:")}`);
15581
+ if (effort.correctionLoops > 0) {
15582
+ lines.push(` ${colors2.warning("•")} ${effort.correctionLoops} correction loop${effort.correctionLoops > 1 ? "s" : ""}`);
15583
+ }
15584
+ if (effort.contextChurnEvents > 0) {
15585
+ lines.push(` ${colors2.warning("•")} ${effort.contextChurnEvents} context churn event${effort.contextChurnEvents > 1 ? "s" : ""}`);
15586
+ }
15587
+ if (effort.regenerationCycles > 0) {
15588
+ lines.push(` ${colors2.dim("•")} ${effort.regenerationCycles} regeneration cycle${effort.regenerationCycles > 1 ? "s" : ""}`);
15589
+ }
15590
+ lines.push("");
15591
+ }
15592
+ lines.push(colors2.dim(" " + "═".repeat(50)));
15593
+ return lines;
15594
+ }
15595
+ function formatLeadershipReport(report) {
15596
+ const lines = [];
15597
+ lines.push("");
15598
+ lines.push(colors2.dim(" " + "═".repeat(60)));
15599
+ lines.push(` ${icons.chart} ${colors2.bold("Leadership Report")} ${colors2.dim(`(${report.period})`)}`);
15600
+ lines.push(colors2.dim(" " + "═".repeat(60)));
15601
+ lines.push("");
15602
+ lines.push(` ${colors2.bold("Executive Summary")}`);
15603
+ lines.push(` ─────────────────────────────────────────────────────`);
15604
+ lines.push(` AI-Assisted Sessions: ${report.aiAssistedTasksCount}`);
15605
+ lines.push(` Total Effort: ${report.totalEffortHours} hours`);
15606
+ lines.push("");
15607
+ lines.push(` ${colors2.bold("Effort Visibility")}`);
15608
+ lines.push(` ─────────────────────────────────────────────────────`);
15609
+ lines.push(` ${colors2.success("AI Generation:")} ${report.totalAIGenerationHours}h ${colors2.dim("← What leadership sees")}`);
15610
+ lines.push(` ${colors2.primary("Review:")} ${report.totalReviewHours}h`);
15611
+ lines.push(` ${colors2.warning("Correction:")} ${report.totalCorrectionHours}h`);
15612
+ lines.push(` ${colors2.dim("Integration:")} ${report.totalIntegrationHours}h`);
15613
+ lines.push(` ─────────────────────────────────────────────────`);
15614
+ lines.push(` ${colors2.bold("True Total:")} ${report.trueEffortHours}h ${colors2.dim("← Actual effort")}`);
15615
+ lines.push("");
15616
+ if (report.hiddenWorkPercent > 20) {
15617
+ lines.push(` ${colors2.warning(icons.warning)} ${colors2.bold("Hidden Work:")}`);
15618
+ lines.push(` ${report.hiddenWorkHours}h (${report.hiddenWorkPercent}%) is invisible to leadership`);
15619
+ lines.push("");
15620
+ }
15621
+ lines.push(` ${colors2.bold("Risk Indicators")}`);
15622
+ lines.push(` ─────────────────────────────────────────────────────`);
15623
+ const burnoutColor = report.burnoutRisk === "high" ? colors2.error : report.burnoutRisk === "medium" ? colors2.warning : colors2.success;
15624
+ lines.push(` Burnout Risk: ${burnoutColor(report.burnoutRisk.toUpperCase())}`);
15625
+ lines.push(` Correction Ratio: ${report.averageCorrectionRatio} ${getRatioIndicator(report.averageCorrectionRatio)}`);
15626
+ lines.push(` Context Churn Rate: ${report.contextChurnRate}`);
15627
+ lines.push("");
15628
+ if (report.insights.length > 0) {
15629
+ lines.push(` ${colors2.bold("Key Insights")}`);
15630
+ lines.push(` ─────────────────────────────────────────────────────`);
15631
+ for (const insight of report.insights) {
15632
+ lines.push(` ${colors2.primary("→")} ${insight}`);
15633
+ }
15634
+ lines.push("");
15635
+ }
15636
+ lines.push(colors2.dim(" " + "═".repeat(60)));
15637
+ return lines;
15638
+ }
15639
+ function formatLeadershipReportMarkdown(report) {
15640
+ let md = `# AI Work Visibility Report
15641
+
15642
+ `;
15643
+ md += `**Period:** ${report.period}
15644
+
15645
+ `;
15646
+ md += `## Executive Summary
15647
+
15648
+ `;
15649
+ md += `| Metric | Value |
15650
+ `;
15651
+ md += `|--------|-------|
15652
+ `;
15653
+ md += `| AI-Assisted Sessions | ${report.aiAssistedTasksCount} |
15654
+ `;
15655
+ md += `| Total Effort | ${report.totalEffortHours} hours |
15656
+ `;
15657
+ md += `| Perceived Effort (AI Generation) | ${report.perceivedEffortHours} hours |
15658
+ `;
15659
+ md += `| Hidden Work | ${report.hiddenWorkHours} hours (${report.hiddenWorkPercent}%) |
15660
+
15661
+ `;
15662
+ md += `## Effort Breakdown
15663
+
15664
+ `;
15665
+ md += `| Phase | Hours | % of Total |
15666
+ `;
15667
+ md += `|-------|-------|------------|
15668
+ `;
15669
+ md += `| AI Generation | ${report.totalAIGenerationHours}h | ${Math.round(report.totalAIGenerationHours / report.totalEffortHours * 100)}% |
15670
+ `;
15671
+ md += `| Review | ${report.totalReviewHours}h | ${Math.round(report.totalReviewHours / report.totalEffortHours * 100)}% |
15672
+ `;
15673
+ md += `| Correction | ${report.totalCorrectionHours}h | ${Math.round(report.totalCorrectionHours / report.totalEffortHours * 100)}% |
15674
+ `;
15675
+ md += `| Integration | ${report.totalIntegrationHours}h | ${Math.round(report.totalIntegrationHours / report.totalEffortHours * 100)}% |
15676
+
15677
+ `;
15678
+ md += `## Risk Assessment
15679
+
15680
+ `;
15681
+ md += `- **Burnout Risk:** ${report.burnoutRisk.toUpperCase()}
15682
+ `;
15683
+ md += `- **Correction Ratio:** ${report.averageCorrectionRatio}
15684
+ `;
15685
+ md += `- **Context Churn Rate:** ${report.contextChurnRate}
15686
+
15687
+ `;
15688
+ md += `## Key Insights
15689
+
15690
+ `;
15691
+ for (const insight of report.insights) {
15692
+ md += `- ${insight}
15693
+ `;
15694
+ }
15695
+ md += `
15696
+ `;
15697
+ md += `---
15698
+
15699
+ `;
15700
+ md += `*Generated by nairon-bench - AI Workflow Benchmarking*
15701
+ `;
15702
+ return md;
15703
+ }
15704
+ function formatTime(minutes) {
15705
+ if (minutes < 60) {
15706
+ return `${minutes}m`;
15707
+ }
15708
+ const hours = Math.floor(minutes / 60);
15709
+ const mins = minutes % 60;
15710
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
15711
+ }
15712
+ function formatRatio(ratio) {
15713
+ return `${Math.round(ratio * 100)}%`;
15714
+ }
15715
+ function getRatioIndicator(ratio) {
15716
+ if (ratio > 0.8)
15717
+ return colors2.error("(high - needs attention)");
15718
+ if (ratio > 0.5)
15719
+ return colors2.warning("(moderate)");
15720
+ return colors2.success("(healthy)");
15721
+ }
15722
+
15349
15723
  // src/lib/optimization-installer.ts
15350
15724
  import { execSync as execSync2, spawnSync } from "node:child_process";
15351
15725
  import { existsSync as existsSync8, writeFileSync as writeFileSync3, readFileSync as readFileSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync4 } from "node:fs";
@@ -15983,6 +16357,13 @@ var scanCommand = defineCommand2({
15983
16357
  const reportPath = generateReport(reportDir, score, git, agents, tests, args.since, sdlcAnalysis, scanCost, analysis);
15984
16358
  console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
15985
16359
  }
16360
+ if (agents && agents.sessions.length > 0 && !args.brief) {
16361
+ const effortBreakdown = analyzeEffort(agents.sessions, git);
16362
+ const effortLines = formatEffortBreakdown(effortBreakdown);
16363
+ for (const line of effortLines) {
16364
+ console.log(line);
16365
+ }
16366
+ }
15986
16367
  if (agents && agents.sessions.length > 0 && !args.brief) {
15987
16368
  const frustrationSummary = detectFrustrations(agents.sessions);
15988
16369
  if (frustrationSummary.totalFrustrations > 0) {
@@ -19005,7 +19386,7 @@ function renderTimelineASCII(timeline) {
19005
19386
  if (timeline.milestones.length > 0) {
19006
19387
  lines.push(" Key Milestones:");
19007
19388
  for (const milestone of timeline.milestones.slice(-5)) {
19008
- const time = formatTime(milestone.timestamp);
19389
+ const time = formatTime2(milestone.timestamp);
19009
19390
  const icon = getMilestoneIcon(milestone.type);
19010
19391
  lines.push(` ${time} ${icon} ${truncate(milestone.title, 40)}`);
19011
19392
  }
@@ -19014,7 +19395,7 @@ function renderTimelineASCII(timeline) {
19014
19395
  if (timeline.strugglePoints.length > 0) {
19015
19396
  lines.push(" Struggle Points:");
19016
19397
  for (const struggle of timeline.strugglePoints.slice(-3)) {
19017
- const time = formatTime(struggle.timestamp);
19398
+ const time = formatTime2(struggle.timestamp);
19018
19399
  const icon = getSeverityIcon(struggle.severity);
19019
19400
  lines.push(` ${time} ${icon} ${truncate(struggle.description, 40)}`);
19020
19401
  }
@@ -19082,7 +19463,7 @@ function truncate(str, len2) {
19082
19463
  function cleanCommitMessage(message) {
19083
19464
  return message.replace(/^(feat|fix|refactor|docs|test|chore|style|perf|ci|build|revert)(\(.+?\))?:\s*/i, "").replace(/^(add|fix|update|remove|implement|refactor)\s+/i, "").trim();
19084
19465
  }
19085
- function formatTime(date) {
19466
+ function formatTime2(date) {
19086
19467
  return date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" });
19087
19468
  }
19088
19469
  function formatDate(date) {
@@ -21411,9 +21792,18 @@ var reportCommand = defineCommand2({
21411
21792
  type: "string",
21412
21793
  description: "Output format: terminal, md, json, or text (legacy)",
21413
21794
  default: "terminal"
21795
+ },
21796
+ leadership: {
21797
+ type: "boolean",
21798
+ description: "Generate leadership report with effort breakdown and hidden work visibility",
21799
+ default: false
21414
21800
  }
21415
21801
  },
21416
21802
  async run({ args }) {
21803
+ if (args.leadership) {
21804
+ await runLeadershipReport(args);
21805
+ return;
21806
+ }
21417
21807
  if (args.hackathon || args.since) {
21418
21808
  await runHackathonReport(args);
21419
21809
  return;
@@ -21706,6 +22096,41 @@ async function publishReport(report) {
21706
22096
  consola.info("Try `nb report --hackathon --format md` for local markdown instead.");
21707
22097
  }
21708
22098
  }
22099
+ async function runLeadershipReport(args) {
22100
+ const projectDir = process.cwd();
22101
+ const since = parseSince2(args.since || "7d");
22102
+ const periodLabel = args.since === "30d" ? "Last 30 days" : args.since === "48h" ? "Last 48 hours" : "Last 7 days";
22103
+ consola.start(`Generating leadership report (${periodLabel})...`);
22104
+ const agents = await collectAgentSessions(since);
22105
+ if (!agents || agents.sessions.length === 0) {
22106
+ consola.warn("No AI sessions found in the specified time range.");
22107
+ consola.info("Try running with --since 30d for a longer time range.");
22108
+ return;
22109
+ }
22110
+ const effort = analyzeEffort(agents.sessions);
22111
+ const report = generateLeadershipReport(effort, periodLabel);
22112
+ const format2 = args.format || "terminal";
22113
+ let output;
22114
+ if (format2 === "md" || format2 === "markdown") {
22115
+ output = formatLeadershipReportMarkdown(report);
22116
+ } else {
22117
+ output = formatLeadershipReport(report).join(`
22118
+ `);
22119
+ }
22120
+ if (args.output) {
22121
+ const filepath = resolve(projectDir, args.output);
22122
+ writeFileSync5(filepath, output);
22123
+ consola.success(`Report saved to ${filepath}`);
22124
+ }
22125
+ console.log(output);
22126
+ consola.log("");
22127
+ consola.info(`Sessions analyzed: ${report.aiAssistedTasksCount}`);
22128
+ consola.info(`True effort: ${report.trueEffortHours}h (${report.hiddenWorkPercent}% invisible)`);
22129
+ consola.info(`Burnout risk: ${report.burnoutRisk.toUpperCase()}`);
22130
+ if (format2 === "terminal" || !format2) {
22131
+ consola.info("Run `nb report --leadership --format md` for exportable markdown");
22132
+ }
22133
+ }
21709
22134
  function parseSince2(since) {
21710
22135
  const match = since.match(/^(\d+)(h|d|w|m)$/);
21711
22136
  if (match) {
@@ -23142,6 +23567,27 @@ var BOLD = "\x1B[1m";
23142
23567
  var RESET = "\x1B[0m";
23143
23568
  var MAGENTA = "\x1B[35m";
23144
23569
  var CHANGELOG = [
23570
+ {
23571
+ version: "0.4.0",
23572
+ date: "2026-02-14",
23573
+ title: "AI Work Visibility",
23574
+ highlights: [
23575
+ "New: Effort breakdown shows generation/review/correction/integration time",
23576
+ "New: nb report --leadership for executive-ready reports",
23577
+ "See hidden work (61% invisible!) that leadership may miss",
23578
+ "Burnout risk detection and true effort multiplier"
23579
+ ]
23580
+ },
23581
+ {
23582
+ version: "0.3.15",
23583
+ date: "2026-02-14",
23584
+ title: "Contextual Recommendations",
23585
+ highlights: [
23586
+ "Recommendations now linked to specific frustrations they prevent",
23587
+ "Total time and cost savings summary",
23588
+ "See exactly which issues each tool would have fixed"
23589
+ ]
23590
+ },
23145
23591
  {
23146
23592
  version: "0.3.14",
23147
23593
  date: "2026-02-14",
@@ -23560,7 +24006,7 @@ function showFullChangelog() {
23560
24006
  // package.json
23561
24007
  var package_default = {
23562
24008
  name: "nairon-bench",
23563
- version: "0.3.14",
24009
+ version: "0.4.0",
23564
24010
  description: "AI workflow benchmarking CLI",
23565
24011
  type: "module",
23566
24012
  bin: {
@@ -26085,7 +26531,7 @@ function formatBytes(bytes) {
26085
26531
  // package.json
26086
26532
  var package_default2 = {
26087
26533
  name: "nairon-bench",
26088
- version: "0.3.14",
26534
+ version: "0.4.0",
26089
26535
  description: "AI workflow benchmarking CLI",
26090
26536
  type: "module",
26091
26537
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nairon-bench",
3
- "version": "0.3.14",
3
+ "version": "0.4.0",
4
4
  "description": "AI workflow benchmarking CLI",
5
5
  "type": "module",
6
6
  "bin": {