nairon-bench 0.5.2 → 0.6.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 +218 -98
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3661,10 +3661,10 @@ class ConvexHttpClient {
3661
3661
  }
3662
3662
  this.isProcessingQueue = true;
3663
3663
  while (this.mutationQueue.length > 0) {
3664
- const { mutation, args, resolve, reject } = this.mutationQueue.shift();
3664
+ const { mutation, args, resolve: resolve2, reject } = this.mutationQueue.shift();
3665
3665
  try {
3666
3666
  const result = await this.mutationInner(mutation, args);
3667
- resolve(result);
3667
+ resolve2(result);
3668
3668
  } catch (error) {
3669
3669
  reject(error);
3670
3670
  }
@@ -3672,8 +3672,8 @@ class ConvexHttpClient {
3672
3672
  this.isProcessingQueue = false;
3673
3673
  }
3674
3674
  enqueueMutation(mutation, args) {
3675
- return new Promise((resolve, reject) => {
3676
- this.mutationQueue.push({ mutation, args, resolve, reject });
3675
+ return new Promise((resolve2, reject) => {
3676
+ this.mutationQueue.push({ mutation, args, resolve: resolve2, reject });
3677
3677
  this.processMutationQueue();
3678
3678
  });
3679
3679
  }
@@ -8518,7 +8518,7 @@ async function collectGit(projectDir, since) {
8518
8518
  // src/collectors/agents.ts
8519
8519
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
8520
8520
  import { homedir as homedir2 } from "node:os";
8521
- import { join as join2, basename } from "node:path";
8521
+ import { join as join2, basename, resolve } from "node:path";
8522
8522
  // ../shared/src/constants.ts
8523
8523
  var PHASE_WEIGHTS = {
8524
8524
  requirements: 0.2,
@@ -8921,17 +8921,18 @@ function getCacheInfo() {
8921
8921
 
8922
8922
  // src/collectors/agents.ts
8923
8923
  var sessionCache = null;
8924
- async function collectAgentSessions(since, useCache = true) {
8924
+ async function collectAgentSessions(since, useCache = true, projectDir) {
8925
8925
  sessionCache = useCache ? new SessionCache : null;
8926
+ const targetProjectDir = projectDir ? resolve(projectDir) : process.cwd();
8926
8927
  const sessions = [];
8927
8928
  const claudeProjectsDir = join2(homedir2(), ".claude", "projects");
8928
8929
  if (existsSync2(claudeProjectsDir)) {
8929
- const claudeSessions = collectClaudeSessions(claudeProjectsDir, since);
8930
+ const claudeSessions = collectClaudeSessions(claudeProjectsDir, since, targetProjectDir);
8930
8931
  sessions.push(...claudeSessions);
8931
8932
  }
8932
8933
  const openCodeDir = join2(homedir2(), ".local", "share", "opencode");
8933
8934
  if (existsSync2(openCodeDir)) {
8934
- const openCodeSessions = collectOpenCodeSessions(openCodeDir, since);
8935
+ const openCodeSessions = collectOpenCodeSessions(openCodeDir, since, targetProjectDir);
8935
8936
  sessions.push(...openCodeSessions);
8936
8937
  }
8937
8938
  const cursorDirs = [
@@ -9011,11 +9012,29 @@ async function collectAgentSessions(since, useCache = true) {
9011
9012
  costByModel
9012
9013
  };
9013
9014
  }
9014
- function collectClaudeSessions(projectsDir, since) {
9015
+ function collectClaudeSessions(projectsDir, since, targetProjectDir) {
9015
9016
  const sessions = [];
9016
9017
  try {
9017
9018
  const projectDirs = readdirSync(projectsDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join2(projectsDir, d2.name));
9019
+ let matchingProjectDir = null;
9018
9020
  for (const projectDir of projectDirs) {
9021
+ const projectMetaFile = join2(projectDir, ".project.json");
9022
+ if (existsSync2(projectMetaFile)) {
9023
+ try {
9024
+ const meta = JSON.parse(readFileSync2(projectMetaFile, "utf-8"));
9025
+ if (meta.directory) {
9026
+ const metaDir = resolve(meta.directory);
9027
+ const isMatch = metaDir === targetProjectDir || targetProjectDir.startsWith(metaDir + "/") || metaDir.startsWith(targetProjectDir + "/");
9028
+ if (isMatch) {
9029
+ matchingProjectDir = projectDir;
9030
+ break;
9031
+ }
9032
+ }
9033
+ } catch {}
9034
+ }
9035
+ }
9036
+ const dirsToScan = matchingProjectDir ? [matchingProjectDir] : projectDirs;
9037
+ for (const projectDir of dirsToScan) {
9019
9038
  const files = readdirSync(projectDir).filter((f3) => f3.endsWith(".jsonl")).map((f3) => join2(projectDir, f3));
9020
9039
  for (const file of files) {
9021
9040
  const session = parseClaudeSessionFile(file, since);
@@ -9289,7 +9308,7 @@ function similarity(a2, b2) {
9289
9308
  const union = new Set([...wordsA, ...wordsB]);
9290
9309
  return union.size > 0 ? intersection.size / union.size : 0;
9291
9310
  }
9292
- function collectOpenCodeSessions(baseDir, since) {
9311
+ function collectOpenCodeSessions(baseDir, since, targetProjectDir) {
9293
9312
  const sessions = [];
9294
9313
  const sessionStorageDir = join2(baseDir, "storage", "session");
9295
9314
  const messageStorageDir = join2(baseDir, "storage", "message");
@@ -9301,7 +9320,7 @@ function collectOpenCodeSessions(baseDir, since) {
9301
9320
  for (const projectDir of projectDirs) {
9302
9321
  const sessionFiles = readdirSync(projectDir).filter((f3) => f3.startsWith("ses_") && f3.endsWith(".json")).map((f3) => join2(projectDir, f3));
9303
9322
  for (const sessionFile of sessionFiles) {
9304
- const session = parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, since);
9323
+ const session = parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, since, targetProjectDir);
9305
9324
  if (session) {
9306
9325
  sessions.push(session);
9307
9326
  }
@@ -9310,7 +9329,7 @@ function collectOpenCodeSessions(baseDir, since) {
9310
9329
  } catch {}
9311
9330
  return sessions;
9312
9331
  }
9313
- function parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, since) {
9332
+ function parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, since, targetProjectDir) {
9314
9333
  try {
9315
9334
  const cached = sessionCache?.get(sessionFile);
9316
9335
  if (cached) {
@@ -9322,6 +9341,13 @@ function parseOpenCodeSession(sessionFile, messageStorageDir, partStorageDir, si
9322
9341
  }
9323
9342
  const sessionContent = readFileSync2(sessionFile, "utf-8");
9324
9343
  const session = JSON.parse(sessionContent);
9344
+ if (session.directory) {
9345
+ const sessionDir = resolve(session.directory);
9346
+ const isMatch = sessionDir === targetProjectDir || targetProjectDir.startsWith(sessionDir + "/") || sessionDir.startsWith(targetProjectDir + "/");
9347
+ if (!isMatch) {
9348
+ return null;
9349
+ }
9350
+ }
9325
9351
  const sessionStart = new Date(session.time.created);
9326
9352
  const sessionEnd = session.time.updated ? new Date(session.time.updated) : sessionStart;
9327
9353
  if (sessionEnd < since)
@@ -11388,7 +11414,7 @@ function generateOverallSummary(requirements, planning, implementation, review,
11388
11414
  }
11389
11415
  return parts.join(" ") || "Balanced across all phases.";
11390
11416
  }
11391
- function formatSDLCForTerminal(analysis) {
11417
+ function formatSDLCPhaseForTerminal(phase, overall) {
11392
11418
  const lines = [];
11393
11419
  const width = 50;
11394
11420
  const CYAN = "\x1B[36m";
@@ -11415,48 +11441,55 @@ function formatSDLCForTerminal(analysis) {
11415
11441
  }
11416
11442
  };
11417
11443
  lines.push("");
11418
- lines.push(`${BOLD}${CYAN} SDLC PHASE BREAKDOWN${RESET}`);
11444
+ lines.push(`${BOLD}${CYAN} SDLC OPTIMIZATION${RESET}`);
11419
11445
  lines.push(`${DIM} ${"─".repeat(width)}${RESET}`);
11420
11446
  lines.push("");
11421
- const overall = analysis.overall;
11422
11447
  lines.push(` ${BOLD}Overall:${RESET} ${gradeColor(overall.grade)}${overall.grade}${RESET} (${overall.score}/100)`);
11423
11448
  lines.push(` ${DIM}${overall.summary}${RESET}`);
11424
11449
  lines.push("");
11425
- const phases = [
11426
- { emoji: "1", ...analysis.requirements },
11427
- { emoji: "2", ...analysis.planning },
11428
- { emoji: "3", ...analysis.implementation },
11429
- { emoji: "4", ...analysis.review }
11430
- ];
11431
- for (const phase of phases) {
11432
- const color = gradeColor(phase.grade);
11433
- const bar = renderBar(phase.score, 15);
11434
- lines.push(` ${BOLD}${phase.emoji}. ${phase.name}${RESET}`);
11435
- lines.push(` ${bar} ${color}${phase.grade}${RESET} ${DIM}(${phase.score}/100)${RESET}`);
11436
- if (phase.metrics.length > 0) {
11437
- const topMetrics = phase.metrics.slice(0, 2);
11438
- for (const m2 of topMetrics) {
11439
- const statusIcon = m2.status === "good" ? GREEN + "+" : m2.status === "warning" ? YELLOW + "~" : RED + "-";
11440
- lines.push(` ${statusIcon}${RESET} ${m2.name}: ${m2.value}${m2.target ? ` ${DIM}(target: ${m2.target})${RESET}` : ""}`);
11441
- }
11442
- }
11443
- if (phase.recommendations.length > 0) {
11444
- const rec = phase.recommendations[0];
11450
+ const phaseColor = gradeColor(phase.grade);
11451
+ lines.push(` ${BOLD}Focus:${RESET} ${phase.name}`);
11452
+ lines.push(` ${phaseColor}${phase.grade}${RESET} ${DIM}(${phase.score}/100)${RESET}`);
11453
+ lines.push("");
11454
+ if (phase.metrics.length > 0) {
11455
+ lines.push(` ${BOLD}Signals:${RESET}`);
11456
+ const topMetrics = phase.metrics.slice(0, 4);
11457
+ for (const m2 of topMetrics) {
11458
+ const statusIcon = m2.status === "good" ? GREEN + "+" : m2.status === "warning" ? YELLOW + "~" : RED + "-";
11459
+ lines.push(` ${statusIcon}${RESET} ${m2.name}: ${m2.value}${m2.target ? ` ${DIM}(target: ${m2.target})${RESET}` : ""}`);
11460
+ }
11461
+ lines.push("");
11462
+ }
11463
+ if (phase.strengths.length > 0) {
11464
+ lines.push(` ${BOLD}Strengths:${RESET}`);
11465
+ for (const item of phase.strengths.slice(0, 3)) {
11466
+ lines.push(` ${GREEN}+${RESET} ${item}`);
11467
+ }
11468
+ lines.push("");
11469
+ }
11470
+ if (phase.weaknesses.length > 0) {
11471
+ lines.push(` ${BOLD}Weak Spots:${RESET}`);
11472
+ for (const item of phase.weaknesses.slice(0, 3)) {
11473
+ lines.push(` ${RED}!${RESET} ${item}`);
11474
+ }
11475
+ lines.push("");
11476
+ }
11477
+ lines.push(` ${BOLD}Optimizations:${RESET}`);
11478
+ if (phase.recommendations.length === 0) {
11479
+ lines.push(` ${DIM}No optimizations available for this phase.${RESET}`);
11480
+ } else {
11481
+ for (const rec of phase.recommendations) {
11445
11482
  const impactColor = rec.impact === "high" ? RED : rec.impact === "medium" ? YELLOW : DIM;
11446
- lines.push(` ${impactColor}!${RESET} ${rec.title}`);
11483
+ lines.push(` ${impactColor}!${RESET} ${rec.title}`);
11484
+ lines.push(` ${DIM}${rec.description}${RESET}`);
11447
11485
  if (rec.installCommand) {
11448
- lines.push(` ${DIM}${rec.installCommand}${RESET}`);
11486
+ lines.push(` ${DIM}${rec.installCommand}${RESET}`);
11449
11487
  }
11450
11488
  }
11451
- lines.push("");
11452
11489
  }
11490
+ lines.push("");
11453
11491
  return lines;
11454
11492
  }
11455
- function renderBar(value, width) {
11456
- const filled = Math.round(value / 100 * width);
11457
- const empty = width - filled;
11458
- return "█".repeat(filled) + "░".repeat(empty);
11459
- }
11460
11493
 
11461
11494
  // src/lib/frustration-detector.ts
11462
11495
  import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as fsReaddirSync } from "node:fs";
@@ -14763,7 +14796,7 @@ var icons = {
14763
14796
  brain: "\uD83E\uDDE0",
14764
14797
  target: "\uD83C\uDFAF"
14765
14798
  };
14766
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
14799
+ var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
14767
14800
  function clearLine2() {
14768
14801
  readline.clearLine(process.stdout, 0);
14769
14802
  readline.cursorTo(process.stdout, 0);
@@ -15110,8 +15143,12 @@ function extractFrustrationPrompts(sessionPath, patterns) {
15110
15143
  }
15111
15144
  }
15112
15145
  } else if (sessionPath.endsWith(".json")) {
15113
- for (const _3 of patterns) {
15114
- prompts.push("");
15146
+ const sessionData = JSON.parse(content);
15147
+ const sessionId = sessionData.id;
15148
+ const userPrompts = extractOpenCodePrompts(sessionId);
15149
+ for (const pattern of patterns) {
15150
+ const frustrationMsg = findFrustrationMessage(userPrompts);
15151
+ prompts.push(frustrationMsg || userPrompts[0] || "");
15115
15152
  }
15116
15153
  }
15117
15154
  return prompts;
@@ -15119,6 +15156,45 @@ function extractFrustrationPrompts(sessionPath, patterns) {
15119
15156
  return [];
15120
15157
  }
15121
15158
  }
15159
+ function extractOpenCodePrompts(sessionId) {
15160
+ const home = homedir6();
15161
+ const messageDir = join7(home, ".local", "share", "opencode", "storage", "message", sessionId);
15162
+ const partBaseDir = join7(home, ".local", "share", "opencode", "storage", "part");
15163
+ if (!existsSync7(messageDir))
15164
+ return [];
15165
+ const prompts = [];
15166
+ try {
15167
+ const messageFiles = fsReaddirSync(messageDir).filter((f3) => f3.endsWith(".json")).sort();
15168
+ for (const msgFile of messageFiles) {
15169
+ try {
15170
+ const msgPath = join7(messageDir, msgFile);
15171
+ const msgData = JSON.parse(readFileSync7(msgPath, "utf-8"));
15172
+ if (msgData.role !== "user")
15173
+ continue;
15174
+ const messageId = msgData.id;
15175
+ const timestamp = msgData.time?.created;
15176
+ const partDir = join7(partBaseDir, messageId);
15177
+ if (!existsSync7(partDir))
15178
+ continue;
15179
+ const partFiles = fsReaddirSync(partDir).filter((f3) => f3.endsWith(".json"));
15180
+ let messageText = "";
15181
+ for (const partFile of partFiles) {
15182
+ try {
15183
+ const partData = JSON.parse(readFileSync7(join7(partDir, partFile), "utf-8"));
15184
+ if (partData.type === "text" && partData.text) {
15185
+ messageText += partData.text + " ";
15186
+ }
15187
+ } catch {}
15188
+ }
15189
+ if (messageText.trim()) {
15190
+ const timeStr = timestamp ? new Date(timestamp).toLocaleTimeString() : "";
15191
+ prompts.push(`[${timeStr}] ${messageText.trim()}`);
15192
+ }
15193
+ } catch {}
15194
+ }
15195
+ } catch {}
15196
+ return prompts;
15197
+ }
15122
15198
  function extractMessageText(msg) {
15123
15199
  if (typeof msg.content === "string")
15124
15200
  return msg.content;
@@ -15313,25 +15389,40 @@ function formatFrustrationSummary(summary) {
15313
15389
  }
15314
15390
  function formatFrustrationEvent(f3, index) {
15315
15391
  const lines = [];
15316
- lines.push(` ${colors2.warning(`${index}.`)} ${colors2.bold(f3.patternDescription)} ${colors2.dim(`(${f3.agent})`)}`);
15317
- lines.push(` ${colors2.dim("Session:")} ${f3.sessionId.slice(0, 12)}...`);
15318
- lines.push(` ${colors2.dim("Time wasted:")} ~${f3.estimatedTimeWastedMinutes} min`);
15319
- lines.push(` ${colors2.dim("Cause:")} ${formatCauseLabel(f3.rootCause.category)}`);
15392
+ lines.push(` ${colors2.error(`${index}.`)} ${formatCauseLabel(f3.rootCause.category)}`);
15393
+ lines.push(` ${colors2.dim("Session:")} ${f3.sessionId.slice(0, 16)}... ${colors2.dim(`(${f3.agent})`)}`);
15394
+ if (f3.timestamp) {
15395
+ const date = new Date(f3.timestamp);
15396
+ lines.push(` ${colors2.dim("When:")} ${date.toLocaleDateString()} ${date.toLocaleTimeString()}`);
15397
+ }
15398
+ lines.push(` ${colors2.dim("Time wasted:")} ${colors2.warning(`~${f3.estimatedTimeWastedMinutes} min`)}`);
15320
15399
  if (f3.exactPrompt && f3.exactPrompt.length > 0 && !f3.exactPrompt.startsWith("(Unable")) {
15321
15400
  lines.push("");
15322
- lines.push(` ${colors2.dim("┌" + "─".repeat(40))}`);
15323
- const promptLines = wrapText(f3.exactPrompt, 38);
15324
- for (const line of promptLines.slice(0, 4)) {
15325
- lines.push(` ${colors2.dim("")} ${line}`);
15401
+ lines.push(` ${colors2.bold("Your prompt:")}`);
15402
+ lines.push(` ${colors2.dim("┌" + "─".repeat(50))}`);
15403
+ let promptText = f3.exactPrompt;
15404
+ if (promptText.startsWith("[")) {
15405
+ const closeBracket = promptText.indexOf("]");
15406
+ if (closeBracket > 0 && closeBracket < 15) {
15407
+ promptText = promptText.slice(closeBracket + 2);
15408
+ }
15326
15409
  }
15327
- if (promptLines.length > 4) {
15328
- lines.push(` ${colors2.dim("│")} ${colors2.dim("...")}`);
15410
+ const promptLines = wrapText(promptText, 48);
15411
+ for (const line of promptLines.slice(0, 6)) {
15412
+ lines.push(` ${colors2.dim("│")} ${colors2.primary(line)}`);
15329
15413
  }
15330
- lines.push(` ${colors2.dim("└" + "─".repeat(40))}`);
15414
+ if (promptLines.length > 6) {
15415
+ lines.push(` ${colors2.dim("│")} ${colors2.dim(`... (${promptLines.length - 6} more lines)`)}`);
15416
+ }
15417
+ lines.push(` ${colors2.dim("└" + "─".repeat(50))}`);
15418
+ } else {
15419
+ lines.push("");
15420
+ lines.push(` ${colors2.dim("(Prompt not captured - " + f3.patternDescription + ")")}`);
15331
15421
  }
15332
15422
  lines.push("");
15333
- lines.push(` ${colors2.success("Fix:")} ${f3.recommendedFix.tool}`);
15334
- lines.push(` ${colors2.dim("$")} ${colors2.primary(f3.recommendedFix.installCommand)}`);
15423
+ lines.push(` ${colors2.success("Fix with:")} ${colors2.bold(f3.recommendedFix.tool)}`);
15424
+ lines.push(` ${colors2.dim(f3.recommendedFix.description)}`);
15425
+ lines.push(` ${colors2.dim("$")} ${colors2.primary(f3.recommendedFix.installCommand)}`);
15335
15426
  return lines;
15336
15427
  }
15337
15428
  function formatCauseLabel(cause) {
@@ -16376,7 +16467,7 @@ var scanCommand = defineCommand2({
16376
16467
  const timestamp = new Date().toLocaleTimeString();
16377
16468
  try {
16378
16469
  const git2 = await collectGit(projectDir, since);
16379
- const agents2 = await collectAgentSessions(since, useCache);
16470
+ const agents2 = await collectAgentSessions(since, useCache, projectDir);
16380
16471
  const tests2 = await collectTestResults(projectDir);
16381
16472
  const score2 = computeNaironScore(git2 ?? undefined, agents2 ?? undefined, tests2 ?? undefined);
16382
16473
  const delta = scanCount > 1 ? score2.overall - lastScore : 0;
@@ -16421,7 +16512,7 @@ var scanCommand = defineCommand2({
16421
16512
  }
16422
16513
  if (!silent)
16423
16514
  await thinkingStep("Scanning AI session logs...", 600);
16424
- const agents = await collectAgentSessions(since, useCache);
16515
+ const agents = await collectAgentSessions(since, useCache, projectDir);
16425
16516
  if (agents && !silent) {
16426
16517
  await revealDiscovery(icons.success, `Sessions: ${colors2.primary(agents.totalSessions.toString())} sessions loaded`, 200);
16427
16518
  }
@@ -16550,29 +16641,37 @@ var scanCommand = defineCommand2({
16550
16641
  } else if (sdlc.overall.summary.includes("optimizations available")) {
16551
16642
  sdlc.overall.summary = sdlc.overall.summary.replace(/\d+ optimizations available\./, "All recommended tools installed.");
16552
16643
  }
16553
- const sdlcLines = formatSDLCForTerminal(sdlc);
16644
+ const phaseChoice = await consola.prompt("Which SDLC phase do you want to optimize?", {
16645
+ type: "select",
16646
+ options: [
16647
+ { value: "requirements", label: "Requirements & Context" },
16648
+ { value: "planning", label: "Planning & Design" },
16649
+ { value: "implementation", label: "Implementation" },
16650
+ { value: "review", label: "Review & Testing" }
16651
+ ]
16652
+ });
16653
+ const selectedPhase = phaseChoice === "planning" ? sdlc.planning : phaseChoice === "implementation" ? sdlc.implementation : phaseChoice === "review" ? sdlc.review : sdlc.requirements;
16654
+ const sdlcLines = formatSDLCPhaseForTerminal(selectedPhase, sdlc.overall);
16554
16655
  for (const line of sdlcLines) {
16555
16656
  console.log(line);
16556
16657
  }
16557
- let allRecs = [];
16558
- for (const phase of [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review]) {
16559
- for (const rec of phase.recommendations) {
16560
- if (rec.installCommand) {
16561
- allRecs.push(phaseRecToInstallable(rec));
16562
- }
16658
+ let phaseRecs = [];
16659
+ for (const rec of selectedPhase.recommendations) {
16660
+ if (rec.installCommand) {
16661
+ phaseRecs.push(phaseRecToInstallable(rec));
16563
16662
  }
16564
16663
  }
16565
- allRecs = filterAlreadyInstalled(allRecs, installedStatus);
16566
- if (allRecs.length > 0) {
16664
+ phaseRecs = filterAlreadyInstalled(phaseRecs, installedStatus);
16665
+ if (phaseRecs.length > 0) {
16567
16666
  console.log();
16568
- const optionLabels = allRecs.map((rec) => `${rec.name} - ${rec.description}`);
16569
- const selected = await consola.prompt(`${allRecs.length} optimizations available. Select to install (space to select, enter to confirm):`, {
16667
+ const optionLabels = phaseRecs.map((rec) => `${rec.name} - ${rec.description}`);
16668
+ const selected = await consola.prompt(`${phaseRecs.length} optimizations available for ${selectedPhase.name}. Select to install (space to select, enter to confirm):`, {
16570
16669
  type: "multiselect",
16571
16670
  options: optionLabels,
16572
16671
  required: false
16573
16672
  });
16574
16673
  if (selected && typeof selected !== "symbol" && Array.isArray(selected) && selected.length > 0) {
16575
- const toInstall = allRecs.filter((rec) => selected.some((s2) => s2.startsWith(rec.name))).map((r3) => ({ ...r3, selected: true }));
16674
+ const toInstall = phaseRecs.filter((rec) => selected.some((s2) => s2.startsWith(rec.name))).map((r3) => ({ ...r3, selected: true }));
16576
16675
  if (toInstall.length > 0) {
16577
16676
  console.log();
16578
16677
  console.log(` ${colors2.bold("Installing...")}${colors2.dim("")}`);
@@ -20474,10 +20573,10 @@ function renderCoverageSummary(report) {
20474
20573
  lines.push(` Format: ${report.format}`);
20475
20574
  lines.push(` Overall: ${report.summary.overall}%`);
20476
20575
  lines.push("");
20477
- lines.push(` Lines: ${renderBar2(report.summary.lines.percentage)} ${report.summary.lines.percentage}%`);
20478
- lines.push(` Statements: ${renderBar2(report.summary.statements.percentage)} ${report.summary.statements.percentage}%`);
20479
- lines.push(` Functions: ${renderBar2(report.summary.functions.percentage)} ${report.summary.functions.percentage}%`);
20480
- lines.push(` Branches: ${renderBar2(report.summary.branches.percentage)} ${report.summary.branches.percentage}%`);
20576
+ lines.push(` Lines: ${renderBar(report.summary.lines.percentage)} ${report.summary.lines.percentage}%`);
20577
+ lines.push(` Statements: ${renderBar(report.summary.statements.percentage)} ${report.summary.statements.percentage}%`);
20578
+ lines.push(` Functions: ${renderBar(report.summary.functions.percentage)} ${report.summary.functions.percentage}%`);
20579
+ lines.push(` Branches: ${renderBar(report.summary.branches.percentage)} ${report.summary.branches.percentage}%`);
20481
20580
  if (report.uncoveredFiles.length > 0) {
20482
20581
  lines.push("");
20483
20582
  lines.push(` Uncovered files: ${report.uncoveredFiles.length}`);
@@ -20523,7 +20622,7 @@ function renderCoverageMarkdown(report) {
20523
20622
  }
20524
20623
  return lines;
20525
20624
  }
20526
- function renderBar2(percentage, width = 15) {
20625
+ function renderBar(percentage, width = 15) {
20527
20626
  const filled = Math.round(percentage / 100 * width);
20528
20627
  const empty = width - filled;
20529
20628
  return "█".repeat(filled) + "░".repeat(empty);
@@ -21706,10 +21805,10 @@ function formatReportAsTerminal(report) {
21706
21805
  lines.push(` ${report.gradeDescription}`);
21707
21806
  lines.push("");
21708
21807
  lines.push(" Score Breakdown:");
21709
- lines.push(` SDLC Coverage: ${renderBar3(report.scores.sdlcCoverage)} ${report.scores.sdlcCoverage}`);
21710
- lines.push(` Prompt Quality: ${renderBar3(report.scores.promptQuality)} ${report.scores.promptQuality}`);
21711
- lines.push(` Tool Utilization: ${renderBar3(report.scores.toolUtilization)} ${report.scores.toolUtilization}`);
21712
- lines.push(` Efficiency: ${renderBar3(report.scores.efficiency)} ${report.scores.efficiency}`);
21808
+ lines.push(` SDLC Coverage: ${renderBar2(report.scores.sdlcCoverage)} ${report.scores.sdlcCoverage}`);
21809
+ lines.push(` Prompt Quality: ${renderBar2(report.scores.promptQuality)} ${report.scores.promptQuality}`);
21810
+ lines.push(` Tool Utilization: ${renderBar2(report.scores.toolUtilization)} ${report.scores.toolUtilization}`);
21811
+ lines.push(` Efficiency: ${renderBar2(report.scores.efficiency)} ${report.scores.efficiency}`);
21713
21812
  lines.push("");
21714
21813
  lines.push(" Quick Stats:");
21715
21814
  lines.push(` Sessions: ${report.summary.totalSessions} | Prompts: ${report.summary.totalPrompts} | Commits: ${report.summary.totalCommits}`);
@@ -21796,7 +21895,7 @@ function formatReportAsTerminal(report) {
21796
21895
  return lines.join(`
21797
21896
  `);
21798
21897
  }
21799
- function renderBar3(value, width = 20) {
21898
+ function renderBar2(value, width = 20) {
21800
21899
  const filled = Math.round(value / 100 * width);
21801
21900
  const empty = width - filled;
21802
21901
  return "█".repeat(filled) + "░".repeat(empty);
@@ -21888,7 +21987,7 @@ function formatReportAsJSON(report) {
21888
21987
 
21889
21988
  // src/commands/report.ts
21890
21989
  import { writeFileSync as writeFileSync6 } from "node:fs";
21891
- import { resolve } from "node:path";
21990
+ import { resolve as resolve2 } from "node:path";
21892
21991
  var reportCommand = defineCommand2({
21893
21992
  meta: {
21894
21993
  name: "report",
@@ -22098,7 +22197,7 @@ async function runHackathonReport(args) {
22098
22197
  break;
22099
22198
  }
22100
22199
  if (args.output) {
22101
- const filepath = resolve(projectDir, args.output);
22200
+ const filepath = resolve2(projectDir, args.output);
22102
22201
  writeFileSync6(filepath, output);
22103
22202
  consola.success(`Report saved to ${filepath}`);
22104
22203
  }
@@ -22250,7 +22349,7 @@ async function runLeadershipReport(args) {
22250
22349
  const since = parseSince2(args.since || "7d");
22251
22350
  const periodLabel = args.since === "30d" ? "Last 30 days" : args.since === "48h" ? "Last 48 hours" : "Last 7 days";
22252
22351
  consola.start(`Generating leadership report (${periodLabel})...`);
22253
- const agents = await collectAgentSessions(since);
22352
+ const agents = await collectAgentSessions(since, true, projectDir);
22254
22353
  if (!agents || agents.sessions.length === 0) {
22255
22354
  consola.warn("No AI sessions found in the specified time range.");
22256
22355
  consola.info("Try running with --since 30d for a longer time range.");
@@ -22267,7 +22366,7 @@ async function runLeadershipReport(args) {
22267
22366
  `);
22268
22367
  }
22269
22368
  if (args.output) {
22270
- const filepath = resolve(projectDir, args.output);
22369
+ const filepath = resolve2(projectDir, args.output);
22271
22370
  writeFileSync6(filepath, output);
22272
22371
  consola.success(`Report saved to ${filepath}`);
22273
22372
  }
@@ -22350,7 +22449,7 @@ async function showHistory(client, clerkId, limit, format2) {
22350
22449
  const recent = trend.slice(-limit);
22351
22450
  for (const entry of recent) {
22352
22451
  const date = new Date(entry.date).toLocaleDateString();
22353
- const bar = renderBar4(entry.overall, 30);
22452
+ const bar = renderBar3(entry.overall, 30);
22354
22453
  const eff = `${entry.tokenEfficiency}x`;
22355
22454
  consola.log(` ${date.padEnd(12)} ${String(entry.overall).padStart(3)}/100 ${bar} eff: ${eff}`);
22356
22455
  }
@@ -22405,7 +22504,7 @@ function renderScanReport(scan, phaseFilter) {
22405
22504
  const phases = phaseFilter ? scan.phases.filter((p) => p.phase === phaseFilter) : scan.phases;
22406
22505
  lines.push("Phase Scores:");
22407
22506
  for (const phase of phases) {
22408
- const bar = renderBar4(phase.score, 20);
22507
+ const bar = renderBar3(phase.score, 20);
22409
22508
  const delta = phase.delta !== undefined && phase.delta !== null ? ` (${phase.delta > 0 ? "+" : ""}${phase.delta})` : "";
22410
22509
  lines.push(` ${phase.phase.padEnd(16)} ${String(phase.score).padStart(3)}/100 ${bar}${delta}`);
22411
22510
  }
@@ -22423,7 +22522,7 @@ function renderScanReport(scan, phaseFilter) {
22423
22522
  return lines.join(`
22424
22523
  `);
22425
22524
  }
22426
- function renderBar4(value, width) {
22525
+ function renderBar3(value, width) {
22427
22526
  const filled = Math.round(value / 100 * width);
22428
22527
  const empty = width - filled;
22429
22528
  return "█".repeat(filled) + "░".repeat(empty);
@@ -23272,10 +23371,10 @@ async function prompt2(question) {
23272
23371
  input: process.stdin,
23273
23372
  output: process.stdout
23274
23373
  });
23275
- return new Promise((resolve2) => {
23374
+ return new Promise((resolve3) => {
23276
23375
  rl.question(question, (answer) => {
23277
23376
  rl.close();
23278
- resolve2(answer.trim());
23377
+ resolve3(answer.trim());
23279
23378
  });
23280
23379
  });
23281
23380
  }
@@ -23720,6 +23819,27 @@ var BOLD = "\x1B[1m";
23720
23819
  var RESET = "\x1B[0m";
23721
23820
  var MAGENTA = "\x1B[35m";
23722
23821
  var CHANGELOG = [
23822
+ {
23823
+ version: "0.6.0",
23824
+ date: "2026-02-15",
23825
+ title: "Phase-Focused Optimizations",
23826
+ highlights: [
23827
+ "nb scan now asks which SDLC phase to optimize",
23828
+ "Phase-specific optimization breakdown and installs",
23829
+ "New SDLC optimization formatter and coverage test"
23830
+ ]
23831
+ },
23832
+ {
23833
+ version: "0.5.3",
23834
+ date: "2026-02-14",
23835
+ title: "Project-Scoped Analysis",
23836
+ highlights: [
23837
+ "CRITICAL FIX: Sessions now filtered to current project only",
23838
+ "No more mixing data from different projects in reports",
23839
+ "Improved frustration display with exact prompts and timestamps",
23840
+ "Better OpenCode prompt extraction for frustration analysis"
23841
+ ]
23842
+ },
23723
23843
  {
23724
23844
  version: "0.5.2",
23725
23845
  date: "2026-02-14",
@@ -24191,7 +24311,7 @@ function showFullChangelog() {
24191
24311
  // package.json
24192
24312
  var package_default = {
24193
24313
  name: "nairon-bench",
24194
- version: "0.5.2",
24314
+ version: "0.6.0",
24195
24315
  description: "AI workflow benchmarking CLI",
24196
24316
  type: "module",
24197
24317
  bin: {
@@ -24380,7 +24500,7 @@ async function fetchGitHubUser(accessToken) {
24380
24500
  return await response.json();
24381
24501
  }
24382
24502
  function sleep2(ms) {
24383
- return new Promise((resolve2) => setTimeout(resolve2, ms));
24503
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
24384
24504
  }
24385
24505
 
24386
24506
  // src/commands/init.ts
@@ -25169,7 +25289,7 @@ var safeJSON = (text) => {
25169
25289
  };
25170
25290
 
25171
25291
  // ../../node_modules/supermemory/internal/utils/sleep.mjs
25172
- var sleep3 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
25292
+ var sleep3 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
25173
25293
 
25174
25294
  // ../../node_modules/supermemory/version.mjs
25175
25295
  var VERSION = "4.11.1";
@@ -25811,8 +25931,8 @@ var _APIPromise_client;
25811
25931
 
25812
25932
  class APIPromise extends Promise {
25813
25933
  constructor(client, responsePromise, parseResponse = defaultParseResponse) {
25814
- super((resolve2) => {
25815
- resolve2(null);
25934
+ super((resolve3) => {
25935
+ resolve3(null);
25816
25936
  });
25817
25937
  this.responsePromise = responsePromise;
25818
25938
  this.parseResponse = parseResponse;
@@ -26718,7 +26838,7 @@ function formatBytes(bytes) {
26718
26838
  // package.json
26719
26839
  var package_default2 = {
26720
26840
  name: "nairon-bench",
26721
- version: "0.5.2",
26841
+ version: "0.6.0",
26722
26842
  description: "AI workflow benchmarking CLI",
26723
26843
  type: "module",
26724
26844
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nairon-bench",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "AI workflow benchmarking CLI",
5
5
  "type": "module",
6
6
  "bin": {