nairon-bench 0.3.13 → 0.3.15

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 +2169 -1632
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4447,8 +4447,8 @@ function defineCommand2(def) {
4447
4447
 
4448
4448
  // src/commands/scan.ts
4449
4449
  init_dist();
4450
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, readFileSync as readFileSync8 } from "node:fs";
4451
- import { join as join8 } from "node:path";
4450
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, readFileSync as readFileSync9 } from "node:fs";
4451
+ import { join as join9 } from "node:path";
4452
4452
 
4453
4453
  // ../../node_modules/simple-git/dist/esm/index.js
4454
4454
  var import_file_exists = __toESM(require_dist(), 1);
@@ -11458,854 +11458,448 @@ function renderBar(value, width) {
11458
11458
  return "█".repeat(filled) + "░".repeat(empty);
11459
11459
  }
11460
11460
 
11461
- // src/lib/optimization-installer.ts
11462
- import { execSync as execSync2, spawnSync } from "node:child_process";
11463
- import { existsSync as existsSync7, writeFileSync as writeFileSync3, readFileSync as readFileSync7, mkdirSync as mkdirSync3, readdirSync as readdirSync4 } from "node:fs";
11464
- import { join as join7 } from "node:path";
11461
+ // src/lib/frustration-detector.ts
11462
+ import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as fsReaddirSync } from "node:fs";
11465
11463
  import { homedir as homedir6 } from "node:os";
11466
- async function installOptimizations(optimizations, projectDir, options = {}) {
11467
- const results = [];
11468
- for (const opt of optimizations) {
11469
- if (!opt.selected)
11470
- continue;
11471
- try {
11472
- const result = await installSingleOptimization(opt, projectDir, options);
11473
- results.push(result);
11474
- } catch (err) {
11475
- results.push({
11476
- id: opt.id,
11477
- name: opt.name,
11478
- success: false,
11479
- message: "Installation failed",
11480
- error: err instanceof Error ? err.message : String(err)
11481
- });
11482
- }
11483
- }
11484
- return results;
11485
- }
11486
- async function installSingleOptimization(opt, projectDir, options) {
11487
- if (options.dryRun) {
11488
- return {
11489
- id: opt.id,
11490
- name: opt.name,
11491
- success: true,
11492
- message: `[DRY RUN] Would install: ${opt.installCommand || opt.configPath || "unknown"}`
11493
- };
11494
- }
11495
- switch (opt.type) {
11496
- case "mcp":
11497
- return installMCP(opt, projectDir, options.agent);
11498
- case "skill":
11499
- return installSkill(opt);
11500
- case "config":
11501
- return installConfig(opt, projectDir);
11502
- case "hook":
11503
- return installHook(opt, projectDir);
11504
- default:
11505
- return {
11506
- id: opt.id,
11507
- name: opt.name,
11508
- success: false,
11509
- message: "Unknown optimization type"
11510
- };
11511
- }
11512
- }
11513
- function installMCP(opt, projectDir, agent) {
11514
- if (opt.installCommand?.startsWith("claude mcp add")) {
11515
- try {
11516
- execSync2(opt.installCommand, {
11517
- encoding: "utf-8",
11518
- stdio: "pipe",
11519
- timeout: 60000
11520
- });
11521
- return {
11522
- id: opt.id,
11523
- name: opt.name,
11524
- success: true,
11525
- message: `Installed via: ${opt.installCommand}`
11526
- };
11527
- } catch (err) {}
11528
- }
11529
- if (opt.id === "beads" || opt.name.toLowerCase().includes("beads")) {
11530
- try {
11531
- execSync2("bun add -g beads", { encoding: "utf-8", stdio: "pipe", timeout: 60000 });
11532
- execSync2("bd init", { cwd: projectDir, encoding: "utf-8", stdio: "pipe", timeout: 30000 });
11533
- return {
11534
- id: opt.id,
11535
- name: opt.name,
11536
- success: true,
11537
- message: "Installed Beads globally and initialized in project"
11538
- };
11539
- } catch (err) {
11540
- return {
11541
- id: opt.id,
11542
- name: opt.name,
11543
- success: false,
11544
- message: "Failed to install Beads",
11545
- error: err instanceof Error ? err.message : String(err)
11546
- };
11547
- }
11464
+ import { join as join7 } from "node:path";
11465
+
11466
+ // ../../node_modules/ora/index.js
11467
+ import process8 from "node:process";
11468
+ import { stripVTControlCharacters } from "node:util";
11469
+
11470
+ // ../../node_modules/chalk/source/vendor/ansi-styles/index.js
11471
+ var ANSI_BACKGROUND_OFFSET = 10;
11472
+ var wrapAnsi16 = (offset = 0) => (code2) => `\x1B[${code2 + offset}m`;
11473
+ var wrapAnsi256 = (offset = 0) => (code2) => `\x1B[${38 + offset};5;${code2}m`;
11474
+ var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
11475
+ var styles = {
11476
+ modifier: {
11477
+ reset: [0, 0],
11478
+ bold: [1, 22],
11479
+ dim: [2, 22],
11480
+ italic: [3, 23],
11481
+ underline: [4, 24],
11482
+ overline: [53, 55],
11483
+ inverse: [7, 27],
11484
+ hidden: [8, 28],
11485
+ strikethrough: [9, 29]
11486
+ },
11487
+ color: {
11488
+ black: [30, 39],
11489
+ red: [31, 39],
11490
+ green: [32, 39],
11491
+ yellow: [33, 39],
11492
+ blue: [34, 39],
11493
+ magenta: [35, 39],
11494
+ cyan: [36, 39],
11495
+ white: [37, 39],
11496
+ blackBright: [90, 39],
11497
+ gray: [90, 39],
11498
+ grey: [90, 39],
11499
+ redBright: [91, 39],
11500
+ greenBright: [92, 39],
11501
+ yellowBright: [93, 39],
11502
+ blueBright: [94, 39],
11503
+ magentaBright: [95, 39],
11504
+ cyanBright: [96, 39],
11505
+ whiteBright: [97, 39]
11506
+ },
11507
+ bgColor: {
11508
+ bgBlack: [40, 49],
11509
+ bgRed: [41, 49],
11510
+ bgGreen: [42, 49],
11511
+ bgYellow: [43, 49],
11512
+ bgBlue: [44, 49],
11513
+ bgMagenta: [45, 49],
11514
+ bgCyan: [46, 49],
11515
+ bgWhite: [47, 49],
11516
+ bgBlackBright: [100, 49],
11517
+ bgGray: [100, 49],
11518
+ bgGrey: [100, 49],
11519
+ bgRedBright: [101, 49],
11520
+ bgGreenBright: [102, 49],
11521
+ bgYellowBright: [103, 49],
11522
+ bgBlueBright: [104, 49],
11523
+ bgMagentaBright: [105, 49],
11524
+ bgCyanBright: [106, 49],
11525
+ bgWhiteBright: [107, 49]
11548
11526
  }
11549
- if (opt.installCommand) {
11550
- try {
11551
- execSync2(opt.installCommand, {
11552
- encoding: "utf-8",
11553
- stdio: "pipe",
11554
- timeout: 60000
11555
- });
11556
- return {
11557
- id: opt.id,
11558
- name: opt.name,
11559
- success: true,
11560
- message: `Installed via: ${opt.installCommand}`
11561
- };
11562
- } catch (err) {
11563
- return {
11564
- id: opt.id,
11565
- name: opt.name,
11566
- success: false,
11567
- message: "Failed to install MCP",
11568
- error: err instanceof Error ? err.message : String(err)
11527
+ };
11528
+ var modifierNames = Object.keys(styles.modifier);
11529
+ var foregroundColorNames = Object.keys(styles.color);
11530
+ var backgroundColorNames = Object.keys(styles.bgColor);
11531
+ var colorNames = [...foregroundColorNames, ...backgroundColorNames];
11532
+ function assembleStyles() {
11533
+ const codes = new Map;
11534
+ for (const [groupName, group] of Object.entries(styles)) {
11535
+ for (const [styleName, style] of Object.entries(group)) {
11536
+ styles[styleName] = {
11537
+ open: `\x1B[${style[0]}m`,
11538
+ close: `\x1B[${style[1]}m`
11569
11539
  };
11540
+ group[styleName] = styles[styleName];
11541
+ codes.set(style[0], style[1]);
11570
11542
  }
11571
- }
11572
- return {
11573
- id: opt.id,
11574
- name: opt.name,
11575
- success: false,
11576
- message: "No install command available"
11577
- };
11578
- }
11579
- function installSkill(opt) {
11580
- if (!opt.installCommand) {
11581
- return {
11582
- id: opt.id,
11583
- name: opt.name,
11584
- success: false,
11585
- message: "No install command for skill"
11586
- };
11587
- }
11588
- try {
11589
- const skillPath = opt.installCommand.replace(/^npx\s+/, "").replace(/^bunx\s+/, "").replace(/^skills\s+add\s+/, "").trim();
11590
- let result = spawnSync("bunx", ["--bun", "skills", "add", skillPath], {
11591
- encoding: "utf-8",
11592
- stdio: "pipe",
11593
- timeout: 120000
11543
+ Object.defineProperty(styles, groupName, {
11544
+ value: group,
11545
+ enumerable: false
11594
11546
  });
11595
- if (result.status !== 0) {
11596
- result = spawnSync("npx", ["-y", "skills", "add", skillPath], {
11597
- encoding: "utf-8",
11598
- stdio: "pipe",
11599
- timeout: 120000
11600
- });
11601
- }
11602
- if (result.status === 0) {
11603
- return {
11604
- id: opt.id,
11605
- name: opt.name,
11606
- success: true,
11607
- message: `Skill installed successfully`
11608
- };
11609
- } else {
11610
- const output = (result.stderr || result.stdout || "").toLowerCase();
11611
- if (output.includes("already exists") || output.includes("already installed")) {
11612
- return {
11613
- id: opt.id,
11614
- name: opt.name,
11615
- success: true,
11616
- message: `Skill already installed`
11617
- };
11618
- }
11619
- return {
11620
- id: opt.id,
11621
- name: opt.name,
11622
- success: false,
11623
- message: "Skill installation failed",
11624
- error: result.stderr || result.stdout || "Unknown error"
11625
- };
11626
- }
11627
- } catch (err) {
11628
- return {
11629
- id: opt.id,
11630
- name: opt.name,
11631
- success: false,
11632
- message: "Failed to install skill",
11633
- error: err instanceof Error ? err.message : String(err)
11634
- };
11635
11547
  }
11636
- }
11637
- function installConfig(opt, projectDir) {
11638
- if (!opt.configPath || !opt.configContent) {
11639
- return {
11640
- id: opt.id,
11641
- name: opt.name,
11642
- success: false,
11643
- message: "No config path or content provided"
11644
- };
11645
- }
11646
- try {
11647
- const fullPath = opt.configPath.startsWith("/") || opt.configPath.startsWith("~") ? opt.configPath.replace("~", homedir6()) : join7(projectDir, opt.configPath);
11648
- if (existsSync7(fullPath)) {
11649
- return {
11650
- id: opt.id,
11651
- name: opt.name,
11652
- success: true,
11653
- message: `Config already exists: ${opt.configPath}`
11654
- };
11655
- }
11656
- const parentDir = fullPath.substring(0, fullPath.lastIndexOf("/"));
11657
- if (parentDir && !existsSync7(parentDir)) {
11658
- mkdirSync3(parentDir, { recursive: true });
11548
+ Object.defineProperty(styles, "codes", {
11549
+ value: codes,
11550
+ enumerable: false
11551
+ });
11552
+ styles.color.close = "\x1B[39m";
11553
+ styles.bgColor.close = "\x1B[49m";
11554
+ styles.color.ansi = wrapAnsi16();
11555
+ styles.color.ansi256 = wrapAnsi256();
11556
+ styles.color.ansi16m = wrapAnsi16m();
11557
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
11558
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
11559
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
11560
+ Object.defineProperties(styles, {
11561
+ rgbToAnsi256: {
11562
+ value(red, green, blue) {
11563
+ if (red === green && green === blue) {
11564
+ if (red < 8) {
11565
+ return 16;
11566
+ }
11567
+ if (red > 248) {
11568
+ return 231;
11569
+ }
11570
+ return Math.round((red - 8) / 247 * 24) + 232;
11571
+ }
11572
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
11573
+ },
11574
+ enumerable: false
11575
+ },
11576
+ hexToRgb: {
11577
+ value(hex) {
11578
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
11579
+ if (!matches) {
11580
+ return [0, 0, 0];
11581
+ }
11582
+ let [colorString] = matches;
11583
+ if (colorString.length === 3) {
11584
+ colorString = [...colorString].map((character) => character + character).join("");
11585
+ }
11586
+ const integer = Number.parseInt(colorString, 16);
11587
+ return [
11588
+ integer >> 16 & 255,
11589
+ integer >> 8 & 255,
11590
+ integer & 255
11591
+ ];
11592
+ },
11593
+ enumerable: false
11594
+ },
11595
+ hexToAnsi256: {
11596
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
11597
+ enumerable: false
11598
+ },
11599
+ ansi256ToAnsi: {
11600
+ value(code2) {
11601
+ if (code2 < 8) {
11602
+ return 30 + code2;
11603
+ }
11604
+ if (code2 < 16) {
11605
+ return 90 + (code2 - 8);
11606
+ }
11607
+ let red;
11608
+ let green;
11609
+ let blue;
11610
+ if (code2 >= 232) {
11611
+ red = ((code2 - 232) * 10 + 8) / 255;
11612
+ green = red;
11613
+ blue = red;
11614
+ } else {
11615
+ code2 -= 16;
11616
+ const remainder = code2 % 36;
11617
+ red = Math.floor(code2 / 36) / 5;
11618
+ green = Math.floor(remainder / 6) / 5;
11619
+ blue = remainder % 6 / 5;
11620
+ }
11621
+ const value = Math.max(red, green, blue) * 2;
11622
+ if (value === 0) {
11623
+ return 30;
11624
+ }
11625
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
11626
+ if (value === 2) {
11627
+ result += 60;
11628
+ }
11629
+ return result;
11630
+ },
11631
+ enumerable: false
11632
+ },
11633
+ rgbToAnsi: {
11634
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
11635
+ enumerable: false
11636
+ },
11637
+ hexToAnsi: {
11638
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
11639
+ enumerable: false
11659
11640
  }
11660
- writeFileSync3(fullPath, opt.configContent);
11661
- return {
11662
- id: opt.id,
11663
- name: opt.name,
11664
- success: true,
11665
- message: `Created: ${opt.configPath}`
11666
- };
11667
- } catch (err) {
11668
- return {
11669
- id: opt.id,
11670
- name: opt.name,
11671
- success: false,
11672
- message: "Failed to create config",
11673
- error: err instanceof Error ? err.message : String(err)
11674
- };
11675
- }
11641
+ });
11642
+ return styles;
11676
11643
  }
11677
- function installHook(opt, projectDir) {
11678
- const agentsMdPath = join7(projectDir, "AGENTS.md");
11679
- try {
11680
- let content = "";
11681
- if (existsSync7(agentsMdPath)) {
11682
- content = readFileSync7(agentsMdPath, "utf-8");
11683
- } else {
11684
- content = `# Agent Instructions
11644
+ var ansiStyles = assembleStyles();
11645
+ var ansi_styles_default = ansiStyles;
11685
11646
 
11686
- `;
11647
+ // ../../node_modules/chalk/source/vendor/supports-color/index.js
11648
+ import process2 from "node:process";
11649
+ import os from "node:os";
11650
+ import tty2 from "node:tty";
11651
+ function hasFlag(flag, argv2 = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
11652
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
11653
+ const position = argv2.indexOf(prefix + flag);
11654
+ const terminatorPosition = argv2.indexOf("--");
11655
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
11656
+ }
11657
+ var { env: env2 } = process2;
11658
+ var flagForceColor;
11659
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
11660
+ flagForceColor = 0;
11661
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
11662
+ flagForceColor = 1;
11663
+ }
11664
+ function envForceColor() {
11665
+ if ("FORCE_COLOR" in env2) {
11666
+ if (env2.FORCE_COLOR === "true") {
11667
+ return 1;
11687
11668
  }
11688
- const hookName = opt.name.toLowerCase();
11689
- if (content.toLowerCase().includes(hookName)) {
11690
- return {
11691
- id: opt.id,
11692
- name: opt.name,
11693
- success: true,
11694
- message: "Hook already configured in AGENTS.md"
11695
- };
11669
+ if (env2.FORCE_COLOR === "false") {
11670
+ return 0;
11696
11671
  }
11697
- const hookSection = opt.configContent || `
11698
- ## ${opt.name}
11699
- ${opt.description}
11700
- `;
11701
- content += `
11702
- ` + hookSection;
11703
- writeFileSync3(agentsMdPath, content);
11704
- return {
11705
- id: opt.id,
11706
- name: opt.name,
11707
- success: true,
11708
- message: "Added hook to AGENTS.md"
11709
- };
11710
- } catch (err) {
11711
- return {
11712
- id: opt.id,
11713
- name: opt.name,
11714
- success: false,
11715
- message: "Failed to add hook",
11716
- error: err instanceof Error ? err.message : String(err)
11717
- };
11672
+ return env2.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env2.FORCE_COLOR, 10), 3);
11718
11673
  }
11719
11674
  }
11720
- function phaseRecToInstallable(rec) {
11675
+ function translateLevel(level) {
11676
+ if (level === 0) {
11677
+ return false;
11678
+ }
11721
11679
  return {
11722
- id: rec.title.toLowerCase().replace(/\s+/g, "-"),
11723
- name: rec.title,
11724
- type: rec.type,
11725
- description: rec.description,
11726
- installCommand: rec.installCommand,
11727
- selected: false
11680
+ level,
11681
+ hasBasic: true,
11682
+ has256: level >= 2,
11683
+ has16m: level >= 3
11728
11684
  };
11729
11685
  }
11730
- function checkInstalledStatus(projectDir) {
11731
- const home = homedir6();
11732
- const status = {
11733
- context7: false,
11734
- supermemory: false,
11735
- nia: false,
11736
- beads: false,
11737
- skills: []
11738
- };
11739
- const mcpConfigPaths = [
11740
- join7(home, ".claude.json"),
11741
- join7(home, ".claude", "claude_desktop_config.json"),
11742
- join7(home, ".claude", "settings.json"),
11743
- join7(home, ".claude", "settings.local.json"),
11744
- join7(projectDir, ".mcp.json"),
11745
- join7(projectDir, ".claude", "settings.local.json")
11746
- ];
11747
- for (const configPath of mcpConfigPaths) {
11748
- if (existsSync7(configPath)) {
11749
- try {
11750
- const config = JSON.parse(readFileSync7(configPath, "utf-8"));
11751
- const mcpServers = config.mcpServers || config.mcp_servers || {};
11752
- for (const name of Object.keys(mcpServers)) {
11753
- const mcpName = name.toLowerCase();
11754
- if (mcpName.includes("context7") || mcpName === "c7") {
11755
- status.context7 = true;
11756
- }
11757
- if (mcpName.includes("supermemory") || mcpName.includes("memory")) {
11758
- status.supermemory = true;
11759
- }
11760
- if (mcpName.includes("nia")) {
11761
- status.nia = true;
11762
- }
11763
- }
11764
- } catch {}
11765
- }
11686
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
11687
+ const noFlagForceColor = envForceColor();
11688
+ if (noFlagForceColor !== undefined) {
11689
+ flagForceColor = noFlagForceColor;
11766
11690
  }
11767
- status.beads = existsSync7(join7(projectDir, ".beads"));
11768
- const skillsDirs = [
11769
- join7(home, ".claude", "skills"),
11770
- join7(home, ".config", "opencode", "skills"),
11771
- join7(home, ".agents", "skills"),
11772
- join7(projectDir, ".opencode", "skill")
11773
- ];
11774
- for (const dir of skillsDirs) {
11775
- if (existsSync7(dir)) {
11776
- try {
11777
- const skills = readdirSync4(dir);
11778
- for (const skill of skills) {
11779
- if (!status.skills.includes(skill.toLowerCase())) {
11780
- status.skills.push(skill.toLowerCase());
11781
- }
11782
- }
11783
- } catch {}
11691
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
11692
+ if (forceColor === 0) {
11693
+ return 0;
11694
+ }
11695
+ if (sniffFlags) {
11696
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
11697
+ return 3;
11698
+ }
11699
+ if (hasFlag("color=256")) {
11700
+ return 2;
11784
11701
  }
11785
11702
  }
11786
- return status;
11787
- }
11788
- function filterAlreadyInstalled(optimizations, status) {
11789
- return optimizations.filter((opt) => {
11790
- const id = opt.id.toLowerCase();
11791
- if (id === "context7" && status.context7)
11792
- return false;
11793
- if (id === "supermemory" && status.supermemory)
11794
- return false;
11795
- if (id === "nia" && status.nia)
11796
- return false;
11797
- if (id === "beads" && status.beads)
11798
- return false;
11799
- if (opt.type === "skill") {
11800
- const skillName = opt.name.toLowerCase().replace(/\s+/g, "-");
11801
- if (status.skills.some((s2) => s2.includes(skillName) || skillName.includes(s2) || s2.includes("tdd") && id.includes("tdd") || s2.includes("debug") && id.includes("debug") || s2.includes("plan") && id.includes("plan"))) {
11802
- return false;
11703
+ if ("TF_BUILD" in env2 && "AGENT_NAME" in env2) {
11704
+ return 1;
11705
+ }
11706
+ if (haveStream && !streamIsTTY && forceColor === undefined) {
11707
+ return 0;
11708
+ }
11709
+ const min = forceColor || 0;
11710
+ if (env2.TERM === "dumb") {
11711
+ return min;
11712
+ }
11713
+ if (process2.platform === "win32") {
11714
+ const osRelease = os.release().split(".");
11715
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
11716
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
11717
+ }
11718
+ return 1;
11719
+ }
11720
+ if ("CI" in env2) {
11721
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env2))) {
11722
+ return 3;
11723
+ }
11724
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env2)) || env2.CI_NAME === "codeship") {
11725
+ return 1;
11726
+ }
11727
+ return min;
11728
+ }
11729
+ if ("TEAMCITY_VERSION" in env2) {
11730
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env2.TEAMCITY_VERSION) ? 1 : 0;
11731
+ }
11732
+ if (env2.COLORTERM === "truecolor") {
11733
+ return 3;
11734
+ }
11735
+ if (env2.TERM === "xterm-kitty") {
11736
+ return 3;
11737
+ }
11738
+ if (env2.TERM === "xterm-ghostty") {
11739
+ return 3;
11740
+ }
11741
+ if (env2.TERM === "wezterm") {
11742
+ return 3;
11743
+ }
11744
+ if ("TERM_PROGRAM" in env2) {
11745
+ const version2 = Number.parseInt((env2.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
11746
+ switch (env2.TERM_PROGRAM) {
11747
+ case "iTerm.app": {
11748
+ return version2 >= 3 ? 3 : 2;
11749
+ }
11750
+ case "Apple_Terminal": {
11751
+ return 2;
11803
11752
  }
11804
11753
  }
11805
- return true;
11754
+ }
11755
+ if (/-256(color)?$/i.test(env2.TERM)) {
11756
+ return 2;
11757
+ }
11758
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env2.TERM)) {
11759
+ return 1;
11760
+ }
11761
+ if ("COLORTERM" in env2) {
11762
+ return 1;
11763
+ }
11764
+ return min;
11765
+ }
11766
+ function createSupportsColor(stream, options = {}) {
11767
+ const level = _supportsColor(stream, {
11768
+ streamIsTTY: stream && stream.isTTY,
11769
+ ...options
11806
11770
  });
11771
+ return translateLevel(level);
11807
11772
  }
11808
- var QUICK_INSTALL_PRESETS = {
11809
- essential: [
11810
- {
11811
- id: "context7",
11812
- name: "Context7 MCP",
11813
- type: "mcp",
11814
- description: "Up-to-date library documentation",
11815
- installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
11816
- selected: true
11817
- },
11818
- {
11819
- id: "supermemory",
11820
- name: "Supermemory MCP",
11821
- type: "mcp",
11822
- description: "Persistent memory across sessions",
11823
- installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest",
11824
- selected: true
11825
- },
11826
- {
11827
- id: "beads",
11828
- name: "Beads Task Manager",
11829
- type: "mcp",
11830
- description: "Persistent task tracking",
11831
- installCommand: "bun add -g beads && bd init",
11832
- selected: true
11833
- }
11834
- ],
11835
- productivity: [
11836
- {
11837
- id: "claude-md",
11838
- name: "CLAUDE.md",
11839
- type: "config",
11840
- description: "Project context file",
11841
- configPath: "CLAUDE.md",
11842
- configContent: `# Project Instructions
11843
-
11844
- ## Build & Test Commands
11845
- \`\`\`bash
11846
- bun test # Run tests
11847
- bun run build # Build project
11848
- bun run lint # Run linter
11849
- \`\`\`
11850
-
11851
- ## Architecture
11852
- Describe your project structure here.
11853
-
11854
- ## Conventions
11855
- - Use TypeScript
11856
- - Write tests for new features
11857
- - Use conventional commits
11858
- `,
11859
- selected: true
11860
- },
11861
- {
11862
- id: "vitest",
11863
- name: "Vitest",
11864
- type: "library",
11865
- description: "Fast unit testing framework",
11866
- installCommand: "bun add -D vitest",
11867
- selected: true
11868
- }
11869
- ]
11773
+ var supportsColor = {
11774
+ stdout: createSupportsColor({ isTTY: tty2.isatty(1) }),
11775
+ stderr: createSupportsColor({ isTTY: tty2.isatty(2) })
11870
11776
  };
11777
+ var supports_color_default = supportsColor;
11871
11778
 
11872
- // ../../node_modules/ora/index.js
11873
- import process8 from "node:process";
11874
- import { stripVTControlCharacters } from "node:util";
11779
+ // ../../node_modules/chalk/source/utilities.js
11780
+ function stringReplaceAll(string, substring, replacer) {
11781
+ let index = string.indexOf(substring);
11782
+ if (index === -1) {
11783
+ return string;
11784
+ }
11785
+ const substringLength = substring.length;
11786
+ let endIndex = 0;
11787
+ let returnValue = "";
11788
+ do {
11789
+ returnValue += string.slice(endIndex, index) + substring + replacer;
11790
+ endIndex = index + substringLength;
11791
+ index = string.indexOf(substring, endIndex);
11792
+ } while (index !== -1);
11793
+ returnValue += string.slice(endIndex);
11794
+ return returnValue;
11795
+ }
11796
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
11797
+ let endIndex = 0;
11798
+ let returnValue = "";
11799
+ do {
11800
+ const gotCR = string[index - 1] === "\r";
11801
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
11802
+ ` : `
11803
+ `) + postfix;
11804
+ endIndex = index + 1;
11805
+ index = string.indexOf(`
11806
+ `, endIndex);
11807
+ } while (index !== -1);
11808
+ returnValue += string.slice(endIndex);
11809
+ return returnValue;
11810
+ }
11875
11811
 
11876
- // ../../node_modules/chalk/source/vendor/ansi-styles/index.js
11877
- var ANSI_BACKGROUND_OFFSET = 10;
11878
- var wrapAnsi16 = (offset = 0) => (code2) => `\x1B[${code2 + offset}m`;
11879
- var wrapAnsi256 = (offset = 0) => (code2) => `\x1B[${38 + offset};5;${code2}m`;
11880
- var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
11881
- var styles = {
11882
- modifier: {
11883
- reset: [0, 0],
11884
- bold: [1, 22],
11885
- dim: [2, 22],
11886
- italic: [3, 23],
11887
- underline: [4, 24],
11888
- overline: [53, 55],
11889
- inverse: [7, 27],
11890
- hidden: [8, 28],
11891
- strikethrough: [9, 29]
11892
- },
11893
- color: {
11894
- black: [30, 39],
11895
- red: [31, 39],
11896
- green: [32, 39],
11897
- yellow: [33, 39],
11898
- blue: [34, 39],
11899
- magenta: [35, 39],
11900
- cyan: [36, 39],
11901
- white: [37, 39],
11902
- blackBright: [90, 39],
11903
- gray: [90, 39],
11904
- grey: [90, 39],
11905
- redBright: [91, 39],
11906
- greenBright: [92, 39],
11907
- yellowBright: [93, 39],
11908
- blueBright: [94, 39],
11909
- magentaBright: [95, 39],
11910
- cyanBright: [96, 39],
11911
- whiteBright: [97, 39]
11912
- },
11913
- bgColor: {
11914
- bgBlack: [40, 49],
11915
- bgRed: [41, 49],
11916
- bgGreen: [42, 49],
11917
- bgYellow: [43, 49],
11918
- bgBlue: [44, 49],
11919
- bgMagenta: [45, 49],
11920
- bgCyan: [46, 49],
11921
- bgWhite: [47, 49],
11922
- bgBlackBright: [100, 49],
11923
- bgGray: [100, 49],
11924
- bgGrey: [100, 49],
11925
- bgRedBright: [101, 49],
11926
- bgGreenBright: [102, 49],
11927
- bgYellowBright: [103, 49],
11928
- bgBlueBright: [104, 49],
11929
- bgMagentaBright: [105, 49],
11930
- bgCyanBright: [106, 49],
11931
- bgWhiteBright: [107, 49]
11812
+ // ../../node_modules/chalk/source/index.js
11813
+ var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
11814
+ var GENERATOR = Symbol("GENERATOR");
11815
+ var STYLER = Symbol("STYLER");
11816
+ var IS_EMPTY = Symbol("IS_EMPTY");
11817
+ var levelMapping = [
11818
+ "ansi",
11819
+ "ansi",
11820
+ "ansi256",
11821
+ "ansi16m"
11822
+ ];
11823
+ var styles2 = Object.create(null);
11824
+ var applyOptions = (object, options = {}) => {
11825
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
11826
+ throw new Error("The `level` option should be an integer from 0 to 3");
11932
11827
  }
11828
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
11829
+ object.level = options.level === undefined ? colorLevel : options.level;
11933
11830
  };
11934
- var modifierNames = Object.keys(styles.modifier);
11935
- var foregroundColorNames = Object.keys(styles.color);
11936
- var backgroundColorNames = Object.keys(styles.bgColor);
11937
- var colorNames = [...foregroundColorNames, ...backgroundColorNames];
11938
- function assembleStyles() {
11939
- const codes = new Map;
11940
- for (const [groupName, group] of Object.entries(styles)) {
11941
- for (const [styleName, style] of Object.entries(group)) {
11942
- styles[styleName] = {
11943
- open: `\x1B[${style[0]}m`,
11944
- close: `\x1B[${style[1]}m`
11945
- };
11946
- group[styleName] = styles[styleName];
11947
- codes.set(style[0], style[1]);
11948
- }
11949
- Object.defineProperty(styles, groupName, {
11950
- value: group,
11951
- enumerable: false
11952
- });
11953
- }
11954
- Object.defineProperty(styles, "codes", {
11955
- value: codes,
11956
- enumerable: false
11957
- });
11958
- styles.color.close = "\x1B[39m";
11959
- styles.bgColor.close = "\x1B[49m";
11960
- styles.color.ansi = wrapAnsi16();
11961
- styles.color.ansi256 = wrapAnsi256();
11962
- styles.color.ansi16m = wrapAnsi16m();
11963
- styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
11964
- styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
11965
- styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
11966
- Object.defineProperties(styles, {
11967
- rgbToAnsi256: {
11968
- value(red, green, blue) {
11969
- if (red === green && green === blue) {
11970
- if (red < 8) {
11971
- return 16;
11972
- }
11973
- if (red > 248) {
11974
- return 231;
11975
- }
11976
- return Math.round((red - 8) / 247 * 24) + 232;
11977
- }
11978
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
11979
- },
11980
- enumerable: false
11981
- },
11982
- hexToRgb: {
11983
- value(hex) {
11984
- const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
11985
- if (!matches) {
11986
- return [0, 0, 0];
11987
- }
11988
- let [colorString] = matches;
11989
- if (colorString.length === 3) {
11990
- colorString = [...colorString].map((character) => character + character).join("");
11991
- }
11992
- const integer = Number.parseInt(colorString, 16);
11993
- return [
11994
- integer >> 16 & 255,
11995
- integer >> 8 & 255,
11996
- integer & 255
11997
- ];
11998
- },
11999
- enumerable: false
12000
- },
12001
- hexToAnsi256: {
12002
- value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
12003
- enumerable: false
12004
- },
12005
- ansi256ToAnsi: {
12006
- value(code2) {
12007
- if (code2 < 8) {
12008
- return 30 + code2;
12009
- }
12010
- if (code2 < 16) {
12011
- return 90 + (code2 - 8);
12012
- }
12013
- let red;
12014
- let green;
12015
- let blue;
12016
- if (code2 >= 232) {
12017
- red = ((code2 - 232) * 10 + 8) / 255;
12018
- green = red;
12019
- blue = red;
12020
- } else {
12021
- code2 -= 16;
12022
- const remainder = code2 % 36;
12023
- red = Math.floor(code2 / 36) / 5;
12024
- green = Math.floor(remainder / 6) / 5;
12025
- blue = remainder % 6 / 5;
12026
- }
12027
- const value = Math.max(red, green, blue) * 2;
12028
- if (value === 0) {
12029
- return 30;
12030
- }
12031
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
12032
- if (value === 2) {
12033
- result += 60;
12034
- }
12035
- return result;
12036
- },
12037
- enumerable: false
12038
- },
12039
- rgbToAnsi: {
12040
- value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
12041
- enumerable: false
12042
- },
12043
- hexToAnsi: {
12044
- value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
12045
- enumerable: false
12046
- }
12047
- });
12048
- return styles;
12049
- }
12050
- var ansiStyles = assembleStyles();
12051
- var ansi_styles_default = ansiStyles;
12052
-
12053
- // ../../node_modules/chalk/source/vendor/supports-color/index.js
12054
- import process2 from "node:process";
12055
- import os from "node:os";
12056
- import tty2 from "node:tty";
12057
- function hasFlag(flag, argv2 = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
12058
- const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
12059
- const position = argv2.indexOf(prefix + flag);
12060
- const terminatorPosition = argv2.indexOf("--");
12061
- return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
12062
- }
12063
- var { env: env2 } = process2;
12064
- var flagForceColor;
12065
- if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
12066
- flagForceColor = 0;
12067
- } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
12068
- flagForceColor = 1;
11831
+ var chalkFactory = (options) => {
11832
+ const chalk = (...strings) => strings.join(" ");
11833
+ applyOptions(chalk, options);
11834
+ Object.setPrototypeOf(chalk, createChalk.prototype);
11835
+ return chalk;
11836
+ };
11837
+ function createChalk(options) {
11838
+ return chalkFactory(options);
12069
11839
  }
12070
- function envForceColor() {
12071
- if ("FORCE_COLOR" in env2) {
12072
- if (env2.FORCE_COLOR === "true") {
12073
- return 1;
12074
- }
12075
- if (env2.FORCE_COLOR === "false") {
12076
- return 0;
11840
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
11841
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
11842
+ styles2[styleName] = {
11843
+ get() {
11844
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
11845
+ Object.defineProperty(this, styleName, { value: builder });
11846
+ return builder;
12077
11847
  }
12078
- return env2.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env2.FORCE_COLOR, 10), 3);
12079
- }
12080
- }
12081
- function translateLevel(level) {
12082
- if (level === 0) {
12083
- return false;
12084
- }
12085
- return {
12086
- level,
12087
- hasBasic: true,
12088
- has256: level >= 2,
12089
- has16m: level >= 3
12090
11848
  };
12091
11849
  }
12092
- function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
12093
- const noFlagForceColor = envForceColor();
12094
- if (noFlagForceColor !== undefined) {
12095
- flagForceColor = noFlagForceColor;
12096
- }
12097
- const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
12098
- if (forceColor === 0) {
12099
- return 0;
11850
+ styles2.visible = {
11851
+ get() {
11852
+ const builder = createBuilder(this, this[STYLER], true);
11853
+ Object.defineProperty(this, "visible", { value: builder });
11854
+ return builder;
12100
11855
  }
12101
- if (sniffFlags) {
12102
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
12103
- return 3;
11856
+ };
11857
+ var getModelAnsi = (model, level, type, ...arguments_) => {
11858
+ if (model === "rgb") {
11859
+ if (level === "ansi16m") {
11860
+ return ansi_styles_default[type].ansi16m(...arguments_);
12104
11861
  }
12105
- if (hasFlag("color=256")) {
12106
- return 2;
11862
+ if (level === "ansi256") {
11863
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
12107
11864
  }
11865
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
12108
11866
  }
12109
- if ("TF_BUILD" in env2 && "AGENT_NAME" in env2) {
12110
- return 1;
12111
- }
12112
- if (haveStream && !streamIsTTY && forceColor === undefined) {
12113
- return 0;
12114
- }
12115
- const min = forceColor || 0;
12116
- if (env2.TERM === "dumb") {
12117
- return min;
11867
+ if (model === "hex") {
11868
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
12118
11869
  }
12119
- if (process2.platform === "win32") {
12120
- const osRelease = os.release().split(".");
12121
- if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
12122
- return Number(osRelease[2]) >= 14931 ? 3 : 2;
11870
+ return ansi_styles_default[type][model](...arguments_);
11871
+ };
11872
+ var usedModels = ["rgb", "hex", "ansi256"];
11873
+ for (const model of usedModels) {
11874
+ styles2[model] = {
11875
+ get() {
11876
+ const { level } = this;
11877
+ return function(...arguments_) {
11878
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
11879
+ return createBuilder(this, styler, this[IS_EMPTY]);
11880
+ };
12123
11881
  }
12124
- return 1;
12125
- }
12126
- if ("CI" in env2) {
12127
- if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env2))) {
12128
- return 3;
11882
+ };
11883
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
11884
+ styles2[bgModel] = {
11885
+ get() {
11886
+ const { level } = this;
11887
+ return function(...arguments_) {
11888
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
11889
+ return createBuilder(this, styler, this[IS_EMPTY]);
11890
+ };
12129
11891
  }
12130
- if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env2)) || env2.CI_NAME === "codeship") {
12131
- return 1;
12132
- }
12133
- return min;
12134
- }
12135
- if ("TEAMCITY_VERSION" in env2) {
12136
- return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env2.TEAMCITY_VERSION) ? 1 : 0;
12137
- }
12138
- if (env2.COLORTERM === "truecolor") {
12139
- return 3;
12140
- }
12141
- if (env2.TERM === "xterm-kitty") {
12142
- return 3;
12143
- }
12144
- if (env2.TERM === "xterm-ghostty") {
12145
- return 3;
12146
- }
12147
- if (env2.TERM === "wezterm") {
12148
- return 3;
12149
- }
12150
- if ("TERM_PROGRAM" in env2) {
12151
- const version2 = Number.parseInt((env2.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
12152
- switch (env2.TERM_PROGRAM) {
12153
- case "iTerm.app": {
12154
- return version2 >= 3 ? 3 : 2;
12155
- }
12156
- case "Apple_Terminal": {
12157
- return 2;
12158
- }
12159
- }
12160
- }
12161
- if (/-256(color)?$/i.test(env2.TERM)) {
12162
- return 2;
12163
- }
12164
- if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env2.TERM)) {
12165
- return 1;
12166
- }
12167
- if ("COLORTERM" in env2) {
12168
- return 1;
12169
- }
12170
- return min;
12171
- }
12172
- function createSupportsColor(stream, options = {}) {
12173
- const level = _supportsColor(stream, {
12174
- streamIsTTY: stream && stream.isTTY,
12175
- ...options
12176
- });
12177
- return translateLevel(level);
12178
- }
12179
- var supportsColor = {
12180
- stdout: createSupportsColor({ isTTY: tty2.isatty(1) }),
12181
- stderr: createSupportsColor({ isTTY: tty2.isatty(2) })
12182
- };
12183
- var supports_color_default = supportsColor;
12184
-
12185
- // ../../node_modules/chalk/source/utilities.js
12186
- function stringReplaceAll(string, substring, replacer) {
12187
- let index = string.indexOf(substring);
12188
- if (index === -1) {
12189
- return string;
12190
- }
12191
- const substringLength = substring.length;
12192
- let endIndex = 0;
12193
- let returnValue = "";
12194
- do {
12195
- returnValue += string.slice(endIndex, index) + substring + replacer;
12196
- endIndex = index + substringLength;
12197
- index = string.indexOf(substring, endIndex);
12198
- } while (index !== -1);
12199
- returnValue += string.slice(endIndex);
12200
- return returnValue;
12201
- }
12202
- function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
12203
- let endIndex = 0;
12204
- let returnValue = "";
12205
- do {
12206
- const gotCR = string[index - 1] === "\r";
12207
- returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
12208
- ` : `
12209
- `) + postfix;
12210
- endIndex = index + 1;
12211
- index = string.indexOf(`
12212
- `, endIndex);
12213
- } while (index !== -1);
12214
- returnValue += string.slice(endIndex);
12215
- return returnValue;
12216
- }
12217
-
12218
- // ../../node_modules/chalk/source/index.js
12219
- var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
12220
- var GENERATOR = Symbol("GENERATOR");
12221
- var STYLER = Symbol("STYLER");
12222
- var IS_EMPTY = Symbol("IS_EMPTY");
12223
- var levelMapping = [
12224
- "ansi",
12225
- "ansi",
12226
- "ansi256",
12227
- "ansi16m"
12228
- ];
12229
- var styles2 = Object.create(null);
12230
- var applyOptions = (object, options = {}) => {
12231
- if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
12232
- throw new Error("The `level` option should be an integer from 0 to 3");
12233
- }
12234
- const colorLevel = stdoutColor ? stdoutColor.level : 0;
12235
- object.level = options.level === undefined ? colorLevel : options.level;
12236
- };
12237
- var chalkFactory = (options) => {
12238
- const chalk = (...strings) => strings.join(" ");
12239
- applyOptions(chalk, options);
12240
- Object.setPrototypeOf(chalk, createChalk.prototype);
12241
- return chalk;
12242
- };
12243
- function createChalk(options) {
12244
- return chalkFactory(options);
12245
- }
12246
- Object.setPrototypeOf(createChalk.prototype, Function.prototype);
12247
- for (const [styleName, style] of Object.entries(ansi_styles_default)) {
12248
- styles2[styleName] = {
12249
- get() {
12250
- const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
12251
- Object.defineProperty(this, styleName, { value: builder });
12252
- return builder;
12253
- }
12254
- };
12255
- }
12256
- styles2.visible = {
12257
- get() {
12258
- const builder = createBuilder(this, this[STYLER], true);
12259
- Object.defineProperty(this, "visible", { value: builder });
12260
- return builder;
12261
- }
12262
- };
12263
- var getModelAnsi = (model, level, type, ...arguments_) => {
12264
- if (model === "rgb") {
12265
- if (level === "ansi16m") {
12266
- return ansi_styles_default[type].ansi16m(...arguments_);
12267
- }
12268
- if (level === "ansi256") {
12269
- return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
12270
- }
12271
- return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
12272
- }
12273
- if (model === "hex") {
12274
- return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
12275
- }
12276
- return ansi_styles_default[type][model](...arguments_);
12277
- };
12278
- var usedModels = ["rgb", "hex", "ansi256"];
12279
- for (const model of usedModels) {
12280
- styles2[model] = {
12281
- get() {
12282
- const { level } = this;
12283
- return function(...arguments_) {
12284
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
12285
- return createBuilder(this, styler, this[IS_EMPTY]);
12286
- };
12287
- }
12288
- };
12289
- const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
12290
- styles2[bgModel] = {
12291
- get() {
12292
- const { level } = this;
12293
- return function(...arguments_) {
12294
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
12295
- return createBuilder(this, styler, this[IS_EMPTY]);
12296
- };
12297
- }
12298
- };
12299
- }
12300
- var proto = Object.defineProperties(() => {}, {
12301
- ...styles2,
12302
- level: {
12303
- enumerable: true,
12304
- get() {
12305
- return this[GENERATOR].level;
12306
- },
12307
- set(level) {
12308
- this[GENERATOR].level = level;
11892
+ };
11893
+ }
11894
+ var proto = Object.defineProperties(() => {}, {
11895
+ ...styles2,
11896
+ level: {
11897
+ enumerable: true,
11898
+ get() {
11899
+ return this[GENERATOR].level;
11900
+ },
11901
+ set(level) {
11902
+ this[GENERATOR].level = level;
12309
11903
  }
12310
11904
  }
12311
11905
  });
@@ -14524,748 +14118,1662 @@ function validate2(codePoint) {
14524
14118
  if (!Number.isSafeInteger(codePoint)) {
14525
14119
  throw new TypeError(`Expected a code point, got \`${typeof codePoint}\`.`);
14526
14120
  }
14527
- }
14528
- function eastAsianWidth2(codePoint, { ambiguousAsWide = false } = {}) {
14529
- validate2(codePoint);
14530
- if (isFullWidth2(codePoint) || isWide2(codePoint) || ambiguousAsWide && isAmbiguous2(codePoint)) {
14531
- return 2;
14121
+ }
14122
+ function eastAsianWidth2(codePoint, { ambiguousAsWide = false } = {}) {
14123
+ validate2(codePoint);
14124
+ if (isFullWidth2(codePoint) || isWide2(codePoint) || ambiguousAsWide && isAmbiguous2(codePoint)) {
14125
+ return 2;
14126
+ }
14127
+ return 1;
14128
+ }
14129
+
14130
+ // ../../node_modules/string-width/index.js
14131
+ var segmenter2 = new Intl.Segmenter;
14132
+ var zeroWidthClusterRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Format}|\p{Mark}|\p{Surrogate})+$/v;
14133
+ var leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v;
14134
+ var rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
14135
+ function baseVisible(segment) {
14136
+ return segment.replace(leadingNonPrintingRegex, "");
14137
+ }
14138
+ function isZeroWidthCluster(segment) {
14139
+ return zeroWidthClusterRegex.test(segment);
14140
+ }
14141
+ function trailingHalfwidthWidth(segment, eastAsianWidthOptions) {
14142
+ let extra = 0;
14143
+ if (segment.length > 1) {
14144
+ for (const char of segment.slice(1)) {
14145
+ if (char >= "＀" && char <= "￯") {
14146
+ extra += eastAsianWidth2(char.codePointAt(0), eastAsianWidthOptions);
14147
+ }
14148
+ }
14149
+ }
14150
+ return extra;
14151
+ }
14152
+ function stringWidth2(input, options = {}) {
14153
+ if (typeof input !== "string" || input.length === 0) {
14154
+ return 0;
14155
+ }
14156
+ const {
14157
+ ambiguousIsNarrow = true,
14158
+ countAnsiEscapeCodes = false
14159
+ } = options;
14160
+ let string = input;
14161
+ if (!countAnsiEscapeCodes) {
14162
+ string = stripAnsi3(string);
14163
+ }
14164
+ if (string.length === 0) {
14165
+ return 0;
14166
+ }
14167
+ let width = 0;
14168
+ const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
14169
+ for (const { segment } of segmenter2.segment(string)) {
14170
+ if (isZeroWidthCluster(segment)) {
14171
+ continue;
14172
+ }
14173
+ if (rgiEmojiRegex.test(segment)) {
14174
+ width += 2;
14175
+ continue;
14176
+ }
14177
+ const codePoint = baseVisible(segment).codePointAt(0);
14178
+ width += eastAsianWidth2(codePoint, eastAsianWidthOptions);
14179
+ width += trailingHalfwidthWidth(segment, eastAsianWidthOptions);
14180
+ }
14181
+ return width;
14182
+ }
14183
+
14184
+ // ../../node_modules/is-interactive/index.js
14185
+ function isInteractive({ stream = process.stdout } = {}) {
14186
+ return Boolean(stream && stream.isTTY && process.env.TERM !== "dumb" && !("CI" in process.env));
14187
+ }
14188
+
14189
+ // ../../node_modules/stdin-discarder/index.js
14190
+ import process7 from "node:process";
14191
+ var ASCII_ETX_CODE = 3;
14192
+
14193
+ class StdinDiscarder {
14194
+ #activeCount = 0;
14195
+ #stdin;
14196
+ #stdinWasPaused = false;
14197
+ #stdinWasRaw = false;
14198
+ #handleInputBound = (chunk) => {
14199
+ if (!chunk?.length) {
14200
+ return;
14201
+ }
14202
+ const code2 = typeof chunk === "string" ? chunk.codePointAt(0) : chunk[0];
14203
+ if (code2 === ASCII_ETX_CODE) {
14204
+ if (process7.listenerCount("SIGINT") > 0) {
14205
+ process7.emit("SIGINT");
14206
+ } else {
14207
+ process7.kill(process7.pid, "SIGINT");
14208
+ }
14209
+ }
14210
+ };
14211
+ start() {
14212
+ this.#activeCount++;
14213
+ if (this.#activeCount === 1) {
14214
+ this.#realStart();
14215
+ }
14216
+ }
14217
+ stop() {
14218
+ if (this.#activeCount === 0) {
14219
+ return;
14220
+ }
14221
+ if (--this.#activeCount === 0) {
14222
+ this.#realStop();
14223
+ }
14224
+ }
14225
+ #realStart() {
14226
+ const { stdin: stdin2 } = process7;
14227
+ if (process7.platform === "win32" || !stdin2?.isTTY || typeof stdin2.setRawMode !== "function") {
14228
+ this.#stdin = undefined;
14229
+ return;
14230
+ }
14231
+ this.#stdin = stdin2;
14232
+ this.#stdinWasPaused = stdin2.isPaused();
14233
+ this.#stdinWasRaw = Boolean(stdin2.isRaw);
14234
+ stdin2.setRawMode(true);
14235
+ stdin2.prependListener("data", this.#handleInputBound);
14236
+ if (this.#stdinWasPaused) {
14237
+ stdin2.resume();
14238
+ }
14239
+ }
14240
+ #realStop() {
14241
+ if (!this.#stdin) {
14242
+ return;
14243
+ }
14244
+ const stdin2 = this.#stdin;
14245
+ stdin2.off("data", this.#handleInputBound);
14246
+ if (stdin2.isTTY) {
14247
+ stdin2.setRawMode?.(this.#stdinWasRaw);
14248
+ }
14249
+ if (this.#stdinWasPaused) {
14250
+ stdin2.pause();
14251
+ }
14252
+ this.#stdin = undefined;
14253
+ this.#stdinWasPaused = false;
14254
+ this.#stdinWasRaw = false;
14255
+ }
14256
+ }
14257
+ var stdinDiscarder = new StdinDiscarder;
14258
+ var stdin_discarder_default = Object.freeze(stdinDiscarder);
14259
+
14260
+ // ../../node_modules/ora/index.js
14261
+ var RENDER_DEFERRAL_TIMEOUT = 200;
14262
+ var SYNCHRONIZED_OUTPUT_ENABLE = "\x1B[?2026h";
14263
+ var SYNCHRONIZED_OUTPUT_DISABLE = "\x1B[?2026l";
14264
+ var activeHooksPerStream = new Map;
14265
+
14266
+ class Ora {
14267
+ #linesToClear = 0;
14268
+ #frameIndex = -1;
14269
+ #lastFrameTime = 0;
14270
+ #options;
14271
+ #spinner;
14272
+ #stream;
14273
+ #id;
14274
+ #hookedStreams = new Map;
14275
+ #isInternalWrite = false;
14276
+ #drainHandler;
14277
+ #deferRenderTimer;
14278
+ #isDiscardingStdin = false;
14279
+ color;
14280
+ #internalWrite(fn) {
14281
+ this.#isInternalWrite = true;
14282
+ try {
14283
+ return fn();
14284
+ } finally {
14285
+ this.#isInternalWrite = false;
14286
+ }
14287
+ }
14288
+ #tryRender() {
14289
+ if (this.isSpinning) {
14290
+ this.render();
14291
+ }
14292
+ }
14293
+ #stringifyChunk(chunk, encoding) {
14294
+ if (chunk === undefined || chunk === null) {
14295
+ return "";
14296
+ }
14297
+ if (typeof chunk === "string") {
14298
+ return chunk;
14299
+ }
14300
+ if (Buffer.isBuffer(chunk) || ArrayBuffer.isView(chunk)) {
14301
+ const normalizedEncoding = typeof encoding === "string" && encoding && encoding !== "buffer" ? encoding : "utf8";
14302
+ return Buffer.from(chunk).toString(normalizedEncoding);
14303
+ }
14304
+ return String(chunk);
14305
+ }
14306
+ #chunkTerminatesLine(chunkString) {
14307
+ if (!chunkString) {
14308
+ return false;
14309
+ }
14310
+ const lastCharacter = chunkString.at(-1);
14311
+ return lastCharacter === `
14312
+ ` || lastCharacter === "\r";
14313
+ }
14314
+ #scheduleRenderDeferral() {
14315
+ if (this.#deferRenderTimer) {
14316
+ return;
14317
+ }
14318
+ this.#deferRenderTimer = setTimeout(() => {
14319
+ this.#deferRenderTimer = undefined;
14320
+ if (this.isSpinning) {
14321
+ this.#tryRender();
14322
+ }
14323
+ }, RENDER_DEFERRAL_TIMEOUT);
14324
+ if (typeof this.#deferRenderTimer?.unref === "function") {
14325
+ this.#deferRenderTimer.unref();
14326
+ }
14327
+ }
14328
+ #clearRenderDeferral() {
14329
+ if (this.#deferRenderTimer) {
14330
+ clearTimeout(this.#deferRenderTimer);
14331
+ this.#deferRenderTimer = undefined;
14332
+ }
14333
+ }
14334
+ #buildOutputLine(symbol, text, prefixText, suffixText) {
14335
+ const fullPrefixText = this.#getFullPrefixText(prefixText, " ");
14336
+ const separatorText = symbol ? " " : "";
14337
+ const fullText = typeof text === "string" ? separatorText + text : "";
14338
+ const fullSuffixText = this.#getFullSuffixText(suffixText, " ");
14339
+ return fullPrefixText + symbol + fullText + fullSuffixText;
14340
+ }
14341
+ constructor(options) {
14342
+ if (typeof options === "string") {
14343
+ options = {
14344
+ text: options
14345
+ };
14346
+ }
14347
+ this.#options = {
14348
+ color: "cyan",
14349
+ stream: process8.stderr,
14350
+ discardStdin: true,
14351
+ hideCursor: true,
14352
+ ...options
14353
+ };
14354
+ this.color = this.#options.color;
14355
+ this.#stream = this.#options.stream;
14356
+ if (typeof this.#options.isEnabled !== "boolean") {
14357
+ this.#options.isEnabled = isInteractive({ stream: this.#stream });
14358
+ }
14359
+ if (typeof this.#options.isSilent !== "boolean") {
14360
+ this.#options.isSilent = false;
14361
+ }
14362
+ const userInterval = this.#options.interval;
14363
+ this.spinner = this.#options.spinner;
14364
+ this.#options.interval = userInterval;
14365
+ this.text = this.#options.text;
14366
+ this.prefixText = this.#options.prefixText;
14367
+ this.suffixText = this.#options.suffixText;
14368
+ this.indent = this.#options.indent;
14369
+ if (process8.env.NODE_ENV === "test") {
14370
+ this._stream = this.#stream;
14371
+ this._isEnabled = this.#options.isEnabled;
14372
+ Object.defineProperty(this, "_linesToClear", {
14373
+ get() {
14374
+ return this.#linesToClear;
14375
+ },
14376
+ set(newValue) {
14377
+ this.#linesToClear = newValue;
14378
+ }
14379
+ });
14380
+ Object.defineProperty(this, "_frameIndex", {
14381
+ get() {
14382
+ return this.#frameIndex;
14383
+ }
14384
+ });
14385
+ Object.defineProperty(this, "_lineCount", {
14386
+ get() {
14387
+ const columns = this.#stream.columns ?? 80;
14388
+ const prefixText = typeof this.#options.prefixText === "function" ? "" : this.#options.prefixText;
14389
+ const suffixText = typeof this.#options.suffixText === "function" ? "" : this.#options.suffixText;
14390
+ const fullPrefixText = typeof prefixText === "string" && prefixText !== "" ? prefixText + " " : "";
14391
+ const fullSuffixText = typeof suffixText === "string" && suffixText !== "" ? " " + suffixText : "";
14392
+ const spinnerChar = "-";
14393
+ const fullText = " ".repeat(this.#options.indent) + fullPrefixText + spinnerChar + (typeof this.#options.text === "string" ? " " + this.#options.text : "") + fullSuffixText;
14394
+ return this.#computeLineCountFrom(fullText, columns);
14395
+ }
14396
+ });
14397
+ }
14398
+ }
14399
+ get indent() {
14400
+ return this.#options.indent;
14401
+ }
14402
+ set indent(indent = 0) {
14403
+ if (!(indent >= 0 && Number.isInteger(indent))) {
14404
+ throw new Error("The `indent` option must be an integer from 0 and up");
14405
+ }
14406
+ this.#options.indent = indent;
14407
+ }
14408
+ get interval() {
14409
+ return this.#options.interval ?? this.#spinner.interval ?? 100;
14410
+ }
14411
+ get spinner() {
14412
+ return this.#spinner;
14413
+ }
14414
+ set spinner(spinner) {
14415
+ this.#frameIndex = -1;
14416
+ this.#options.interval = undefined;
14417
+ if (typeof spinner === "object") {
14418
+ if (!Array.isArray(spinner.frames) || spinner.frames.length === 0 || spinner.frames.some((frame) => typeof frame !== "string")) {
14419
+ throw new Error("The given spinner must have a non-empty `frames` array of strings");
14420
+ }
14421
+ if (spinner.interval !== undefined && !(Number.isInteger(spinner.interval) && spinner.interval > 0)) {
14422
+ throw new Error("`spinner.interval` must be a positive integer if provided");
14423
+ }
14424
+ this.#spinner = spinner;
14425
+ } else if (!isUnicodeSupported2()) {
14426
+ this.#spinner = cli_spinners_default.line;
14427
+ } else if (spinner === undefined) {
14428
+ this.#spinner = cli_spinners_default.dots;
14429
+ } else if (spinner !== "default" && cli_spinners_default[spinner]) {
14430
+ this.#spinner = cli_spinners_default[spinner];
14431
+ } else {
14432
+ throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`);
14433
+ }
14434
+ }
14435
+ get text() {
14436
+ return this.#options.text;
14437
+ }
14438
+ set text(value = "") {
14439
+ this.#options.text = value;
14440
+ }
14441
+ get prefixText() {
14442
+ return this.#options.prefixText;
14532
14443
  }
14533
- return 1;
14534
- }
14535
-
14536
- // ../../node_modules/string-width/index.js
14537
- var segmenter2 = new Intl.Segmenter;
14538
- var zeroWidthClusterRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Format}|\p{Mark}|\p{Surrogate})+$/v;
14539
- var leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v;
14540
- var rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
14541
- function baseVisible(segment) {
14542
- return segment.replace(leadingNonPrintingRegex, "");
14543
- }
14544
- function isZeroWidthCluster(segment) {
14545
- return zeroWidthClusterRegex.test(segment);
14546
- }
14547
- function trailingHalfwidthWidth(segment, eastAsianWidthOptions) {
14548
- let extra = 0;
14549
- if (segment.length > 1) {
14550
- for (const char of segment.slice(1)) {
14551
- if (char >= "＀" && char <= "￯") {
14552
- extra += eastAsianWidth2(char.codePointAt(0), eastAsianWidthOptions);
14553
- }
14444
+ set prefixText(value = "") {
14445
+ this.#options.prefixText = value;
14446
+ }
14447
+ get suffixText() {
14448
+ return this.#options.suffixText;
14449
+ }
14450
+ set suffixText(value = "") {
14451
+ this.#options.suffixText = value;
14452
+ }
14453
+ get isSpinning() {
14454
+ return this.#id !== undefined;
14455
+ }
14456
+ #formatAffix(value, separator, placeBefore = false) {
14457
+ const resolved = typeof value === "function" ? value() : value;
14458
+ if (typeof resolved === "string" && resolved !== "") {
14459
+ return placeBefore ? separator + resolved : resolved + separator;
14554
14460
  }
14461
+ return "";
14555
14462
  }
14556
- return extra;
14557
- }
14558
- function stringWidth2(input, options = {}) {
14559
- if (typeof input !== "string" || input.length === 0) {
14560
- return 0;
14463
+ #getFullPrefixText(prefixText = this.#options.prefixText, postfix = " ") {
14464
+ return this.#formatAffix(prefixText, postfix, false);
14561
14465
  }
14562
- const {
14563
- ambiguousIsNarrow = true,
14564
- countAnsiEscapeCodes = false
14565
- } = options;
14566
- let string = input;
14567
- if (!countAnsiEscapeCodes) {
14568
- string = stripAnsi3(string);
14466
+ #getFullSuffixText(suffixText = this.#options.suffixText, prefix = " ") {
14467
+ return this.#formatAffix(suffixText, prefix, true);
14569
14468
  }
14570
- if (string.length === 0) {
14571
- return 0;
14469
+ #computeLineCountFrom(text, columns) {
14470
+ let count = 0;
14471
+ for (const line of stripVTControlCharacters(text).split(`
14472
+ `)) {
14473
+ count += Math.max(1, Math.ceil(stringWidth2(line) / columns));
14474
+ }
14475
+ return count;
14572
14476
  }
14573
- let width = 0;
14574
- const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
14575
- for (const { segment } of segmenter2.segment(string)) {
14576
- if (isZeroWidthCluster(segment)) {
14577
- continue;
14477
+ get isEnabled() {
14478
+ return this.#options.isEnabled && !this.#options.isSilent;
14479
+ }
14480
+ set isEnabled(value) {
14481
+ if (typeof value !== "boolean") {
14482
+ throw new TypeError("The `isEnabled` option must be a boolean");
14578
14483
  }
14579
- if (rgiEmojiRegex.test(segment)) {
14580
- width += 2;
14581
- continue;
14484
+ this.#options.isEnabled = value;
14485
+ }
14486
+ get isSilent() {
14487
+ return this.#options.isSilent;
14488
+ }
14489
+ set isSilent(value) {
14490
+ if (typeof value !== "boolean") {
14491
+ throw new TypeError("The `isSilent` option must be a boolean");
14582
14492
  }
14583
- const codePoint = baseVisible(segment).codePointAt(0);
14584
- width += eastAsianWidth2(codePoint, eastAsianWidthOptions);
14585
- width += trailingHalfwidthWidth(segment, eastAsianWidthOptions);
14493
+ this.#options.isSilent = value;
14586
14494
  }
14587
- return width;
14588
- }
14589
-
14590
- // ../../node_modules/is-interactive/index.js
14591
- function isInteractive({ stream = process.stdout } = {}) {
14592
- return Boolean(stream && stream.isTTY && process.env.TERM !== "dumb" && !("CI" in process.env));
14593
- }
14594
-
14595
- // ../../node_modules/stdin-discarder/index.js
14596
- import process7 from "node:process";
14597
- var ASCII_ETX_CODE = 3;
14598
-
14599
- class StdinDiscarder {
14600
- #activeCount = 0;
14601
- #stdin;
14602
- #stdinWasPaused = false;
14603
- #stdinWasRaw = false;
14604
- #handleInputBound = (chunk) => {
14605
- if (!chunk?.length) {
14606
- return;
14495
+ frame() {
14496
+ const now = Date.now();
14497
+ if (this.#frameIndex === -1 || now - this.#lastFrameTime >= this.interval) {
14498
+ this.#frameIndex = (this.#frameIndex + 1) % this.#spinner.frames.length;
14499
+ this.#lastFrameTime = now;
14607
14500
  }
14608
- const code2 = typeof chunk === "string" ? chunk.codePointAt(0) : chunk[0];
14609
- if (code2 === ASCII_ETX_CODE) {
14610
- if (process7.listenerCount("SIGINT") > 0) {
14611
- process7.emit("SIGINT");
14612
- } else {
14613
- process7.kill(process7.pid, "SIGINT");
14614
- }
14501
+ const { frames } = this.#spinner;
14502
+ let frame = frames[this.#frameIndex];
14503
+ if (this.color) {
14504
+ frame = source_default[this.color](frame);
14615
14505
  }
14616
- };
14617
- start() {
14618
- this.#activeCount++;
14619
- if (this.#activeCount === 1) {
14620
- this.#realStart();
14506
+ const fullPrefixText = this.#getFullPrefixText(this.#options.prefixText, " ");
14507
+ const fullText = typeof this.text === "string" ? " " + this.text : "";
14508
+ const fullSuffixText = this.#getFullSuffixText(this.#options.suffixText, " ");
14509
+ return fullPrefixText + frame + fullText + fullSuffixText;
14510
+ }
14511
+ clear() {
14512
+ if (!this.isEnabled || !this.#stream.isTTY) {
14513
+ return this;
14621
14514
  }
14515
+ this.#internalWrite(() => {
14516
+ this.#stream.cursorTo(0);
14517
+ for (let index = 0;index < this.#linesToClear; index++) {
14518
+ if (index > 0) {
14519
+ this.#stream.moveCursor(0, -1);
14520
+ }
14521
+ this.#stream.clearLine(1);
14522
+ }
14523
+ if (this.#options.indent) {
14524
+ this.#stream.cursorTo(this.#options.indent);
14525
+ }
14526
+ });
14527
+ this.#linesToClear = 0;
14528
+ return this;
14622
14529
  }
14623
- stop() {
14624
- if (this.#activeCount === 0) {
14530
+ #hookStream(stream) {
14531
+ if (!stream || this.#hookedStreams.has(stream) || !stream.isTTY || typeof stream.write !== "function") {
14625
14532
  return;
14626
14533
  }
14627
- if (--this.#activeCount === 0) {
14628
- this.#realStop();
14534
+ if (activeHooksPerStream.has(stream)) {
14535
+ console.warn("[ora] Multiple concurrent spinners detected. This may cause visual corruption. Use one spinner at a time.");
14629
14536
  }
14537
+ const originalWrite = stream.write;
14538
+ this.#hookedStreams.set(stream, originalWrite);
14539
+ activeHooksPerStream.set(stream, this);
14540
+ stream.write = (chunk, encoding, callback) => this.#hookedWrite(stream, originalWrite, chunk, encoding, callback);
14630
14541
  }
14631
- #realStart() {
14632
- const { stdin: stdin2 } = process7;
14633
- if (process7.platform === "win32" || !stdin2?.isTTY || typeof stdin2.setRawMode !== "function") {
14634
- this.#stdin = undefined;
14542
+ #installHook() {
14543
+ if (!this.isEnabled || this.#hookedStreams.size > 0) {
14635
14544
  return;
14636
14545
  }
14637
- this.#stdin = stdin2;
14638
- this.#stdinWasPaused = stdin2.isPaused();
14639
- this.#stdinWasRaw = Boolean(stdin2.isRaw);
14640
- stdin2.setRawMode(true);
14641
- stdin2.prependListener("data", this.#handleInputBound);
14642
- if (this.#stdinWasPaused) {
14643
- stdin2.resume();
14546
+ const streamsToHook = new Set([this.#stream, process8.stdout, process8.stderr]);
14547
+ for (const stream of streamsToHook) {
14548
+ this.#hookStream(stream);
14644
14549
  }
14645
14550
  }
14646
- #realStop() {
14647
- if (!this.#stdin) {
14648
- return;
14551
+ #uninstallHook() {
14552
+ for (const [stream, originalWrite] of this.#hookedStreams) {
14553
+ stream.write = originalWrite;
14554
+ if (activeHooksPerStream.get(stream) === this) {
14555
+ activeHooksPerStream.delete(stream);
14556
+ }
14649
14557
  }
14650
- const stdin2 = this.#stdin;
14651
- stdin2.off("data", this.#handleInputBound);
14652
- if (stdin2.isTTY) {
14653
- stdin2.setRawMode?.(this.#stdinWasRaw);
14558
+ this.#hookedStreams.clear();
14559
+ }
14560
+ #hookedWrite(stream, originalWrite, chunk, encoding, callback) {
14561
+ if (typeof encoding === "function") {
14562
+ callback = encoding;
14563
+ encoding = undefined;
14654
14564
  }
14655
- if (this.#stdinWasPaused) {
14656
- stdin2.pause();
14565
+ if (this.#isInternalWrite) {
14566
+ return originalWrite.call(stream, chunk, encoding, callback);
14657
14567
  }
14658
- this.#stdin = undefined;
14659
- this.#stdinWasPaused = false;
14660
- this.#stdinWasRaw = false;
14568
+ this.clear();
14569
+ const chunkString = this.#stringifyChunk(chunk, encoding);
14570
+ const chunkTerminatesLine = this.#chunkTerminatesLine(chunkString);
14571
+ const writeResult = originalWrite.call(stream, chunk, encoding, callback);
14572
+ if (chunkTerminatesLine) {
14573
+ this.#clearRenderDeferral();
14574
+ } else if (chunkString.length > 0) {
14575
+ this.#scheduleRenderDeferral();
14576
+ }
14577
+ if (this.isSpinning && !this.#deferRenderTimer) {
14578
+ this.render();
14579
+ }
14580
+ return writeResult;
14661
14581
  }
14662
- }
14663
- var stdinDiscarder = new StdinDiscarder;
14664
- var stdin_discarder_default = Object.freeze(stdinDiscarder);
14665
-
14666
- // ../../node_modules/ora/index.js
14667
- var RENDER_DEFERRAL_TIMEOUT = 200;
14668
- var SYNCHRONIZED_OUTPUT_ENABLE = "\x1B[?2026h";
14669
- var SYNCHRONIZED_OUTPUT_DISABLE = "\x1B[?2026l";
14670
- var activeHooksPerStream = new Map;
14671
-
14672
- class Ora {
14673
- #linesToClear = 0;
14674
- #frameIndex = -1;
14675
- #lastFrameTime = 0;
14676
- #options;
14677
- #spinner;
14678
- #stream;
14679
- #id;
14680
- #hookedStreams = new Map;
14681
- #isInternalWrite = false;
14682
- #drainHandler;
14683
- #deferRenderTimer;
14684
- #isDiscardingStdin = false;
14685
- color;
14686
- #internalWrite(fn) {
14687
- this.#isInternalWrite = true;
14582
+ render() {
14583
+ if (!this.isEnabled || this.#drainHandler || this.#deferRenderTimer) {
14584
+ return this;
14585
+ }
14586
+ const useSynchronizedOutput = this.#stream.isTTY;
14587
+ let shouldDisableSynchronizedOutput = false;
14688
14588
  try {
14689
- return fn();
14589
+ if (useSynchronizedOutput) {
14590
+ this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_ENABLE));
14591
+ shouldDisableSynchronizedOutput = true;
14592
+ }
14593
+ this.clear();
14594
+ let frameContent = this.frame();
14595
+ const columns = this.#stream.columns ?? 80;
14596
+ const actualLineCount = this.#computeLineCountFrom(frameContent, columns);
14597
+ const consoleHeight = this.#stream.rows;
14598
+ if (consoleHeight && consoleHeight > 1 && actualLineCount > consoleHeight) {
14599
+ const lines = frameContent.split(`
14600
+ `);
14601
+ const maxLines = consoleHeight - 1;
14602
+ frameContent = [...lines.slice(0, maxLines), "... (content truncated to fit terminal)"].join(`
14603
+ `);
14604
+ }
14605
+ const canContinue = this.#internalWrite(() => this.#stream.write(frameContent));
14606
+ if (canContinue === false && this.#stream.isTTY) {
14607
+ this.#drainHandler = () => {
14608
+ this.#drainHandler = undefined;
14609
+ this.#tryRender();
14610
+ };
14611
+ this.#stream.once("drain", this.#drainHandler);
14612
+ }
14613
+ this.#linesToClear = this.#computeLineCountFrom(frameContent, columns);
14690
14614
  } finally {
14691
- this.#isInternalWrite = false;
14615
+ if (shouldDisableSynchronizedOutput) {
14616
+ this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_DISABLE));
14617
+ }
14692
14618
  }
14619
+ return this;
14693
14620
  }
14694
- #tryRender() {
14695
- if (this.isSpinning) {
14696
- this.render();
14621
+ start(text) {
14622
+ if (text) {
14623
+ this.text = text;
14697
14624
  }
14698
- }
14699
- #stringifyChunk(chunk, encoding) {
14700
- if (chunk === undefined || chunk === null) {
14701
- return "";
14625
+ if (this.isSilent) {
14626
+ return this;
14702
14627
  }
14703
- if (typeof chunk === "string") {
14704
- return chunk;
14628
+ if (!this.isEnabled) {
14629
+ const symbol = this.text ? "-" : "";
14630
+ const line = " ".repeat(this.#options.indent) + this.#buildOutputLine(symbol, this.text, this.#options.prefixText, this.#options.suffixText);
14631
+ if (line.trim() !== "") {
14632
+ this.#internalWrite(() => this.#stream.write(line + `
14633
+ `));
14634
+ }
14635
+ return this;
14705
14636
  }
14706
- if (Buffer.isBuffer(chunk) || ArrayBuffer.isView(chunk)) {
14707
- const normalizedEncoding = typeof encoding === "string" && encoding && encoding !== "buffer" ? encoding : "utf8";
14708
- return Buffer.from(chunk).toString(normalizedEncoding);
14637
+ if (this.isSpinning) {
14638
+ return this;
14709
14639
  }
14710
- return String(chunk);
14711
- }
14712
- #chunkTerminatesLine(chunkString) {
14713
- if (!chunkString) {
14714
- return false;
14640
+ if (this.#options.hideCursor) {
14641
+ cli_cursor_default.hide(this.#stream);
14715
14642
  }
14716
- const lastCharacter = chunkString.at(-1);
14717
- return lastCharacter === `
14718
- ` || lastCharacter === "\r";
14643
+ if (this.#options.discardStdin && process8.stdin.isTTY) {
14644
+ stdin_discarder_default.start();
14645
+ this.#isDiscardingStdin = true;
14646
+ }
14647
+ this.#installHook();
14648
+ this.render();
14649
+ this.#id = setInterval(this.render.bind(this), this.interval);
14650
+ return this;
14719
14651
  }
14720
- #scheduleRenderDeferral() {
14721
- if (this.#deferRenderTimer) {
14722
- return;
14652
+ stop() {
14653
+ clearInterval(this.#id);
14654
+ this.#id = undefined;
14655
+ this.#frameIndex = -1;
14656
+ this.#lastFrameTime = 0;
14657
+ this.#clearRenderDeferral();
14658
+ this.#uninstallHook();
14659
+ if (this.#drainHandler) {
14660
+ this.#stream.removeListener("drain", this.#drainHandler);
14661
+ this.#drainHandler = undefined;
14723
14662
  }
14724
- this.#deferRenderTimer = setTimeout(() => {
14725
- this.#deferRenderTimer = undefined;
14726
- if (this.isSpinning) {
14727
- this.#tryRender();
14663
+ if (this.isEnabled) {
14664
+ this.clear();
14665
+ if (this.#options.hideCursor) {
14666
+ cli_cursor_default.show(this.#stream);
14728
14667
  }
14729
- }, RENDER_DEFERRAL_TIMEOUT);
14730
- if (typeof this.#deferRenderTimer?.unref === "function") {
14731
- this.#deferRenderTimer.unref();
14732
14668
  }
14733
- }
14734
- #clearRenderDeferral() {
14735
- if (this.#deferRenderTimer) {
14736
- clearTimeout(this.#deferRenderTimer);
14737
- this.#deferRenderTimer = undefined;
14669
+ if (this.#isDiscardingStdin) {
14670
+ this.#isDiscardingStdin = false;
14671
+ stdin_discarder_default.stop();
14738
14672
  }
14673
+ return this;
14739
14674
  }
14740
- #buildOutputLine(symbol, text, prefixText, suffixText) {
14741
- const fullPrefixText = this.#getFullPrefixText(prefixText, " ");
14742
- const separatorText = symbol ? " " : "";
14743
- const fullText = typeof text === "string" ? separatorText + text : "";
14744
- const fullSuffixText = this.#getFullSuffixText(suffixText, " ");
14745
- return fullPrefixText + symbol + fullText + fullSuffixText;
14675
+ succeed(text) {
14676
+ return this.stopAndPersist({ symbol: exports_symbols.success, text });
14746
14677
  }
14747
- constructor(options) {
14748
- if (typeof options === "string") {
14749
- options = {
14750
- text: options
14751
- };
14752
- }
14753
- this.#options = {
14754
- color: "cyan",
14755
- stream: process8.stderr,
14756
- discardStdin: true,
14757
- hideCursor: true,
14758
- ...options
14759
- };
14760
- this.color = this.#options.color;
14761
- this.#stream = this.#options.stream;
14762
- if (typeof this.#options.isEnabled !== "boolean") {
14763
- this.#options.isEnabled = isInteractive({ stream: this.#stream });
14764
- }
14765
- if (typeof this.#options.isSilent !== "boolean") {
14766
- this.#options.isSilent = false;
14767
- }
14768
- const userInterval = this.#options.interval;
14769
- this.spinner = this.#options.spinner;
14770
- this.#options.interval = userInterval;
14771
- this.text = this.#options.text;
14772
- this.prefixText = this.#options.prefixText;
14773
- this.suffixText = this.#options.suffixText;
14774
- this.indent = this.#options.indent;
14775
- if (process8.env.NODE_ENV === "test") {
14776
- this._stream = this.#stream;
14777
- this._isEnabled = this.#options.isEnabled;
14778
- Object.defineProperty(this, "_linesToClear", {
14779
- get() {
14780
- return this.#linesToClear;
14781
- },
14782
- set(newValue) {
14783
- this.#linesToClear = newValue;
14784
- }
14785
- });
14786
- Object.defineProperty(this, "_frameIndex", {
14787
- get() {
14788
- return this.#frameIndex;
14789
- }
14790
- });
14791
- Object.defineProperty(this, "_lineCount", {
14792
- get() {
14793
- const columns = this.#stream.columns ?? 80;
14794
- const prefixText = typeof this.#options.prefixText === "function" ? "" : this.#options.prefixText;
14795
- const suffixText = typeof this.#options.suffixText === "function" ? "" : this.#options.suffixText;
14796
- const fullPrefixText = typeof prefixText === "string" && prefixText !== "" ? prefixText + " " : "";
14797
- const fullSuffixText = typeof suffixText === "string" && suffixText !== "" ? " " + suffixText : "";
14798
- const spinnerChar = "-";
14799
- const fullText = " ".repeat(this.#options.indent) + fullPrefixText + spinnerChar + (typeof this.#options.text === "string" ? " " + this.#options.text : "") + fullSuffixText;
14800
- return this.#computeLineCountFrom(fullText, columns);
14801
- }
14802
- });
14803
- }
14678
+ fail(text) {
14679
+ return this.stopAndPersist({ symbol: exports_symbols.error, text });
14804
14680
  }
14805
- get indent() {
14806
- return this.#options.indent;
14681
+ warn(text) {
14682
+ return this.stopAndPersist({ symbol: exports_symbols.warning, text });
14807
14683
  }
14808
- set indent(indent = 0) {
14809
- if (!(indent >= 0 && Number.isInteger(indent))) {
14810
- throw new Error("The `indent` option must be an integer from 0 and up");
14684
+ info(text) {
14685
+ return this.stopAndPersist({ symbol: exports_symbols.info, text });
14686
+ }
14687
+ stopAndPersist(options = {}) {
14688
+ if (this.isSilent) {
14689
+ return this;
14690
+ }
14691
+ const symbol = options.symbol ?? " ";
14692
+ const text = options.text ?? this.text;
14693
+ const prefixText = options.prefixText ?? this.#options.prefixText;
14694
+ const suffixText = options.suffixText ?? this.#options.suffixText;
14695
+ const textToWrite = this.#buildOutputLine(symbol, text, prefixText, suffixText) + `
14696
+ `;
14697
+ this.stop();
14698
+ this.#internalWrite(() => this.#stream.write(textToWrite));
14699
+ return this;
14700
+ }
14701
+ }
14702
+ function ora(options) {
14703
+ return new Ora(options);
14704
+ }
14705
+
14706
+ // src/lib/ui.ts
14707
+ var import_picocolors = __toESM(require_picocolors(), 1);
14708
+ import * as readline from "readline";
14709
+ var colors2 = {
14710
+ primary: import_picocolors.default.cyan,
14711
+ secondary: import_picocolors.default.blue,
14712
+ accent: import_picocolors.default.magenta,
14713
+ success: import_picocolors.default.green,
14714
+ warning: import_picocolors.default.yellow,
14715
+ error: import_picocolors.default.red,
14716
+ info: import_picocolors.default.blue,
14717
+ dim: import_picocolors.default.dim,
14718
+ bold: import_picocolors.default.bold,
14719
+ italic: import_picocolors.default.italic,
14720
+ excellent: import_picocolors.default.green,
14721
+ good: import_picocolors.default.cyan,
14722
+ moderate: import_picocolors.default.yellow,
14723
+ poor: import_picocolors.default.red,
14724
+ highlight: (text) => import_picocolors.default.bold(import_picocolors.default.cyan(text)),
14725
+ muted: (text) => import_picocolors.default.dim(import_picocolors.default.gray(text)),
14726
+ link: (text) => import_picocolors.default.underline(import_picocolors.default.blue(text))
14727
+ };
14728
+ function createSpinner(text) {
14729
+ return ora({
14730
+ text,
14731
+ spinner: "dots",
14732
+ color: "cyan"
14733
+ });
14734
+ }
14735
+ function formatTier(tier) {
14736
+ const tierColors = {
14737
+ elite: (s2) => import_picocolors.default.bold(import_picocolors.default.magenta(s2)),
14738
+ expert: (s2) => import_picocolors.default.bold(import_picocolors.default.cyan(s2)),
14739
+ advanced: (s2) => import_picocolors.default.bold(import_picocolors.default.green(s2)),
14740
+ proficient: (s2) => import_picocolors.default.blue(s2),
14741
+ intermediate: (s2) => import_picocolors.default.yellow(s2),
14742
+ developing: (s2) => import_picocolors.default.dim(s2)
14743
+ };
14744
+ const colorFn = tierColors[tier.toLowerCase()] ?? colors2.dim;
14745
+ return colorFn(tier.charAt(0).toUpperCase() + tier.slice(1));
14746
+ }
14747
+ var icons = {
14748
+ success: colors2.success("✔"),
14749
+ error: colors2.error("✖"),
14750
+ warning: colors2.warning("⚠"),
14751
+ info: colors2.info("ℹ"),
14752
+ arrow: colors2.primary("→"),
14753
+ bullet: colors2.dim("•"),
14754
+ star: colors2.warning("★"),
14755
+ check: colors2.success("✓"),
14756
+ cross: colors2.error("✗"),
14757
+ pending: colors2.dim("○"),
14758
+ lightning: "⚡",
14759
+ fire: "\uD83D\uDD25",
14760
+ rocket: "\uD83D\uDE80",
14761
+ chart: "\uD83D\uDCCA",
14762
+ money: "\uD83D\uDCB0",
14763
+ brain: "\uD83E\uDDE0",
14764
+ target: "\uD83C\uDFAF"
14765
+ };
14766
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
14767
+ function clearLine2() {
14768
+ readline.clearLine(process.stdout, 0);
14769
+ readline.cursorTo(process.stdout, 0);
14770
+ }
14771
+ function write(text) {
14772
+ process.stdout.write(text);
14773
+ }
14774
+ async function animateProgressBar(label, score, options = {}) {
14775
+ const { width = 20, charDelay = 40, labelWidth = 20 } = options;
14776
+ const filled = Math.round(score / 100 * width);
14777
+ let barColor = colors2.excellent;
14778
+ if (score < 40)
14779
+ barColor = colors2.poor;
14780
+ else if (score < 60)
14781
+ barColor = colors2.moderate;
14782
+ else if (score < 75)
14783
+ barColor = colors2.good;
14784
+ const paddedLabel = label.padEnd(labelWidth);
14785
+ const scoreStr = `${score}/100`.padStart(7);
14786
+ write(` ${colors2.dim(paddedLabel)} ${colors2.dim(scoreStr)} `);
14787
+ for (let i3 = 0;i3 < width; i3++) {
14788
+ if (i3 < filled) {
14789
+ write(barColor("█"));
14790
+ } else {
14791
+ write(colors2.dim("░"));
14811
14792
  }
14812
- this.#options.indent = indent;
14813
- }
14814
- get interval() {
14815
- return this.#options.interval ?? this.#spinner.interval ?? 100;
14793
+ await sleep(charDelay);
14816
14794
  }
14817
- get spinner() {
14818
- return this.#spinner;
14795
+ console.log();
14796
+ }
14797
+ async function thinkingStep(thinkingText, duration = 800, successText) {
14798
+ const spinner = createSpinner(thinkingText);
14799
+ spinner.start();
14800
+ await sleep(duration);
14801
+ spinner.succeed(successText ?? thinkingText);
14802
+ }
14803
+ async function revealDiscovery(icon, text, delay2 = 300) {
14804
+ await sleep(delay2);
14805
+ console.log(` ${icon} ${text}`);
14806
+ }
14807
+ async function showPhaseHeader(phase, total, title, delay2 = 400) {
14808
+ await sleep(delay2);
14809
+ console.log();
14810
+ console.log(` ${colors2.dim(`Phase ${phase}/${total}:`)} ${colors2.bold(colors2.primary(title))}`);
14811
+ console.log(colors2.dim(" " + "─".repeat(40)));
14812
+ }
14813
+ async function animateScoreReveal(score, tier, options = {}) {
14814
+ const { countDuration = 1000, barDelay = 50 } = options;
14815
+ const scoreColor = score >= 80 ? colors2.excellent : score >= 60 ? colors2.good : score >= 40 ? colors2.moderate : colors2.poor;
14816
+ console.log();
14817
+ console.log(colors2.dim(" " + "═".repeat(45)));
14818
+ console.log();
14819
+ const steps = 30;
14820
+ const stepDuration = countDuration / steps;
14821
+ for (let i3 = 1;i3 <= steps; i3++) {
14822
+ const current = Math.round(i3 / steps * score);
14823
+ clearLine2();
14824
+ write(` ${colors2.bold("Your Score:")} ${scoreColor(colors2.bold(current.toString()))}/100`);
14825
+ await sleep(stepDuration);
14819
14826
  }
14820
- set spinner(spinner) {
14821
- this.#frameIndex = -1;
14822
- this.#options.interval = undefined;
14823
- if (typeof spinner === "object") {
14824
- if (!Array.isArray(spinner.frames) || spinner.frames.length === 0 || spinner.frames.some((frame) => typeof frame !== "string")) {
14825
- throw new Error("The given spinner must have a non-empty `frames` array of strings");
14826
- }
14827
- if (spinner.interval !== undefined && !(Number.isInteger(spinner.interval) && spinner.interval > 0)) {
14828
- throw new Error("`spinner.interval` must be a positive integer if provided");
14829
- }
14830
- this.#spinner = spinner;
14831
- } else if (!isUnicodeSupported2()) {
14832
- this.#spinner = cli_spinners_default.line;
14833
- } else if (spinner === undefined) {
14834
- this.#spinner = cli_spinners_default.dots;
14835
- } else if (spinner !== "default" && cli_spinners_default[spinner]) {
14836
- this.#spinner = cli_spinners_default[spinner];
14827
+ write(` ${colors2.dim("(")}${formatTier(tier)}${colors2.dim(")")}`);
14828
+ console.log();
14829
+ console.log();
14830
+ const barWidth = 30;
14831
+ const filled = Math.round(score / 100 * barWidth);
14832
+ write(" ");
14833
+ for (let i3 = 0;i3 < barWidth; i3++) {
14834
+ if (i3 < filled) {
14835
+ write(scoreColor(""));
14837
14836
  } else {
14838
- throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`);
14837
+ write(colors2.dim("░"));
14839
14838
  }
14839
+ await sleep(barDelay);
14840
14840
  }
14841
- get text() {
14842
- return this.#options.text;
14843
- }
14844
- set text(value = "") {
14845
- this.#options.text = value;
14846
- }
14847
- get prefixText() {
14848
- return this.#options.prefixText;
14849
- }
14850
- set prefixText(value = "") {
14851
- this.#options.prefixText = value;
14852
- }
14853
- get suffixText() {
14854
- return this.#options.suffixText;
14855
- }
14856
- set suffixText(value = "") {
14857
- this.#options.suffixText = value;
14858
- }
14859
- get isSpinning() {
14860
- return this.#id !== undefined;
14861
- }
14862
- #formatAffix(value, separator, placeBefore = false) {
14863
- const resolved = typeof value === "function" ? value() : value;
14864
- if (typeof resolved === "string" && resolved !== "") {
14865
- return placeBefore ? separator + resolved : resolved + separator;
14841
+ console.log();
14842
+ console.log();
14843
+ console.log(colors2.dim(" " + "═".repeat(45)));
14844
+ }
14845
+ async function showFinding(type, label, value, unit = "", delay2 = 250) {
14846
+ await sleep(delay2);
14847
+ const iconMap = {
14848
+ warning: icons.warning,
14849
+ error: icons.error,
14850
+ info: icons.info,
14851
+ success: icons.success
14852
+ };
14853
+ const colorMap = {
14854
+ warning: colors2.warning,
14855
+ error: colors2.error,
14856
+ info: colors2.info,
14857
+ success: colors2.success
14858
+ };
14859
+ const icon = iconMap[type];
14860
+ const valueColor = colorMap[type];
14861
+ console.log(` ${icon} ${label}: ${valueColor(value.toString())}${unit}`);
14862
+ }
14863
+
14864
+ // src/lib/frustration-detector.ts
14865
+ var ROOT_CAUSE_PATTERNS = {
14866
+ hallucination: {
14867
+ patterns: [
14868
+ /doesn'?t exist/i,
14869
+ /not a valid/i,
14870
+ /no such (file|method|function|class|property|module)/i,
14871
+ /wrong api/i,
14872
+ /outdated/i,
14873
+ /deprecated/i,
14874
+ /that'?s not (how|the)/i,
14875
+ /method doesn'?t/i,
14876
+ /property doesn'?t/i,
14877
+ /hallucinating/i,
14878
+ /made up/i,
14879
+ /invented/i,
14880
+ /that method/i,
14881
+ /wrong syntax/i
14882
+ ],
14883
+ description: "AI hallucinated non-existent API/method/syntax",
14884
+ fix: {
14885
+ tool: "Context7",
14886
+ description: "Provides up-to-date library documentation to prevent hallucinations",
14887
+ installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
14888
+ estimatedImprovementPercent: 70
14866
14889
  }
14867
- return "";
14868
- }
14869
- #getFullPrefixText(prefixText = this.#options.prefixText, postfix = " ") {
14870
- return this.#formatAffix(prefixText, postfix, false);
14871
- }
14872
- #getFullSuffixText(suffixText = this.#options.suffixText, prefix = " ") {
14873
- return this.#formatAffix(suffixText, prefix, true);
14874
- }
14875
- #computeLineCountFrom(text, columns) {
14876
- let count = 0;
14877
- for (const line of stripVTControlCharacters(text).split(`
14878
- `)) {
14879
- count += Math.max(1, Math.ceil(stringWidth2(line) / columns));
14890
+ },
14891
+ missing_docs: {
14892
+ patterns: [
14893
+ /how do (i|you)/i,
14894
+ /what'?s the (syntax|api|method|way)/i,
14895
+ /documentation/i,
14896
+ /check the docs/i,
14897
+ /look up/i,
14898
+ /find the right/i,
14899
+ /correct (api|method|syntax)/i,
14900
+ /which (api|method|function)/i
14901
+ ],
14902
+ description: "Needed documentation that AI didn't have access to",
14903
+ fix: {
14904
+ tool: "Context7",
14905
+ description: "Live documentation lookup prevents outdated information",
14906
+ installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
14907
+ estimatedImprovementPercent: 65
14908
+ }
14909
+ },
14910
+ context_loss: {
14911
+ patterns: [
14912
+ /i already told you/i,
14913
+ /as i (said|mentioned)/i,
14914
+ /we discussed/i,
14915
+ /you forgot/i,
14916
+ /remember/i,
14917
+ /i explained/i,
14918
+ /sessions ago/i,
14919
+ /previously/i,
14920
+ /earlier/i,
14921
+ /context/i,
14922
+ /last time/i,
14923
+ /before/i
14924
+ ],
14925
+ description: "AI forgot previously established context",
14926
+ fix: {
14927
+ tool: "Supermemory",
14928
+ description: "Persistent memory across sessions prevents context loss",
14929
+ installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest",
14930
+ estimatedImprovementPercent: 80
14931
+ }
14932
+ },
14933
+ unclear_prompt: {
14934
+ patterns: [
14935
+ /what do you mean/i,
14936
+ /clarify/i,
14937
+ /specify/i,
14938
+ /more (details|context|information)/i,
14939
+ /not sure what/i,
14940
+ /which (file|one|version)/i,
14941
+ /be more specific/i
14942
+ ],
14943
+ description: "Prompt was unclear, leading to misunderstanding",
14944
+ fix: {
14945
+ tool: "Writing Plans Skill",
14946
+ description: "Helps write clearer, more structured prompts",
14947
+ installCommand: "npx skills add obra/superpowers@writing-plans --yes",
14948
+ estimatedImprovementPercent: 50
14949
+ }
14950
+ },
14951
+ tool_failure: {
14952
+ patterns: [
14953
+ /tool failed/i,
14954
+ /mcp error/i,
14955
+ /error executing/i,
14956
+ /command failed/i,
14957
+ /couldn'?t run/i,
14958
+ /failed to/i,
14959
+ /error:/i,
14960
+ /exception/i
14961
+ ],
14962
+ description: "Tool or MCP server failed to execute",
14963
+ fix: {
14964
+ tool: "MCP Doctor",
14965
+ description: "Fix MCP configuration issues",
14966
+ installCommand: "claude doctor && claude mcp list",
14967
+ estimatedImprovementPercent: 40
14968
+ }
14969
+ },
14970
+ undo_loop: {
14971
+ patterns: [
14972
+ /undo/i,
14973
+ /revert/i,
14974
+ /go back/i,
14975
+ /try again/i,
14976
+ /start over/i,
14977
+ /that'?s wrong/i,
14978
+ /not what i wanted/i,
14979
+ /roll back/i,
14980
+ /put it back/i,
14981
+ /restore/i,
14982
+ /wrong/i
14983
+ ],
14984
+ description: "Got stuck in undo/redo loop from incorrect changes",
14985
+ fix: {
14986
+ tool: "Systematic Debugging Skill",
14987
+ description: "Structured approach to fixing issues without loops",
14988
+ installCommand: "npx skills add obra/superpowers@systematic-debugging --yes",
14989
+ estimatedImprovementPercent: 55
14880
14990
  }
14881
- return count;
14882
- }
14883
- get isEnabled() {
14884
- return this.#options.isEnabled && !this.#options.isSilent;
14885
14991
  }
14886
- set isEnabled(value) {
14887
- if (typeof value !== "boolean") {
14888
- throw new TypeError("The `isEnabled` option must be a boolean");
14992
+ };
14993
+ function detectFrustrations(sessions) {
14994
+ const frustrations = [];
14995
+ for (const session of sessions) {
14996
+ const frustrationPatterns = session.patterns.filter((p) => p.type !== "smooth_flow" && p.type !== "long_back_forth");
14997
+ if (frustrationPatterns.length === 0)
14998
+ continue;
14999
+ const sessionPath = findSessionPath(session.sessionId, session.agent);
15000
+ const exactPrompts = sessionPath ? extractFrustrationPrompts(sessionPath, frustrationPatterns) : [];
15001
+ for (let i3 = 0;i3 < frustrationPatterns.length; i3++) {
15002
+ const pattern = frustrationPatterns[i3];
15003
+ const exactPrompt = exactPrompts[i3] || "(Unable to extract prompt from session file)";
15004
+ const rootCause = analyzeRootCause(exactPrompt, pattern.type);
15005
+ const timeWasted = Math.round(pattern.estimatedWastedTokens / 1000);
15006
+ const costWasted = pattern.estimatedWastedTokens * 0.000015;
15007
+ frustrations.push({
15008
+ sessionId: session.sessionId,
15009
+ sessionPath: sessionPath || `${session.agent}/${session.sessionId}`,
15010
+ agent: session.agent,
15011
+ timestamp: session.startedAt,
15012
+ exactPrompt: exactPrompt.slice(0, 500),
15013
+ promptTruncated: exactPrompt.length > 500,
15014
+ patternType: pattern.type,
15015
+ patternDescription: getPatternDescription(pattern.type),
15016
+ rootCause,
15017
+ estimatedTimeWastedMinutes: timeWasted,
15018
+ estimatedTokensWasted: pattern.estimatedWastedTokens,
15019
+ estimatedCostWasted: Math.round(costWasted * 100) / 100,
15020
+ recommendedFix: getRecommendedFix(rootCause.category)
15021
+ });
14889
15022
  }
14890
- this.#options.isEnabled = value;
14891
15023
  }
14892
- get isSilent() {
14893
- return this.#options.isSilent;
15024
+ frustrations.sort((a2, b2) => b2.estimatedTimeWastedMinutes - a2.estimatedTimeWastedMinutes);
15025
+ const totalTimeWasted = frustrations.reduce((a2, f3) => a2 + f3.estimatedTimeWastedMinutes, 0);
15026
+ const totalTokensWasted = frustrations.reduce((a2, f3) => a2 + f3.estimatedTokensWasted, 0);
15027
+ const totalCostWasted = frustrations.reduce((a2, f3) => a2 + f3.estimatedCostWasted, 0);
15028
+ const byRootCause = {};
15029
+ for (const f3 of frustrations) {
15030
+ byRootCause[f3.rootCause.category] = (byRootCause[f3.rootCause.category] || 0) + 1;
14894
15031
  }
14895
- set isSilent(value) {
14896
- if (typeof value !== "boolean") {
14897
- throw new TypeError("The `isSilent` option must be a boolean");
15032
+ const recommendations = aggregateRecommendations(frustrations);
15033
+ return {
15034
+ totalFrustrations: frustrations.length,
15035
+ totalTimeWastedMinutes: totalTimeWasted,
15036
+ totalTokensWasted,
15037
+ totalCostWasted: Math.round(totalCostWasted * 100) / 100,
15038
+ byRootCause,
15039
+ topFrustrations: frustrations.slice(0, 5),
15040
+ recommendations
15041
+ };
15042
+ }
15043
+ function findSessionPath(sessionId, agent) {
15044
+ const home = homedir6();
15045
+ if (agent === "claude") {
15046
+ const projectsDir = join7(home, ".claude", "projects");
15047
+ if (!existsSync7(projectsDir))
15048
+ return null;
15049
+ try {
15050
+ const projectDirs = fsReaddirSync(projectsDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join7(projectsDir, d2.name));
15051
+ for (const projectDir of projectDirs) {
15052
+ const sessionFile = join7(projectDir, `${sessionId}.jsonl`);
15053
+ if (existsSync7(sessionFile)) {
15054
+ return sessionFile;
15055
+ }
15056
+ }
15057
+ } catch {
15058
+ return null;
14898
15059
  }
14899
- this.#options.isSilent = value;
14900
15060
  }
14901
- frame() {
14902
- const now = Date.now();
14903
- if (this.#frameIndex === -1 || now - this.#lastFrameTime >= this.interval) {
14904
- this.#frameIndex = (this.#frameIndex + 1) % this.#spinner.frames.length;
14905
- this.#lastFrameTime = now;
14906
- }
14907
- const { frames } = this.#spinner;
14908
- let frame = frames[this.#frameIndex];
14909
- if (this.color) {
14910
- frame = source_default[this.color](frame);
15061
+ if (agent === "opencode") {
15062
+ const baseDir = join7(home, ".local", "share", "opencode", "storage", "session");
15063
+ if (!existsSync7(baseDir))
15064
+ return null;
15065
+ try {
15066
+ const projectDirs = fsReaddirSync(baseDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join7(baseDir, d2.name));
15067
+ for (const projectDir of projectDirs) {
15068
+ const sessionFile = join7(projectDir, `${sessionId}.json`);
15069
+ if (existsSync7(sessionFile)) {
15070
+ return sessionFile;
15071
+ }
15072
+ }
15073
+ } catch {
15074
+ return null;
14911
15075
  }
14912
- const fullPrefixText = this.#getFullPrefixText(this.#options.prefixText, " ");
14913
- const fullText = typeof this.text === "string" ? " " + this.text : "";
14914
- const fullSuffixText = this.#getFullSuffixText(this.#options.suffixText, " ");
14915
- return fullPrefixText + frame + fullText + fullSuffixText;
14916
15076
  }
14917
- clear() {
14918
- if (!this.isEnabled || !this.#stream.isTTY) {
14919
- return this;
14920
- }
14921
- this.#internalWrite(() => {
14922
- this.#stream.cursorTo(0);
14923
- for (let index = 0;index < this.#linesToClear; index++) {
14924
- if (index > 0) {
14925
- this.#stream.moveCursor(0, -1);
15077
+ return null;
15078
+ }
15079
+ function extractFrustrationPrompts(sessionPath, patterns) {
15080
+ try {
15081
+ const content = readFileSync7(sessionPath, "utf-8");
15082
+ const prompts = [];
15083
+ if (sessionPath.endsWith(".jsonl")) {
15084
+ const lines = content.trim().split(`
15085
+ `);
15086
+ const messages = [];
15087
+ for (const line of lines) {
15088
+ try {
15089
+ messages.push(JSON.parse(line));
15090
+ } catch {}
15091
+ }
15092
+ const userMessages = messages.map((m2, i3) => ({ msg: m2, index: i3 })).filter((m2) => {
15093
+ const role = m2.msg.role ?? m2.msg.message?.role;
15094
+ return role === "user";
15095
+ });
15096
+ for (const pattern of patterns) {
15097
+ if (pattern.messageIndices && pattern.messageIndices.length > 0) {
15098
+ const idx = pattern.messageIndices[0];
15099
+ const userMsg = userMessages.find((m2) => m2.index === idx);
15100
+ if (userMsg) {
15101
+ const text = extractMessageText(userMsg.msg);
15102
+ prompts.push(text);
15103
+ } else {
15104
+ const frustrationMsg = findFrustrationMessage(userMessages.map((m2) => extractMessageText(m2.msg)));
15105
+ prompts.push(frustrationMsg || "");
15106
+ }
15107
+ } else {
15108
+ const frustrationMsg = findFrustrationMessage(userMessages.map((m2) => extractMessageText(m2.msg)));
15109
+ prompts.push(frustrationMsg || "");
14926
15110
  }
14927
- this.#stream.clearLine(1);
14928
15111
  }
14929
- if (this.#options.indent) {
14930
- this.#stream.cursorTo(this.#options.indent);
15112
+ } else if (sessionPath.endsWith(".json")) {
15113
+ for (const _3 of patterns) {
15114
+ prompts.push("");
14931
15115
  }
14932
- });
14933
- this.#linesToClear = 0;
14934
- return this;
14935
- }
14936
- #hookStream(stream) {
14937
- if (!stream || this.#hookedStreams.has(stream) || !stream.isTTY || typeof stream.write !== "function") {
14938
- return;
14939
15116
  }
14940
- if (activeHooksPerStream.has(stream)) {
14941
- console.warn("[ora] Multiple concurrent spinners detected. This may cause visual corruption. Use one spinner at a time.");
14942
- }
14943
- const originalWrite = stream.write;
14944
- this.#hookedStreams.set(stream, originalWrite);
14945
- activeHooksPerStream.set(stream, this);
14946
- stream.write = (chunk, encoding, callback) => this.#hookedWrite(stream, originalWrite, chunk, encoding, callback);
15117
+ return prompts;
15118
+ } catch {
15119
+ return [];
14947
15120
  }
14948
- #installHook() {
14949
- if (!this.isEnabled || this.#hookedStreams.size > 0) {
14950
- return;
14951
- }
14952
- const streamsToHook = new Set([this.#stream, process8.stdout, process8.stderr]);
14953
- for (const stream of streamsToHook) {
14954
- this.#hookStream(stream);
15121
+ }
15122
+ function extractMessageText(msg) {
15123
+ if (typeof msg.content === "string")
15124
+ return msg.content;
15125
+ if (Array.isArray(msg.content)) {
15126
+ return msg.content.filter((c3) => c3.type === "text" && c3.text).map((c3) => c3.text).join(" ");
15127
+ }
15128
+ return "";
15129
+ }
15130
+ function findFrustrationMessage(messages) {
15131
+ const frustrationIndicators = [
15132
+ /[A-Z]{5,}/,
15133
+ /!{2,}/,
15134
+ /\?{2,}/,
15135
+ /\b(ugh|argh|wtf|wrong|no|stop|don't|undo|revert)\b/i
15136
+ ];
15137
+ for (const msg of messages) {
15138
+ for (const indicator of frustrationIndicators) {
15139
+ if (indicator.test(msg)) {
15140
+ return msg;
15141
+ }
14955
15142
  }
14956
15143
  }
14957
- #uninstallHook() {
14958
- for (const [stream, originalWrite] of this.#hookedStreams) {
14959
- stream.write = originalWrite;
14960
- if (activeHooksPerStream.get(stream) === this) {
14961
- activeHooksPerStream.delete(stream);
15144
+ return null;
15145
+ }
15146
+ function analyzeRootCause(prompt2, patternType) {
15147
+ if (patternType === "memory_loss") {
15148
+ return {
15149
+ category: "context_loss",
15150
+ description: ROOT_CAUSE_PATTERNS.context_loss.description,
15151
+ confidence: "high"
15152
+ };
15153
+ }
15154
+ if (patternType === "undo_loop") {
15155
+ for (const pattern of ROOT_CAUSE_PATTERNS.hallucination.patterns) {
15156
+ if (pattern.test(prompt2)) {
15157
+ return {
15158
+ category: "hallucination",
15159
+ description: ROOT_CAUSE_PATTERNS.hallucination.description,
15160
+ confidence: "medium"
15161
+ };
14962
15162
  }
14963
15163
  }
14964
- this.#hookedStreams.clear();
15164
+ return {
15165
+ category: "undo_loop",
15166
+ description: ROOT_CAUSE_PATTERNS.undo_loop.description,
15167
+ confidence: "high"
15168
+ };
14965
15169
  }
14966
- #hookedWrite(stream, originalWrite, chunk, encoding, callback) {
14967
- if (typeof encoding === "function") {
14968
- callback = encoding;
14969
- encoding = undefined;
15170
+ if (patternType === "tool_failure") {
15171
+ return {
15172
+ category: "tool_failure",
15173
+ description: ROOT_CAUSE_PATTERNS.tool_failure.description,
15174
+ confidence: "high"
15175
+ };
15176
+ }
15177
+ for (const [category, config] of Object.entries(ROOT_CAUSE_PATTERNS)) {
15178
+ for (const pattern of config.patterns) {
15179
+ if (pattern.test(prompt2)) {
15180
+ return {
15181
+ category,
15182
+ description: config.description,
15183
+ confidence: "medium"
15184
+ };
15185
+ }
14970
15186
  }
14971
- if (this.#isInternalWrite) {
14972
- return originalWrite.call(stream, chunk, encoding, callback);
15187
+ }
15188
+ return {
15189
+ category: "unknown",
15190
+ description: "Unable to determine specific root cause",
15191
+ confidence: "low"
15192
+ };
15193
+ }
15194
+ function getRecommendedFix(category) {
15195
+ const fixes = {
15196
+ hallucination: ROOT_CAUSE_PATTERNS.hallucination.fix,
15197
+ missing_docs: ROOT_CAUSE_PATTERNS.missing_docs.fix,
15198
+ context_loss: ROOT_CAUSE_PATTERNS.context_loss.fix,
15199
+ unclear_prompt: ROOT_CAUSE_PATTERNS.unclear_prompt.fix,
15200
+ tool_failure: ROOT_CAUSE_PATTERNS.tool_failure.fix,
15201
+ undo_loop: ROOT_CAUSE_PATTERNS.undo_loop.fix,
15202
+ unknown: {
15203
+ tool: "CLAUDE.md",
15204
+ description: "Create project instructions to prevent common issues",
15205
+ installCommand: `echo '# Project Instructions
15206
+ ' > CLAUDE.md`,
15207
+ estimatedImprovementPercent: 30
14973
15208
  }
14974
- this.clear();
14975
- const chunkString = this.#stringifyChunk(chunk, encoding);
14976
- const chunkTerminatesLine = this.#chunkTerminatesLine(chunkString);
14977
- const writeResult = originalWrite.call(stream, chunk, encoding, callback);
14978
- if (chunkTerminatesLine) {
14979
- this.#clearRenderDeferral();
14980
- } else if (chunkString.length > 0) {
14981
- this.#scheduleRenderDeferral();
15209
+ };
15210
+ return fixes[category];
15211
+ }
15212
+ function getPatternDescription(patternType) {
15213
+ const descriptions = {
15214
+ undo_loop: "Got stuck in undo/redo cycle",
15215
+ memory_loss: "Had to re-explain context",
15216
+ frustration_caps: "Expressed frustration (ALL CAPS)",
15217
+ repeated_rephrasing: "Had to rephrase multiple times",
15218
+ context_compaction: "Lost context due to compaction",
15219
+ tool_failure: "Tool or MCP failed",
15220
+ long_back_forth: "Excessive back-and-forth"
15221
+ };
15222
+ return descriptions[patternType] || patternType;
15223
+ }
15224
+ function aggregateRecommendations(frustrations) {
15225
+ const byTool = {};
15226
+ for (const f3 of frustrations) {
15227
+ const key = f3.recommendedFix.tool;
15228
+ if (!byTool[key]) {
15229
+ byTool[key] = { count: 0, timeSaved: 0, costSaved: 0, fix: f3.recommendedFix, frustrations: [] };
14982
15230
  }
14983
- if (this.isSpinning && !this.#deferRenderTimer) {
14984
- this.render();
15231
+ byTool[key].count++;
15232
+ byTool[key].timeSaved += f3.estimatedTimeWastedMinutes * (f3.recommendedFix.estimatedImprovementPercent / 100);
15233
+ byTool[key].costSaved += f3.estimatedCostWasted * (f3.recommendedFix.estimatedImprovementPercent / 100);
15234
+ byTool[key].frustrations.push(f3);
15235
+ }
15236
+ const recommendations = [];
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
+ }));
15243
+ recommendations.push({
15244
+ tool,
15245
+ description: data.fix.description,
15246
+ installCommand: data.fix.installCommand,
15247
+ wouldHavePreventedCount: data.count,
15248
+ estimatedTimeSavedMinutes: Math.round(data.timeSaved),
15249
+ estimatedCostSaved: Math.round(data.costSaved * 100) / 100,
15250
+ preventedFrustrations
15251
+ });
15252
+ }
15253
+ recommendations.sort((a2, b2) => b2.estimatedTimeSavedMinutes - a2.estimatedTimeSavedMinutes);
15254
+ return recommendations;
15255
+ }
15256
+ function formatFrustrationSummary(summary) {
15257
+ const lines = [];
15258
+ if (summary.totalFrustrations === 0) {
15259
+ lines.push(` ${icons.success} ${colors2.success("No frustration events detected!")}`);
15260
+ return lines;
15261
+ }
15262
+ lines.push("");
15263
+ lines.push(colors2.dim(" " + "═".repeat(50)));
15264
+ lines.push(` ${colors2.error("!")} ${colors2.bold("Frustration Analysis")}`);
15265
+ lines.push(colors2.dim(" " + "═".repeat(50)));
15266
+ lines.push("");
15267
+ lines.push(` ${colors2.bold("Summary:")}`);
15268
+ lines.push(` ${icons.warning} ${summary.totalFrustrations} frustration events detected`);
15269
+ lines.push(` ${colors2.dim(">")} ~${summary.totalTimeWastedMinutes} min wasted`);
15270
+ lines.push(` ${icons.money} ~$${summary.totalCostWasted.toFixed(2)} in tokens`);
15271
+ lines.push("");
15272
+ if (Object.keys(summary.byRootCause).length > 0) {
15273
+ lines.push(` ${colors2.bold("Root Causes:")}`);
15274
+ for (const [cause, count] of Object.entries(summary.byRootCause)) {
15275
+ const label = formatCauseLabel(cause);
15276
+ lines.push(` ${colors2.dim("•")} ${label}: ${count}x`);
14985
15277
  }
14986
- return writeResult;
15278
+ lines.push("");
14987
15279
  }
14988
- render() {
14989
- if (!this.isEnabled || this.#drainHandler || this.#deferRenderTimer) {
14990
- return this;
15280
+ if (summary.topFrustrations.length > 0) {
15281
+ lines.push(` ${colors2.bold("Top Frustrations:")}`);
15282
+ lines.push("");
15283
+ for (let i3 = 0;i3 < Math.min(3, summary.topFrustrations.length); i3++) {
15284
+ const f3 = summary.topFrustrations[i3];
15285
+ lines.push(...formatFrustrationEvent(f3, i3 + 1));
15286
+ lines.push("");
14991
15287
  }
14992
- const useSynchronizedOutput = this.#stream.isTTY;
14993
- let shouldDisableSynchronizedOutput = false;
14994
- try {
14995
- if (useSynchronizedOutput) {
14996
- this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_ENABLE));
14997
- shouldDisableSynchronizedOutput = true;
14998
- }
14999
- this.clear();
15000
- let frameContent = this.frame();
15001
- const columns = this.#stream.columns ?? 80;
15002
- const actualLineCount = this.#computeLineCountFrom(frameContent, columns);
15003
- const consoleHeight = this.#stream.rows;
15004
- if (consoleHeight && consoleHeight > 1 && actualLineCount > consoleHeight) {
15005
- const lines = frameContent.split(`
15006
- `);
15007
- const maxLines = consoleHeight - 1;
15008
- frameContent = [...lines.slice(0, maxLines), "... (content truncated to fit terminal)"].join(`
15009
- `);
15010
- }
15011
- const canContinue = this.#internalWrite(() => this.#stream.write(frameContent));
15012
- if (canContinue === false && this.#stream.isTTY) {
15013
- this.#drainHandler = () => {
15014
- this.#drainHandler = undefined;
15015
- this.#tryRender();
15016
- };
15017
- this.#stream.once("drain", this.#drainHandler);
15018
- }
15019
- this.#linesToClear = this.#computeLineCountFrom(frameContent, columns);
15020
- } finally {
15021
- if (shouldDisableSynchronizedOutput) {
15022
- this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_DISABLE));
15288
+ }
15289
+ if (summary.recommendations.length > 0) {
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)}`)}`);
15295
+ lines.push("");
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
+ }
15023
15306
  }
15307
+ lines.push(` ${colors2.dim("Install:")} ${colors2.primary(rec.installCommand)}`);
15308
+ lines.push("");
15024
15309
  }
15025
- return this;
15026
15310
  }
15027
- start(text) {
15028
- if (text) {
15029
- this.text = text;
15311
+ lines.push(colors2.dim(" " + "═".repeat(50)));
15312
+ return lines;
15313
+ }
15314
+ function formatFrustrationEvent(f3, index) {
15315
+ 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)}`);
15320
+ if (f3.exactPrompt && f3.exactPrompt.length > 0 && !f3.exactPrompt.startsWith("(Unable")) {
15321
+ 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}`);
15030
15326
  }
15031
- if (this.isSilent) {
15032
- return this;
15327
+ if (promptLines.length > 4) {
15328
+ lines.push(` ${colors2.dim("│")} ${colors2.dim("...")}`);
15033
15329
  }
15034
- if (!this.isEnabled) {
15035
- const symbol = this.text ? "-" : "";
15036
- const line = " ".repeat(this.#options.indent) + this.#buildOutputLine(symbol, this.text, this.#options.prefixText, this.#options.suffixText);
15037
- if (line.trim() !== "") {
15038
- this.#internalWrite(() => this.#stream.write(line + `
15039
- `));
15040
- }
15041
- return this;
15330
+ lines.push(` ${colors2.dim("└" + "─".repeat(40))}`);
15331
+ }
15332
+ lines.push("");
15333
+ lines.push(` ${colors2.success("Fix:")} ${f3.recommendedFix.tool}`);
15334
+ lines.push(` ${colors2.dim("$")} ${colors2.primary(f3.recommendedFix.installCommand)}`);
15335
+ return lines;
15336
+ }
15337
+ function formatCauseLabel(cause) {
15338
+ const labels = {
15339
+ hallucination: colors2.error("API Hallucination"),
15340
+ missing_docs: colors2.warning("Missing Docs"),
15341
+ context_loss: colors2.warning("Context Loss"),
15342
+ unclear_prompt: colors2.dim("Unclear Prompt"),
15343
+ tool_failure: colors2.error("Tool Failure"),
15344
+ undo_loop: colors2.warning("Undo Loop"),
15345
+ unknown: colors2.dim("Unknown")
15346
+ };
15347
+ return labels[cause] || cause;
15348
+ }
15349
+ function wrapText(text, width) {
15350
+ const words = text.split(/\s+/);
15351
+ const lines = [];
15352
+ let currentLine = "";
15353
+ for (const word of words) {
15354
+ if (currentLine.length + word.length + 1 <= width) {
15355
+ currentLine += (currentLine ? " " : "") + word;
15356
+ } else {
15357
+ if (currentLine)
15358
+ lines.push(currentLine);
15359
+ currentLine = word.slice(0, width);
15042
15360
  }
15043
- if (this.isSpinning) {
15044
- return this;
15361
+ }
15362
+ if (currentLine)
15363
+ lines.push(currentLine);
15364
+ return lines;
15365
+ }
15366
+
15367
+ // src/lib/optimization-installer.ts
15368
+ import { execSync as execSync2, spawnSync } from "node:child_process";
15369
+ import { existsSync as existsSync8, writeFileSync as writeFileSync3, readFileSync as readFileSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync4 } from "node:fs";
15370
+ import { join as join8 } from "node:path";
15371
+ import { homedir as homedir7 } from "node:os";
15372
+ async function installOptimizations(optimizations, projectDir, options = {}) {
15373
+ const results = [];
15374
+ for (const opt of optimizations) {
15375
+ if (!opt.selected)
15376
+ continue;
15377
+ try {
15378
+ const result = await installSingleOptimization(opt, projectDir, options);
15379
+ results.push(result);
15380
+ } catch (err) {
15381
+ results.push({
15382
+ id: opt.id,
15383
+ name: opt.name,
15384
+ success: false,
15385
+ message: "Installation failed",
15386
+ error: err instanceof Error ? err.message : String(err)
15387
+ });
15045
15388
  }
15046
- if (this.#options.hideCursor) {
15047
- cli_cursor_default.hide(this.#stream);
15389
+ }
15390
+ return results;
15391
+ }
15392
+ async function installSingleOptimization(opt, projectDir, options) {
15393
+ if (options.dryRun) {
15394
+ return {
15395
+ id: opt.id,
15396
+ name: opt.name,
15397
+ success: true,
15398
+ message: `[DRY RUN] Would install: ${opt.installCommand || opt.configPath || "unknown"}`
15399
+ };
15400
+ }
15401
+ switch (opt.type) {
15402
+ case "mcp":
15403
+ return installMCP(opt, projectDir, options.agent);
15404
+ case "skill":
15405
+ return installSkill(opt);
15406
+ case "config":
15407
+ return installConfig(opt, projectDir);
15408
+ case "hook":
15409
+ return installHook(opt, projectDir);
15410
+ default:
15411
+ return {
15412
+ id: opt.id,
15413
+ name: opt.name,
15414
+ success: false,
15415
+ message: "Unknown optimization type"
15416
+ };
15417
+ }
15418
+ }
15419
+ function installMCP(opt, projectDir, agent) {
15420
+ if (opt.installCommand?.startsWith("claude mcp add")) {
15421
+ try {
15422
+ execSync2(opt.installCommand, {
15423
+ encoding: "utf-8",
15424
+ stdio: "pipe",
15425
+ timeout: 60000
15426
+ });
15427
+ return {
15428
+ id: opt.id,
15429
+ name: opt.name,
15430
+ success: true,
15431
+ message: `Installed via: ${opt.installCommand}`
15432
+ };
15433
+ } catch (err) {}
15434
+ }
15435
+ if (opt.id === "beads" || opt.name.toLowerCase().includes("beads")) {
15436
+ try {
15437
+ execSync2("bun add -g beads", { encoding: "utf-8", stdio: "pipe", timeout: 60000 });
15438
+ execSync2("bd init", { cwd: projectDir, encoding: "utf-8", stdio: "pipe", timeout: 30000 });
15439
+ return {
15440
+ id: opt.id,
15441
+ name: opt.name,
15442
+ success: true,
15443
+ message: "Installed Beads globally and initialized in project"
15444
+ };
15445
+ } catch (err) {
15446
+ return {
15447
+ id: opt.id,
15448
+ name: opt.name,
15449
+ success: false,
15450
+ message: "Failed to install Beads",
15451
+ error: err instanceof Error ? err.message : String(err)
15452
+ };
15048
15453
  }
15049
- if (this.#options.discardStdin && process8.stdin.isTTY) {
15050
- stdin_discarder_default.start();
15051
- this.#isDiscardingStdin = true;
15454
+ }
15455
+ if (opt.installCommand) {
15456
+ try {
15457
+ execSync2(opt.installCommand, {
15458
+ encoding: "utf-8",
15459
+ stdio: "pipe",
15460
+ timeout: 60000
15461
+ });
15462
+ return {
15463
+ id: opt.id,
15464
+ name: opt.name,
15465
+ success: true,
15466
+ message: `Installed via: ${opt.installCommand}`
15467
+ };
15468
+ } catch (err) {
15469
+ return {
15470
+ id: opt.id,
15471
+ name: opt.name,
15472
+ success: false,
15473
+ message: "Failed to install MCP",
15474
+ error: err instanceof Error ? err.message : String(err)
15475
+ };
15052
15476
  }
15053
- this.#installHook();
15054
- this.render();
15055
- this.#id = setInterval(this.render.bind(this), this.interval);
15056
- return this;
15057
15477
  }
15058
- stop() {
15059
- clearInterval(this.#id);
15060
- this.#id = undefined;
15061
- this.#frameIndex = -1;
15062
- this.#lastFrameTime = 0;
15063
- this.#clearRenderDeferral();
15064
- this.#uninstallHook();
15065
- if (this.#drainHandler) {
15066
- this.#stream.removeListener("drain", this.#drainHandler);
15067
- this.#drainHandler = undefined;
15478
+ return {
15479
+ id: opt.id,
15480
+ name: opt.name,
15481
+ success: false,
15482
+ message: "No install command available"
15483
+ };
15484
+ }
15485
+ function installSkill(opt) {
15486
+ if (!opt.installCommand) {
15487
+ return {
15488
+ id: opt.id,
15489
+ name: opt.name,
15490
+ success: false,
15491
+ message: "No install command for skill"
15492
+ };
15493
+ }
15494
+ try {
15495
+ const skillPath = opt.installCommand.replace(/^npx\s+/, "").replace(/^bunx\s+/, "").replace(/^skills\s+add\s+/, "").trim();
15496
+ let result = spawnSync("bunx", ["--bun", "skills", "add", skillPath], {
15497
+ encoding: "utf-8",
15498
+ stdio: "pipe",
15499
+ timeout: 120000
15500
+ });
15501
+ if (result.status !== 0) {
15502
+ result = spawnSync("npx", ["-y", "skills", "add", skillPath], {
15503
+ encoding: "utf-8",
15504
+ stdio: "pipe",
15505
+ timeout: 120000
15506
+ });
15068
15507
  }
15069
- if (this.isEnabled) {
15070
- this.clear();
15071
- if (this.#options.hideCursor) {
15072
- cli_cursor_default.show(this.#stream);
15508
+ if (result.status === 0) {
15509
+ return {
15510
+ id: opt.id,
15511
+ name: opt.name,
15512
+ success: true,
15513
+ message: `Skill installed successfully`
15514
+ };
15515
+ } else {
15516
+ const output = (result.stderr || result.stdout || "").toLowerCase();
15517
+ if (output.includes("already exists") || output.includes("already installed")) {
15518
+ return {
15519
+ id: opt.id,
15520
+ name: opt.name,
15521
+ success: true,
15522
+ message: `Skill already installed`
15523
+ };
15073
15524
  }
15525
+ return {
15526
+ id: opt.id,
15527
+ name: opt.name,
15528
+ success: false,
15529
+ message: "Skill installation failed",
15530
+ error: result.stderr || result.stdout || "Unknown error"
15531
+ };
15074
15532
  }
15075
- if (this.#isDiscardingStdin) {
15076
- this.#isDiscardingStdin = false;
15077
- stdin_discarder_default.stop();
15078
- }
15079
- return this;
15080
- }
15081
- succeed(text) {
15082
- return this.stopAndPersist({ symbol: exports_symbols.success, text });
15083
- }
15084
- fail(text) {
15085
- return this.stopAndPersist({ symbol: exports_symbols.error, text });
15086
- }
15087
- warn(text) {
15088
- return this.stopAndPersist({ symbol: exports_symbols.warning, text });
15533
+ } catch (err) {
15534
+ return {
15535
+ id: opt.id,
15536
+ name: opt.name,
15537
+ success: false,
15538
+ message: "Failed to install skill",
15539
+ error: err instanceof Error ? err.message : String(err)
15540
+ };
15089
15541
  }
15090
- info(text) {
15091
- return this.stopAndPersist({ symbol: exports_symbols.info, text });
15542
+ }
15543
+ function installConfig(opt, projectDir) {
15544
+ if (!opt.configPath || !opt.configContent) {
15545
+ return {
15546
+ id: opt.id,
15547
+ name: opt.name,
15548
+ success: false,
15549
+ message: "No config path or content provided"
15550
+ };
15092
15551
  }
15093
- stopAndPersist(options = {}) {
15094
- if (this.isSilent) {
15095
- return this;
15552
+ try {
15553
+ const fullPath = opt.configPath.startsWith("/") || opt.configPath.startsWith("~") ? opt.configPath.replace("~", homedir7()) : join8(projectDir, opt.configPath);
15554
+ if (existsSync8(fullPath)) {
15555
+ return {
15556
+ id: opt.id,
15557
+ name: opt.name,
15558
+ success: true,
15559
+ message: `Config already exists: ${opt.configPath}`
15560
+ };
15096
15561
  }
15097
- const symbol = options.symbol ?? " ";
15098
- const text = options.text ?? this.text;
15099
- const prefixText = options.prefixText ?? this.#options.prefixText;
15100
- const suffixText = options.suffixText ?? this.#options.suffixText;
15101
- const textToWrite = this.#buildOutputLine(symbol, text, prefixText, suffixText) + `
15102
- `;
15103
- this.stop();
15104
- this.#internalWrite(() => this.#stream.write(textToWrite));
15105
- return this;
15106
- }
15107
- }
15108
- function ora(options) {
15109
- return new Ora(options);
15110
- }
15111
-
15112
- // src/lib/ui.ts
15113
- var import_picocolors = __toESM(require_picocolors(), 1);
15114
- import * as readline from "readline";
15115
- var colors2 = {
15116
- primary: import_picocolors.default.cyan,
15117
- secondary: import_picocolors.default.blue,
15118
- accent: import_picocolors.default.magenta,
15119
- success: import_picocolors.default.green,
15120
- warning: import_picocolors.default.yellow,
15121
- error: import_picocolors.default.red,
15122
- info: import_picocolors.default.blue,
15123
- dim: import_picocolors.default.dim,
15124
- bold: import_picocolors.default.bold,
15125
- italic: import_picocolors.default.italic,
15126
- excellent: import_picocolors.default.green,
15127
- good: import_picocolors.default.cyan,
15128
- moderate: import_picocolors.default.yellow,
15129
- poor: import_picocolors.default.red,
15130
- highlight: (text) => import_picocolors.default.bold(import_picocolors.default.cyan(text)),
15131
- muted: (text) => import_picocolors.default.dim(import_picocolors.default.gray(text)),
15132
- link: (text) => import_picocolors.default.underline(import_picocolors.default.blue(text))
15133
- };
15134
- function createSpinner(text) {
15135
- return ora({
15136
- text,
15137
- spinner: "dots",
15138
- color: "cyan"
15139
- });
15140
- }
15141
- function formatTier(tier) {
15142
- const tierColors = {
15143
- elite: (s2) => import_picocolors.default.bold(import_picocolors.default.magenta(s2)),
15144
- expert: (s2) => import_picocolors.default.bold(import_picocolors.default.cyan(s2)),
15145
- advanced: (s2) => import_picocolors.default.bold(import_picocolors.default.green(s2)),
15146
- proficient: (s2) => import_picocolors.default.blue(s2),
15147
- intermediate: (s2) => import_picocolors.default.yellow(s2),
15148
- developing: (s2) => import_picocolors.default.dim(s2)
15149
- };
15150
- const colorFn = tierColors[tier.toLowerCase()] ?? colors2.dim;
15151
- return colorFn(tier.charAt(0).toUpperCase() + tier.slice(1));
15152
- }
15153
- var icons = {
15154
- success: colors2.success("✔"),
15155
- error: colors2.error("✖"),
15156
- warning: colors2.warning("⚠"),
15157
- info: colors2.info("ℹ"),
15158
- arrow: colors2.primary("→"),
15159
- bullet: colors2.dim("•"),
15160
- star: colors2.warning("★"),
15161
- check: colors2.success("✓"),
15162
- cross: colors2.error("✗"),
15163
- pending: colors2.dim("○"),
15164
- lightning: "⚡",
15165
- fire: "\uD83D\uDD25",
15166
- rocket: "\uD83D\uDE80",
15167
- chart: "\uD83D\uDCCA",
15168
- money: "\uD83D\uDCB0",
15169
- brain: "\uD83E\uDDE0",
15170
- target: "\uD83C\uDFAF"
15171
- };
15172
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
15173
- function clearLine2() {
15174
- readline.clearLine(process.stdout, 0);
15175
- readline.cursorTo(process.stdout, 0);
15176
- }
15177
- function write(text) {
15178
- process.stdout.write(text);
15179
- }
15180
- async function animateProgressBar(label, score, options = {}) {
15181
- const { width = 20, charDelay = 40, labelWidth = 20 } = options;
15182
- const filled = Math.round(score / 100 * width);
15183
- let barColor = colors2.excellent;
15184
- if (score < 40)
15185
- barColor = colors2.poor;
15186
- else if (score < 60)
15187
- barColor = colors2.moderate;
15188
- else if (score < 75)
15189
- barColor = colors2.good;
15190
- const paddedLabel = label.padEnd(labelWidth);
15191
- const scoreStr = `${score}/100`.padStart(7);
15192
- write(` ${colors2.dim(paddedLabel)} ${colors2.dim(scoreStr)} `);
15193
- for (let i3 = 0;i3 < width; i3++) {
15194
- if (i3 < filled) {
15195
- write(barColor("█"));
15562
+ const parentDir = fullPath.substring(0, fullPath.lastIndexOf("/"));
15563
+ if (parentDir && !existsSync8(parentDir)) {
15564
+ mkdirSync3(parentDir, { recursive: true });
15565
+ }
15566
+ writeFileSync3(fullPath, opt.configContent);
15567
+ return {
15568
+ id: opt.id,
15569
+ name: opt.name,
15570
+ success: true,
15571
+ message: `Created: ${opt.configPath}`
15572
+ };
15573
+ } catch (err) {
15574
+ return {
15575
+ id: opt.id,
15576
+ name: opt.name,
15577
+ success: false,
15578
+ message: "Failed to create config",
15579
+ error: err instanceof Error ? err.message : String(err)
15580
+ };
15581
+ }
15582
+ }
15583
+ function installHook(opt, projectDir) {
15584
+ const agentsMdPath = join8(projectDir, "AGENTS.md");
15585
+ try {
15586
+ let content = "";
15587
+ if (existsSync8(agentsMdPath)) {
15588
+ content = readFileSync8(agentsMdPath, "utf-8");
15196
15589
  } else {
15197
- write(colors2.dim("░"));
15590
+ content = `# Agent Instructions
15591
+
15592
+ `;
15198
15593
  }
15199
- await sleep(charDelay);
15594
+ const hookName = opt.name.toLowerCase();
15595
+ if (content.toLowerCase().includes(hookName)) {
15596
+ return {
15597
+ id: opt.id,
15598
+ name: opt.name,
15599
+ success: true,
15600
+ message: "Hook already configured in AGENTS.md"
15601
+ };
15602
+ }
15603
+ const hookSection = opt.configContent || `
15604
+ ## ${opt.name}
15605
+ ${opt.description}
15606
+ `;
15607
+ content += `
15608
+ ` + hookSection;
15609
+ writeFileSync3(agentsMdPath, content);
15610
+ return {
15611
+ id: opt.id,
15612
+ name: opt.name,
15613
+ success: true,
15614
+ message: "Added hook to AGENTS.md"
15615
+ };
15616
+ } catch (err) {
15617
+ return {
15618
+ id: opt.id,
15619
+ name: opt.name,
15620
+ success: false,
15621
+ message: "Failed to add hook",
15622
+ error: err instanceof Error ? err.message : String(err)
15623
+ };
15200
15624
  }
15201
- console.log();
15202
- }
15203
- async function thinkingStep(thinkingText, duration = 800, successText) {
15204
- const spinner = createSpinner(thinkingText);
15205
- spinner.start();
15206
- await sleep(duration);
15207
- spinner.succeed(successText ?? thinkingText);
15208
15625
  }
15209
- async function revealDiscovery(icon, text, delay2 = 300) {
15210
- await sleep(delay2);
15211
- console.log(` ${icon} ${text}`);
15212
- }
15213
- async function showPhaseHeader(phase, total, title, delay2 = 400) {
15214
- await sleep(delay2);
15215
- console.log();
15216
- console.log(` ${colors2.dim(`Phase ${phase}/${total}:`)} ${colors2.bold(colors2.primary(title))}`);
15217
- console.log(colors2.dim(" " + "─".repeat(40)));
15626
+ function phaseRecToInstallable(rec) {
15627
+ return {
15628
+ id: rec.title.toLowerCase().replace(/\s+/g, "-"),
15629
+ name: rec.title,
15630
+ type: rec.type,
15631
+ description: rec.description,
15632
+ installCommand: rec.installCommand,
15633
+ selected: false
15634
+ };
15218
15635
  }
15219
- async function animateScoreReveal(score, tier, options = {}) {
15220
- const { countDuration = 1000, barDelay = 50 } = options;
15221
- const scoreColor = score >= 80 ? colors2.excellent : score >= 60 ? colors2.good : score >= 40 ? colors2.moderate : colors2.poor;
15222
- console.log();
15223
- console.log(colors2.dim(" " + "═".repeat(45)));
15224
- console.log();
15225
- const steps = 30;
15226
- const stepDuration = countDuration / steps;
15227
- for (let i3 = 1;i3 <= steps; i3++) {
15228
- const current = Math.round(i3 / steps * score);
15229
- clearLine2();
15230
- write(` ${colors2.bold("Your Score:")} ${scoreColor(colors2.bold(current.toString()))}/100`);
15231
- await sleep(stepDuration);
15636
+ function checkInstalledStatus(projectDir) {
15637
+ const home = homedir7();
15638
+ const status = {
15639
+ context7: false,
15640
+ supermemory: false,
15641
+ nia: false,
15642
+ beads: false,
15643
+ skills: []
15644
+ };
15645
+ const mcpConfigPaths = [
15646
+ join8(home, ".claude.json"),
15647
+ join8(home, ".claude", "claude_desktop_config.json"),
15648
+ join8(home, ".claude", "settings.json"),
15649
+ join8(home, ".claude", "settings.local.json"),
15650
+ join8(projectDir, ".mcp.json"),
15651
+ join8(projectDir, ".claude", "settings.local.json")
15652
+ ];
15653
+ for (const configPath of mcpConfigPaths) {
15654
+ if (existsSync8(configPath)) {
15655
+ try {
15656
+ const config = JSON.parse(readFileSync8(configPath, "utf-8"));
15657
+ const mcpServers = config.mcpServers || config.mcp_servers || {};
15658
+ for (const name of Object.keys(mcpServers)) {
15659
+ const mcpName = name.toLowerCase();
15660
+ if (mcpName.includes("context7") || mcpName === "c7") {
15661
+ status.context7 = true;
15662
+ }
15663
+ if (mcpName.includes("supermemory") || mcpName.includes("memory")) {
15664
+ status.supermemory = true;
15665
+ }
15666
+ if (mcpName.includes("nia")) {
15667
+ status.nia = true;
15668
+ }
15669
+ }
15670
+ } catch {}
15671
+ }
15232
15672
  }
15233
- write(` ${colors2.dim("(")}${formatTier(tier)}${colors2.dim(")")}`);
15234
- console.log();
15235
- console.log();
15236
- const barWidth = 30;
15237
- const filled = Math.round(score / 100 * barWidth);
15238
- write(" ");
15239
- for (let i3 = 0;i3 < barWidth; i3++) {
15240
- if (i3 < filled) {
15241
- write(scoreColor("█"));
15242
- } else {
15243
- write(colors2.dim("░"));
15673
+ status.beads = existsSync8(join8(projectDir, ".beads"));
15674
+ const skillsDirs = [
15675
+ join8(home, ".claude", "skills"),
15676
+ join8(home, ".config", "opencode", "skills"),
15677
+ join8(home, ".agents", "skills"),
15678
+ join8(projectDir, ".opencode", "skill")
15679
+ ];
15680
+ for (const dir of skillsDirs) {
15681
+ if (existsSync8(dir)) {
15682
+ try {
15683
+ const skills = readdirSync4(dir);
15684
+ for (const skill of skills) {
15685
+ if (!status.skills.includes(skill.toLowerCase())) {
15686
+ status.skills.push(skill.toLowerCase());
15687
+ }
15688
+ }
15689
+ } catch {}
15244
15690
  }
15245
- await sleep(barDelay);
15246
15691
  }
15247
- console.log();
15248
- console.log();
15249
- console.log(colors2.dim(" " + "═".repeat(45)));
15692
+ return status;
15250
15693
  }
15251
- async function showFinding(type, label, value, unit = "", delay2 = 250) {
15252
- await sleep(delay2);
15253
- const iconMap = {
15254
- warning: icons.warning,
15255
- error: icons.error,
15256
- info: icons.info,
15257
- success: icons.success
15258
- };
15259
- const colorMap = {
15260
- warning: colors2.warning,
15261
- error: colors2.error,
15262
- info: colors2.info,
15263
- success: colors2.success
15264
- };
15265
- const icon = iconMap[type];
15266
- const valueColor = colorMap[type];
15267
- console.log(` ${icon} ${label}: ${valueColor(value.toString())}${unit}`);
15694
+ function filterAlreadyInstalled(optimizations, status) {
15695
+ return optimizations.filter((opt) => {
15696
+ const id = opt.id.toLowerCase();
15697
+ if (id === "context7" && status.context7)
15698
+ return false;
15699
+ if (id === "supermemory" && status.supermemory)
15700
+ return false;
15701
+ if (id === "nia" && status.nia)
15702
+ return false;
15703
+ if (id === "beads" && status.beads)
15704
+ return false;
15705
+ if (opt.type === "skill") {
15706
+ const skillName = opt.name.toLowerCase().replace(/\s+/g, "-");
15707
+ if (status.skills.some((s2) => s2.includes(skillName) || skillName.includes(s2) || s2.includes("tdd") && id.includes("tdd") || s2.includes("debug") && id.includes("debug") || s2.includes("plan") && id.includes("plan"))) {
15708
+ return false;
15709
+ }
15710
+ }
15711
+ return true;
15712
+ });
15268
15713
  }
15714
+ var QUICK_INSTALL_PRESETS = {
15715
+ essential: [
15716
+ {
15717
+ id: "context7",
15718
+ name: "Context7 MCP",
15719
+ type: "mcp",
15720
+ description: "Up-to-date library documentation",
15721
+ installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
15722
+ selected: true
15723
+ },
15724
+ {
15725
+ id: "supermemory",
15726
+ name: "Supermemory MCP",
15727
+ type: "mcp",
15728
+ description: "Persistent memory across sessions",
15729
+ installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest",
15730
+ selected: true
15731
+ },
15732
+ {
15733
+ id: "beads",
15734
+ name: "Beads Task Manager",
15735
+ type: "mcp",
15736
+ description: "Persistent task tracking",
15737
+ installCommand: "bun add -g beads && bd init",
15738
+ selected: true
15739
+ }
15740
+ ],
15741
+ productivity: [
15742
+ {
15743
+ id: "claude-md",
15744
+ name: "CLAUDE.md",
15745
+ type: "config",
15746
+ description: "Project context file",
15747
+ configPath: "CLAUDE.md",
15748
+ configContent: `# Project Instructions
15749
+
15750
+ ## Build & Test Commands
15751
+ \`\`\`bash
15752
+ bun test # Run tests
15753
+ bun run build # Build project
15754
+ bun run lint # Run linter
15755
+ \`\`\`
15756
+
15757
+ ## Architecture
15758
+ Describe your project structure here.
15759
+
15760
+ ## Conventions
15761
+ - Use TypeScript
15762
+ - Write tests for new features
15763
+ - Use conventional commits
15764
+ `,
15765
+ selected: true
15766
+ },
15767
+ {
15768
+ id: "vitest",
15769
+ name: "Vitest",
15770
+ type: "library",
15771
+ description: "Fast unit testing framework",
15772
+ installCommand: "bun add -D vitest",
15773
+ selected: true
15774
+ }
15775
+ ]
15776
+ };
15269
15777
 
15270
15778
  // src/commands/scan.ts
15271
15779
  var scanCommand = defineCommand2({
@@ -15489,10 +15997,19 @@ var scanCommand = defineCommand2({
15489
15997
  console.log(colors2.dim(" " + "═".repeat(45)));
15490
15998
  console.log();
15491
15999
  if (!args["no-report"]) {
15492
- const reportDir = join8(projectDir, args["report-dir"]);
16000
+ const reportDir = join9(projectDir, args["report-dir"]);
15493
16001
  const reportPath = generateReport(reportDir, score, git, agents, tests, args.since, sdlcAnalysis, scanCost, analysis);
15494
16002
  console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
15495
16003
  }
16004
+ if (agents && agents.sessions.length > 0 && !args.brief) {
16005
+ const frustrationSummary = detectFrustrations(agents.sessions);
16006
+ if (frustrationSummary.totalFrustrations > 0) {
16007
+ const frustrationLines = formatFrustrationSummary(frustrationSummary);
16008
+ for (const line of frustrationLines) {
16009
+ console.log(line);
16010
+ }
16011
+ }
16012
+ }
15496
16013
  if (!args.brief) {
15497
16014
  const installedStatus = checkInstalledStatus(projectDir);
15498
16015
  const projectContext = loadProjectContext(projectDir);
@@ -15754,12 +16271,12 @@ var scanCommand = defineCommand2({
15754
16271
  }
15755
16272
  });
15756
16273
  function loadProjectContext(projectDir) {
15757
- const contextPath = join8(projectDir, ".nairon", "context.json");
15758
- if (!existsSync8(contextPath)) {
16274
+ const contextPath = join9(projectDir, ".nairon", "context.json");
16275
+ if (!existsSync9(contextPath)) {
15759
16276
  return null;
15760
16277
  }
15761
16278
  try {
15762
- const raw = readFileSync8(contextPath, "utf-8");
16279
+ const raw = readFileSync9(contextPath, "utf-8");
15763
16280
  const data = JSON.parse(raw);
15764
16281
  const context = {
15765
16282
  painPoints: data.painPoints || [],
@@ -15812,12 +16329,12 @@ function parseSince(since) {
15812
16329
  return new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
15813
16330
  }
15814
16331
  function generateReport(reportDir, score, git, agents, tests, since, sdlcAnalysis, scanCost, analysis) {
15815
- if (!existsSync8(reportDir)) {
16332
+ if (!existsSync9(reportDir)) {
15816
16333
  mkdirSync4(reportDir, { recursive: true });
15817
16334
  }
15818
16335
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
15819
16336
  const filename = `scan-${timestamp}.md`;
15820
- const filepath = join8(reportDir, filename);
16337
+ const filepath = join9(reportDir, filename);
15821
16338
  const lines = [];
15822
16339
  lines.push(`# NaironAI Scan Report`);
15823
16340
  lines.push("");
@@ -15958,11 +16475,11 @@ init_dist();
15958
16475
  init_client();
15959
16476
 
15960
16477
  // src/collectors/report-data.ts
15961
- import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
15962
- import { homedir as homedir7 } from "node:os";
15963
- import { join as join9, basename as basename2 } from "node:path";
16478
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
16479
+ import { homedir as homedir8 } from "node:os";
16480
+ import { join as join10, basename as basename3 } from "node:path";
15964
16481
  async function collectReportData(projectDir, since, until = new Date, harness) {
15965
- const projectName = basename2(projectDir);
16482
+ const projectName = basename3(projectDir);
15966
16483
  const [commits, allSessions, mcpConfigs] = await Promise.all([
15967
16484
  collectGitCommits(projectDir, since, until),
15968
16485
  collectAllSessions(since, until, projectDir),
@@ -16043,12 +16560,12 @@ function detectAIAssistedCommit(message) {
16043
16560
  }
16044
16561
  async function collectAllSessions(since, until, projectDir) {
16045
16562
  const sessions = [];
16046
- const claudeDir = join9(homedir7(), ".claude");
16047
- if (existsSync9(claudeDir)) {
16563
+ const claudeDir = join10(homedir8(), ".claude");
16564
+ if (existsSync10(claudeDir)) {
16048
16565
  sessions.push(...collectClaudeCodeSessions(claudeDir, since, until, projectDir));
16049
16566
  }
16050
- const openCodeDir = join9(homedir7(), ".local", "share", "opencode");
16051
- if (existsSync9(openCodeDir)) {
16567
+ const openCodeDir = join10(homedir8(), ".local", "share", "opencode");
16568
+ if (existsSync10(openCodeDir)) {
16052
16569
  sessions.push(...collectOpenCodeSessions2(openCodeDir, since, until));
16053
16570
  }
16054
16571
  sessions.sort((a2, b2) => a2.startTime.getTime() - b2.startTime.getTime());
@@ -16056,15 +16573,15 @@ async function collectAllSessions(since, until, projectDir) {
16056
16573
  }
16057
16574
  function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
16058
16575
  const sessions = [];
16059
- const transcriptsDir = join9(claudeDir, "transcripts");
16060
- if (!existsSync9(transcriptsDir))
16576
+ const transcriptsDir = join10(claudeDir, "transcripts");
16577
+ if (!existsSync10(transcriptsDir))
16061
16578
  return sessions;
16062
16579
  const projectHash = projectDir ? hashPath(projectDir) : null;
16063
- const projectsDir = join9(claudeDir, "projects");
16580
+ const projectsDir = join10(claudeDir, "projects");
16064
16581
  try {
16065
16582
  const transcriptFiles = readdirSync5(transcriptsDir).filter((f3) => f3.endsWith(".jsonl"));
16066
16583
  for (const file of transcriptFiles) {
16067
- const filePath = join9(transcriptsDir, file);
16584
+ const filePath = join10(transcriptsDir, file);
16068
16585
  const stat = statSync2(filePath);
16069
16586
  if (stat.mtime < since || stat.mtime > until)
16070
16587
  continue;
@@ -16075,12 +16592,12 @@ function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
16075
16592
  }
16076
16593
  } catch {}
16077
16594
  }
16078
- if (projectHash && existsSync9(projectsDir)) {
16079
- const projectSessionDir = join9(projectsDir, projectHash);
16080
- if (existsSync9(projectSessionDir)) {
16595
+ if (projectHash && existsSync10(projectsDir)) {
16596
+ const projectSessionDir = join10(projectsDir, projectHash);
16597
+ if (existsSync10(projectSessionDir)) {
16081
16598
  const projectFiles = readdirSync5(projectSessionDir).filter((f3) => f3.endsWith(".jsonl"));
16082
16599
  for (const file of projectFiles) {
16083
- const filePath = join9(projectSessionDir, file);
16600
+ const filePath = join10(projectSessionDir, file);
16084
16601
  const stat = statSync2(filePath);
16085
16602
  if (stat.mtime < since || stat.mtime > until)
16086
16603
  continue;
@@ -16099,7 +16616,7 @@ function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
16099
16616
  return sessions;
16100
16617
  }
16101
16618
  function parseClaudeTranscript(filePath, fileName) {
16102
- const content = readFileSync9(filePath, "utf-8");
16619
+ const content = readFileSync10(filePath, "utf-8");
16103
16620
  const lines = content.trim().split(`
16104
16621
  `).filter((l2) => l2.trim());
16105
16622
  if (lines.length === 0)
@@ -16189,25 +16706,25 @@ function parseClaudeTranscript(filePath, fileName) {
16189
16706
  }
16190
16707
  function collectOpenCodeSessions2(openCodeDir, since, until) {
16191
16708
  const sessions = [];
16192
- const sessionDir = join9(openCodeDir, "storage", "session");
16193
- const messageDir = join9(openCodeDir, "storage", "message");
16194
- const partDir = join9(openCodeDir, "storage", "part");
16195
- if (!existsSync9(sessionDir))
16709
+ const sessionDir = join10(openCodeDir, "storage", "session");
16710
+ const messageDir = join10(openCodeDir, "storage", "message");
16711
+ const partDir = join10(openCodeDir, "storage", "part");
16712
+ if (!existsSync10(sessionDir))
16196
16713
  return sessions;
16197
16714
  try {
16198
- const projectDirs = readdirSync5(sessionDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join9(sessionDir, d2.name));
16715
+ const projectDirs = readdirSync5(sessionDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join10(sessionDir, d2.name));
16199
16716
  for (const projectDir of projectDirs) {
16200
16717
  const sessionFiles = readdirSync5(projectDir).filter((f3) => f3.endsWith(".json"));
16201
16718
  for (const sessionFile of sessionFiles) {
16202
16719
  try {
16203
- const sessionPath = join9(projectDir, sessionFile);
16204
- const sessionData = JSON.parse(readFileSync9(sessionPath, "utf-8"));
16720
+ const sessionPath = join10(projectDir, sessionFile);
16721
+ const sessionData = JSON.parse(readFileSync10(sessionPath, "utf-8"));
16205
16722
  const createdAt = new Date(sessionData.time?.created || 0);
16206
16723
  const updatedAt = new Date(sessionData.time?.updated || sessionData.time?.created || 0);
16207
16724
  if (updatedAt < since || createdAt > until)
16208
16725
  continue;
16209
16726
  const sessionId = sessionData.id;
16210
- const sessionMsgDir = join9(messageDir, sessionId);
16727
+ const sessionMsgDir = join10(messageDir, sessionId);
16211
16728
  const prompts = [];
16212
16729
  const responses = [];
16213
16730
  const toolsUsed = new Set;
@@ -16218,12 +16735,12 @@ function collectOpenCodeSessions2(openCodeDir, since, until) {
16218
16735
  let model = "unknown";
16219
16736
  let startTime = createdAt;
16220
16737
  let endTime = updatedAt;
16221
- if (existsSync9(sessionMsgDir)) {
16738
+ if (existsSync10(sessionMsgDir)) {
16222
16739
  const msgFiles = readdirSync5(sessionMsgDir).filter((f3) => f3.endsWith(".json")).sort();
16223
16740
  for (const msgFile of msgFiles) {
16224
16741
  try {
16225
- const msgPath = join9(sessionMsgDir, msgFile);
16226
- const msgData = JSON.parse(readFileSync9(msgPath, "utf-8"));
16742
+ const msgPath = join10(sessionMsgDir, msgFile);
16743
+ const msgData = JSON.parse(readFileSync10(msgPath, "utf-8"));
16227
16744
  const msgId = msgData.id;
16228
16745
  if (msgData.model?.modelID) {
16229
16746
  model = msgData.model.modelID;
@@ -16233,14 +16750,14 @@ function collectOpenCodeSessions2(openCodeDir, since, until) {
16233
16750
  startTime = msgTime;
16234
16751
  if (msgTime > endTime)
16235
16752
  endTime = msgTime;
16236
- const msgPartDir = join9(partDir, msgId);
16753
+ const msgPartDir = join10(partDir, msgId);
16237
16754
  let messageText = "";
16238
- if (existsSync9(msgPartDir)) {
16755
+ if (existsSync10(msgPartDir)) {
16239
16756
  const partFiles = readdirSync5(msgPartDir).filter((f3) => f3.endsWith(".json")).sort();
16240
16757
  for (const partFile of partFiles) {
16241
16758
  try {
16242
- const partPath = join9(msgPartDir, partFile);
16243
- const partData = JSON.parse(readFileSync9(partPath, "utf-8"));
16759
+ const partPath = join10(msgPartDir, partFile);
16760
+ const partData = JSON.parse(readFileSync10(partPath, "utf-8"));
16244
16761
  if (partData.type === "text" && partData.text && !partData.synthetic) {
16245
16762
  messageText += partData.text + `
16246
16763
  `;
@@ -16414,10 +16931,10 @@ function analyzeResponse(text, sessionId, index, promptId, timestamp) {
16414
16931
  }
16415
16932
  function collectMCPConfigs() {
16416
16933
  const configs = [];
16417
- const claudeConfig = join9(homedir7(), ".claude.json");
16418
- if (existsSync9(claudeConfig)) {
16934
+ const claudeConfig = join10(homedir8(), ".claude.json");
16935
+ if (existsSync10(claudeConfig)) {
16419
16936
  try {
16420
- const data = JSON.parse(readFileSync9(claudeConfig, "utf-8"));
16937
+ const data = JSON.parse(readFileSync10(claudeConfig, "utf-8"));
16421
16938
  const mcpServers = data.mcpServers || {};
16422
16939
  for (const [name, config] of Object.entries(mcpServers)) {
16423
16940
  configs.push({
@@ -16430,13 +16947,13 @@ function collectMCPConfigs() {
16430
16947
  } catch {}
16431
16948
  }
16432
16949
  const openCodeConfigs = [
16433
- join9(homedir7(), ".opencode.json"),
16434
- join9(homedir7(), ".config", "opencode", "opencode.json")
16950
+ join10(homedir8(), ".opencode.json"),
16951
+ join10(homedir8(), ".config", "opencode", "opencode.json")
16435
16952
  ];
16436
16953
  for (const configPath of openCodeConfigs) {
16437
- if (existsSync9(configPath)) {
16954
+ if (existsSync10(configPath)) {
16438
16955
  try {
16439
- const data = JSON.parse(readFileSync9(configPath, "utf-8"));
16956
+ const data = JSON.parse(readFileSync10(configPath, "utf-8"));
16440
16957
  const mcpServers = data.mcp || {};
16441
16958
  for (const [name, config] of Object.entries(mcpServers)) {
16442
16959
  if (!configs.find((c3) => c3.name === name)) {
@@ -16451,10 +16968,10 @@ function collectMCPConfigs() {
16451
16968
  } catch {}
16452
16969
  }
16453
16970
  }
16454
- const cursorConfig = join9(homedir7(), ".cursor", "mcp.json");
16455
- if (existsSync9(cursorConfig)) {
16971
+ const cursorConfig = join10(homedir8(), ".cursor", "mcp.json");
16972
+ if (existsSync10(cursorConfig)) {
16456
16973
  try {
16457
- const data = JSON.parse(readFileSync9(cursorConfig, "utf-8"));
16974
+ const data = JSON.parse(readFileSync10(cursorConfig, "utf-8"));
16458
16975
  const mcpServers = data.mcpServers || {};
16459
16976
  for (const [name, config] of Object.entries(mcpServers)) {
16460
16977
  if (!configs.find((c3) => c3.name === name)) {
@@ -18655,9 +19172,9 @@ function getSeverityEmoji(severity) {
18655
19172
  }
18656
19173
 
18657
19174
  // src/lib/tool-analyzer.ts
18658
- import { existsSync as existsSync10, readdirSync as readdirSync6 } from "node:fs";
18659
- import { homedir as homedir8 } from "node:os";
18660
- import { join as join10 } from "node:path";
19175
+ import { existsSync as existsSync11, readdirSync as readdirSync6 } from "node:fs";
19176
+ import { homedir as homedir9 } from "node:os";
19177
+ import { join as join11 } from "node:path";
18661
19178
  function analyzeToolUtilization(data) {
18662
19179
  const mcpServers = analyzeMCPServers(data);
18663
19180
  const mcpSummary = buildMCPSummary(mcpServers);
@@ -18807,24 +19324,24 @@ function categorizeTools(tools) {
18807
19324
  }
18808
19325
  function analyzeSkills() {
18809
19326
  const skills = [];
18810
- const home = homedir8();
19327
+ const home = homedir9();
18811
19328
  const skillsDirs = [
18812
- join10(home, ".claude", "skills"),
18813
- join10(home, ".agents", "skills"),
18814
- join10(home, ".config", "claude", "skills")
19329
+ join11(home, ".claude", "skills"),
19330
+ join11(home, ".agents", "skills"),
19331
+ join11(home, ".config", "claude", "skills")
18815
19332
  ];
18816
19333
  const projectSkillsDirs = [
18817
- join10(process.cwd(), ".claude", "skills"),
18818
- join10(process.cwd(), ".agents", "skills")
19334
+ join11(process.cwd(), ".claude", "skills"),
19335
+ join11(process.cwd(), ".agents", "skills")
18819
19336
  ];
18820
19337
  const openCodeSkillDirs = [
18821
- join10(home, ".config", "opencode", "skills"),
18822
- join10(home, ".local", "share", "opencode", "skills")
19338
+ join11(home, ".config", "opencode", "skills"),
19339
+ join11(home, ".local", "share", "opencode", "skills")
18823
19340
  ];
18824
19341
  const allSkillDirs = [...skillsDirs, ...projectSkillsDirs, ...openCodeSkillDirs];
18825
19342
  const seenSkills = new Set;
18826
19343
  for (const skillsDir of allSkillDirs) {
18827
- if (existsSync10(skillsDir)) {
19344
+ if (existsSync11(skillsDir)) {
18828
19345
  try {
18829
19346
  const entries = readdirSync6(skillsDir, { withFileTypes: true });
18830
19347
  for (const entry of entries) {
@@ -18835,7 +19352,7 @@ function analyzeSkills() {
18835
19352
  if (entry.isDirectory() || entry.isSymbolicLink() || entry.name.endsWith(".md")) {
18836
19353
  skills.push({
18837
19354
  name: skillName,
18838
- path: join10(skillsDir, entry.name),
19355
+ path: join11(skillsDir, entry.name),
18839
19356
  used: false,
18840
19357
  usageCount: 0
18841
19358
  });
@@ -19132,22 +19649,22 @@ function renderToolUtilizationMarkdown(analysis) {
19132
19649
  }
19133
19650
 
19134
19651
  // src/lib/coverage-parser.ts
19135
- import { existsSync as existsSync11, readFileSync as readFileSync11 } from "node:fs";
19136
- import { join as join11 } from "node:path";
19652
+ import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
19653
+ import { join as join12 } from "node:path";
19137
19654
  function parseCoverageReport(projectDir) {
19138
19655
  const coveragePaths = [
19139
- { path: join11(projectDir, "coverage", "lcov.info"), parser: parseLcov },
19140
- { path: join11(projectDir, "coverage", "coverage-summary.json"), parser: parseIstanbulSummary },
19141
- { path: join11(projectDir, "coverage", "coverage-final.json"), parser: parseIstanbulFinal },
19142
- { path: join11(projectDir, "coverage", "cobertura-coverage.xml"), parser: parseCobertura },
19143
- { path: join11(projectDir, "coverage", "clover.xml"), parser: parseClover },
19144
- { path: join11(projectDir, "lcov.info"), parser: parseLcov },
19145
- { path: join11(projectDir, ".nyc_output", "coverage-summary.json"), parser: parseIstanbulSummary }
19656
+ { path: join12(projectDir, "coverage", "lcov.info"), parser: parseLcov },
19657
+ { path: join12(projectDir, "coverage", "coverage-summary.json"), parser: parseIstanbulSummary },
19658
+ { path: join12(projectDir, "coverage", "coverage-final.json"), parser: parseIstanbulFinal },
19659
+ { path: join12(projectDir, "coverage", "cobertura-coverage.xml"), parser: parseCobertura },
19660
+ { path: join12(projectDir, "coverage", "clover.xml"), parser: parseClover },
19661
+ { path: join12(projectDir, "lcov.info"), parser: parseLcov },
19662
+ { path: join12(projectDir, ".nyc_output", "coverage-summary.json"), parser: parseIstanbulSummary }
19146
19663
  ];
19147
19664
  for (const { path, parser: parser4 } of coveragePaths) {
19148
- if (existsSync11(path)) {
19665
+ if (existsSync12(path)) {
19149
19666
  try {
19150
- const content = readFileSync11(path, "utf-8");
19667
+ const content = readFileSync12(path, "utf-8");
19151
19668
  return parser4(content, path);
19152
19669
  } catch {}
19153
19670
  }
@@ -21357,9 +21874,9 @@ function renderBar4(value, width) {
21357
21874
  }
21358
21875
 
21359
21876
  // src/commands/doctor.ts
21360
- import { existsSync as existsSync12 } from "node:fs";
21361
- import { homedir as homedir9 } from "node:os";
21362
- import { join as join12 } from "node:path";
21877
+ import { existsSync as existsSync13 } from "node:fs";
21878
+ import { homedir as homedir10 } from "node:os";
21879
+ import { join as join13 } from "node:path";
21363
21880
  init_client();
21364
21881
 
21365
21882
  // src/lib/recommendations-db.ts
@@ -22016,16 +22533,16 @@ var doctorCommand = defineCommand2({
22016
22533
  results.push({ label: "OS", status: "info", value: `${process.platform} ${process.arch}` });
22017
22534
  results.push({ label: "Bun", status: "info", value: typeof Bun !== "undefined" ? Bun.version : "N/A" });
22018
22535
  results.push({ label: "Node", status: "info", value: process.version });
22019
- results.push({ label: "Home", status: "info", value: homedir9() });
22020
- const gitDir = join12(process.cwd(), ".git");
22021
- if (existsSync12(gitDir)) {
22536
+ results.push({ label: "Home", status: "info", value: homedir10() });
22537
+ const gitDir = join13(process.cwd(), ".git");
22538
+ if (existsSync13(gitDir)) {
22022
22539
  results.push({ label: "Git", status: "success", value: "Repository detected" });
22023
22540
  } else {
22024
22541
  results.push({ label: "Git", status: "warn", value: "No repository in current directory" });
22025
22542
  }
22026
22543
  for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
22027
- const resolvedPath = pathTemplate.replace("~", homedir9());
22028
- if (existsSync12(resolvedPath)) {
22544
+ const resolvedPath = pathTemplate.replace("~", homedir10());
22545
+ if (existsSync13(resolvedPath)) {
22029
22546
  results.push({ label: agent, status: "success", value: `found at ${resolvedPath}` });
22030
22547
  }
22031
22548
  }
@@ -22079,9 +22596,9 @@ var doctorCommand = defineCommand2({
22079
22596
  });
22080
22597
 
22081
22598
  // src/commands/setup.ts
22082
- import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, copyFileSync } from "node:fs";
22083
- import { join as join13, dirname as dirname2 } from "node:path";
22084
- import { homedir as homedir10, platform as platform2 } from "node:os";
22599
+ import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, copyFileSync } from "node:fs";
22600
+ import { join as join14, dirname as dirname2 } from "node:path";
22601
+ import { homedir as homedir11, platform as platform2 } from "node:os";
22085
22602
  import { execSync as execSync3 } from "node:child_process";
22086
22603
  import { createInterface } from "node:readline";
22087
22604
  var CONVEX_SITE_URL = "https://steady-bass-841.convex.site";
@@ -22100,7 +22617,7 @@ var FALLBACK_HARNESSES = [
22100
22617
  id: "claude-code",
22101
22618
  name: "Claude Code",
22102
22619
  configPaths: [
22103
- join13(homedir10(), ".claude.json")
22620
+ join14(homedir11(), ".claude.json")
22104
22621
  ],
22105
22622
  mcpConfigKey: "mcpServers",
22106
22623
  detected: false
@@ -22109,8 +22626,8 @@ var FALLBACK_HARNESSES = [
22109
22626
  id: "opencode",
22110
22627
  name: "OpenCode",
22111
22628
  configPaths: [
22112
- join13(homedir10(), ".opencode.json"),
22113
- join13(homedir10(), ".config", "opencode", "opencode.json")
22629
+ join14(homedir11(), ".opencode.json"),
22630
+ join14(homedir11(), ".config", "opencode", "opencode.json")
22114
22631
  ],
22115
22632
  mcpConfigKey: "mcp",
22116
22633
  detected: false
@@ -22119,8 +22636,8 @@ var FALLBACK_HARNESSES = [
22119
22636
  id: "cursor",
22120
22637
  name: "Cursor",
22121
22638
  configPaths: [
22122
- join13(homedir10(), ".cursor", "mcp.json"),
22123
- join13(homedir10(), "Library", "Application Support", "Cursor", "User", "globalStorage", "mcp.json")
22639
+ join14(homedir11(), ".cursor", "mcp.json"),
22640
+ join14(homedir11(), "Library", "Application Support", "Cursor", "User", "globalStorage", "mcp.json")
22124
22641
  ],
22125
22642
  mcpConfigKey: "mcpServers",
22126
22643
  detected: false
@@ -22137,7 +22654,7 @@ function getConfigPathsForPlatform(harness) {
22137
22654
  } else {
22138
22655
  paths = pathMap.linux;
22139
22656
  }
22140
- return paths.map((p) => p.replace(/^~/, homedir10()).replace(/%USERPROFILE%/gi, homedir10()).replace(/%APPDATA%/gi, join13(homedir10(), "AppData", "Roaming")));
22657
+ return paths.map((p) => p.replace(/^~/, homedir11()).replace(/%USERPROFILE%/gi, homedir11()).replace(/%APPDATA%/gi, join14(homedir11(), "AppData", "Roaming")));
22141
22658
  }
22142
22659
  function convertAPIHarnessToConfig(harness) {
22143
22660
  return {
@@ -22152,7 +22669,7 @@ function detectHarnesses(harnesses) {
22152
22669
  const detected = [];
22153
22670
  for (const harness of harnesses) {
22154
22671
  for (const configPath of harness.configPaths) {
22155
- if (existsSync13(configPath)) {
22672
+ if (existsSync14(configPath)) {
22156
22673
  detected.push({
22157
22674
  ...harness,
22158
22675
  detected: true,
@@ -22165,19 +22682,19 @@ function detectHarnesses(harnesses) {
22165
22682
  return detected;
22166
22683
  }
22167
22684
  function backupConfig(configPath) {
22168
- const backupDir = join13(homedir10(), ".nairon", "backups");
22169
- if (!existsSync13(backupDir)) {
22685
+ const backupDir = join14(homedir11(), ".nairon", "backups");
22686
+ if (!existsSync14(backupDir)) {
22170
22687
  mkdirSync5(backupDir, { recursive: true });
22171
22688
  }
22172
22689
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
22173
22690
  const filename = configPath.replace(/[/\\]/g, "_").replace(/^_/, "");
22174
- const backupPath = join13(backupDir, `${timestamp}_${filename}`);
22691
+ const backupPath = join14(backupDir, `${timestamp}_${filename}`);
22175
22692
  copyFileSync(configPath, backupPath);
22176
22693
  return backupPath;
22177
22694
  }
22178
22695
  function readJsonConfig(configPath) {
22179
22696
  try {
22180
- const content = readFileSync12(configPath, "utf-8");
22697
+ const content = readFileSync13(configPath, "utf-8");
22181
22698
  return JSON.parse(content);
22182
22699
  } catch {
22183
22700
  return {};
@@ -22185,7 +22702,7 @@ function readJsonConfig(configPath) {
22185
22702
  }
22186
22703
  function writeJsonConfig(configPath, config) {
22187
22704
  const dir = dirname2(configPath);
22188
- if (!existsSync13(dir)) {
22705
+ if (!existsSync14(dir)) {
22189
22706
  mkdirSync5(dir, { recursive: true });
22190
22707
  }
22191
22708
  writeFileSync6(configPath, JSON.stringify(config, null, 2));
@@ -22229,7 +22746,7 @@ function getInstalledMCPs(harnesses) {
22229
22746
  const installed = new Set;
22230
22747
  for (const harness of harnesses) {
22231
22748
  for (const configPath of harness.configPaths) {
22232
- if (!existsSync13(configPath))
22749
+ if (!existsSync14(configPath))
22233
22750
  continue;
22234
22751
  const config = readJsonConfig(configPath);
22235
22752
  const mcpServers = config[harness.mcpConfigKey];
@@ -22253,7 +22770,7 @@ async function installMCPServer(serverName, instruction, harness) {
22253
22770
  if (!harness.configPath || !instruction.mcpServerConfig)
22254
22771
  return false;
22255
22772
  try {
22256
- if (existsSync13(harness.configPath)) {
22773
+ if (existsSync14(harness.configPath)) {
22257
22774
  backupConfig(harness.configPath);
22258
22775
  }
22259
22776
  const config = readJsonConfig(harness.configPath);
@@ -22298,8 +22815,8 @@ async function installBeads(projectDir) {
22298
22815
  }
22299
22816
  }
22300
22817
  }
22301
- const beadsDir = join13(projectDir, ".beads");
22302
- if (!existsSync13(beadsDir)) {
22818
+ const beadsDir = join14(projectDir, ".beads");
22819
+ if (!existsSync14(beadsDir)) {
22303
22820
  const projectName = projectDir.split(/[/\\]/).pop() ?? "project";
22304
22821
  const initSpinner = createSpinner(`Initializing Beads in ${projectName}...`);
22305
22822
  initSpinner.start();
@@ -22318,9 +22835,9 @@ async function installBeads(projectDir) {
22318
22835
  }
22319
22836
  }
22320
22837
  async function createClaudeRules(projectDir) {
22321
- const claudeMdPath = join13(projectDir, "CLAUDE.md");
22322
- const agentsMdPath = join13(projectDir, "AGENTS.md");
22323
- if (existsSync13(claudeMdPath) || existsSync13(agentsMdPath)) {
22838
+ const claudeMdPath = join14(projectDir, "CLAUDE.md");
22839
+ const agentsMdPath = join14(projectDir, "AGENTS.md");
22840
+ if (existsSync14(claudeMdPath) || existsSync14(agentsMdPath)) {
22324
22841
  console.log(` ${icons.info} CLAUDE.md or AGENTS.md already exists`);
22325
22842
  return true;
22326
22843
  }
@@ -22643,6 +23160,26 @@ var BOLD = "\x1B[1m";
22643
23160
  var RESET = "\x1B[0m";
22644
23161
  var MAGENTA = "\x1B[35m";
22645
23162
  var CHANGELOG = [
23163
+ {
23164
+ version: "0.3.15",
23165
+ date: "2026-02-14",
23166
+ title: "Contextual Recommendations",
23167
+ highlights: [
23168
+ "Recommendations now linked to specific frustrations they prevent",
23169
+ "Total time and cost savings summary",
23170
+ "See exactly which issues each tool would have fixed"
23171
+ ]
23172
+ },
23173
+ {
23174
+ version: "0.3.14",
23175
+ date: "2026-02-14",
23176
+ title: "Enhanced Frustration Detection",
23177
+ highlights: [
23178
+ "Scan now shows exact prompts that caused frustration",
23179
+ "Root cause analysis: hallucination, context loss, undo loops",
23180
+ "Each frustration linked to a specific fix with install command"
23181
+ ]
23182
+ },
22646
23183
  {
22647
23184
  version: "0.3.13",
22648
23185
  date: "2026-02-14",
@@ -23051,7 +23588,7 @@ function showFullChangelog() {
23051
23588
  // package.json
23052
23589
  var package_default = {
23053
23590
  name: "nairon-bench",
23054
- version: "0.3.13",
23591
+ version: "0.3.15",
23055
23592
  description: "AI workflow benchmarking CLI",
23056
23593
  type: "module",
23057
23594
  bin: {
@@ -23110,9 +23647,9 @@ var versionCommand = defineCommand2({
23110
23647
 
23111
23648
  // src/commands/init.ts
23112
23649
  init_dist();
23113
- import { existsSync as existsSync14 } from "node:fs";
23114
- import { homedir as homedir11, platform as platform3, arch } from "node:os";
23115
- import { join as join14 } from "node:path";
23650
+ import { existsSync as existsSync15 } from "node:fs";
23651
+ import { homedir as homedir12, platform as platform3, arch } from "node:os";
23652
+ import { join as join15 } from "node:path";
23116
23653
  init_client();
23117
23654
 
23118
23655
  // src/lib/github-auth.ts
@@ -23282,8 +23819,8 @@ var initCommand = defineCommand2({
23282
23819
  consola.start("Step 2/6: Detecting AI agents...");
23283
23820
  const detectedAgents = [];
23284
23821
  for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
23285
- const resolvedPath = pathTemplate.replace("~", homedir11());
23286
- if (existsSync14(resolvedPath)) {
23822
+ const resolvedPath = pathTemplate.replace("~", homedir12());
23823
+ if (existsSync15(resolvedPath)) {
23287
23824
  detectedAgents.push(agent);
23288
23825
  consola.success(` Found: ${agent}`);
23289
23826
  }
@@ -23386,7 +23923,7 @@ var initCommand = defineCommand2({
23386
23923
  { name: "playwright", configs: ["playwright.config.ts", "playwright.config.js"] }
23387
23924
  ];
23388
23925
  for (const fw of testFrameworks) {
23389
- if (fw.configs.some((c3) => existsSync14(join14(process.cwd(), c3)))) {
23926
+ if (fw.configs.some((c3) => existsSync15(join15(process.cwd(), c3)))) {
23390
23927
  toolStack.testing.push(fw.name);
23391
23928
  }
23392
23929
  }
@@ -23455,13 +23992,13 @@ var initCommand = defineCommand2({
23455
23992
  });
23456
23993
  function detectPackageManager() {
23457
23994
  const cwd = process.cwd();
23458
- if (existsSync14(join14(cwd, "bun.lock")) || existsSync14(join14(cwd, "bun.lockb")))
23995
+ if (existsSync15(join15(cwd, "bun.lock")) || existsSync15(join15(cwd, "bun.lockb")))
23459
23996
  return "bun";
23460
- if (existsSync14(join14(cwd, "pnpm-lock.yaml")))
23997
+ if (existsSync15(join15(cwd, "pnpm-lock.yaml")))
23461
23998
  return "pnpm";
23462
- if (existsSync14(join14(cwd, "yarn.lock")))
23999
+ if (existsSync15(join15(cwd, "yarn.lock")))
23463
24000
  return "yarn";
23464
- if (existsSync14(join14(cwd, "package-lock.json")))
24001
+ if (existsSync15(join15(cwd, "package-lock.json")))
23465
24002
  return "npm";
23466
24003
  return;
23467
24004
  }
@@ -23476,15 +24013,15 @@ var CLAUDE_CODE_SKILLS = [
23476
24013
  ];
23477
24014
  function detectEasyWins(detectedAgents) {
23478
24015
  const wins = [];
23479
- const home = homedir11();
24016
+ const home = homedir12();
23480
24017
  const hasClaudeCode = detectedAgents.some((a2) => a2.toLowerCase().includes("claude") || a2.toLowerCase().includes("opencode"));
23481
- const claudeConfigDir = join14(home, ".claude");
23482
- const hasClaudeConfig = existsSync14(claudeConfigDir);
24018
+ const claudeConfigDir = join15(home, ".claude");
24019
+ const hasClaudeConfig = existsSync15(claudeConfigDir);
23483
24020
  if (hasClaudeCode || hasClaudeConfig) {
23484
- const skillsDir = join14(home, ".claude", "skills");
24021
+ const skillsDir = join15(home, ".claude", "skills");
23485
24022
  for (const skill of CLAUDE_CODE_SKILLS) {
23486
- const skillPath = join14(skillsDir, skill.id);
23487
- const isInstalled = existsSync14(skillPath);
24023
+ const skillPath = join15(skillsDir, skill.id);
24024
+ const isInstalled = existsSync15(skillPath);
23488
24025
  if (!isInstalled) {
23489
24026
  wins.push({
23490
24027
  icon: skill.icon,
@@ -23500,14 +24037,14 @@ function detectEasyWins(detectedAgents) {
23500
24037
  // src/commands/onboard.ts
23501
24038
  init_dist();
23502
24039
  init_client();
23503
- import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync7, readdirSync as readdirSync9 } from "node:fs";
23504
- import { join as join16 } from "node:path";
24040
+ import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync7, readdirSync as readdirSync9 } from "node:fs";
24041
+ import { join as join17 } from "node:path";
23505
24042
 
23506
24043
  // src/lib/project-context-detector.ts
23507
- import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "node:fs";
23508
- import { join as join15, basename as basename3 } from "node:path";
24044
+ import { existsSync as existsSync16, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "node:fs";
24045
+ import { join as join16, basename as basename4 } from "node:path";
23509
24046
  function detectProjectContext(projectDir) {
23510
- const projectName = basename3(projectDir);
24047
+ const projectName = basename4(projectDir);
23511
24048
  const projectId = generateProjectId(projectDir);
23512
24049
  const techStack = detectTechStack(projectDir);
23513
24050
  const projectType = inferProjectType(projectDir, techStack);
@@ -23541,11 +24078,11 @@ function detectTechStack(projectDir) {
23541
24078
  };
23542
24079
  }
23543
24080
  function readPackageJson(projectDir) {
23544
- const pkgPath = join15(projectDir, "package.json");
23545
- if (!existsSync15(pkgPath))
24081
+ const pkgPath = join16(projectDir, "package.json");
24082
+ if (!existsSync16(pkgPath))
23546
24083
  return {};
23547
24084
  try {
23548
- return JSON.parse(readFileSync13(pkgPath, "utf-8"));
24085
+ return JSON.parse(readFileSync14(pkgPath, "utf-8"));
23549
24086
  } catch {
23550
24087
  return {};
23551
24088
  }
@@ -23566,34 +24103,34 @@ function detectFramework(deps) {
23566
24103
  return null;
23567
24104
  }
23568
24105
  function detectMetaFramework(deps, projectDir) {
23569
- if (deps.next || existsSync15(join15(projectDir, "next.config.js")) || existsSync15(join15(projectDir, "next.config.mjs")))
24106
+ if (deps.next || existsSync16(join16(projectDir, "next.config.js")) || existsSync16(join16(projectDir, "next.config.mjs")))
23570
24107
  return "next";
23571
- if (deps.nuxt || existsSync15(join15(projectDir, "nuxt.config.ts")))
24108
+ if (deps.nuxt || existsSync16(join16(projectDir, "nuxt.config.ts")))
23572
24109
  return "nuxt";
23573
24110
  if (deps["@sveltejs/kit"])
23574
24111
  return "sveltekit";
23575
- if (deps.astro || existsSync15(join15(projectDir, "astro.config.mjs")))
24112
+ if (deps.astro || existsSync16(join16(projectDir, "astro.config.mjs")))
23576
24113
  return "astro";
23577
24114
  if (deps["@remix-run/react"])
23578
24115
  return "remix";
23579
24116
  if (deps.gatsby)
23580
24117
  return "gatsby";
23581
- if (deps.vite || existsSync15(join15(projectDir, "vite.config.ts")))
24118
+ if (deps.vite || existsSync16(join16(projectDir, "vite.config.ts")))
23582
24119
  return "vite";
23583
24120
  return null;
23584
24121
  }
23585
24122
  function detectRuntime(projectDir) {
23586
- if (existsSync15(join15(projectDir, "bun.lockb")) || existsSync15(join15(projectDir, "bun.lock")))
24123
+ if (existsSync16(join16(projectDir, "bun.lockb")) || existsSync16(join16(projectDir, "bun.lock")))
23587
24124
  return "bun";
23588
- if (existsSync15(join15(projectDir, "deno.json")) || existsSync15(join15(projectDir, "deno.lock")))
24125
+ if (existsSync16(join16(projectDir, "deno.json")) || existsSync16(join16(projectDir, "deno.lock")))
23589
24126
  return "deno";
23590
24127
  return "node";
23591
24128
  }
23592
24129
  function detectLanguage(projectDir) {
23593
- if (existsSync15(join15(projectDir, "tsconfig.json")))
24130
+ if (existsSync16(join16(projectDir, "tsconfig.json")))
23594
24131
  return "typescript";
23595
- const srcDir = join15(projectDir, "src");
23596
- if (existsSync15(srcDir)) {
24132
+ const srcDir = join16(projectDir, "src");
24133
+ if (existsSync16(srcDir)) {
23597
24134
  try {
23598
24135
  const files = readdirSync8(srcDir);
23599
24136
  if (files.some((f3) => f3.endsWith(".ts") || f3.endsWith(".tsx")))
@@ -23604,7 +24141,7 @@ function detectLanguage(projectDir) {
23604
24141
  }
23605
24142
  function detectStyling(deps, projectDir) {
23606
24143
  const tools = [];
23607
- if (deps.tailwindcss || existsSync15(join15(projectDir, "tailwind.config.js")) || existsSync15(join15(projectDir, "tailwind.config.ts")))
24144
+ if (deps.tailwindcss || existsSync16(join16(projectDir, "tailwind.config.js")) || existsSync16(join16(projectDir, "tailwind.config.ts")))
23608
24145
  tools.push("tailwind");
23609
24146
  if (deps["styled-components"])
23610
24147
  tools.push("styled-components");
@@ -23649,13 +24186,13 @@ function detectORM(deps) {
23649
24186
  }
23650
24187
  function detectTesting(deps, projectDir) {
23651
24188
  const tools = [];
23652
- if (deps.vitest || existsSync15(join15(projectDir, "vitest.config.ts")))
24189
+ if (deps.vitest || existsSync16(join16(projectDir, "vitest.config.ts")))
23653
24190
  tools.push("vitest");
23654
- if (deps.jest || existsSync15(join15(projectDir, "jest.config.js")))
24191
+ if (deps.jest || existsSync16(join16(projectDir, "jest.config.js")))
23655
24192
  tools.push("jest");
23656
- if (deps["@playwright/test"] || existsSync15(join15(projectDir, "playwright.config.ts")))
24193
+ if (deps["@playwright/test"] || existsSync16(join16(projectDir, "playwright.config.ts")))
23657
24194
  tools.push("playwright");
23658
- if (deps.cypress || existsSync15(join15(projectDir, "cypress.config.ts")))
24195
+ if (deps.cypress || existsSync16(join16(projectDir, "cypress.config.ts")))
23659
24196
  tools.push("cypress");
23660
24197
  if (deps["@testing-library/react"] || deps["@testing-library/vue"])
23661
24198
  tools.push("testing-library");
@@ -23687,7 +24224,7 @@ function detectBuildTools(deps, projectDir) {
23687
24224
  tools.push("esbuild");
23688
24225
  if (deps.webpack)
23689
24226
  tools.push("webpack");
23690
- if (deps.turbo || existsSync15(join15(projectDir, "turbo.json")))
24227
+ if (deps.turbo || existsSync16(join16(projectDir, "turbo.json")))
23691
24228
  tools.push("turborepo");
23692
24229
  if (deps.tsup)
23693
24230
  tools.push("tsup");
@@ -23697,17 +24234,17 @@ function detectBuildTools(deps, projectDir) {
23697
24234
  }
23698
24235
  function detectDeployment(projectDir) {
23699
24236
  const tools = [];
23700
- if (existsSync15(join15(projectDir, "vercel.json")) || existsSync15(join15(projectDir, ".vercel")))
24237
+ if (existsSync16(join16(projectDir, "vercel.json")) || existsSync16(join16(projectDir, ".vercel")))
23701
24238
  tools.push("vercel");
23702
- if (existsSync15(join15(projectDir, "netlify.toml")))
24239
+ if (existsSync16(join16(projectDir, "netlify.toml")))
23703
24240
  tools.push("netlify");
23704
- if (existsSync15(join15(projectDir, "fly.toml")))
24241
+ if (existsSync16(join16(projectDir, "fly.toml")))
23705
24242
  tools.push("fly");
23706
- if (existsSync15(join15(projectDir, "railway.json")))
24243
+ if (existsSync16(join16(projectDir, "railway.json")))
23707
24244
  tools.push("railway");
23708
- if (existsSync15(join15(projectDir, "Dockerfile")))
24245
+ if (existsSync16(join16(projectDir, "Dockerfile")))
23709
24246
  tools.push("docker");
23710
- if (existsSync15(join15(projectDir, ".github", "workflows")))
24247
+ if (existsSync16(join16(projectDir, ".github", "workflows")))
23711
24248
  tools.push("github-actions");
23712
24249
  return tools;
23713
24250
  }
@@ -23730,20 +24267,20 @@ function detectAIML(deps) {
23730
24267
  return tools;
23731
24268
  }
23732
24269
  function inferProjectType(projectDir, techStack) {
23733
- if (existsSync15(join15(projectDir, "packages")) || existsSync15(join15(projectDir, "apps"))) {
24270
+ if (existsSync16(join16(projectDir, "packages")) || existsSync16(join16(projectDir, "apps"))) {
23734
24271
  return "monorepo";
23735
24272
  }
23736
24273
  const pkg = readPackageJson(projectDir);
23737
- if (pkg.bin || existsSync15(join15(projectDir, "src", "cli.ts")) || existsSync15(join15(projectDir, "src", "index.ts"))) {
24274
+ if (pkg.bin || existsSync16(join16(projectDir, "src", "cli.ts")) || existsSync16(join16(projectDir, "src", "index.ts"))) {
23738
24275
  const hasBin = !!pkg.bin;
23739
- const hasCommands = existsSync15(join15(projectDir, "src", "commands"));
24276
+ const hasCommands = existsSync16(join16(projectDir, "src", "commands"));
23740
24277
  if (hasBin || hasCommands)
23741
24278
  return "cli";
23742
24279
  }
23743
24280
  if (!techStack.framework && !techStack.metaFramework && pkg.main) {
23744
24281
  return "library";
23745
24282
  }
23746
- if (existsSync15(join15(projectDir, "app.json")) || existsSync15(join15(projectDir, "expo"))) {
24283
+ if (existsSync16(join16(projectDir, "app.json")) || existsSync16(join16(projectDir, "expo"))) {
23747
24284
  return "mobile";
23748
24285
  }
23749
24286
  if (techStack.database.length > 0 && !techStack.framework) {
@@ -23760,10 +24297,10 @@ function extractDescription(projectDir) {
23760
24297
  return pkg.description;
23761
24298
  const readmePaths = ["README.md", "readme.md", "Readme.md"];
23762
24299
  for (const readme of readmePaths) {
23763
- const path = join15(projectDir, readme);
23764
- if (existsSync15(path)) {
24300
+ const path = join16(projectDir, readme);
24301
+ if (existsSync16(path)) {
23765
24302
  try {
23766
- const content = readFileSync13(path, "utf-8");
24303
+ const content = readFileSync14(path, "utf-8");
23767
24304
  const lines = content.split(`
23768
24305
  `);
23769
24306
  let foundTitle = false;
@@ -25181,9 +25718,9 @@ var onboardCommand = defineCommand2({
25181
25718
  },
25182
25719
  async run({ args }) {
25183
25720
  const projectPath = process.cwd();
25184
- const contextPath = join16(projectPath, ".nairon", "context.json");
25185
- if (existsSync16(contextPath) && !args.force) {
25186
- const existing = JSON.parse(readFileSync14(contextPath, "utf-8"));
25721
+ const contextPath = join17(projectPath, ".nairon", "context.json");
25722
+ if (existsSync17(contextPath) && !args.force) {
25723
+ const existing = JSON.parse(readFileSync15(contextPath, "utf-8"));
25187
25724
  consola.info("Project already onboarded. Use --force to re-run.");
25188
25725
  consola.info(`Business: ${existing.businessContext?.domain || existing.businessContext?.slice?.(0, 50) || "Not set"}...`);
25189
25726
  consola.info(`Stack: ${formatTechStackSummary(existing)}`);
@@ -25331,8 +25868,8 @@ var onboardCommand = defineCommand2({
25331
25868
  createdAt: new Date().toISOString(),
25332
25869
  updatedAt: new Date().toISOString()
25333
25870
  };
25334
- const naironDir = join16(projectPath, ".nairon");
25335
- if (!existsSync16(naironDir)) {
25871
+ const naironDir = join17(projectPath, ".nairon");
25872
+ if (!existsSync17(naironDir)) {
25336
25873
  mkdirSync6(naironDir, { recursive: true });
25337
25874
  }
25338
25875
  writeFileSync7(contextPath, JSON.stringify(fullContext, null, 2));
@@ -25423,51 +25960,51 @@ function formatTechStackSummary(context) {
25423
25960
  function detectInstalledTools(projectPath) {
25424
25961
  const tools = [];
25425
25962
  const home = process.env.HOME || "";
25426
- const userClaudeJson = join16(home, ".claude.json");
25427
- if (existsSync16(userClaudeJson)) {
25963
+ const userClaudeJson = join17(home, ".claude.json");
25964
+ if (existsSync17(userClaudeJson)) {
25428
25965
  try {
25429
- const config = JSON.parse(readFileSync14(userClaudeJson, "utf-8"));
25966
+ const config = JSON.parse(readFileSync15(userClaudeJson, "utf-8"));
25430
25967
  if (config.mcpServers) {
25431
25968
  tools.push(...Object.keys(config.mcpServers));
25432
25969
  }
25433
25970
  } catch {}
25434
25971
  }
25435
- const claudeDesktopConfig = join16(home, ".claude", "claude_desktop_config.json");
25436
- if (existsSync16(claudeDesktopConfig)) {
25972
+ const claudeDesktopConfig = join17(home, ".claude", "claude_desktop_config.json");
25973
+ if (existsSync17(claudeDesktopConfig)) {
25437
25974
  try {
25438
- const config = JSON.parse(readFileSync14(claudeDesktopConfig, "utf-8"));
25975
+ const config = JSON.parse(readFileSync15(claudeDesktopConfig, "utf-8"));
25439
25976
  if (config.mcpServers) {
25440
25977
  tools.push(...Object.keys(config.mcpServers));
25441
25978
  }
25442
25979
  } catch {}
25443
25980
  }
25444
- const projectMcpJson = join16(projectPath, ".mcp.json");
25445
- if (existsSync16(projectMcpJson)) {
25981
+ const projectMcpJson = join17(projectPath, ".mcp.json");
25982
+ if (existsSync17(projectMcpJson)) {
25446
25983
  try {
25447
- const config = JSON.parse(readFileSync14(projectMcpJson, "utf-8"));
25984
+ const config = JSON.parse(readFileSync15(projectMcpJson, "utf-8"));
25448
25985
  if (config.mcpServers) {
25449
25986
  tools.push(...Object.keys(config.mcpServers));
25450
25987
  }
25451
25988
  } catch {}
25452
25989
  }
25453
- const projectClaudeConfig = join16(projectPath, ".claude", "settings.json");
25454
- if (existsSync16(projectClaudeConfig)) {
25990
+ const projectClaudeConfig = join17(projectPath, ".claude", "settings.json");
25991
+ if (existsSync17(projectClaudeConfig)) {
25455
25992
  try {
25456
- const config = JSON.parse(readFileSync14(projectClaudeConfig, "utf-8"));
25993
+ const config = JSON.parse(readFileSync15(projectClaudeConfig, "utf-8"));
25457
25994
  if (config.mcpServers) {
25458
25995
  tools.push(...Object.keys(config.mcpServers));
25459
25996
  }
25460
25997
  } catch {}
25461
25998
  }
25462
- const skillsDir = join16(home, ".config", "opencode", "skills");
25463
- if (existsSync16(skillsDir)) {
25999
+ const skillsDir = join17(home, ".config", "opencode", "skills");
26000
+ if (existsSync17(skillsDir)) {
25464
26001
  try {
25465
26002
  const skills = readdirSync9(skillsDir);
25466
26003
  tools.push(...skills.filter((s2) => !s2.startsWith(".")));
25467
26004
  } catch {}
25468
26005
  }
25469
- const agentsSkillsDir = join16(home, ".agents", "skills");
25470
- if (existsSync16(agentsSkillsDir)) {
26006
+ const agentsSkillsDir = join17(home, ".agents", "skills");
26007
+ if (existsSync17(agentsSkillsDir)) {
25471
26008
  try {
25472
26009
  const skills = readdirSync9(agentsSkillsDir);
25473
26010
  tools.push(...skills.filter((s2) => !s2.startsWith(".")));
@@ -25477,11 +26014,11 @@ function detectInstalledTools(projectPath) {
25477
26014
  }
25478
26015
  function detectPrimaryAgent() {
25479
26016
  const home = process.env.HOME || "";
25480
- if (existsSync16(join16(home, ".claude")))
26017
+ if (existsSync17(join17(home, ".claude")))
25481
26018
  return "claude-code";
25482
- if (existsSync16(join16(home, ".cursor")))
26019
+ if (existsSync17(join17(home, ".cursor")))
25483
26020
  return "cursor";
25484
- if (existsSync16(join16(home, ".config", "opencode")))
26021
+ if (existsSync17(join17(home, ".config", "opencode")))
25485
26022
  return "opencode";
25486
26023
  return;
25487
26024
  }
@@ -25576,7 +26113,7 @@ function formatBytes(bytes) {
25576
26113
  // package.json
25577
26114
  var package_default2 = {
25578
26115
  name: "nairon-bench",
25579
- version: "0.3.13",
26116
+ version: "0.3.15",
25580
26117
  description: "AI workflow benchmarking CLI",
25581
26118
  type: "module",
25582
26119
  bin: {