nairon-bench 0.3.12 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2302 -1143
- package/package.json +4 -3
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
|
|
4451
|
-
import { join as
|
|
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);
|
|
@@ -10767,7 +10767,7 @@ function getTechStackSpecificRecommendations(projectContext) {
|
|
|
10767
10767
|
description: "Vercel's React and Next.js patterns for your stack",
|
|
10768
10768
|
impact: "medium",
|
|
10769
10769
|
type: "skill",
|
|
10770
|
-
installCommand: "npx skills add vercel/react-best-practices"
|
|
10770
|
+
installCommand: "npx skills add vercel-labs/agent-skills@vercel-react-best-practices --yes"
|
|
10771
10771
|
});
|
|
10772
10772
|
}
|
|
10773
10773
|
if (database2?.includes("convex")) {
|
|
@@ -11458,652 +11458,246 @@ function renderBar(value, width) {
|
|
|
11458
11458
|
return "█".repeat(filled) + "░".repeat(empty);
|
|
11459
11459
|
}
|
|
11460
11460
|
|
|
11461
|
-
// src/lib/
|
|
11462
|
-
import {
|
|
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
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
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
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
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
|
-
|
|
11573
|
-
|
|
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
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
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
|
-
|
|
11661
|
-
|
|
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
|
-
|
|
11678
|
-
|
|
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
|
-
|
|
11689
|
-
|
|
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
|
-
|
|
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
|
|
11675
|
+
function translateLevel(level) {
|
|
11676
|
+
if (level === 0) {
|
|
11677
|
+
return false;
|
|
11678
|
+
}
|
|
11721
11679
|
return {
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
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
|
|
11731
|
-
const
|
|
11732
|
-
|
|
11733
|
-
|
|
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
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
if (
|
|
11776
|
-
|
|
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 {}
|
|
11784
|
-
}
|
|
11785
|
-
}
|
|
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;
|
|
11803
|
-
}
|
|
11804
|
-
}
|
|
11805
|
-
return true;
|
|
11806
|
-
});
|
|
11807
|
-
}
|
|
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
|
-
]
|
|
11870
|
-
};
|
|
11871
|
-
|
|
11872
|
-
// ../../node_modules/ora/index.js
|
|
11873
|
-
import process8 from "node:process";
|
|
11874
|
-
import { stripVTControlCharacters } from "node:util";
|
|
11875
|
-
|
|
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]
|
|
11932
|
-
}
|
|
11933
|
-
};
|
|
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;
|
|
12069
|
-
}
|
|
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;
|
|
12077
|
-
}
|
|
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
|
-
};
|
|
12091
|
-
}
|
|
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;
|
|
12100
|
-
}
|
|
12101
|
-
if (sniffFlags) {
|
|
12102
|
-
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
|
|
12103
|
-
return 3;
|
|
12104
|
-
}
|
|
12105
|
-
if (hasFlag("color=256")) {
|
|
12106
|
-
return 2;
|
|
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;
|
|
12107
11701
|
}
|
|
12108
11702
|
}
|
|
12109
11703
|
if ("TF_BUILD" in env2 && "AGENT_NAME" in env2) {
|
|
@@ -14878,394 +14472,1290 @@ class Ora {
|
|
|
14878
14472
|
`)) {
|
|
14879
14473
|
count += Math.max(1, Math.ceil(stringWidth2(line) / columns));
|
|
14880
14474
|
}
|
|
14881
|
-
return count;
|
|
14882
|
-
}
|
|
14883
|
-
get isEnabled() {
|
|
14884
|
-
return this.#options.isEnabled && !this.#options.isSilent;
|
|
14475
|
+
return count;
|
|
14476
|
+
}
|
|
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");
|
|
14483
|
+
}
|
|
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");
|
|
14492
|
+
}
|
|
14493
|
+
this.#options.isSilent = value;
|
|
14494
|
+
}
|
|
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;
|
|
14500
|
+
}
|
|
14501
|
+
const { frames } = this.#spinner;
|
|
14502
|
+
let frame = frames[this.#frameIndex];
|
|
14503
|
+
if (this.color) {
|
|
14504
|
+
frame = source_default[this.color](frame);
|
|
14505
|
+
}
|
|
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;
|
|
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;
|
|
14529
|
+
}
|
|
14530
|
+
#hookStream(stream) {
|
|
14531
|
+
if (!stream || this.#hookedStreams.has(stream) || !stream.isTTY || typeof stream.write !== "function") {
|
|
14532
|
+
return;
|
|
14533
|
+
}
|
|
14534
|
+
if (activeHooksPerStream.has(stream)) {
|
|
14535
|
+
console.warn("[ora] Multiple concurrent spinners detected. This may cause visual corruption. Use one spinner at a time.");
|
|
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);
|
|
14541
|
+
}
|
|
14542
|
+
#installHook() {
|
|
14543
|
+
if (!this.isEnabled || this.#hookedStreams.size > 0) {
|
|
14544
|
+
return;
|
|
14545
|
+
}
|
|
14546
|
+
const streamsToHook = new Set([this.#stream, process8.stdout, process8.stderr]);
|
|
14547
|
+
for (const stream of streamsToHook) {
|
|
14548
|
+
this.#hookStream(stream);
|
|
14549
|
+
}
|
|
14550
|
+
}
|
|
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
|
+
}
|
|
14557
|
+
}
|
|
14558
|
+
this.#hookedStreams.clear();
|
|
14559
|
+
}
|
|
14560
|
+
#hookedWrite(stream, originalWrite, chunk, encoding, callback) {
|
|
14561
|
+
if (typeof encoding === "function") {
|
|
14562
|
+
callback = encoding;
|
|
14563
|
+
encoding = undefined;
|
|
14564
|
+
}
|
|
14565
|
+
if (this.#isInternalWrite) {
|
|
14566
|
+
return originalWrite.call(stream, chunk, encoding, callback);
|
|
14567
|
+
}
|
|
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;
|
|
14581
|
+
}
|
|
14582
|
+
render() {
|
|
14583
|
+
if (!this.isEnabled || this.#drainHandler || this.#deferRenderTimer) {
|
|
14584
|
+
return this;
|
|
14585
|
+
}
|
|
14586
|
+
const useSynchronizedOutput = this.#stream.isTTY;
|
|
14587
|
+
let shouldDisableSynchronizedOutput = false;
|
|
14588
|
+
try {
|
|
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);
|
|
14614
|
+
} finally {
|
|
14615
|
+
if (shouldDisableSynchronizedOutput) {
|
|
14616
|
+
this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_DISABLE));
|
|
14617
|
+
}
|
|
14618
|
+
}
|
|
14619
|
+
return this;
|
|
14620
|
+
}
|
|
14621
|
+
start(text) {
|
|
14622
|
+
if (text) {
|
|
14623
|
+
this.text = text;
|
|
14624
|
+
}
|
|
14625
|
+
if (this.isSilent) {
|
|
14626
|
+
return this;
|
|
14627
|
+
}
|
|
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;
|
|
14636
|
+
}
|
|
14637
|
+
if (this.isSpinning) {
|
|
14638
|
+
return this;
|
|
14639
|
+
}
|
|
14640
|
+
if (this.#options.hideCursor) {
|
|
14641
|
+
cli_cursor_default.hide(this.#stream);
|
|
14642
|
+
}
|
|
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;
|
|
14885
14651
|
}
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
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;
|
|
14889
14662
|
}
|
|
14890
|
-
this
|
|
14663
|
+
if (this.isEnabled) {
|
|
14664
|
+
this.clear();
|
|
14665
|
+
if (this.#options.hideCursor) {
|
|
14666
|
+
cli_cursor_default.show(this.#stream);
|
|
14667
|
+
}
|
|
14668
|
+
}
|
|
14669
|
+
if (this.#isDiscardingStdin) {
|
|
14670
|
+
this.#isDiscardingStdin = false;
|
|
14671
|
+
stdin_discarder_default.stop();
|
|
14672
|
+
}
|
|
14673
|
+
return this;
|
|
14891
14674
|
}
|
|
14892
|
-
|
|
14893
|
-
return this
|
|
14675
|
+
succeed(text) {
|
|
14676
|
+
return this.stopAndPersist({ symbol: exports_symbols.success, text });
|
|
14894
14677
|
}
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
throw new TypeError("The `isSilent` option must be a boolean");
|
|
14898
|
-
}
|
|
14899
|
-
this.#options.isSilent = value;
|
|
14678
|
+
fail(text) {
|
|
14679
|
+
return this.stopAndPersist({ symbol: exports_symbols.error, text });
|
|
14900
14680
|
}
|
|
14901
|
-
|
|
14902
|
-
|
|
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);
|
|
14911
|
-
}
|
|
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;
|
|
14681
|
+
warn(text) {
|
|
14682
|
+
return this.stopAndPersist({ symbol: exports_symbols.warning, text });
|
|
14916
14683
|
}
|
|
14917
|
-
|
|
14918
|
-
|
|
14684
|
+
info(text) {
|
|
14685
|
+
return this.stopAndPersist({ symbol: exports_symbols.info, text });
|
|
14686
|
+
}
|
|
14687
|
+
stopAndPersist(options = {}) {
|
|
14688
|
+
if (this.isSilent) {
|
|
14919
14689
|
return this;
|
|
14920
14690
|
}
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
if (this.#options.indent) {
|
|
14930
|
-
this.#stream.cursorTo(this.#options.indent);
|
|
14931
|
-
}
|
|
14932
|
-
});
|
|
14933
|
-
this.#linesToClear = 0;
|
|
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));
|
|
14934
14699
|
return this;
|
|
14935
14700
|
}
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
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("░"));
|
|
14939
14792
|
}
|
|
14940
|
-
|
|
14941
|
-
|
|
14793
|
+
await sleep(charDelay);
|
|
14794
|
+
}
|
|
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);
|
|
14826
|
+
}
|
|
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("█"));
|
|
14836
|
+
} else {
|
|
14837
|
+
write(colors2.dim("░"));
|
|
14942
14838
|
}
|
|
14943
|
-
|
|
14944
|
-
this.#hookedStreams.set(stream, originalWrite);
|
|
14945
|
-
activeHooksPerStream.set(stream, this);
|
|
14946
|
-
stream.write = (chunk, encoding, callback) => this.#hookedWrite(stream, originalWrite, chunk, encoding, callback);
|
|
14839
|
+
await sleep(barDelay);
|
|
14947
14840
|
}
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
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
|
|
14951
14889
|
}
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
|
|
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
|
|
14955
14908
|
}
|
|
14956
|
-
}
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
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
|
|
14963
14931
|
}
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
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
|
|
14970
14949
|
}
|
|
14971
|
-
|
|
14972
|
-
|
|
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
|
|
14973
14968
|
}
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
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
|
|
14982
14990
|
}
|
|
14983
|
-
|
|
14984
|
-
|
|
14991
|
+
}
|
|
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
|
+
});
|
|
14985
15022
|
}
|
|
14986
|
-
return writeResult;
|
|
14987
15023
|
}
|
|
14988
|
-
|
|
14989
|
-
|
|
14990
|
-
|
|
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;
|
|
15031
|
+
}
|
|
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;
|
|
14991
15059
|
}
|
|
14992
|
-
|
|
14993
|
-
|
|
15060
|
+
}
|
|
15061
|
+
if (agent === "opencode") {
|
|
15062
|
+
const baseDir = join7(home, ".local", "share", "opencode", "storage", "session");
|
|
15063
|
+
if (!existsSync7(baseDir))
|
|
15064
|
+
return null;
|
|
14994
15065
|
try {
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
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
|
+
}
|
|
14998
15072
|
}
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15073
|
+
} catch {
|
|
15074
|
+
return null;
|
|
15075
|
+
}
|
|
15076
|
+
}
|
|
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(`
|
|
15009
15085
|
`);
|
|
15086
|
+
const messages = [];
|
|
15087
|
+
for (const line of lines) {
|
|
15088
|
+
try {
|
|
15089
|
+
messages.push(JSON.parse(line));
|
|
15090
|
+
} catch {}
|
|
15010
15091
|
}
|
|
15011
|
-
const
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15017
|
-
|
|
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 || "");
|
|
15110
|
+
}
|
|
15018
15111
|
}
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_DISABLE));
|
|
15112
|
+
} else if (sessionPath.endsWith(".json")) {
|
|
15113
|
+
for (const _3 of patterns) {
|
|
15114
|
+
prompts.push("");
|
|
15023
15115
|
}
|
|
15024
15116
|
}
|
|
15025
|
-
return
|
|
15117
|
+
return prompts;
|
|
15118
|
+
} catch {
|
|
15119
|
+
return [];
|
|
15026
15120
|
}
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
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
|
+
}
|
|
15030
15142
|
}
|
|
15031
|
-
|
|
15032
|
-
|
|
15143
|
+
}
|
|
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
|
+
};
|
|
15162
|
+
}
|
|
15033
15163
|
}
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
|
|
15164
|
+
return {
|
|
15165
|
+
category: "undo_loop",
|
|
15166
|
+
description: ROOT_CAUSE_PATTERNS.undo_loop.description,
|
|
15167
|
+
confidence: "high"
|
|
15168
|
+
};
|
|
15169
|
+
}
|
|
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
|
+
};
|
|
15040
15185
|
}
|
|
15041
|
-
return this;
|
|
15042
15186
|
}
|
|
15043
|
-
|
|
15044
|
-
|
|
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
|
|
15045
15208
|
}
|
|
15046
|
-
|
|
15047
|
-
|
|
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 };
|
|
15048
15230
|
}
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
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
|
+
}
|
|
15235
|
+
const recommendations = [];
|
|
15236
|
+
for (const [tool, data] of Object.entries(byTool)) {
|
|
15237
|
+
recommendations.push({
|
|
15238
|
+
tool,
|
|
15239
|
+
description: data.fix.description,
|
|
15240
|
+
installCommand: data.fix.installCommand,
|
|
15241
|
+
wouldHavePreventedCount: data.count,
|
|
15242
|
+
estimatedTimeSavedMinutes: Math.round(data.timeSaved),
|
|
15243
|
+
estimatedCostSaved: Math.round(data.costSaved * 100) / 100
|
|
15244
|
+
});
|
|
15245
|
+
}
|
|
15246
|
+
recommendations.sort((a2, b2) => b2.estimatedTimeSavedMinutes - a2.estimatedTimeSavedMinutes);
|
|
15247
|
+
return recommendations;
|
|
15248
|
+
}
|
|
15249
|
+
function formatFrustrationSummary(summary) {
|
|
15250
|
+
const lines = [];
|
|
15251
|
+
if (summary.totalFrustrations === 0) {
|
|
15252
|
+
lines.push(` ${icons.success} ${colors2.success("No frustration events detected!")}`);
|
|
15253
|
+
return lines;
|
|
15254
|
+
}
|
|
15255
|
+
lines.push("");
|
|
15256
|
+
lines.push(colors2.dim(" " + "═".repeat(50)));
|
|
15257
|
+
lines.push(` ${colors2.error("!")} ${colors2.bold("Frustration Analysis")}`);
|
|
15258
|
+
lines.push(colors2.dim(" " + "═".repeat(50)));
|
|
15259
|
+
lines.push("");
|
|
15260
|
+
lines.push(` ${colors2.bold("Summary:")}`);
|
|
15261
|
+
lines.push(` ${icons.warning} ${summary.totalFrustrations} frustration events detected`);
|
|
15262
|
+
lines.push(` ${colors2.dim(">")} ~${summary.totalTimeWastedMinutes} min wasted`);
|
|
15263
|
+
lines.push(` ${icons.money} ~$${summary.totalCostWasted.toFixed(2)} in tokens`);
|
|
15264
|
+
lines.push("");
|
|
15265
|
+
if (Object.keys(summary.byRootCause).length > 0) {
|
|
15266
|
+
lines.push(` ${colors2.bold("Root Causes:")}`);
|
|
15267
|
+
for (const [cause, count] of Object.entries(summary.byRootCause)) {
|
|
15268
|
+
const label = formatCauseLabel(cause);
|
|
15269
|
+
lines.push(` ${colors2.dim("•")} ${label}: ${count}x`);
|
|
15052
15270
|
}
|
|
15053
|
-
|
|
15054
|
-
this.render();
|
|
15055
|
-
this.#id = setInterval(this.render.bind(this), this.interval);
|
|
15056
|
-
return this;
|
|
15271
|
+
lines.push("");
|
|
15057
15272
|
}
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
15061
|
-
|
|
15062
|
-
|
|
15063
|
-
|
|
15064
|
-
|
|
15065
|
-
if (this.#drainHandler) {
|
|
15066
|
-
this.#stream.removeListener("drain", this.#drainHandler);
|
|
15067
|
-
this.#drainHandler = undefined;
|
|
15273
|
+
if (summary.topFrustrations.length > 0) {
|
|
15274
|
+
lines.push(` ${colors2.bold("Top Frustrations:")}`);
|
|
15275
|
+
lines.push("");
|
|
15276
|
+
for (let i3 = 0;i3 < Math.min(3, summary.topFrustrations.length); i3++) {
|
|
15277
|
+
const f3 = summary.topFrustrations[i3];
|
|
15278
|
+
lines.push(...formatFrustrationEvent(f3, i3 + 1));
|
|
15279
|
+
lines.push("");
|
|
15068
15280
|
}
|
|
15069
|
-
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
15073
|
-
|
|
15281
|
+
}
|
|
15282
|
+
if (summary.recommendations.length > 0) {
|
|
15283
|
+
lines.push(` ${colors2.bold(colors2.primary("Recommended Fixes:"))}`);
|
|
15284
|
+
lines.push("");
|
|
15285
|
+
for (const rec of summary.recommendations.slice(0, 3)) {
|
|
15286
|
+
lines.push(` ${colors2.success("+")} ${colors2.bold(rec.tool)}`);
|
|
15287
|
+
lines.push(` Would have prevented ${rec.wouldHavePreventedCount} issue${rec.wouldHavePreventedCount > 1 ? "s" : ""}`);
|
|
15288
|
+
lines.push(` Est. time saved: ${colors2.success(`~${rec.estimatedTimeSavedMinutes} min`)}`);
|
|
15289
|
+
lines.push(` ${colors2.dim("$")} ${colors2.primary(rec.installCommand)}`);
|
|
15290
|
+
lines.push("");
|
|
15074
15291
|
}
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
|
|
15292
|
+
}
|
|
15293
|
+
lines.push(colors2.dim(" " + "═".repeat(50)));
|
|
15294
|
+
return lines;
|
|
15295
|
+
}
|
|
15296
|
+
function formatFrustrationEvent(f3, index) {
|
|
15297
|
+
const lines = [];
|
|
15298
|
+
lines.push(` ${colors2.warning(`${index}.`)} ${colors2.bold(f3.patternDescription)} ${colors2.dim(`(${f3.agent})`)}`);
|
|
15299
|
+
lines.push(` ${colors2.dim("Session:")} ${f3.sessionId.slice(0, 12)}...`);
|
|
15300
|
+
lines.push(` ${colors2.dim("Time wasted:")} ~${f3.estimatedTimeWastedMinutes} min`);
|
|
15301
|
+
lines.push(` ${colors2.dim("Cause:")} ${formatCauseLabel(f3.rootCause.category)}`);
|
|
15302
|
+
if (f3.exactPrompt && f3.exactPrompt.length > 0 && !f3.exactPrompt.startsWith("(Unable")) {
|
|
15303
|
+
lines.push("");
|
|
15304
|
+
lines.push(` ${colors2.dim("┌" + "─".repeat(40))}`);
|
|
15305
|
+
const promptLines = wrapText(f3.exactPrompt, 38);
|
|
15306
|
+
for (const line of promptLines.slice(0, 4)) {
|
|
15307
|
+
lines.push(` ${colors2.dim("│")} ${line}`);
|
|
15308
|
+
}
|
|
15309
|
+
if (promptLines.length > 4) {
|
|
15310
|
+
lines.push(` ${colors2.dim("│")} ${colors2.dim("...")}`);
|
|
15311
|
+
}
|
|
15312
|
+
lines.push(` ${colors2.dim("└" + "─".repeat(40))}`);
|
|
15313
|
+
}
|
|
15314
|
+
lines.push("");
|
|
15315
|
+
lines.push(` ${colors2.success("Fix:")} ${f3.recommendedFix.tool}`);
|
|
15316
|
+
lines.push(` ${colors2.dim("$")} ${colors2.primary(f3.recommendedFix.installCommand)}`);
|
|
15317
|
+
return lines;
|
|
15318
|
+
}
|
|
15319
|
+
function formatCauseLabel(cause) {
|
|
15320
|
+
const labels = {
|
|
15321
|
+
hallucination: colors2.error("API Hallucination"),
|
|
15322
|
+
missing_docs: colors2.warning("Missing Docs"),
|
|
15323
|
+
context_loss: colors2.warning("Context Loss"),
|
|
15324
|
+
unclear_prompt: colors2.dim("Unclear Prompt"),
|
|
15325
|
+
tool_failure: colors2.error("Tool Failure"),
|
|
15326
|
+
undo_loop: colors2.warning("Undo Loop"),
|
|
15327
|
+
unknown: colors2.dim("Unknown")
|
|
15328
|
+
};
|
|
15329
|
+
return labels[cause] || cause;
|
|
15330
|
+
}
|
|
15331
|
+
function wrapText(text, width) {
|
|
15332
|
+
const words = text.split(/\s+/);
|
|
15333
|
+
const lines = [];
|
|
15334
|
+
let currentLine = "";
|
|
15335
|
+
for (const word of words) {
|
|
15336
|
+
if (currentLine.length + word.length + 1 <= width) {
|
|
15337
|
+
currentLine += (currentLine ? " " : "") + word;
|
|
15338
|
+
} else {
|
|
15339
|
+
if (currentLine)
|
|
15340
|
+
lines.push(currentLine);
|
|
15341
|
+
currentLine = word.slice(0, width);
|
|
15342
|
+
}
|
|
15343
|
+
}
|
|
15344
|
+
if (currentLine)
|
|
15345
|
+
lines.push(currentLine);
|
|
15346
|
+
return lines;
|
|
15347
|
+
}
|
|
15348
|
+
|
|
15349
|
+
// src/lib/optimization-installer.ts
|
|
15350
|
+
import { execSync as execSync2, spawnSync } from "node:child_process";
|
|
15351
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync3, readFileSync as readFileSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync4 } from "node:fs";
|
|
15352
|
+
import { join as join8 } from "node:path";
|
|
15353
|
+
import { homedir as homedir7 } from "node:os";
|
|
15354
|
+
async function installOptimizations(optimizations, projectDir, options = {}) {
|
|
15355
|
+
const results = [];
|
|
15356
|
+
for (const opt of optimizations) {
|
|
15357
|
+
if (!opt.selected)
|
|
15358
|
+
continue;
|
|
15359
|
+
try {
|
|
15360
|
+
const result = await installSingleOptimization(opt, projectDir, options);
|
|
15361
|
+
results.push(result);
|
|
15362
|
+
} catch (err) {
|
|
15363
|
+
results.push({
|
|
15364
|
+
id: opt.id,
|
|
15365
|
+
name: opt.name,
|
|
15366
|
+
success: false,
|
|
15367
|
+
message: "Installation failed",
|
|
15368
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15369
|
+
});
|
|
15078
15370
|
}
|
|
15079
|
-
return this;
|
|
15080
15371
|
}
|
|
15081
|
-
|
|
15082
|
-
|
|
15372
|
+
return results;
|
|
15373
|
+
}
|
|
15374
|
+
async function installSingleOptimization(opt, projectDir, options) {
|
|
15375
|
+
if (options.dryRun) {
|
|
15376
|
+
return {
|
|
15377
|
+
id: opt.id,
|
|
15378
|
+
name: opt.name,
|
|
15379
|
+
success: true,
|
|
15380
|
+
message: `[DRY RUN] Would install: ${opt.installCommand || opt.configPath || "unknown"}`
|
|
15381
|
+
};
|
|
15083
15382
|
}
|
|
15084
|
-
|
|
15085
|
-
|
|
15383
|
+
switch (opt.type) {
|
|
15384
|
+
case "mcp":
|
|
15385
|
+
return installMCP(opt, projectDir, options.agent);
|
|
15386
|
+
case "skill":
|
|
15387
|
+
return installSkill(opt);
|
|
15388
|
+
case "config":
|
|
15389
|
+
return installConfig(opt, projectDir);
|
|
15390
|
+
case "hook":
|
|
15391
|
+
return installHook(opt, projectDir);
|
|
15392
|
+
default:
|
|
15393
|
+
return {
|
|
15394
|
+
id: opt.id,
|
|
15395
|
+
name: opt.name,
|
|
15396
|
+
success: false,
|
|
15397
|
+
message: "Unknown optimization type"
|
|
15398
|
+
};
|
|
15086
15399
|
}
|
|
15087
|
-
|
|
15088
|
-
|
|
15400
|
+
}
|
|
15401
|
+
function installMCP(opt, projectDir, agent) {
|
|
15402
|
+
if (opt.installCommand?.startsWith("claude mcp add")) {
|
|
15403
|
+
try {
|
|
15404
|
+
execSync2(opt.installCommand, {
|
|
15405
|
+
encoding: "utf-8",
|
|
15406
|
+
stdio: "pipe",
|
|
15407
|
+
timeout: 60000
|
|
15408
|
+
});
|
|
15409
|
+
return {
|
|
15410
|
+
id: opt.id,
|
|
15411
|
+
name: opt.name,
|
|
15412
|
+
success: true,
|
|
15413
|
+
message: `Installed via: ${opt.installCommand}`
|
|
15414
|
+
};
|
|
15415
|
+
} catch (err) {}
|
|
15089
15416
|
}
|
|
15090
|
-
|
|
15091
|
-
|
|
15417
|
+
if (opt.id === "beads" || opt.name.toLowerCase().includes("beads")) {
|
|
15418
|
+
try {
|
|
15419
|
+
execSync2("bun add -g beads", { encoding: "utf-8", stdio: "pipe", timeout: 60000 });
|
|
15420
|
+
execSync2("bd init", { cwd: projectDir, encoding: "utf-8", stdio: "pipe", timeout: 30000 });
|
|
15421
|
+
return {
|
|
15422
|
+
id: opt.id,
|
|
15423
|
+
name: opt.name,
|
|
15424
|
+
success: true,
|
|
15425
|
+
message: "Installed Beads globally and initialized in project"
|
|
15426
|
+
};
|
|
15427
|
+
} catch (err) {
|
|
15428
|
+
return {
|
|
15429
|
+
id: opt.id,
|
|
15430
|
+
name: opt.name,
|
|
15431
|
+
success: false,
|
|
15432
|
+
message: "Failed to install Beads",
|
|
15433
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15434
|
+
};
|
|
15435
|
+
}
|
|
15092
15436
|
}
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15437
|
+
if (opt.installCommand) {
|
|
15438
|
+
try {
|
|
15439
|
+
execSync2(opt.installCommand, {
|
|
15440
|
+
encoding: "utf-8",
|
|
15441
|
+
stdio: "pipe",
|
|
15442
|
+
timeout: 60000
|
|
15443
|
+
});
|
|
15444
|
+
return {
|
|
15445
|
+
id: opt.id,
|
|
15446
|
+
name: opt.name,
|
|
15447
|
+
success: true,
|
|
15448
|
+
message: `Installed via: ${opt.installCommand}`
|
|
15449
|
+
};
|
|
15450
|
+
} catch (err) {
|
|
15451
|
+
return {
|
|
15452
|
+
id: opt.id,
|
|
15453
|
+
name: opt.name,
|
|
15454
|
+
success: false,
|
|
15455
|
+
message: "Failed to install MCP",
|
|
15456
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15457
|
+
};
|
|
15096
15458
|
}
|
|
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
15459
|
}
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
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)
|
|
15460
|
+
return {
|
|
15461
|
+
id: opt.id,
|
|
15462
|
+
name: opt.name,
|
|
15463
|
+
success: false,
|
|
15464
|
+
message: "No install command available"
|
|
15149
15465
|
};
|
|
15150
|
-
const colorFn = tierColors[tier.toLowerCase()] ?? colors2.dim;
|
|
15151
|
-
return colorFn(tier.charAt(0).toUpperCase() + tier.slice(1));
|
|
15152
15466
|
}
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15467
|
+
function installSkill(opt) {
|
|
15468
|
+
if (!opt.installCommand) {
|
|
15469
|
+
return {
|
|
15470
|
+
id: opt.id,
|
|
15471
|
+
name: opt.name,
|
|
15472
|
+
success: false,
|
|
15473
|
+
message: "No install command for skill"
|
|
15474
|
+
};
|
|
15475
|
+
}
|
|
15476
|
+
try {
|
|
15477
|
+
const skillPath = opt.installCommand.replace(/^npx\s+/, "").replace(/^bunx\s+/, "").replace(/^skills\s+add\s+/, "").trim();
|
|
15478
|
+
let result = spawnSync("bunx", ["--bun", "skills", "add", skillPath], {
|
|
15479
|
+
encoding: "utf-8",
|
|
15480
|
+
stdio: "pipe",
|
|
15481
|
+
timeout: 120000
|
|
15482
|
+
});
|
|
15483
|
+
if (result.status !== 0) {
|
|
15484
|
+
result = spawnSync("npx", ["-y", "skills", "add", skillPath], {
|
|
15485
|
+
encoding: "utf-8",
|
|
15486
|
+
stdio: "pipe",
|
|
15487
|
+
timeout: 120000
|
|
15488
|
+
});
|
|
15489
|
+
}
|
|
15490
|
+
if (result.status === 0) {
|
|
15491
|
+
return {
|
|
15492
|
+
id: opt.id,
|
|
15493
|
+
name: opt.name,
|
|
15494
|
+
success: true,
|
|
15495
|
+
message: `Skill installed successfully`
|
|
15496
|
+
};
|
|
15497
|
+
} else {
|
|
15498
|
+
const output = (result.stderr || result.stdout || "").toLowerCase();
|
|
15499
|
+
if (output.includes("already exists") || output.includes("already installed")) {
|
|
15500
|
+
return {
|
|
15501
|
+
id: opt.id,
|
|
15502
|
+
name: opt.name,
|
|
15503
|
+
success: true,
|
|
15504
|
+
message: `Skill already installed`
|
|
15505
|
+
};
|
|
15506
|
+
}
|
|
15507
|
+
return {
|
|
15508
|
+
id: opt.id,
|
|
15509
|
+
name: opt.name,
|
|
15510
|
+
success: false,
|
|
15511
|
+
message: "Skill installation failed",
|
|
15512
|
+
error: result.stderr || result.stdout || "Unknown error"
|
|
15513
|
+
};
|
|
15514
|
+
}
|
|
15515
|
+
} catch (err) {
|
|
15516
|
+
return {
|
|
15517
|
+
id: opt.id,
|
|
15518
|
+
name: opt.name,
|
|
15519
|
+
success: false,
|
|
15520
|
+
message: "Failed to install skill",
|
|
15521
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15522
|
+
};
|
|
15523
|
+
}
|
|
15176
15524
|
}
|
|
15177
|
-
function
|
|
15178
|
-
|
|
15525
|
+
function installConfig(opt, projectDir) {
|
|
15526
|
+
if (!opt.configPath || !opt.configContent) {
|
|
15527
|
+
return {
|
|
15528
|
+
id: opt.id,
|
|
15529
|
+
name: opt.name,
|
|
15530
|
+
success: false,
|
|
15531
|
+
message: "No config path or content provided"
|
|
15532
|
+
};
|
|
15533
|
+
}
|
|
15534
|
+
try {
|
|
15535
|
+
const fullPath = opt.configPath.startsWith("/") || opt.configPath.startsWith("~") ? opt.configPath.replace("~", homedir7()) : join8(projectDir, opt.configPath);
|
|
15536
|
+
if (existsSync8(fullPath)) {
|
|
15537
|
+
return {
|
|
15538
|
+
id: opt.id,
|
|
15539
|
+
name: opt.name,
|
|
15540
|
+
success: true,
|
|
15541
|
+
message: `Config already exists: ${opt.configPath}`
|
|
15542
|
+
};
|
|
15543
|
+
}
|
|
15544
|
+
const parentDir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
15545
|
+
if (parentDir && !existsSync8(parentDir)) {
|
|
15546
|
+
mkdirSync3(parentDir, { recursive: true });
|
|
15547
|
+
}
|
|
15548
|
+
writeFileSync3(fullPath, opt.configContent);
|
|
15549
|
+
return {
|
|
15550
|
+
id: opt.id,
|
|
15551
|
+
name: opt.name,
|
|
15552
|
+
success: true,
|
|
15553
|
+
message: `Created: ${opt.configPath}`
|
|
15554
|
+
};
|
|
15555
|
+
} catch (err) {
|
|
15556
|
+
return {
|
|
15557
|
+
id: opt.id,
|
|
15558
|
+
name: opt.name,
|
|
15559
|
+
success: false,
|
|
15560
|
+
message: "Failed to create config",
|
|
15561
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15562
|
+
};
|
|
15563
|
+
}
|
|
15179
15564
|
}
|
|
15180
|
-
|
|
15181
|
-
const
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
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("█"));
|
|
15565
|
+
function installHook(opt, projectDir) {
|
|
15566
|
+
const agentsMdPath = join8(projectDir, "AGENTS.md");
|
|
15567
|
+
try {
|
|
15568
|
+
let content = "";
|
|
15569
|
+
if (existsSync8(agentsMdPath)) {
|
|
15570
|
+
content = readFileSync8(agentsMdPath, "utf-8");
|
|
15196
15571
|
} else {
|
|
15197
|
-
|
|
15572
|
+
content = `# Agent Instructions
|
|
15573
|
+
|
|
15574
|
+
`;
|
|
15198
15575
|
}
|
|
15199
|
-
|
|
15576
|
+
const hookName = opt.name.toLowerCase();
|
|
15577
|
+
if (content.toLowerCase().includes(hookName)) {
|
|
15578
|
+
return {
|
|
15579
|
+
id: opt.id,
|
|
15580
|
+
name: opt.name,
|
|
15581
|
+
success: true,
|
|
15582
|
+
message: "Hook already configured in AGENTS.md"
|
|
15583
|
+
};
|
|
15584
|
+
}
|
|
15585
|
+
const hookSection = opt.configContent || `
|
|
15586
|
+
## ${opt.name}
|
|
15587
|
+
${opt.description}
|
|
15588
|
+
`;
|
|
15589
|
+
content += `
|
|
15590
|
+
` + hookSection;
|
|
15591
|
+
writeFileSync3(agentsMdPath, content);
|
|
15592
|
+
return {
|
|
15593
|
+
id: opt.id,
|
|
15594
|
+
name: opt.name,
|
|
15595
|
+
success: true,
|
|
15596
|
+
message: "Added hook to AGENTS.md"
|
|
15597
|
+
};
|
|
15598
|
+
} catch (err) {
|
|
15599
|
+
return {
|
|
15600
|
+
id: opt.id,
|
|
15601
|
+
name: opt.name,
|
|
15602
|
+
success: false,
|
|
15603
|
+
message: "Failed to add hook",
|
|
15604
|
+
error: err instanceof Error ? err.message : String(err)
|
|
15605
|
+
};
|
|
15200
15606
|
}
|
|
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
|
-
}
|
|
15209
|
-
async function revealDiscovery(icon, text, delay2 = 300) {
|
|
15210
|
-
await sleep(delay2);
|
|
15211
|
-
console.log(` ${icon} ${text}`);
|
|
15212
15607
|
}
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15608
|
+
function phaseRecToInstallable(rec) {
|
|
15609
|
+
return {
|
|
15610
|
+
id: rec.title.toLowerCase().replace(/\s+/g, "-"),
|
|
15611
|
+
name: rec.title,
|
|
15612
|
+
type: rec.type,
|
|
15613
|
+
description: rec.description,
|
|
15614
|
+
installCommand: rec.installCommand,
|
|
15615
|
+
selected: false
|
|
15616
|
+
};
|
|
15218
15617
|
}
|
|
15219
|
-
|
|
15220
|
-
const
|
|
15221
|
-
const
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15618
|
+
function checkInstalledStatus(projectDir) {
|
|
15619
|
+
const home = homedir7();
|
|
15620
|
+
const status = {
|
|
15621
|
+
context7: false,
|
|
15622
|
+
supermemory: false,
|
|
15623
|
+
nia: false,
|
|
15624
|
+
beads: false,
|
|
15625
|
+
skills: []
|
|
15626
|
+
};
|
|
15627
|
+
const mcpConfigPaths = [
|
|
15628
|
+
join8(home, ".claude.json"),
|
|
15629
|
+
join8(home, ".claude", "claude_desktop_config.json"),
|
|
15630
|
+
join8(home, ".claude", "settings.json"),
|
|
15631
|
+
join8(home, ".claude", "settings.local.json"),
|
|
15632
|
+
join8(projectDir, ".mcp.json"),
|
|
15633
|
+
join8(projectDir, ".claude", "settings.local.json")
|
|
15634
|
+
];
|
|
15635
|
+
for (const configPath of mcpConfigPaths) {
|
|
15636
|
+
if (existsSync8(configPath)) {
|
|
15637
|
+
try {
|
|
15638
|
+
const config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
15639
|
+
const mcpServers = config.mcpServers || config.mcp_servers || {};
|
|
15640
|
+
for (const name of Object.keys(mcpServers)) {
|
|
15641
|
+
const mcpName = name.toLowerCase();
|
|
15642
|
+
if (mcpName.includes("context7") || mcpName === "c7") {
|
|
15643
|
+
status.context7 = true;
|
|
15644
|
+
}
|
|
15645
|
+
if (mcpName.includes("supermemory") || mcpName.includes("memory")) {
|
|
15646
|
+
status.supermemory = true;
|
|
15647
|
+
}
|
|
15648
|
+
if (mcpName.includes("nia")) {
|
|
15649
|
+
status.nia = true;
|
|
15650
|
+
}
|
|
15651
|
+
}
|
|
15652
|
+
} catch {}
|
|
15653
|
+
}
|
|
15232
15654
|
}
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15655
|
+
status.beads = existsSync8(join8(projectDir, ".beads"));
|
|
15656
|
+
const skillsDirs = [
|
|
15657
|
+
join8(home, ".claude", "skills"),
|
|
15658
|
+
join8(home, ".config", "opencode", "skills"),
|
|
15659
|
+
join8(home, ".agents", "skills"),
|
|
15660
|
+
join8(projectDir, ".opencode", "skill")
|
|
15661
|
+
];
|
|
15662
|
+
for (const dir of skillsDirs) {
|
|
15663
|
+
if (existsSync8(dir)) {
|
|
15664
|
+
try {
|
|
15665
|
+
const skills = readdirSync4(dir);
|
|
15666
|
+
for (const skill of skills) {
|
|
15667
|
+
if (!status.skills.includes(skill.toLowerCase())) {
|
|
15668
|
+
status.skills.push(skill.toLowerCase());
|
|
15669
|
+
}
|
|
15670
|
+
}
|
|
15671
|
+
} catch {}
|
|
15244
15672
|
}
|
|
15245
|
-
await sleep(barDelay);
|
|
15246
15673
|
}
|
|
15247
|
-
|
|
15248
|
-
console.log();
|
|
15249
|
-
console.log(colors2.dim(" " + "═".repeat(45)));
|
|
15674
|
+
return status;
|
|
15250
15675
|
}
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15676
|
+
function filterAlreadyInstalled(optimizations, status) {
|
|
15677
|
+
return optimizations.filter((opt) => {
|
|
15678
|
+
const id = opt.id.toLowerCase();
|
|
15679
|
+
if (id === "context7" && status.context7)
|
|
15680
|
+
return false;
|
|
15681
|
+
if (id === "supermemory" && status.supermemory)
|
|
15682
|
+
return false;
|
|
15683
|
+
if (id === "nia" && status.nia)
|
|
15684
|
+
return false;
|
|
15685
|
+
if (id === "beads" && status.beads)
|
|
15686
|
+
return false;
|
|
15687
|
+
if (opt.type === "skill") {
|
|
15688
|
+
const skillName = opt.name.toLowerCase().replace(/\s+/g, "-");
|
|
15689
|
+
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"))) {
|
|
15690
|
+
return false;
|
|
15691
|
+
}
|
|
15692
|
+
}
|
|
15693
|
+
return true;
|
|
15694
|
+
});
|
|
15268
15695
|
}
|
|
15696
|
+
var QUICK_INSTALL_PRESETS = {
|
|
15697
|
+
essential: [
|
|
15698
|
+
{
|
|
15699
|
+
id: "context7",
|
|
15700
|
+
name: "Context7 MCP",
|
|
15701
|
+
type: "mcp",
|
|
15702
|
+
description: "Up-to-date library documentation",
|
|
15703
|
+
installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
|
|
15704
|
+
selected: true
|
|
15705
|
+
},
|
|
15706
|
+
{
|
|
15707
|
+
id: "supermemory",
|
|
15708
|
+
name: "Supermemory MCP",
|
|
15709
|
+
type: "mcp",
|
|
15710
|
+
description: "Persistent memory across sessions",
|
|
15711
|
+
installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest",
|
|
15712
|
+
selected: true
|
|
15713
|
+
},
|
|
15714
|
+
{
|
|
15715
|
+
id: "beads",
|
|
15716
|
+
name: "Beads Task Manager",
|
|
15717
|
+
type: "mcp",
|
|
15718
|
+
description: "Persistent task tracking",
|
|
15719
|
+
installCommand: "bun add -g beads && bd init",
|
|
15720
|
+
selected: true
|
|
15721
|
+
}
|
|
15722
|
+
],
|
|
15723
|
+
productivity: [
|
|
15724
|
+
{
|
|
15725
|
+
id: "claude-md",
|
|
15726
|
+
name: "CLAUDE.md",
|
|
15727
|
+
type: "config",
|
|
15728
|
+
description: "Project context file",
|
|
15729
|
+
configPath: "CLAUDE.md",
|
|
15730
|
+
configContent: `# Project Instructions
|
|
15731
|
+
|
|
15732
|
+
## Build & Test Commands
|
|
15733
|
+
\`\`\`bash
|
|
15734
|
+
bun test # Run tests
|
|
15735
|
+
bun run build # Build project
|
|
15736
|
+
bun run lint # Run linter
|
|
15737
|
+
\`\`\`
|
|
15738
|
+
|
|
15739
|
+
## Architecture
|
|
15740
|
+
Describe your project structure here.
|
|
15741
|
+
|
|
15742
|
+
## Conventions
|
|
15743
|
+
- Use TypeScript
|
|
15744
|
+
- Write tests for new features
|
|
15745
|
+
- Use conventional commits
|
|
15746
|
+
`,
|
|
15747
|
+
selected: true
|
|
15748
|
+
},
|
|
15749
|
+
{
|
|
15750
|
+
id: "vitest",
|
|
15751
|
+
name: "Vitest",
|
|
15752
|
+
type: "library",
|
|
15753
|
+
description: "Fast unit testing framework",
|
|
15754
|
+
installCommand: "bun add -D vitest",
|
|
15755
|
+
selected: true
|
|
15756
|
+
}
|
|
15757
|
+
]
|
|
15758
|
+
};
|
|
15269
15759
|
|
|
15270
15760
|
// src/commands/scan.ts
|
|
15271
15761
|
var scanCommand = defineCommand2({
|
|
@@ -15489,10 +15979,19 @@ var scanCommand = defineCommand2({
|
|
|
15489
15979
|
console.log(colors2.dim(" " + "═".repeat(45)));
|
|
15490
15980
|
console.log();
|
|
15491
15981
|
if (!args["no-report"]) {
|
|
15492
|
-
const reportDir =
|
|
15982
|
+
const reportDir = join9(projectDir, args["report-dir"]);
|
|
15493
15983
|
const reportPath = generateReport(reportDir, score, git, agents, tests, args.since, sdlcAnalysis, scanCost, analysis);
|
|
15494
15984
|
console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
|
|
15495
15985
|
}
|
|
15986
|
+
if (agents && agents.sessions.length > 0 && !args.brief) {
|
|
15987
|
+
const frustrationSummary = detectFrustrations(agents.sessions);
|
|
15988
|
+
if (frustrationSummary.totalFrustrations > 0) {
|
|
15989
|
+
const frustrationLines = formatFrustrationSummary(frustrationSummary);
|
|
15990
|
+
for (const line of frustrationLines) {
|
|
15991
|
+
console.log(line);
|
|
15992
|
+
}
|
|
15993
|
+
}
|
|
15994
|
+
}
|
|
15496
15995
|
if (!args.brief) {
|
|
15497
15996
|
const installedStatus = checkInstalledStatus(projectDir);
|
|
15498
15997
|
const projectContext = loadProjectContext(projectDir);
|
|
@@ -15754,12 +16253,12 @@ var scanCommand = defineCommand2({
|
|
|
15754
16253
|
}
|
|
15755
16254
|
});
|
|
15756
16255
|
function loadProjectContext(projectDir) {
|
|
15757
|
-
const contextPath =
|
|
15758
|
-
if (!
|
|
16256
|
+
const contextPath = join9(projectDir, ".nairon", "context.json");
|
|
16257
|
+
if (!existsSync9(contextPath)) {
|
|
15759
16258
|
return null;
|
|
15760
16259
|
}
|
|
15761
16260
|
try {
|
|
15762
|
-
const raw =
|
|
16261
|
+
const raw = readFileSync9(contextPath, "utf-8");
|
|
15763
16262
|
const data = JSON.parse(raw);
|
|
15764
16263
|
const context = {
|
|
15765
16264
|
painPoints: data.painPoints || [],
|
|
@@ -15812,12 +16311,12 @@ function parseSince(since) {
|
|
|
15812
16311
|
return new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
15813
16312
|
}
|
|
15814
16313
|
function generateReport(reportDir, score, git, agents, tests, since, sdlcAnalysis, scanCost, analysis) {
|
|
15815
|
-
if (!
|
|
16314
|
+
if (!existsSync9(reportDir)) {
|
|
15816
16315
|
mkdirSync4(reportDir, { recursive: true });
|
|
15817
16316
|
}
|
|
15818
16317
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
15819
16318
|
const filename = `scan-${timestamp}.md`;
|
|
15820
|
-
const filepath =
|
|
16319
|
+
const filepath = join9(reportDir, filename);
|
|
15821
16320
|
const lines = [];
|
|
15822
16321
|
lines.push(`# NaironAI Scan Report`);
|
|
15823
16322
|
lines.push("");
|
|
@@ -15958,11 +16457,11 @@ init_dist();
|
|
|
15958
16457
|
init_client();
|
|
15959
16458
|
|
|
15960
16459
|
// src/collectors/report-data.ts
|
|
15961
|
-
import { existsSync as
|
|
15962
|
-
import { homedir as
|
|
15963
|
-
import { join as
|
|
16460
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
|
|
16461
|
+
import { homedir as homedir8 } from "node:os";
|
|
16462
|
+
import { join as join10, basename as basename3 } from "node:path";
|
|
15964
16463
|
async function collectReportData(projectDir, since, until = new Date, harness) {
|
|
15965
|
-
const projectName =
|
|
16464
|
+
const projectName = basename3(projectDir);
|
|
15966
16465
|
const [commits, allSessions, mcpConfigs] = await Promise.all([
|
|
15967
16466
|
collectGitCommits(projectDir, since, until),
|
|
15968
16467
|
collectAllSessions(since, until, projectDir),
|
|
@@ -16043,12 +16542,12 @@ function detectAIAssistedCommit(message) {
|
|
|
16043
16542
|
}
|
|
16044
16543
|
async function collectAllSessions(since, until, projectDir) {
|
|
16045
16544
|
const sessions = [];
|
|
16046
|
-
const claudeDir =
|
|
16047
|
-
if (
|
|
16545
|
+
const claudeDir = join10(homedir8(), ".claude");
|
|
16546
|
+
if (existsSync10(claudeDir)) {
|
|
16048
16547
|
sessions.push(...collectClaudeCodeSessions(claudeDir, since, until, projectDir));
|
|
16049
16548
|
}
|
|
16050
|
-
const openCodeDir =
|
|
16051
|
-
if (
|
|
16549
|
+
const openCodeDir = join10(homedir8(), ".local", "share", "opencode");
|
|
16550
|
+
if (existsSync10(openCodeDir)) {
|
|
16052
16551
|
sessions.push(...collectOpenCodeSessions2(openCodeDir, since, until));
|
|
16053
16552
|
}
|
|
16054
16553
|
sessions.sort((a2, b2) => a2.startTime.getTime() - b2.startTime.getTime());
|
|
@@ -16056,15 +16555,15 @@ async function collectAllSessions(since, until, projectDir) {
|
|
|
16056
16555
|
}
|
|
16057
16556
|
function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
|
|
16058
16557
|
const sessions = [];
|
|
16059
|
-
const transcriptsDir =
|
|
16060
|
-
if (!
|
|
16558
|
+
const transcriptsDir = join10(claudeDir, "transcripts");
|
|
16559
|
+
if (!existsSync10(transcriptsDir))
|
|
16061
16560
|
return sessions;
|
|
16062
16561
|
const projectHash = projectDir ? hashPath(projectDir) : null;
|
|
16063
|
-
const projectsDir =
|
|
16562
|
+
const projectsDir = join10(claudeDir, "projects");
|
|
16064
16563
|
try {
|
|
16065
16564
|
const transcriptFiles = readdirSync5(transcriptsDir).filter((f3) => f3.endsWith(".jsonl"));
|
|
16066
16565
|
for (const file of transcriptFiles) {
|
|
16067
|
-
const filePath =
|
|
16566
|
+
const filePath = join10(transcriptsDir, file);
|
|
16068
16567
|
const stat = statSync2(filePath);
|
|
16069
16568
|
if (stat.mtime < since || stat.mtime > until)
|
|
16070
16569
|
continue;
|
|
@@ -16075,12 +16574,12 @@ function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
|
|
|
16075
16574
|
}
|
|
16076
16575
|
} catch {}
|
|
16077
16576
|
}
|
|
16078
|
-
if (projectHash &&
|
|
16079
|
-
const projectSessionDir =
|
|
16080
|
-
if (
|
|
16577
|
+
if (projectHash && existsSync10(projectsDir)) {
|
|
16578
|
+
const projectSessionDir = join10(projectsDir, projectHash);
|
|
16579
|
+
if (existsSync10(projectSessionDir)) {
|
|
16081
16580
|
const projectFiles = readdirSync5(projectSessionDir).filter((f3) => f3.endsWith(".jsonl"));
|
|
16082
16581
|
for (const file of projectFiles) {
|
|
16083
|
-
const filePath =
|
|
16582
|
+
const filePath = join10(projectSessionDir, file);
|
|
16084
16583
|
const stat = statSync2(filePath);
|
|
16085
16584
|
if (stat.mtime < since || stat.mtime > until)
|
|
16086
16585
|
continue;
|
|
@@ -16099,7 +16598,7 @@ function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
|
|
|
16099
16598
|
return sessions;
|
|
16100
16599
|
}
|
|
16101
16600
|
function parseClaudeTranscript(filePath, fileName) {
|
|
16102
|
-
const content =
|
|
16601
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
16103
16602
|
const lines = content.trim().split(`
|
|
16104
16603
|
`).filter((l2) => l2.trim());
|
|
16105
16604
|
if (lines.length === 0)
|
|
@@ -16189,25 +16688,25 @@ function parseClaudeTranscript(filePath, fileName) {
|
|
|
16189
16688
|
}
|
|
16190
16689
|
function collectOpenCodeSessions2(openCodeDir, since, until) {
|
|
16191
16690
|
const sessions = [];
|
|
16192
|
-
const sessionDir =
|
|
16193
|
-
const messageDir =
|
|
16194
|
-
const partDir =
|
|
16195
|
-
if (!
|
|
16691
|
+
const sessionDir = join10(openCodeDir, "storage", "session");
|
|
16692
|
+
const messageDir = join10(openCodeDir, "storage", "message");
|
|
16693
|
+
const partDir = join10(openCodeDir, "storage", "part");
|
|
16694
|
+
if (!existsSync10(sessionDir))
|
|
16196
16695
|
return sessions;
|
|
16197
16696
|
try {
|
|
16198
|
-
const projectDirs = readdirSync5(sessionDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) =>
|
|
16697
|
+
const projectDirs = readdirSync5(sessionDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join10(sessionDir, d2.name));
|
|
16199
16698
|
for (const projectDir of projectDirs) {
|
|
16200
16699
|
const sessionFiles = readdirSync5(projectDir).filter((f3) => f3.endsWith(".json"));
|
|
16201
16700
|
for (const sessionFile of sessionFiles) {
|
|
16202
16701
|
try {
|
|
16203
|
-
const sessionPath =
|
|
16204
|
-
const sessionData = JSON.parse(
|
|
16702
|
+
const sessionPath = join10(projectDir, sessionFile);
|
|
16703
|
+
const sessionData = JSON.parse(readFileSync10(sessionPath, "utf-8"));
|
|
16205
16704
|
const createdAt = new Date(sessionData.time?.created || 0);
|
|
16206
16705
|
const updatedAt = new Date(sessionData.time?.updated || sessionData.time?.created || 0);
|
|
16207
16706
|
if (updatedAt < since || createdAt > until)
|
|
16208
16707
|
continue;
|
|
16209
16708
|
const sessionId = sessionData.id;
|
|
16210
|
-
const sessionMsgDir =
|
|
16709
|
+
const sessionMsgDir = join10(messageDir, sessionId);
|
|
16211
16710
|
const prompts = [];
|
|
16212
16711
|
const responses = [];
|
|
16213
16712
|
const toolsUsed = new Set;
|
|
@@ -16218,12 +16717,12 @@ function collectOpenCodeSessions2(openCodeDir, since, until) {
|
|
|
16218
16717
|
let model = "unknown";
|
|
16219
16718
|
let startTime = createdAt;
|
|
16220
16719
|
let endTime = updatedAt;
|
|
16221
|
-
if (
|
|
16720
|
+
if (existsSync10(sessionMsgDir)) {
|
|
16222
16721
|
const msgFiles = readdirSync5(sessionMsgDir).filter((f3) => f3.endsWith(".json")).sort();
|
|
16223
16722
|
for (const msgFile of msgFiles) {
|
|
16224
16723
|
try {
|
|
16225
|
-
const msgPath =
|
|
16226
|
-
const msgData = JSON.parse(
|
|
16724
|
+
const msgPath = join10(sessionMsgDir, msgFile);
|
|
16725
|
+
const msgData = JSON.parse(readFileSync10(msgPath, "utf-8"));
|
|
16227
16726
|
const msgId = msgData.id;
|
|
16228
16727
|
if (msgData.model?.modelID) {
|
|
16229
16728
|
model = msgData.model.modelID;
|
|
@@ -16233,14 +16732,14 @@ function collectOpenCodeSessions2(openCodeDir, since, until) {
|
|
|
16233
16732
|
startTime = msgTime;
|
|
16234
16733
|
if (msgTime > endTime)
|
|
16235
16734
|
endTime = msgTime;
|
|
16236
|
-
const msgPartDir =
|
|
16735
|
+
const msgPartDir = join10(partDir, msgId);
|
|
16237
16736
|
let messageText = "";
|
|
16238
|
-
if (
|
|
16737
|
+
if (existsSync10(msgPartDir)) {
|
|
16239
16738
|
const partFiles = readdirSync5(msgPartDir).filter((f3) => f3.endsWith(".json")).sort();
|
|
16240
16739
|
for (const partFile of partFiles) {
|
|
16241
16740
|
try {
|
|
16242
|
-
const partPath =
|
|
16243
|
-
const partData = JSON.parse(
|
|
16741
|
+
const partPath = join10(msgPartDir, partFile);
|
|
16742
|
+
const partData = JSON.parse(readFileSync10(partPath, "utf-8"));
|
|
16244
16743
|
if (partData.type === "text" && partData.text && !partData.synthetic) {
|
|
16245
16744
|
messageText += partData.text + `
|
|
16246
16745
|
`;
|
|
@@ -16414,10 +16913,10 @@ function analyzeResponse(text, sessionId, index, promptId, timestamp) {
|
|
|
16414
16913
|
}
|
|
16415
16914
|
function collectMCPConfigs() {
|
|
16416
16915
|
const configs = [];
|
|
16417
|
-
const claudeConfig =
|
|
16418
|
-
if (
|
|
16916
|
+
const claudeConfig = join10(homedir8(), ".claude.json");
|
|
16917
|
+
if (existsSync10(claudeConfig)) {
|
|
16419
16918
|
try {
|
|
16420
|
-
const data = JSON.parse(
|
|
16919
|
+
const data = JSON.parse(readFileSync10(claudeConfig, "utf-8"));
|
|
16421
16920
|
const mcpServers = data.mcpServers || {};
|
|
16422
16921
|
for (const [name, config] of Object.entries(mcpServers)) {
|
|
16423
16922
|
configs.push({
|
|
@@ -16430,13 +16929,13 @@ function collectMCPConfigs() {
|
|
|
16430
16929
|
} catch {}
|
|
16431
16930
|
}
|
|
16432
16931
|
const openCodeConfigs = [
|
|
16433
|
-
|
|
16434
|
-
|
|
16932
|
+
join10(homedir8(), ".opencode.json"),
|
|
16933
|
+
join10(homedir8(), ".config", "opencode", "opencode.json")
|
|
16435
16934
|
];
|
|
16436
16935
|
for (const configPath of openCodeConfigs) {
|
|
16437
|
-
if (
|
|
16936
|
+
if (existsSync10(configPath)) {
|
|
16438
16937
|
try {
|
|
16439
|
-
const data = JSON.parse(
|
|
16938
|
+
const data = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
16440
16939
|
const mcpServers = data.mcp || {};
|
|
16441
16940
|
for (const [name, config] of Object.entries(mcpServers)) {
|
|
16442
16941
|
if (!configs.find((c3) => c3.name === name)) {
|
|
@@ -16451,10 +16950,10 @@ function collectMCPConfigs() {
|
|
|
16451
16950
|
} catch {}
|
|
16452
16951
|
}
|
|
16453
16952
|
}
|
|
16454
|
-
const cursorConfig =
|
|
16455
|
-
if (
|
|
16953
|
+
const cursorConfig = join10(homedir8(), ".cursor", "mcp.json");
|
|
16954
|
+
if (existsSync10(cursorConfig)) {
|
|
16456
16955
|
try {
|
|
16457
|
-
const data = JSON.parse(
|
|
16956
|
+
const data = JSON.parse(readFileSync10(cursorConfig, "utf-8"));
|
|
16458
16957
|
const mcpServers = data.mcpServers || {};
|
|
16459
16958
|
for (const [name, config] of Object.entries(mcpServers)) {
|
|
16460
16959
|
if (!configs.find((c3) => c3.name === name)) {
|
|
@@ -18655,9 +19154,9 @@ function getSeverityEmoji(severity) {
|
|
|
18655
19154
|
}
|
|
18656
19155
|
|
|
18657
19156
|
// src/lib/tool-analyzer.ts
|
|
18658
|
-
import { existsSync as
|
|
18659
|
-
import { homedir as
|
|
18660
|
-
import { join as
|
|
19157
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6 } from "node:fs";
|
|
19158
|
+
import { homedir as homedir9 } from "node:os";
|
|
19159
|
+
import { join as join11 } from "node:path";
|
|
18661
19160
|
function analyzeToolUtilization(data) {
|
|
18662
19161
|
const mcpServers = analyzeMCPServers(data);
|
|
18663
19162
|
const mcpSummary = buildMCPSummary(mcpServers);
|
|
@@ -18807,24 +19306,24 @@ function categorizeTools(tools) {
|
|
|
18807
19306
|
}
|
|
18808
19307
|
function analyzeSkills() {
|
|
18809
19308
|
const skills = [];
|
|
18810
|
-
const home =
|
|
19309
|
+
const home = homedir9();
|
|
18811
19310
|
const skillsDirs = [
|
|
18812
|
-
|
|
18813
|
-
|
|
18814
|
-
|
|
19311
|
+
join11(home, ".claude", "skills"),
|
|
19312
|
+
join11(home, ".agents", "skills"),
|
|
19313
|
+
join11(home, ".config", "claude", "skills")
|
|
18815
19314
|
];
|
|
18816
19315
|
const projectSkillsDirs = [
|
|
18817
|
-
|
|
18818
|
-
|
|
19316
|
+
join11(process.cwd(), ".claude", "skills"),
|
|
19317
|
+
join11(process.cwd(), ".agents", "skills")
|
|
18819
19318
|
];
|
|
18820
19319
|
const openCodeSkillDirs = [
|
|
18821
|
-
|
|
18822
|
-
|
|
19320
|
+
join11(home, ".config", "opencode", "skills"),
|
|
19321
|
+
join11(home, ".local", "share", "opencode", "skills")
|
|
18823
19322
|
];
|
|
18824
19323
|
const allSkillDirs = [...skillsDirs, ...projectSkillsDirs, ...openCodeSkillDirs];
|
|
18825
19324
|
const seenSkills = new Set;
|
|
18826
19325
|
for (const skillsDir of allSkillDirs) {
|
|
18827
|
-
if (
|
|
19326
|
+
if (existsSync11(skillsDir)) {
|
|
18828
19327
|
try {
|
|
18829
19328
|
const entries = readdirSync6(skillsDir, { withFileTypes: true });
|
|
18830
19329
|
for (const entry of entries) {
|
|
@@ -18835,7 +19334,7 @@ function analyzeSkills() {
|
|
|
18835
19334
|
if (entry.isDirectory() || entry.isSymbolicLink() || entry.name.endsWith(".md")) {
|
|
18836
19335
|
skills.push({
|
|
18837
19336
|
name: skillName,
|
|
18838
|
-
path:
|
|
19337
|
+
path: join11(skillsDir, entry.name),
|
|
18839
19338
|
used: false,
|
|
18840
19339
|
usageCount: 0
|
|
18841
19340
|
});
|
|
@@ -19132,22 +19631,22 @@ function renderToolUtilizationMarkdown(analysis) {
|
|
|
19132
19631
|
}
|
|
19133
19632
|
|
|
19134
19633
|
// src/lib/coverage-parser.ts
|
|
19135
|
-
import { existsSync as
|
|
19136
|
-
import { join as
|
|
19634
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
|
|
19635
|
+
import { join as join12 } from "node:path";
|
|
19137
19636
|
function parseCoverageReport(projectDir) {
|
|
19138
19637
|
const coveragePaths = [
|
|
19139
|
-
{ path:
|
|
19140
|
-
{ path:
|
|
19141
|
-
{ path:
|
|
19142
|
-
{ path:
|
|
19143
|
-
{ path:
|
|
19144
|
-
{ path:
|
|
19145
|
-
{ path:
|
|
19638
|
+
{ path: join12(projectDir, "coverage", "lcov.info"), parser: parseLcov },
|
|
19639
|
+
{ path: join12(projectDir, "coverage", "coverage-summary.json"), parser: parseIstanbulSummary },
|
|
19640
|
+
{ path: join12(projectDir, "coverage", "coverage-final.json"), parser: parseIstanbulFinal },
|
|
19641
|
+
{ path: join12(projectDir, "coverage", "cobertura-coverage.xml"), parser: parseCobertura },
|
|
19642
|
+
{ path: join12(projectDir, "coverage", "clover.xml"), parser: parseClover },
|
|
19643
|
+
{ path: join12(projectDir, "lcov.info"), parser: parseLcov },
|
|
19644
|
+
{ path: join12(projectDir, ".nyc_output", "coverage-summary.json"), parser: parseIstanbulSummary }
|
|
19146
19645
|
];
|
|
19147
19646
|
for (const { path, parser: parser4 } of coveragePaths) {
|
|
19148
|
-
if (
|
|
19647
|
+
if (existsSync12(path)) {
|
|
19149
19648
|
try {
|
|
19150
|
-
const content =
|
|
19649
|
+
const content = readFileSync12(path, "utf-8");
|
|
19151
19650
|
return parser4(content, path);
|
|
19152
19651
|
} catch {}
|
|
19153
19652
|
}
|
|
@@ -21357,16 +21856,655 @@ function renderBar4(value, width) {
|
|
|
21357
21856
|
}
|
|
21358
21857
|
|
|
21359
21858
|
// src/commands/doctor.ts
|
|
21360
|
-
import { existsSync as
|
|
21361
|
-
import { homedir as
|
|
21362
|
-
import { join as
|
|
21859
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
21860
|
+
import { homedir as homedir10 } from "node:os";
|
|
21861
|
+
import { join as join13 } from "node:path";
|
|
21363
21862
|
init_client();
|
|
21863
|
+
|
|
21864
|
+
// src/lib/recommendations-db.ts
|
|
21865
|
+
var MCP_SERVERS = [
|
|
21866
|
+
{
|
|
21867
|
+
id: "context7",
|
|
21868
|
+
name: "Context7",
|
|
21869
|
+
type: "mcp",
|
|
21870
|
+
description: "Live documentation for any library - reduces API hallucinations by 40%+",
|
|
21871
|
+
installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
|
|
21872
|
+
phases: ["requirements", "implementation"],
|
|
21873
|
+
impact: "high",
|
|
21874
|
+
detectionKey: "context7",
|
|
21875
|
+
website: "https://context7.com",
|
|
21876
|
+
badge: "Essential",
|
|
21877
|
+
agents: {
|
|
21878
|
+
"claude-code": { installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http" },
|
|
21879
|
+
opencode: { installCommand: "Add to ~/.config/opencode/config.json mcpServers" }
|
|
21880
|
+
}
|
|
21881
|
+
},
|
|
21882
|
+
{
|
|
21883
|
+
id: "supermemory",
|
|
21884
|
+
name: "Supermemory",
|
|
21885
|
+
type: "mcp",
|
|
21886
|
+
description: "Persistent memory across sessions - never re-explain context",
|
|
21887
|
+
installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest",
|
|
21888
|
+
phases: ["requirements", "implementation"],
|
|
21889
|
+
impact: "high",
|
|
21890
|
+
detectionKey: "supermemory",
|
|
21891
|
+
website: "https://supermemory.ai",
|
|
21892
|
+
badge: "Essential",
|
|
21893
|
+
agents: {
|
|
21894
|
+
"claude-code": { installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest" }
|
|
21895
|
+
}
|
|
21896
|
+
},
|
|
21897
|
+
{
|
|
21898
|
+
id: "nia",
|
|
21899
|
+
name: "Nia",
|
|
21900
|
+
type: "mcp",
|
|
21901
|
+
description: "Index and search external codebases and documentation",
|
|
21902
|
+
installCommand: "pipx run --no-cache nia-mcp-server",
|
|
21903
|
+
phases: ["requirements"],
|
|
21904
|
+
impact: "medium",
|
|
21905
|
+
detectionKey: "nia",
|
|
21906
|
+
website: "https://trynia.ai"
|
|
21907
|
+
},
|
|
21908
|
+
{
|
|
21909
|
+
id: "greptile",
|
|
21910
|
+
name: "Greptile",
|
|
21911
|
+
type: "mcp",
|
|
21912
|
+
description: "AI-powered code review and PR analysis",
|
|
21913
|
+
installCommand: "claude mcp add greptile -- npx -y @greptile/mcp",
|
|
21914
|
+
phases: ["review"],
|
|
21915
|
+
impact: "high",
|
|
21916
|
+
detectionKey: "greptile",
|
|
21917
|
+
website: "https://www.greptile.com",
|
|
21918
|
+
badge: "Recommended"
|
|
21919
|
+
},
|
|
21920
|
+
{
|
|
21921
|
+
id: "agent-browser",
|
|
21922
|
+
name: "Agent Browser",
|
|
21923
|
+
type: "mcp",
|
|
21924
|
+
description: "Live end-to-end browser testing and automation",
|
|
21925
|
+
installCommand: "claude mcp add agent-browser -- npx -y agent-browser-mcp",
|
|
21926
|
+
phases: ["review"],
|
|
21927
|
+
impact: "high",
|
|
21928
|
+
detectionKey: "agent-browser",
|
|
21929
|
+
website: "https://agent-browser.dev",
|
|
21930
|
+
badge: "Recommended"
|
|
21931
|
+
},
|
|
21932
|
+
{
|
|
21933
|
+
id: "playwright-mcp",
|
|
21934
|
+
name: "Playwright MCP",
|
|
21935
|
+
type: "mcp",
|
|
21936
|
+
description: "Browser automation for E2E testing and web scraping",
|
|
21937
|
+
installCommand: "claude mcp add playwright -- npx -y @anthropic/playwright-mcp",
|
|
21938
|
+
phases: ["review"],
|
|
21939
|
+
impact: "medium",
|
|
21940
|
+
detectionKey: "playwright",
|
|
21941
|
+
website: "https://playwright.dev"
|
|
21942
|
+
},
|
|
21943
|
+
{
|
|
21944
|
+
id: "github-mcp",
|
|
21945
|
+
name: "GitHub MCP",
|
|
21946
|
+
type: "mcp",
|
|
21947
|
+
description: "GitHub integration for PRs, issues, and repo management",
|
|
21948
|
+
installCommand: "claude mcp add github -- npx -y @anthropic/github-mcp",
|
|
21949
|
+
phases: ["planning", "review"],
|
|
21950
|
+
impact: "medium",
|
|
21951
|
+
detectionKey: "github"
|
|
21952
|
+
}
|
|
21953
|
+
];
|
|
21954
|
+
var PLUGINS = [
|
|
21955
|
+
{
|
|
21956
|
+
id: "beads",
|
|
21957
|
+
name: "Beads",
|
|
21958
|
+
type: "plugin",
|
|
21959
|
+
description: "Lightweight issue tracking in git - survives context compaction",
|
|
21960
|
+
installCommand: "bun add -g beads && bd init",
|
|
21961
|
+
phases: ["planning"],
|
|
21962
|
+
impact: "high",
|
|
21963
|
+
detectionKey: "beads",
|
|
21964
|
+
website: "https://github.com/steveyegge/beads",
|
|
21965
|
+
badge: "Recommended"
|
|
21966
|
+
},
|
|
21967
|
+
{
|
|
21968
|
+
id: "linear",
|
|
21969
|
+
name: "Linear",
|
|
21970
|
+
type: "plugin",
|
|
21971
|
+
description: "Sync with Linear issues for team collaboration",
|
|
21972
|
+
phases: ["planning"],
|
|
21973
|
+
impact: "medium",
|
|
21974
|
+
website: "https://linear.app"
|
|
21975
|
+
},
|
|
21976
|
+
{
|
|
21977
|
+
id: "slack",
|
|
21978
|
+
name: "Slack",
|
|
21979
|
+
type: "plugin",
|
|
21980
|
+
description: "Team notifications and updates",
|
|
21981
|
+
phases: ["review"],
|
|
21982
|
+
impact: "low",
|
|
21983
|
+
website: "https://slack.com"
|
|
21984
|
+
}
|
|
21985
|
+
];
|
|
21986
|
+
var SKILLS = [
|
|
21987
|
+
{
|
|
21988
|
+
id: "tdd-skill",
|
|
21989
|
+
name: "Test-Driven Development",
|
|
21990
|
+
type: "skill",
|
|
21991
|
+
description: "Write tests first, then implement - reduces bugs by 40%",
|
|
21992
|
+
installCommand: "npx skills add obra/superpowers@test-driven-development --yes",
|
|
21993
|
+
phases: ["review"],
|
|
21994
|
+
impact: "high",
|
|
21995
|
+
website: "https://skills.sh"
|
|
21996
|
+
},
|
|
21997
|
+
{
|
|
21998
|
+
id: "debugging-skill",
|
|
21999
|
+
name: "Systematic Debugging",
|
|
22000
|
+
type: "skill",
|
|
22001
|
+
description: "Structured approach to finding and fixing bugs",
|
|
22002
|
+
installCommand: "npx skills add obra/superpowers@systematic-debugging --yes",
|
|
22003
|
+
phases: ["implementation", "review"],
|
|
22004
|
+
impact: "medium",
|
|
22005
|
+
website: "https://skills.sh"
|
|
22006
|
+
},
|
|
22007
|
+
{
|
|
22008
|
+
id: "planning-skill",
|
|
22009
|
+
name: "Writing Plans",
|
|
22010
|
+
type: "skill",
|
|
22011
|
+
description: "Structured planning before implementation",
|
|
22012
|
+
installCommand: "npx skills add obra/superpowers@writing-plans --yes",
|
|
22013
|
+
phases: ["planning"],
|
|
22014
|
+
impact: "medium",
|
|
22015
|
+
website: "https://skills.sh"
|
|
22016
|
+
},
|
|
22017
|
+
{
|
|
22018
|
+
id: "code-review-skill",
|
|
22019
|
+
name: "Code Review",
|
|
22020
|
+
type: "skill",
|
|
22021
|
+
description: "Thorough code review checklist and workflow",
|
|
22022
|
+
installCommand: "npx skills add obra/superpowers@requesting-code-review --yes",
|
|
22023
|
+
phases: ["review"],
|
|
22024
|
+
impact: "medium",
|
|
22025
|
+
website: "https://skills.sh"
|
|
22026
|
+
},
|
|
22027
|
+
{
|
|
22028
|
+
id: "react-skill",
|
|
22029
|
+
name: "React Best Practices",
|
|
22030
|
+
type: "skill",
|
|
22031
|
+
description: "Vercel's React and Next.js patterns",
|
|
22032
|
+
installCommand: "npx skills add vercel-labs/agent-skills@vercel-react-best-practices --yes",
|
|
22033
|
+
phases: ["implementation"],
|
|
22034
|
+
impact: "medium",
|
|
22035
|
+
website: "https://skills.sh"
|
|
22036
|
+
},
|
|
22037
|
+
{
|
|
22038
|
+
id: "remotion-skill",
|
|
22039
|
+
name: "Remotion",
|
|
22040
|
+
type: "skill",
|
|
22041
|
+
description: "Video creation with React",
|
|
22042
|
+
installCommand: "npx skills add remotion-dev/skills@remotion-best-practices --yes",
|
|
22043
|
+
phases: ["implementation"],
|
|
22044
|
+
impact: "low",
|
|
22045
|
+
website: "https://skills.sh"
|
|
22046
|
+
},
|
|
22047
|
+
{
|
|
22048
|
+
id: "frontend-design-skill",
|
|
22049
|
+
name: "Frontend Design",
|
|
22050
|
+
type: "skill",
|
|
22051
|
+
description: "Anthropic's frontend design patterns",
|
|
22052
|
+
installCommand: "npx skills add anthropics/skills@frontend-design --yes",
|
|
22053
|
+
phases: ["implementation"],
|
|
22054
|
+
impact: "medium",
|
|
22055
|
+
website: "https://skills.sh"
|
|
22056
|
+
},
|
|
22057
|
+
{
|
|
22058
|
+
id: "web-design-skill",
|
|
22059
|
+
name: "Web Design Guidelines",
|
|
22060
|
+
type: "skill",
|
|
22061
|
+
description: "Vercel's web design guidelines",
|
|
22062
|
+
installCommand: "npx skills add vercel-labs/agent-skills@web-design-guidelines --yes",
|
|
22063
|
+
phases: ["implementation"],
|
|
22064
|
+
impact: "medium",
|
|
22065
|
+
website: "https://skills.sh"
|
|
22066
|
+
}
|
|
22067
|
+
];
|
|
22068
|
+
var LIBRARIES = [
|
|
22069
|
+
{
|
|
22070
|
+
id: "vitest",
|
|
22071
|
+
name: "Vitest",
|
|
22072
|
+
type: "library",
|
|
22073
|
+
description: "Fast unit testing framework for TypeScript/JavaScript",
|
|
22074
|
+
installCommand: "bun add -D vitest",
|
|
22075
|
+
phases: ["review"],
|
|
22076
|
+
impact: "high",
|
|
22077
|
+
website: "https://vitest.dev"
|
|
22078
|
+
},
|
|
22079
|
+
{
|
|
22080
|
+
id: "playwright",
|
|
22081
|
+
name: "Playwright",
|
|
22082
|
+
type: "library",
|
|
22083
|
+
description: "E2E testing framework for web applications",
|
|
22084
|
+
installCommand: "bun add -D playwright @playwright/test",
|
|
22085
|
+
phases: ["review"],
|
|
22086
|
+
impact: "medium",
|
|
22087
|
+
website: "https://playwright.dev"
|
|
22088
|
+
},
|
|
22089
|
+
{
|
|
22090
|
+
id: "biome",
|
|
22091
|
+
name: "Biome",
|
|
22092
|
+
type: "library",
|
|
22093
|
+
description: "Fast linter and formatter (replaces ESLint + Prettier)",
|
|
22094
|
+
installCommand: "bun add -D @biomejs/biome && bunx biome init",
|
|
22095
|
+
phases: ["review"],
|
|
22096
|
+
impact: "medium",
|
|
22097
|
+
website: "https://biomejs.dev"
|
|
22098
|
+
}
|
|
22099
|
+
];
|
|
22100
|
+
var CONFIG_FILES = [
|
|
22101
|
+
{
|
|
22102
|
+
id: "claude-md",
|
|
22103
|
+
name: "CLAUDE.md",
|
|
22104
|
+
type: "config",
|
|
22105
|
+
description: "Project-specific instructions for AI - commands, conventions, architecture",
|
|
22106
|
+
configPath: "CLAUDE.md",
|
|
22107
|
+
configContent: `# Project Instructions
|
|
22108
|
+
|
|
22109
|
+
## Build & Test Commands
|
|
22110
|
+
\`\`\`bash
|
|
22111
|
+
bun test # Run tests
|
|
22112
|
+
bun run build # Build project
|
|
22113
|
+
bun run lint # Run linter
|
|
22114
|
+
\`\`\`
|
|
22115
|
+
|
|
22116
|
+
## Architecture
|
|
22117
|
+
Describe your project structure here.
|
|
22118
|
+
|
|
22119
|
+
## Conventions
|
|
22120
|
+
- Use TypeScript
|
|
22121
|
+
- Write tests for new features
|
|
22122
|
+
- Use conventional commits
|
|
22123
|
+
`,
|
|
22124
|
+
phases: ["requirements"],
|
|
22125
|
+
impact: "high"
|
|
22126
|
+
},
|
|
22127
|
+
{
|
|
22128
|
+
id: "agents-md",
|
|
22129
|
+
name: "AGENTS.md",
|
|
22130
|
+
type: "config",
|
|
22131
|
+
description: "Agent-specific instructions and workflows",
|
|
22132
|
+
configPath: "AGENTS.md",
|
|
22133
|
+
configContent: `# Agent Instructions
|
|
22134
|
+
|
|
22135
|
+
## Package Manager
|
|
22136
|
+
This project uses **bun** exclusively.
|
|
22137
|
+
|
|
22138
|
+
## Workflow
|
|
22139
|
+
1. Read existing code before making changes
|
|
22140
|
+
2. Write tests for new features
|
|
22141
|
+
3. Run \`bun test\` before committing
|
|
22142
|
+
`,
|
|
22143
|
+
phases: ["requirements", "planning"],
|
|
22144
|
+
impact: "medium"
|
|
22145
|
+
}
|
|
22146
|
+
];
|
|
22147
|
+
var WORKFLOW_TOOLS = [
|
|
22148
|
+
{
|
|
22149
|
+
id: "granola",
|
|
22150
|
+
name: "Granola",
|
|
22151
|
+
type: "tool",
|
|
22152
|
+
description: "AI meeting notes and requirements capture",
|
|
22153
|
+
phases: ["requirements"],
|
|
22154
|
+
impact: "medium",
|
|
22155
|
+
website: "https://www.granola.ai/"
|
|
22156
|
+
},
|
|
22157
|
+
{
|
|
22158
|
+
id: "repoprompt",
|
|
22159
|
+
name: "RepoPrompt",
|
|
22160
|
+
type: "tool",
|
|
22161
|
+
description: "Generate AI prompts from your codebase",
|
|
22162
|
+
phases: ["requirements"],
|
|
22163
|
+
impact: "medium",
|
|
22164
|
+
website: "https://repoprompt.com"
|
|
22165
|
+
},
|
|
22166
|
+
{
|
|
22167
|
+
id: "figma",
|
|
22168
|
+
name: "Figma / v0",
|
|
22169
|
+
type: "tool",
|
|
22170
|
+
description: "Design tools for UI/UX planning",
|
|
22171
|
+
phases: ["planning"],
|
|
22172
|
+
impact: "medium",
|
|
22173
|
+
website: "https://figma.com"
|
|
22174
|
+
}
|
|
22175
|
+
];
|
|
22176
|
+
var ALL_RECOMMENDATIONS = [
|
|
22177
|
+
...MCP_SERVERS,
|
|
22178
|
+
...PLUGINS,
|
|
22179
|
+
...SKILLS,
|
|
22180
|
+
...LIBRARIES,
|
|
22181
|
+
...CONFIG_FILES,
|
|
22182
|
+
...WORKFLOW_TOOLS
|
|
22183
|
+
];
|
|
22184
|
+
var INSTALLABLE_RECOMMENDATIONS = ALL_RECOMMENDATIONS.filter((r3) => r3.installCommand || r3.configPath);
|
|
22185
|
+
if (false) {}
|
|
22186
|
+
|
|
22187
|
+
// src/lib/verify-recommendations.ts
|
|
22188
|
+
function validateSkillFormat(command) {
|
|
22189
|
+
if (!command.match(/^(npx|bunx)\s+skills\s+add\s+/)) {
|
|
22190
|
+
return { valid: false, message: "Must start with 'npx skills add' or 'bunx skills add'" };
|
|
22191
|
+
}
|
|
22192
|
+
const match = command.match(/skills\s+add\s+([^\s]+)/);
|
|
22193
|
+
if (!match) {
|
|
22194
|
+
return { valid: false, message: "Could not extract skill path" };
|
|
22195
|
+
}
|
|
22196
|
+
const skillPath = match[1] ?? "";
|
|
22197
|
+
if (!skillPath.includes("@")) {
|
|
22198
|
+
return {
|
|
22199
|
+
valid: false,
|
|
22200
|
+
message: `Invalid format '${skillPath}'. Must use @ separator: owner/repo@skill-name`
|
|
22201
|
+
};
|
|
22202
|
+
}
|
|
22203
|
+
const skillMatch = skillPath.match(/^([^\/]+)\/([^@]+)@([^\/\s]+)$/);
|
|
22204
|
+
if (!skillMatch) {
|
|
22205
|
+
return {
|
|
22206
|
+
valid: false,
|
|
22207
|
+
message: `Invalid skill path '${skillPath}'. Expected: owner/repo@skill-name`
|
|
22208
|
+
};
|
|
22209
|
+
}
|
|
22210
|
+
if (!command.includes("--yes")) {
|
|
22211
|
+
return {
|
|
22212
|
+
valid: false,
|
|
22213
|
+
message: "Missing --yes flag for non-interactive installation"
|
|
22214
|
+
};
|
|
22215
|
+
}
|
|
22216
|
+
return { valid: true, message: "Valid skills.sh format" };
|
|
22217
|
+
}
|
|
22218
|
+
function validateMCPFormat(command) {
|
|
22219
|
+
if (command.startsWith("claude mcp add")) {
|
|
22220
|
+
if (command.includes("-- npx") || command.includes("--transport")) {
|
|
22221
|
+
return { valid: true, message: "Valid Claude MCP format" };
|
|
22222
|
+
}
|
|
22223
|
+
return { valid: false, message: "MCP command should have '-- npx' or '--transport' pattern" };
|
|
22224
|
+
}
|
|
22225
|
+
if (command.startsWith("pipx run")) {
|
|
22226
|
+
return { valid: true, message: "Valid pipx MCP format" };
|
|
22227
|
+
}
|
|
22228
|
+
if (command.includes("config") || command.includes("mcpServers")) {
|
|
22229
|
+
return { valid: true, message: "Manual configuration instruction" };
|
|
22230
|
+
}
|
|
22231
|
+
return { valid: false, message: "Unknown MCP install format" };
|
|
22232
|
+
}
|
|
22233
|
+
function validatePackageFormat(command) {
|
|
22234
|
+
if (command.match(/^bun\s+add\s+(-[dDgG]\s+)?[@\w\/-]+/)) {
|
|
22235
|
+
return { valid: true, message: "Valid bun add format" };
|
|
22236
|
+
}
|
|
22237
|
+
if (command.match(/^npm\s+(install|i)\s+(-[dDgG]\s+)?[@\w\/-]+/)) {
|
|
22238
|
+
return { valid: true, message: "Valid npm install format" };
|
|
22239
|
+
}
|
|
22240
|
+
if (command.match(/^(bunx|npx)\s+/)) {
|
|
22241
|
+
return { valid: true, message: "Valid bunx/npx format" };
|
|
22242
|
+
}
|
|
22243
|
+
return { valid: false, message: "Unknown package install format" };
|
|
22244
|
+
}
|
|
22245
|
+
async function checkNpmPackageExists(packageName) {
|
|
22246
|
+
try {
|
|
22247
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}`, {
|
|
22248
|
+
method: "HEAD",
|
|
22249
|
+
signal: AbortSignal.timeout(5000)
|
|
22250
|
+
});
|
|
22251
|
+
if (response.ok) {
|
|
22252
|
+
return { exists: true, message: "Package exists on npm" };
|
|
22253
|
+
}
|
|
22254
|
+
return { exists: false, message: `Package not found on npm (${response.status})` };
|
|
22255
|
+
} catch (error2) {
|
|
22256
|
+
return { exists: false, message: `Could not verify: ${error2 instanceof Error ? error2.message : String(error2)}` };
|
|
22257
|
+
}
|
|
22258
|
+
}
|
|
22259
|
+
async function checkGitHubRepoExists(owner, repo) {
|
|
22260
|
+
try {
|
|
22261
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
|
|
22262
|
+
method: "HEAD",
|
|
22263
|
+
signal: AbortSignal.timeout(5000)
|
|
22264
|
+
});
|
|
22265
|
+
if (response.ok) {
|
|
22266
|
+
return { exists: true, message: "GitHub repo exists" };
|
|
22267
|
+
}
|
|
22268
|
+
if (response.status === 404) {
|
|
22269
|
+
return { exists: false, message: `GitHub repo ${owner}/${repo} not found` };
|
|
22270
|
+
}
|
|
22271
|
+
return { exists: false, message: `GitHub API error (${response.status})` };
|
|
22272
|
+
} catch (error2) {
|
|
22273
|
+
return { exists: false, message: `Could not verify: ${error2 instanceof Error ? error2.message : String(error2)}` };
|
|
22274
|
+
}
|
|
22275
|
+
}
|
|
22276
|
+
function verifyRecommendation(rec) {
|
|
22277
|
+
const base = {
|
|
22278
|
+
id: rec.id,
|
|
22279
|
+
name: rec.name,
|
|
22280
|
+
type: rec.type,
|
|
22281
|
+
installCommand: rec.installCommand
|
|
22282
|
+
};
|
|
22283
|
+
if (!rec.installCommand && !rec.configPath) {
|
|
22284
|
+
return { ...base, status: "skipped", message: "No install command (informational only)" };
|
|
22285
|
+
}
|
|
22286
|
+
if (rec.type === "config" && rec.configPath) {
|
|
22287
|
+
return { ...base, status: "valid", message: `Config file: ${rec.configPath}` };
|
|
22288
|
+
}
|
|
22289
|
+
if (!rec.installCommand) {
|
|
22290
|
+
return { ...base, status: "skipped", message: "No install command" };
|
|
22291
|
+
}
|
|
22292
|
+
switch (rec.type) {
|
|
22293
|
+
case "skill": {
|
|
22294
|
+
const result = validateSkillFormat(rec.installCommand);
|
|
22295
|
+
return {
|
|
22296
|
+
...base,
|
|
22297
|
+
status: result.valid ? "valid" : "invalid",
|
|
22298
|
+
message: result.message
|
|
22299
|
+
};
|
|
22300
|
+
}
|
|
22301
|
+
case "mcp": {
|
|
22302
|
+
const result = validateMCPFormat(rec.installCommand);
|
|
22303
|
+
return {
|
|
22304
|
+
...base,
|
|
22305
|
+
status: result.valid ? "valid" : "invalid",
|
|
22306
|
+
message: result.message
|
|
22307
|
+
};
|
|
22308
|
+
}
|
|
22309
|
+
case "library":
|
|
22310
|
+
case "plugin": {
|
|
22311
|
+
const result = validatePackageFormat(rec.installCommand);
|
|
22312
|
+
return {
|
|
22313
|
+
...base,
|
|
22314
|
+
status: result.valid ? "valid" : "invalid",
|
|
22315
|
+
message: result.message
|
|
22316
|
+
};
|
|
22317
|
+
}
|
|
22318
|
+
default:
|
|
22319
|
+
return { ...base, status: "warning", message: `Unknown type: ${rec.type}` };
|
|
22320
|
+
}
|
|
22321
|
+
}
|
|
22322
|
+
function verifyAllRecommendations() {
|
|
22323
|
+
const results = [];
|
|
22324
|
+
for (const rec of ALL_RECOMMENDATIONS) {
|
|
22325
|
+
results.push(verifyRecommendation(rec));
|
|
22326
|
+
}
|
|
22327
|
+
const valid = results.filter((r3) => r3.status === "valid").length;
|
|
22328
|
+
const invalid = results.filter((r3) => r3.status === "invalid").length;
|
|
22329
|
+
const warnings = results.filter((r3) => r3.status === "warning").length;
|
|
22330
|
+
const skipped = results.filter((r3) => r3.status === "skipped").length;
|
|
22331
|
+
return {
|
|
22332
|
+
total: results.length,
|
|
22333
|
+
valid,
|
|
22334
|
+
invalid,
|
|
22335
|
+
warnings,
|
|
22336
|
+
skipped,
|
|
22337
|
+
results
|
|
22338
|
+
};
|
|
22339
|
+
}
|
|
22340
|
+
async function verifyRecommendationsThorough() {
|
|
22341
|
+
const basicResults = verifyAllRecommendations();
|
|
22342
|
+
const enhancedResults = [];
|
|
22343
|
+
for (const result of basicResults.results) {
|
|
22344
|
+
if (result.status === "invalid" || result.status === "skipped") {
|
|
22345
|
+
enhancedResults.push(result);
|
|
22346
|
+
continue;
|
|
22347
|
+
}
|
|
22348
|
+
if (result.installCommand?.includes("skills add")) {
|
|
22349
|
+
const match = result.installCommand.match(/skills\s+add\s+([^\/]+)\/([^@]+)@/);
|
|
22350
|
+
if (match && match[1] && match[2]) {
|
|
22351
|
+
const owner = match[1];
|
|
22352
|
+
const repo = match[2];
|
|
22353
|
+
const check = await checkGitHubRepoExists(owner, repo);
|
|
22354
|
+
if (!check.exists) {
|
|
22355
|
+
enhancedResults.push({
|
|
22356
|
+
...result,
|
|
22357
|
+
status: "invalid",
|
|
22358
|
+
message: check.message
|
|
22359
|
+
});
|
|
22360
|
+
continue;
|
|
22361
|
+
}
|
|
22362
|
+
}
|
|
22363
|
+
}
|
|
22364
|
+
if (result.installCommand?.match(/npx\s+-y\s+@?[\w\/-]+/)) {
|
|
22365
|
+
const match = result.installCommand.match(/npx\s+-y\s+(@?[\w\/-]+)/);
|
|
22366
|
+
if (match && match[1]) {
|
|
22367
|
+
const packageName = match[1];
|
|
22368
|
+
const check = await checkNpmPackageExists(packageName);
|
|
22369
|
+
if (!check.exists) {
|
|
22370
|
+
enhancedResults.push({
|
|
22371
|
+
...result,
|
|
22372
|
+
status: "warning",
|
|
22373
|
+
message: check.message
|
|
22374
|
+
});
|
|
22375
|
+
continue;
|
|
22376
|
+
}
|
|
22377
|
+
}
|
|
22378
|
+
}
|
|
22379
|
+
enhancedResults.push(result);
|
|
22380
|
+
}
|
|
22381
|
+
const valid = enhancedResults.filter((r3) => r3.status === "valid").length;
|
|
22382
|
+
const invalid = enhancedResults.filter((r3) => r3.status === "invalid").length;
|
|
22383
|
+
const warnings = enhancedResults.filter((r3) => r3.status === "warning").length;
|
|
22384
|
+
const skipped = enhancedResults.filter((r3) => r3.status === "skipped").length;
|
|
22385
|
+
return {
|
|
22386
|
+
total: enhancedResults.length,
|
|
22387
|
+
valid,
|
|
22388
|
+
invalid,
|
|
22389
|
+
warnings,
|
|
22390
|
+
skipped,
|
|
22391
|
+
results: enhancedResults
|
|
22392
|
+
};
|
|
22393
|
+
}
|
|
22394
|
+
function printVerificationResults(summary, verbose = false) {
|
|
22395
|
+
console.log(`
|
|
22396
|
+
═══════════════════════════════════════════════════════════════`);
|
|
22397
|
+
console.log(" RECOMMENDATION VERIFICATION REPORT");
|
|
22398
|
+
console.log(`═══════════════════════════════════════════════════════════════
|
|
22399
|
+
`);
|
|
22400
|
+
const invalid = summary.results.filter((r3) => r3.status === "invalid");
|
|
22401
|
+
if (invalid.length > 0) {
|
|
22402
|
+
console.log(`❌ INVALID RECOMMENDATIONS:
|
|
22403
|
+
`);
|
|
22404
|
+
for (const r3 of invalid) {
|
|
22405
|
+
console.log(` ${r3.name} (${r3.type})`);
|
|
22406
|
+
console.log(` ID: ${r3.id}`);
|
|
22407
|
+
console.log(` Issue: ${r3.message}`);
|
|
22408
|
+
if (r3.installCommand) {
|
|
22409
|
+
console.log(` Command: ${r3.installCommand}`);
|
|
22410
|
+
}
|
|
22411
|
+
console.log();
|
|
22412
|
+
}
|
|
22413
|
+
}
|
|
22414
|
+
const warnings = summary.results.filter((r3) => r3.status === "warning");
|
|
22415
|
+
if (warnings.length > 0) {
|
|
22416
|
+
console.log(`⚠️ WARNINGS:
|
|
22417
|
+
`);
|
|
22418
|
+
for (const r3 of warnings) {
|
|
22419
|
+
console.log(` ${r3.name}: ${r3.message}`);
|
|
22420
|
+
}
|
|
22421
|
+
console.log();
|
|
22422
|
+
}
|
|
22423
|
+
if (verbose) {
|
|
22424
|
+
const valid = summary.results.filter((r3) => r3.status === "valid");
|
|
22425
|
+
if (valid.length > 0) {
|
|
22426
|
+
console.log(`✓ VALID RECOMMENDATIONS:
|
|
22427
|
+
`);
|
|
22428
|
+
for (const r3 of valid) {
|
|
22429
|
+
console.log(` ${r3.name} (${r3.type}): ${r3.message}`);
|
|
22430
|
+
}
|
|
22431
|
+
console.log();
|
|
22432
|
+
}
|
|
22433
|
+
}
|
|
22434
|
+
console.log("───────────────────────────────────────────────────────────────");
|
|
22435
|
+
console.log(` Total: ${summary.total}`);
|
|
22436
|
+
console.log(` ✓ Valid: ${summary.valid}`);
|
|
22437
|
+
console.log(` ❌ Invalid: ${summary.invalid}`);
|
|
22438
|
+
console.log(` ⚠️ Warnings: ${summary.warnings}`);
|
|
22439
|
+
console.log(` ○ Skipped: ${summary.skipped}`);
|
|
22440
|
+
console.log(`───────────────────────────────────────────────────────────────
|
|
22441
|
+
`);
|
|
22442
|
+
if (summary.invalid > 0) {
|
|
22443
|
+
console.log(`❌ VERIFICATION FAILED - Fix invalid recommendations before release!
|
|
22444
|
+
`);
|
|
22445
|
+
} else if (summary.warnings > 0) {
|
|
22446
|
+
console.log(`⚠️ VERIFICATION PASSED WITH WARNINGS
|
|
22447
|
+
`);
|
|
22448
|
+
} else {
|
|
22449
|
+
console.log(`✓ ALL RECOMMENDATIONS VERIFIED
|
|
22450
|
+
`);
|
|
22451
|
+
}
|
|
22452
|
+
}
|
|
22453
|
+
|
|
22454
|
+
// src/commands/doctor.ts
|
|
21364
22455
|
var doctorCommand = defineCommand2({
|
|
21365
22456
|
meta: {
|
|
21366
22457
|
name: "doctor",
|
|
21367
22458
|
description: "Check tool detection, data sources, and connectivity health"
|
|
21368
22459
|
},
|
|
21369
|
-
|
|
22460
|
+
args: {
|
|
22461
|
+
"verify-recommendations": {
|
|
22462
|
+
type: "boolean",
|
|
22463
|
+
description: "Verify all recommendations in the database are valid",
|
|
22464
|
+
alias: "verify",
|
|
22465
|
+
default: false
|
|
22466
|
+
},
|
|
22467
|
+
thorough: {
|
|
22468
|
+
type: "boolean",
|
|
22469
|
+
description: "Run thorough verification with network checks",
|
|
22470
|
+
alias: "t",
|
|
22471
|
+
default: false
|
|
22472
|
+
},
|
|
22473
|
+
verbose: {
|
|
22474
|
+
type: "boolean",
|
|
22475
|
+
description: "Show all results including valid recommendations",
|
|
22476
|
+
alias: "v",
|
|
22477
|
+
default: false
|
|
22478
|
+
}
|
|
22479
|
+
},
|
|
22480
|
+
async run({ args }) {
|
|
22481
|
+
if (args["verify-recommendations"]) {
|
|
22482
|
+
console.log();
|
|
22483
|
+
console.log(colors2.bold(colors2.primary(" NaironAI Recommendation Verifier")));
|
|
22484
|
+
console.log(colors2.dim(` Validating all recommendation install commands...
|
|
22485
|
+
`));
|
|
22486
|
+
const spinner2 = createSpinner("Verifying recommendations...");
|
|
22487
|
+
spinner2.start();
|
|
22488
|
+
let summary;
|
|
22489
|
+
if (args.thorough) {
|
|
22490
|
+
spinner2.text = "Verifying recommendations (thorough mode with network checks)...";
|
|
22491
|
+
summary = await verifyRecommendationsThorough();
|
|
22492
|
+
} else {
|
|
22493
|
+
summary = verifyAllRecommendations();
|
|
22494
|
+
}
|
|
22495
|
+
if (summary.invalid > 0) {
|
|
22496
|
+
spinner2.fail(`Found ${summary.invalid} invalid recommendation(s)`);
|
|
22497
|
+
} else if (summary.warnings > 0) {
|
|
22498
|
+
spinner2.warn(`Verification passed with ${summary.warnings} warning(s)`);
|
|
22499
|
+
} else {
|
|
22500
|
+
spinner2.succeed("All recommendations verified");
|
|
22501
|
+
}
|
|
22502
|
+
printVerificationResults(summary, args.verbose);
|
|
22503
|
+
if (summary.invalid > 0) {
|
|
22504
|
+
process.exit(1);
|
|
22505
|
+
}
|
|
22506
|
+
return;
|
|
22507
|
+
}
|
|
21370
22508
|
console.log();
|
|
21371
22509
|
console.log(colors2.bold(colors2.primary(" NaironAI Doctor")));
|
|
21372
22510
|
console.log(colors2.dim(` Checking system health...
|
|
@@ -21377,16 +22515,16 @@ var doctorCommand = defineCommand2({
|
|
|
21377
22515
|
results.push({ label: "OS", status: "info", value: `${process.platform} ${process.arch}` });
|
|
21378
22516
|
results.push({ label: "Bun", status: "info", value: typeof Bun !== "undefined" ? Bun.version : "N/A" });
|
|
21379
22517
|
results.push({ label: "Node", status: "info", value: process.version });
|
|
21380
|
-
results.push({ label: "Home", status: "info", value:
|
|
21381
|
-
const gitDir =
|
|
21382
|
-
if (
|
|
22518
|
+
results.push({ label: "Home", status: "info", value: homedir10() });
|
|
22519
|
+
const gitDir = join13(process.cwd(), ".git");
|
|
22520
|
+
if (existsSync13(gitDir)) {
|
|
21383
22521
|
results.push({ label: "Git", status: "success", value: "Repository detected" });
|
|
21384
22522
|
} else {
|
|
21385
22523
|
results.push({ label: "Git", status: "warn", value: "No repository in current directory" });
|
|
21386
22524
|
}
|
|
21387
22525
|
for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
|
|
21388
|
-
const resolvedPath = pathTemplate.replace("~",
|
|
21389
|
-
if (
|
|
22526
|
+
const resolvedPath = pathTemplate.replace("~", homedir10());
|
|
22527
|
+
if (existsSync13(resolvedPath)) {
|
|
21390
22528
|
results.push({ label: agent, status: "success", value: `found at ${resolvedPath}` });
|
|
21391
22529
|
}
|
|
21392
22530
|
}
|
|
@@ -21440,9 +22578,9 @@ var doctorCommand = defineCommand2({
|
|
|
21440
22578
|
});
|
|
21441
22579
|
|
|
21442
22580
|
// src/commands/setup.ts
|
|
21443
|
-
import { existsSync as
|
|
21444
|
-
import { join as
|
|
21445
|
-
import { homedir as
|
|
22581
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, copyFileSync } from "node:fs";
|
|
22582
|
+
import { join as join14, dirname as dirname2 } from "node:path";
|
|
22583
|
+
import { homedir as homedir11, platform as platform2 } from "node:os";
|
|
21446
22584
|
import { execSync as execSync3 } from "node:child_process";
|
|
21447
22585
|
import { createInterface } from "node:readline";
|
|
21448
22586
|
var CONVEX_SITE_URL = "https://steady-bass-841.convex.site";
|
|
@@ -21461,7 +22599,7 @@ var FALLBACK_HARNESSES = [
|
|
|
21461
22599
|
id: "claude-code",
|
|
21462
22600
|
name: "Claude Code",
|
|
21463
22601
|
configPaths: [
|
|
21464
|
-
|
|
22602
|
+
join14(homedir11(), ".claude.json")
|
|
21465
22603
|
],
|
|
21466
22604
|
mcpConfigKey: "mcpServers",
|
|
21467
22605
|
detected: false
|
|
@@ -21470,8 +22608,8 @@ var FALLBACK_HARNESSES = [
|
|
|
21470
22608
|
id: "opencode",
|
|
21471
22609
|
name: "OpenCode",
|
|
21472
22610
|
configPaths: [
|
|
21473
|
-
|
|
21474
|
-
|
|
22611
|
+
join14(homedir11(), ".opencode.json"),
|
|
22612
|
+
join14(homedir11(), ".config", "opencode", "opencode.json")
|
|
21475
22613
|
],
|
|
21476
22614
|
mcpConfigKey: "mcp",
|
|
21477
22615
|
detected: false
|
|
@@ -21480,8 +22618,8 @@ var FALLBACK_HARNESSES = [
|
|
|
21480
22618
|
id: "cursor",
|
|
21481
22619
|
name: "Cursor",
|
|
21482
22620
|
configPaths: [
|
|
21483
|
-
|
|
21484
|
-
|
|
22621
|
+
join14(homedir11(), ".cursor", "mcp.json"),
|
|
22622
|
+
join14(homedir11(), "Library", "Application Support", "Cursor", "User", "globalStorage", "mcp.json")
|
|
21485
22623
|
],
|
|
21486
22624
|
mcpConfigKey: "mcpServers",
|
|
21487
22625
|
detected: false
|
|
@@ -21498,7 +22636,7 @@ function getConfigPathsForPlatform(harness) {
|
|
|
21498
22636
|
} else {
|
|
21499
22637
|
paths = pathMap.linux;
|
|
21500
22638
|
}
|
|
21501
|
-
return paths.map((p) => p.replace(/^~/,
|
|
22639
|
+
return paths.map((p) => p.replace(/^~/, homedir11()).replace(/%USERPROFILE%/gi, homedir11()).replace(/%APPDATA%/gi, join14(homedir11(), "AppData", "Roaming")));
|
|
21502
22640
|
}
|
|
21503
22641
|
function convertAPIHarnessToConfig(harness) {
|
|
21504
22642
|
return {
|
|
@@ -21513,7 +22651,7 @@ function detectHarnesses(harnesses) {
|
|
|
21513
22651
|
const detected = [];
|
|
21514
22652
|
for (const harness of harnesses) {
|
|
21515
22653
|
for (const configPath of harness.configPaths) {
|
|
21516
|
-
if (
|
|
22654
|
+
if (existsSync14(configPath)) {
|
|
21517
22655
|
detected.push({
|
|
21518
22656
|
...harness,
|
|
21519
22657
|
detected: true,
|
|
@@ -21526,19 +22664,19 @@ function detectHarnesses(harnesses) {
|
|
|
21526
22664
|
return detected;
|
|
21527
22665
|
}
|
|
21528
22666
|
function backupConfig(configPath) {
|
|
21529
|
-
const backupDir =
|
|
21530
|
-
if (!
|
|
22667
|
+
const backupDir = join14(homedir11(), ".nairon", "backups");
|
|
22668
|
+
if (!existsSync14(backupDir)) {
|
|
21531
22669
|
mkdirSync5(backupDir, { recursive: true });
|
|
21532
22670
|
}
|
|
21533
22671
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
21534
22672
|
const filename = configPath.replace(/[/\\]/g, "_").replace(/^_/, "");
|
|
21535
|
-
const backupPath =
|
|
22673
|
+
const backupPath = join14(backupDir, `${timestamp}_${filename}`);
|
|
21536
22674
|
copyFileSync(configPath, backupPath);
|
|
21537
22675
|
return backupPath;
|
|
21538
22676
|
}
|
|
21539
22677
|
function readJsonConfig(configPath) {
|
|
21540
22678
|
try {
|
|
21541
|
-
const content =
|
|
22679
|
+
const content = readFileSync13(configPath, "utf-8");
|
|
21542
22680
|
return JSON.parse(content);
|
|
21543
22681
|
} catch {
|
|
21544
22682
|
return {};
|
|
@@ -21546,7 +22684,7 @@ function readJsonConfig(configPath) {
|
|
|
21546
22684
|
}
|
|
21547
22685
|
function writeJsonConfig(configPath, config) {
|
|
21548
22686
|
const dir = dirname2(configPath);
|
|
21549
|
-
if (!
|
|
22687
|
+
if (!existsSync14(dir)) {
|
|
21550
22688
|
mkdirSync5(dir, { recursive: true });
|
|
21551
22689
|
}
|
|
21552
22690
|
writeFileSync6(configPath, JSON.stringify(config, null, 2));
|
|
@@ -21590,7 +22728,7 @@ function getInstalledMCPs(harnesses) {
|
|
|
21590
22728
|
const installed = new Set;
|
|
21591
22729
|
for (const harness of harnesses) {
|
|
21592
22730
|
for (const configPath of harness.configPaths) {
|
|
21593
|
-
if (!
|
|
22731
|
+
if (!existsSync14(configPath))
|
|
21594
22732
|
continue;
|
|
21595
22733
|
const config = readJsonConfig(configPath);
|
|
21596
22734
|
const mcpServers = config[harness.mcpConfigKey];
|
|
@@ -21614,7 +22752,7 @@ async function installMCPServer(serverName, instruction, harness) {
|
|
|
21614
22752
|
if (!harness.configPath || !instruction.mcpServerConfig)
|
|
21615
22753
|
return false;
|
|
21616
22754
|
try {
|
|
21617
|
-
if (
|
|
22755
|
+
if (existsSync14(harness.configPath)) {
|
|
21618
22756
|
backupConfig(harness.configPath);
|
|
21619
22757
|
}
|
|
21620
22758
|
const config = readJsonConfig(harness.configPath);
|
|
@@ -21659,8 +22797,8 @@ async function installBeads(projectDir) {
|
|
|
21659
22797
|
}
|
|
21660
22798
|
}
|
|
21661
22799
|
}
|
|
21662
|
-
const beadsDir =
|
|
21663
|
-
if (!
|
|
22800
|
+
const beadsDir = join14(projectDir, ".beads");
|
|
22801
|
+
if (!existsSync14(beadsDir)) {
|
|
21664
22802
|
const projectName = projectDir.split(/[/\\]/).pop() ?? "project";
|
|
21665
22803
|
const initSpinner = createSpinner(`Initializing Beads in ${projectName}...`);
|
|
21666
22804
|
initSpinner.start();
|
|
@@ -21679,9 +22817,9 @@ async function installBeads(projectDir) {
|
|
|
21679
22817
|
}
|
|
21680
22818
|
}
|
|
21681
22819
|
async function createClaudeRules(projectDir) {
|
|
21682
|
-
const claudeMdPath =
|
|
21683
|
-
const agentsMdPath =
|
|
21684
|
-
if (
|
|
22820
|
+
const claudeMdPath = join14(projectDir, "CLAUDE.md");
|
|
22821
|
+
const agentsMdPath = join14(projectDir, "AGENTS.md");
|
|
22822
|
+
if (existsSync14(claudeMdPath) || existsSync14(agentsMdPath)) {
|
|
21685
22823
|
console.log(` ${icons.info} CLAUDE.md or AGENTS.md already exists`);
|
|
21686
22824
|
return true;
|
|
21687
22825
|
}
|
|
@@ -22004,6 +23142,25 @@ var BOLD = "\x1B[1m";
|
|
|
22004
23142
|
var RESET = "\x1B[0m";
|
|
22005
23143
|
var MAGENTA = "\x1B[35m";
|
|
22006
23144
|
var CHANGELOG = [
|
|
23145
|
+
{
|
|
23146
|
+
version: "0.3.14",
|
|
23147
|
+
date: "2026-02-14",
|
|
23148
|
+
title: "Enhanced Frustration Detection",
|
|
23149
|
+
highlights: [
|
|
23150
|
+
"Scan now shows exact prompts that caused frustration",
|
|
23151
|
+
"Root cause analysis: hallucination, context loss, undo loops",
|
|
23152
|
+
"Each frustration linked to a specific fix with install command"
|
|
23153
|
+
]
|
|
23154
|
+
},
|
|
23155
|
+
{
|
|
23156
|
+
version: "0.3.13",
|
|
23157
|
+
date: "2026-02-14",
|
|
23158
|
+
title: "Reliable Recommendations",
|
|
23159
|
+
highlights: [
|
|
23160
|
+
"Fixed skills.sh format - skills now install correctly",
|
|
23161
|
+
"New: nb doctor --verify-recommendations to validate install commands"
|
|
23162
|
+
]
|
|
23163
|
+
},
|
|
22007
23164
|
{
|
|
22008
23165
|
version: "0.3.12",
|
|
22009
23166
|
date: "2026-02-14",
|
|
@@ -22403,7 +23560,7 @@ function showFullChangelog() {
|
|
|
22403
23560
|
// package.json
|
|
22404
23561
|
var package_default = {
|
|
22405
23562
|
name: "nairon-bench",
|
|
22406
|
-
version: "0.3.
|
|
23563
|
+
version: "0.3.14",
|
|
22407
23564
|
description: "AI workflow benchmarking CLI",
|
|
22408
23565
|
type: "module",
|
|
22409
23566
|
bin: {
|
|
@@ -22435,8 +23592,9 @@ var package_default = {
|
|
|
22435
23592
|
"build:linux-arm64": "bun build --compile --target=bun-linux-arm64 --outfile=dist/nb-linux-arm64 src/index.ts",
|
|
22436
23593
|
"build:windows-x64": "bun build --compile --target=bun-windows-x64 --outfile=dist/nb-windows-x64.exe src/index.ts",
|
|
22437
23594
|
typecheck: "tsc --noEmit",
|
|
22438
|
-
test: "
|
|
22439
|
-
"test:e2e": "
|
|
23595
|
+
test: "vitest run",
|
|
23596
|
+
"test:e2e": "vitest run tests/e2e/",
|
|
23597
|
+
"test:watch": "vitest",
|
|
22440
23598
|
clean: "rm -rf dist",
|
|
22441
23599
|
prepublishOnly: "bun run build"
|
|
22442
23600
|
},
|
|
@@ -22461,9 +23619,9 @@ var versionCommand = defineCommand2({
|
|
|
22461
23619
|
|
|
22462
23620
|
// src/commands/init.ts
|
|
22463
23621
|
init_dist();
|
|
22464
|
-
import { existsSync as
|
|
22465
|
-
import { homedir as
|
|
22466
|
-
import { join as
|
|
23622
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
23623
|
+
import { homedir as homedir12, platform as platform3, arch } from "node:os";
|
|
23624
|
+
import { join as join15 } from "node:path";
|
|
22467
23625
|
init_client();
|
|
22468
23626
|
|
|
22469
23627
|
// src/lib/github-auth.ts
|
|
@@ -22633,8 +23791,8 @@ var initCommand = defineCommand2({
|
|
|
22633
23791
|
consola.start("Step 2/6: Detecting AI agents...");
|
|
22634
23792
|
const detectedAgents = [];
|
|
22635
23793
|
for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
|
|
22636
|
-
const resolvedPath = pathTemplate.replace("~",
|
|
22637
|
-
if (
|
|
23794
|
+
const resolvedPath = pathTemplate.replace("~", homedir12());
|
|
23795
|
+
if (existsSync15(resolvedPath)) {
|
|
22638
23796
|
detectedAgents.push(agent);
|
|
22639
23797
|
consola.success(` Found: ${agent}`);
|
|
22640
23798
|
}
|
|
@@ -22737,7 +23895,7 @@ var initCommand = defineCommand2({
|
|
|
22737
23895
|
{ name: "playwright", configs: ["playwright.config.ts", "playwright.config.js"] }
|
|
22738
23896
|
];
|
|
22739
23897
|
for (const fw of testFrameworks) {
|
|
22740
|
-
if (fw.configs.some((c3) =>
|
|
23898
|
+
if (fw.configs.some((c3) => existsSync15(join15(process.cwd(), c3)))) {
|
|
22741
23899
|
toolStack.testing.push(fw.name);
|
|
22742
23900
|
}
|
|
22743
23901
|
}
|
|
@@ -22806,13 +23964,13 @@ var initCommand = defineCommand2({
|
|
|
22806
23964
|
});
|
|
22807
23965
|
function detectPackageManager() {
|
|
22808
23966
|
const cwd = process.cwd();
|
|
22809
|
-
if (
|
|
23967
|
+
if (existsSync15(join15(cwd, "bun.lock")) || existsSync15(join15(cwd, "bun.lockb")))
|
|
22810
23968
|
return "bun";
|
|
22811
|
-
if (
|
|
23969
|
+
if (existsSync15(join15(cwd, "pnpm-lock.yaml")))
|
|
22812
23970
|
return "pnpm";
|
|
22813
|
-
if (
|
|
23971
|
+
if (existsSync15(join15(cwd, "yarn.lock")))
|
|
22814
23972
|
return "yarn";
|
|
22815
|
-
if (
|
|
23973
|
+
if (existsSync15(join15(cwd, "package-lock.json")))
|
|
22816
23974
|
return "npm";
|
|
22817
23975
|
return;
|
|
22818
23976
|
}
|
|
@@ -22827,15 +23985,15 @@ var CLAUDE_CODE_SKILLS = [
|
|
|
22827
23985
|
];
|
|
22828
23986
|
function detectEasyWins(detectedAgents) {
|
|
22829
23987
|
const wins = [];
|
|
22830
|
-
const home =
|
|
23988
|
+
const home = homedir12();
|
|
22831
23989
|
const hasClaudeCode = detectedAgents.some((a2) => a2.toLowerCase().includes("claude") || a2.toLowerCase().includes("opencode"));
|
|
22832
|
-
const claudeConfigDir =
|
|
22833
|
-
const hasClaudeConfig =
|
|
23990
|
+
const claudeConfigDir = join15(home, ".claude");
|
|
23991
|
+
const hasClaudeConfig = existsSync15(claudeConfigDir);
|
|
22834
23992
|
if (hasClaudeCode || hasClaudeConfig) {
|
|
22835
|
-
const skillsDir =
|
|
23993
|
+
const skillsDir = join15(home, ".claude", "skills");
|
|
22836
23994
|
for (const skill of CLAUDE_CODE_SKILLS) {
|
|
22837
|
-
const skillPath =
|
|
22838
|
-
const isInstalled =
|
|
23995
|
+
const skillPath = join15(skillsDir, skill.id);
|
|
23996
|
+
const isInstalled = existsSync15(skillPath);
|
|
22839
23997
|
if (!isInstalled) {
|
|
22840
23998
|
wins.push({
|
|
22841
23999
|
icon: skill.icon,
|
|
@@ -22851,14 +24009,14 @@ function detectEasyWins(detectedAgents) {
|
|
|
22851
24009
|
// src/commands/onboard.ts
|
|
22852
24010
|
init_dist();
|
|
22853
24011
|
init_client();
|
|
22854
|
-
import { existsSync as
|
|
22855
|
-
import { join as
|
|
24012
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync7, readdirSync as readdirSync9 } from "node:fs";
|
|
24013
|
+
import { join as join17 } from "node:path";
|
|
22856
24014
|
|
|
22857
24015
|
// src/lib/project-context-detector.ts
|
|
22858
|
-
import { existsSync as
|
|
22859
|
-
import { join as
|
|
24016
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "node:fs";
|
|
24017
|
+
import { join as join16, basename as basename4 } from "node:path";
|
|
22860
24018
|
function detectProjectContext(projectDir) {
|
|
22861
|
-
const projectName =
|
|
24019
|
+
const projectName = basename4(projectDir);
|
|
22862
24020
|
const projectId = generateProjectId(projectDir);
|
|
22863
24021
|
const techStack = detectTechStack(projectDir);
|
|
22864
24022
|
const projectType = inferProjectType(projectDir, techStack);
|
|
@@ -22892,11 +24050,11 @@ function detectTechStack(projectDir) {
|
|
|
22892
24050
|
};
|
|
22893
24051
|
}
|
|
22894
24052
|
function readPackageJson(projectDir) {
|
|
22895
|
-
const pkgPath =
|
|
22896
|
-
if (!
|
|
24053
|
+
const pkgPath = join16(projectDir, "package.json");
|
|
24054
|
+
if (!existsSync16(pkgPath))
|
|
22897
24055
|
return {};
|
|
22898
24056
|
try {
|
|
22899
|
-
return JSON.parse(
|
|
24057
|
+
return JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
22900
24058
|
} catch {
|
|
22901
24059
|
return {};
|
|
22902
24060
|
}
|
|
@@ -22917,34 +24075,34 @@ function detectFramework(deps) {
|
|
|
22917
24075
|
return null;
|
|
22918
24076
|
}
|
|
22919
24077
|
function detectMetaFramework(deps, projectDir) {
|
|
22920
|
-
if (deps.next ||
|
|
24078
|
+
if (deps.next || existsSync16(join16(projectDir, "next.config.js")) || existsSync16(join16(projectDir, "next.config.mjs")))
|
|
22921
24079
|
return "next";
|
|
22922
|
-
if (deps.nuxt ||
|
|
24080
|
+
if (deps.nuxt || existsSync16(join16(projectDir, "nuxt.config.ts")))
|
|
22923
24081
|
return "nuxt";
|
|
22924
24082
|
if (deps["@sveltejs/kit"])
|
|
22925
24083
|
return "sveltekit";
|
|
22926
|
-
if (deps.astro ||
|
|
24084
|
+
if (deps.astro || existsSync16(join16(projectDir, "astro.config.mjs")))
|
|
22927
24085
|
return "astro";
|
|
22928
24086
|
if (deps["@remix-run/react"])
|
|
22929
24087
|
return "remix";
|
|
22930
24088
|
if (deps.gatsby)
|
|
22931
24089
|
return "gatsby";
|
|
22932
|
-
if (deps.vite ||
|
|
24090
|
+
if (deps.vite || existsSync16(join16(projectDir, "vite.config.ts")))
|
|
22933
24091
|
return "vite";
|
|
22934
24092
|
return null;
|
|
22935
24093
|
}
|
|
22936
24094
|
function detectRuntime(projectDir) {
|
|
22937
|
-
if (
|
|
24095
|
+
if (existsSync16(join16(projectDir, "bun.lockb")) || existsSync16(join16(projectDir, "bun.lock")))
|
|
22938
24096
|
return "bun";
|
|
22939
|
-
if (
|
|
24097
|
+
if (existsSync16(join16(projectDir, "deno.json")) || existsSync16(join16(projectDir, "deno.lock")))
|
|
22940
24098
|
return "deno";
|
|
22941
24099
|
return "node";
|
|
22942
24100
|
}
|
|
22943
24101
|
function detectLanguage(projectDir) {
|
|
22944
|
-
if (
|
|
24102
|
+
if (existsSync16(join16(projectDir, "tsconfig.json")))
|
|
22945
24103
|
return "typescript";
|
|
22946
|
-
const srcDir =
|
|
22947
|
-
if (
|
|
24104
|
+
const srcDir = join16(projectDir, "src");
|
|
24105
|
+
if (existsSync16(srcDir)) {
|
|
22948
24106
|
try {
|
|
22949
24107
|
const files = readdirSync8(srcDir);
|
|
22950
24108
|
if (files.some((f3) => f3.endsWith(".ts") || f3.endsWith(".tsx")))
|
|
@@ -22955,7 +24113,7 @@ function detectLanguage(projectDir) {
|
|
|
22955
24113
|
}
|
|
22956
24114
|
function detectStyling(deps, projectDir) {
|
|
22957
24115
|
const tools = [];
|
|
22958
|
-
if (deps.tailwindcss ||
|
|
24116
|
+
if (deps.tailwindcss || existsSync16(join16(projectDir, "tailwind.config.js")) || existsSync16(join16(projectDir, "tailwind.config.ts")))
|
|
22959
24117
|
tools.push("tailwind");
|
|
22960
24118
|
if (deps["styled-components"])
|
|
22961
24119
|
tools.push("styled-components");
|
|
@@ -23000,13 +24158,13 @@ function detectORM(deps) {
|
|
|
23000
24158
|
}
|
|
23001
24159
|
function detectTesting(deps, projectDir) {
|
|
23002
24160
|
const tools = [];
|
|
23003
|
-
if (deps.vitest ||
|
|
24161
|
+
if (deps.vitest || existsSync16(join16(projectDir, "vitest.config.ts")))
|
|
23004
24162
|
tools.push("vitest");
|
|
23005
|
-
if (deps.jest ||
|
|
24163
|
+
if (deps.jest || existsSync16(join16(projectDir, "jest.config.js")))
|
|
23006
24164
|
tools.push("jest");
|
|
23007
|
-
if (deps["@playwright/test"] ||
|
|
24165
|
+
if (deps["@playwright/test"] || existsSync16(join16(projectDir, "playwright.config.ts")))
|
|
23008
24166
|
tools.push("playwright");
|
|
23009
|
-
if (deps.cypress ||
|
|
24167
|
+
if (deps.cypress || existsSync16(join16(projectDir, "cypress.config.ts")))
|
|
23010
24168
|
tools.push("cypress");
|
|
23011
24169
|
if (deps["@testing-library/react"] || deps["@testing-library/vue"])
|
|
23012
24170
|
tools.push("testing-library");
|
|
@@ -23038,7 +24196,7 @@ function detectBuildTools(deps, projectDir) {
|
|
|
23038
24196
|
tools.push("esbuild");
|
|
23039
24197
|
if (deps.webpack)
|
|
23040
24198
|
tools.push("webpack");
|
|
23041
|
-
if (deps.turbo ||
|
|
24199
|
+
if (deps.turbo || existsSync16(join16(projectDir, "turbo.json")))
|
|
23042
24200
|
tools.push("turborepo");
|
|
23043
24201
|
if (deps.tsup)
|
|
23044
24202
|
tools.push("tsup");
|
|
@@ -23048,17 +24206,17 @@ function detectBuildTools(deps, projectDir) {
|
|
|
23048
24206
|
}
|
|
23049
24207
|
function detectDeployment(projectDir) {
|
|
23050
24208
|
const tools = [];
|
|
23051
|
-
if (
|
|
24209
|
+
if (existsSync16(join16(projectDir, "vercel.json")) || existsSync16(join16(projectDir, ".vercel")))
|
|
23052
24210
|
tools.push("vercel");
|
|
23053
|
-
if (
|
|
24211
|
+
if (existsSync16(join16(projectDir, "netlify.toml")))
|
|
23054
24212
|
tools.push("netlify");
|
|
23055
|
-
if (
|
|
24213
|
+
if (existsSync16(join16(projectDir, "fly.toml")))
|
|
23056
24214
|
tools.push("fly");
|
|
23057
|
-
if (
|
|
24215
|
+
if (existsSync16(join16(projectDir, "railway.json")))
|
|
23058
24216
|
tools.push("railway");
|
|
23059
|
-
if (
|
|
24217
|
+
if (existsSync16(join16(projectDir, "Dockerfile")))
|
|
23060
24218
|
tools.push("docker");
|
|
23061
|
-
if (
|
|
24219
|
+
if (existsSync16(join16(projectDir, ".github", "workflows")))
|
|
23062
24220
|
tools.push("github-actions");
|
|
23063
24221
|
return tools;
|
|
23064
24222
|
}
|
|
@@ -23081,20 +24239,20 @@ function detectAIML(deps) {
|
|
|
23081
24239
|
return tools;
|
|
23082
24240
|
}
|
|
23083
24241
|
function inferProjectType(projectDir, techStack) {
|
|
23084
|
-
if (
|
|
24242
|
+
if (existsSync16(join16(projectDir, "packages")) || existsSync16(join16(projectDir, "apps"))) {
|
|
23085
24243
|
return "monorepo";
|
|
23086
24244
|
}
|
|
23087
24245
|
const pkg = readPackageJson(projectDir);
|
|
23088
|
-
if (pkg.bin ||
|
|
24246
|
+
if (pkg.bin || existsSync16(join16(projectDir, "src", "cli.ts")) || existsSync16(join16(projectDir, "src", "index.ts"))) {
|
|
23089
24247
|
const hasBin = !!pkg.bin;
|
|
23090
|
-
const hasCommands =
|
|
24248
|
+
const hasCommands = existsSync16(join16(projectDir, "src", "commands"));
|
|
23091
24249
|
if (hasBin || hasCommands)
|
|
23092
24250
|
return "cli";
|
|
23093
24251
|
}
|
|
23094
24252
|
if (!techStack.framework && !techStack.metaFramework && pkg.main) {
|
|
23095
24253
|
return "library";
|
|
23096
24254
|
}
|
|
23097
|
-
if (
|
|
24255
|
+
if (existsSync16(join16(projectDir, "app.json")) || existsSync16(join16(projectDir, "expo"))) {
|
|
23098
24256
|
return "mobile";
|
|
23099
24257
|
}
|
|
23100
24258
|
if (techStack.database.length > 0 && !techStack.framework) {
|
|
@@ -23111,10 +24269,10 @@ function extractDescription(projectDir) {
|
|
|
23111
24269
|
return pkg.description;
|
|
23112
24270
|
const readmePaths = ["README.md", "readme.md", "Readme.md"];
|
|
23113
24271
|
for (const readme of readmePaths) {
|
|
23114
|
-
const path =
|
|
23115
|
-
if (
|
|
24272
|
+
const path = join16(projectDir, readme);
|
|
24273
|
+
if (existsSync16(path)) {
|
|
23116
24274
|
try {
|
|
23117
|
-
const content =
|
|
24275
|
+
const content = readFileSync14(path, "utf-8");
|
|
23118
24276
|
const lines = content.split(`
|
|
23119
24277
|
`);
|
|
23120
24278
|
let foundTitle = false;
|
|
@@ -24532,9 +25690,9 @@ var onboardCommand = defineCommand2({
|
|
|
24532
25690
|
},
|
|
24533
25691
|
async run({ args }) {
|
|
24534
25692
|
const projectPath = process.cwd();
|
|
24535
|
-
const contextPath =
|
|
24536
|
-
if (
|
|
24537
|
-
const existing = JSON.parse(
|
|
25693
|
+
const contextPath = join17(projectPath, ".nairon", "context.json");
|
|
25694
|
+
if (existsSync17(contextPath) && !args.force) {
|
|
25695
|
+
const existing = JSON.parse(readFileSync15(contextPath, "utf-8"));
|
|
24538
25696
|
consola.info("Project already onboarded. Use --force to re-run.");
|
|
24539
25697
|
consola.info(`Business: ${existing.businessContext?.domain || existing.businessContext?.slice?.(0, 50) || "Not set"}...`);
|
|
24540
25698
|
consola.info(`Stack: ${formatTechStackSummary(existing)}`);
|
|
@@ -24682,8 +25840,8 @@ var onboardCommand = defineCommand2({
|
|
|
24682
25840
|
createdAt: new Date().toISOString(),
|
|
24683
25841
|
updatedAt: new Date().toISOString()
|
|
24684
25842
|
};
|
|
24685
|
-
const naironDir =
|
|
24686
|
-
if (!
|
|
25843
|
+
const naironDir = join17(projectPath, ".nairon");
|
|
25844
|
+
if (!existsSync17(naironDir)) {
|
|
24687
25845
|
mkdirSync6(naironDir, { recursive: true });
|
|
24688
25846
|
}
|
|
24689
25847
|
writeFileSync7(contextPath, JSON.stringify(fullContext, null, 2));
|
|
@@ -24774,51 +25932,51 @@ function formatTechStackSummary(context) {
|
|
|
24774
25932
|
function detectInstalledTools(projectPath) {
|
|
24775
25933
|
const tools = [];
|
|
24776
25934
|
const home = process.env.HOME || "";
|
|
24777
|
-
const userClaudeJson =
|
|
24778
|
-
if (
|
|
25935
|
+
const userClaudeJson = join17(home, ".claude.json");
|
|
25936
|
+
if (existsSync17(userClaudeJson)) {
|
|
24779
25937
|
try {
|
|
24780
|
-
const config = JSON.parse(
|
|
25938
|
+
const config = JSON.parse(readFileSync15(userClaudeJson, "utf-8"));
|
|
24781
25939
|
if (config.mcpServers) {
|
|
24782
25940
|
tools.push(...Object.keys(config.mcpServers));
|
|
24783
25941
|
}
|
|
24784
25942
|
} catch {}
|
|
24785
25943
|
}
|
|
24786
|
-
const claudeDesktopConfig =
|
|
24787
|
-
if (
|
|
25944
|
+
const claudeDesktopConfig = join17(home, ".claude", "claude_desktop_config.json");
|
|
25945
|
+
if (existsSync17(claudeDesktopConfig)) {
|
|
24788
25946
|
try {
|
|
24789
|
-
const config = JSON.parse(
|
|
25947
|
+
const config = JSON.parse(readFileSync15(claudeDesktopConfig, "utf-8"));
|
|
24790
25948
|
if (config.mcpServers) {
|
|
24791
25949
|
tools.push(...Object.keys(config.mcpServers));
|
|
24792
25950
|
}
|
|
24793
25951
|
} catch {}
|
|
24794
25952
|
}
|
|
24795
|
-
const projectMcpJson =
|
|
24796
|
-
if (
|
|
25953
|
+
const projectMcpJson = join17(projectPath, ".mcp.json");
|
|
25954
|
+
if (existsSync17(projectMcpJson)) {
|
|
24797
25955
|
try {
|
|
24798
|
-
const config = JSON.parse(
|
|
25956
|
+
const config = JSON.parse(readFileSync15(projectMcpJson, "utf-8"));
|
|
24799
25957
|
if (config.mcpServers) {
|
|
24800
25958
|
tools.push(...Object.keys(config.mcpServers));
|
|
24801
25959
|
}
|
|
24802
25960
|
} catch {}
|
|
24803
25961
|
}
|
|
24804
|
-
const projectClaudeConfig =
|
|
24805
|
-
if (
|
|
25962
|
+
const projectClaudeConfig = join17(projectPath, ".claude", "settings.json");
|
|
25963
|
+
if (existsSync17(projectClaudeConfig)) {
|
|
24806
25964
|
try {
|
|
24807
|
-
const config = JSON.parse(
|
|
25965
|
+
const config = JSON.parse(readFileSync15(projectClaudeConfig, "utf-8"));
|
|
24808
25966
|
if (config.mcpServers) {
|
|
24809
25967
|
tools.push(...Object.keys(config.mcpServers));
|
|
24810
25968
|
}
|
|
24811
25969
|
} catch {}
|
|
24812
25970
|
}
|
|
24813
|
-
const skillsDir =
|
|
24814
|
-
if (
|
|
25971
|
+
const skillsDir = join17(home, ".config", "opencode", "skills");
|
|
25972
|
+
if (existsSync17(skillsDir)) {
|
|
24815
25973
|
try {
|
|
24816
25974
|
const skills = readdirSync9(skillsDir);
|
|
24817
25975
|
tools.push(...skills.filter((s2) => !s2.startsWith(".")));
|
|
24818
25976
|
} catch {}
|
|
24819
25977
|
}
|
|
24820
|
-
const agentsSkillsDir =
|
|
24821
|
-
if (
|
|
25978
|
+
const agentsSkillsDir = join17(home, ".agents", "skills");
|
|
25979
|
+
if (existsSync17(agentsSkillsDir)) {
|
|
24822
25980
|
try {
|
|
24823
25981
|
const skills = readdirSync9(agentsSkillsDir);
|
|
24824
25982
|
tools.push(...skills.filter((s2) => !s2.startsWith(".")));
|
|
@@ -24828,11 +25986,11 @@ function detectInstalledTools(projectPath) {
|
|
|
24828
25986
|
}
|
|
24829
25987
|
function detectPrimaryAgent() {
|
|
24830
25988
|
const home = process.env.HOME || "";
|
|
24831
|
-
if (
|
|
25989
|
+
if (existsSync17(join17(home, ".claude")))
|
|
24832
25990
|
return "claude-code";
|
|
24833
|
-
if (
|
|
25991
|
+
if (existsSync17(join17(home, ".cursor")))
|
|
24834
25992
|
return "cursor";
|
|
24835
|
-
if (
|
|
25993
|
+
if (existsSync17(join17(home, ".config", "opencode")))
|
|
24836
25994
|
return "opencode";
|
|
24837
25995
|
return;
|
|
24838
25996
|
}
|
|
@@ -24927,7 +26085,7 @@ function formatBytes(bytes) {
|
|
|
24927
26085
|
// package.json
|
|
24928
26086
|
var package_default2 = {
|
|
24929
26087
|
name: "nairon-bench",
|
|
24930
|
-
version: "0.3.
|
|
26088
|
+
version: "0.3.14",
|
|
24931
26089
|
description: "AI workflow benchmarking CLI",
|
|
24932
26090
|
type: "module",
|
|
24933
26091
|
bin: {
|
|
@@ -24959,8 +26117,9 @@ var package_default2 = {
|
|
|
24959
26117
|
"build:linux-arm64": "bun build --compile --target=bun-linux-arm64 --outfile=dist/nb-linux-arm64 src/index.ts",
|
|
24960
26118
|
"build:windows-x64": "bun build --compile --target=bun-windows-x64 --outfile=dist/nb-windows-x64.exe src/index.ts",
|
|
24961
26119
|
typecheck: "tsc --noEmit",
|
|
24962
|
-
test: "
|
|
24963
|
-
"test:e2e": "
|
|
26120
|
+
test: "vitest run",
|
|
26121
|
+
"test:e2e": "vitest run tests/e2e/",
|
|
26122
|
+
"test:watch": "vitest",
|
|
24964
26123
|
clean: "rm -rf dist",
|
|
24965
26124
|
prepublishOnly: "bun run build"
|
|
24966
26125
|
},
|