nairon-bench 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4447,8 +4447,8 @@ function defineCommand2(def) {
4447
4447
 
4448
4448
  // src/commands/scan.ts
4449
4449
  init_dist();
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";
4450
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, readFileSync as readFileSync10 } from "node:fs";
4451
+ import { join as join10 } 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);
@@ -16067,159 +16067,261 @@ function filterAlreadyInstalled(optimizations, status) {
16067
16067
  return true;
16068
16068
  });
16069
16069
  }
16070
- var QUICK_INSTALL_PRESETS = {
16071
- essential: [
16072
- {
16073
- id: "context7",
16074
- name: "Context7 MCP",
16075
- type: "mcp",
16076
- description: "Up-to-date library documentation",
16077
- installCommand: "claude mcp add context7 https://mcp.context7.com/mcp --transport http",
16078
- selected: true
16079
- },
16080
- {
16081
- id: "supermemory",
16082
- name: "Supermemory MCP",
16083
- type: "mcp",
16084
- description: "Persistent memory across sessions",
16085
- installCommand: "claude mcp add supermemory -- npx -y @supermemory/mcp@latest",
16086
- selected: true
16087
- },
16088
- {
16089
- id: "beads",
16090
- name: "Beads Task Manager",
16091
- type: "mcp",
16092
- description: "Persistent task tracking",
16093
- installCommand: "bun add -g beads && bd init",
16094
- selected: true
16095
- }
16096
- ],
16097
- productivity: [
16098
- {
16099
- id: "claude-md",
16100
- name: "CLAUDE.md",
16101
- type: "config",
16102
- description: "Project context file",
16103
- configPath: "CLAUDE.md",
16104
- configContent: `# Project Instructions
16105
-
16106
- ## Build & Test Commands
16107
- \`\`\`bash
16108
- bun test # Run tests
16109
- bun run build # Build project
16110
- bun run lint # Run linter
16111
- \`\`\`
16112
-
16113
- ## Architecture
16114
- Describe your project structure here.
16115
16070
 
16116
- ## Conventions
16117
- - Use TypeScript
16118
- - Write tests for new features
16119
- - Use conventional commits
16120
- `,
16121
- selected: true
16122
- },
16123
- {
16124
- id: "vitest",
16125
- name: "Vitest",
16126
- type: "library",
16127
- description: "Fast unit testing framework",
16128
- installCommand: "bun add -D vitest",
16129
- selected: true
16071
+ // src/lib/first-run.ts
16072
+ init_dist();
16073
+ import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
16074
+ import { homedir as homedir8 } from "node:os";
16075
+ import { join as join9 } from "node:path";
16076
+ import { spawnSync as spawnSync2 } from "node:child_process";
16077
+ var FIRST_RUN_FLAG = join9(homedir8(), ".nairon-bench", "first-run-complete");
16078
+ var PROJECT_SCAN_MARKER = ".nairon/first-scan-complete";
16079
+ function isFirstScanInProject(projectDir) {
16080
+ return !existsSync9(join9(projectDir, PROJECT_SCAN_MARKER));
16081
+ }
16082
+ function markProjectScanned(projectDir) {
16083
+ const naironDir = join9(projectDir, ".nairon");
16084
+ if (!existsSync9(naironDir)) {
16085
+ mkdirSync4(naironDir, { recursive: true });
16086
+ }
16087
+ writeFileSync4(join9(projectDir, PROJECT_SCAN_MARKER), new Date().toISOString());
16088
+ }
16089
+ function detectAgent() {
16090
+ const home = homedir8();
16091
+ const claudeConfig = join9(home, ".claude");
16092
+ const hasClaudeCode = existsSync9(claudeConfig);
16093
+ const opencodeConfig = join9(home, ".config", "opencode");
16094
+ const opencodeData = join9(home, ".local", "share", "opencode");
16095
+ const hasOpenCode = existsSync9(opencodeConfig) || existsSync9(opencodeData);
16096
+ const cursorConfig = join9(home, ".cursor");
16097
+ const hasCursor = existsSync9(cursorConfig);
16098
+ if (hasClaudeCode)
16099
+ return "claude-code";
16100
+ if (hasOpenCode)
16101
+ return "opencode";
16102
+ if (hasCursor)
16103
+ return "cursor";
16104
+ return "unknown";
16105
+ }
16106
+ function analyzeReadme(projectDir) {
16107
+ const readmePath = join9(projectDir, "README.md");
16108
+ if (!existsSync9(readmePath)) {
16109
+ const alternatives = ["readme.md", "Readme.md", "README.MD", "README"];
16110
+ for (const alt of alternatives) {
16111
+ const altPath = join9(projectDir, alt);
16112
+ if (existsSync9(altPath)) {
16113
+ return parseReadme(readFileSync9(altPath, "utf-8"), projectDir);
16114
+ }
16130
16115
  }
16131
- ]
16132
- };
16116
+ return null;
16117
+ }
16118
+ const content = readFileSync9(readmePath, "utf-8");
16119
+ return parseReadme(content, projectDir);
16120
+ }
16121
+ function parseReadme(content, projectDir) {
16122
+ let name = "Unknown Project";
16123
+ const pkgPath = join9(projectDir, "package.json");
16124
+ if (existsSync9(pkgPath)) {
16125
+ try {
16126
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
16127
+ if (pkg.name && !pkg.name.startsWith("@") && pkg.name !== "root") {
16128
+ name = pkg.name;
16129
+ }
16130
+ } catch {}
16131
+ }
16132
+ if (name === "Unknown Project") {
16133
+ const headingMatch = content.match(/^#\s+([^<\n]+)$/m);
16134
+ if (headingMatch && headingMatch[1]) {
16135
+ const heading = headingMatch[1].trim();
16136
+ if (!heading.toLowerCase().includes("install") && !heading.toLowerCase().includes("getting started") && heading.length < 50) {
16137
+ name = heading;
16138
+ }
16139
+ }
16140
+ }
16141
+ let description = "";
16142
+ const descMatch = content.match(/^#[^#].*\n\n(.+?)(\n\n|$)/s);
16143
+ if (descMatch && descMatch[1]) {
16144
+ description = descMatch[1].replace(/\n/g, " ").trim().slice(0, 200);
16145
+ }
16146
+ const techStack = [];
16147
+ const techPatterns = {
16148
+ react: /\breact\b/i,
16149
+ "next.js": /\bnext\.?js\b/i,
16150
+ vue: /\bvue\b/i,
16151
+ svelte: /\bsvelte\b/i,
16152
+ typescript: /\btypescript\b/i,
16153
+ python: /\bpython\b/i,
16154
+ rust: /\brust\b/i,
16155
+ go: /\bgolang?\b/i,
16156
+ node: /\bnode\.?js\b/i,
16157
+ bun: /\bbun\b/i,
16158
+ deno: /\bdeno\b/i,
16159
+ tailwind: /\btailwind\b/i,
16160
+ prisma: /\bprisma\b/i,
16161
+ postgres: /\bpostgres\b/i,
16162
+ mongodb: /\bmongo\b/i,
16163
+ redis: /\bredis\b/i,
16164
+ docker: /\bdocker\b/i,
16165
+ kubernetes: /\bkubernetes\b|\bk8s\b/i,
16166
+ aws: /\baws\b/i,
16167
+ vercel: /\bvercel\b/i,
16168
+ cloudflare: /\bcloudflare\b/i,
16169
+ openai: /\bopenai\b/i,
16170
+ anthropic: /\banthropic\b|\bclaude\b/i,
16171
+ langchain: /\blangchain\b/i,
16172
+ convex: /\bconvex\b/i,
16173
+ supabase: /\bsupabase\b/i,
16174
+ firebase: /\bfirebase\b/i,
16175
+ stripe: /\bstripe\b/i,
16176
+ playwright: /\bplaywright\b/i,
16177
+ vitest: /\bvitest\b/i,
16178
+ jest: /\bjest\b/i,
16179
+ remotion: /\bremotion\b/i
16180
+ };
16181
+ for (const [tech, pattern] of Object.entries(techPatterns)) {
16182
+ if (pattern.test(content)) {
16183
+ techStack.push(tech);
16184
+ }
16185
+ }
16186
+ const keywords = [];
16187
+ const keywordPatterns = {
16188
+ api: /\bapi\b|\brest\b|\bgraphql\b/i,
16189
+ cli: /\bcli\b|\bcommand.?line\b/i,
16190
+ web: /\bweb\b|\bwebsite\b|\bfrontend\b/i,
16191
+ mobile: /\bmobile\b|\bios\b|\bandroid\b|\breact.?native\b/i,
16192
+ ai: /\bai\b|\bmachine.?learning\b|\bllm\b|\bgpt\b/i,
16193
+ video: /\bvideo\b|\bstreaming\b|\bmedia\b/i,
16194
+ ecommerce: /\becommerce\b|\bshop\b|\bstore\b|\bpayment\b/i,
16195
+ auth: /\bauth\b|\blogin\b|\bsession\b/i,
16196
+ testing: /\btest\b|\btesting\b|\btdd\b/i,
16197
+ devtools: /\bdevtool\b|\bdeveloper.?tool\b/i
16198
+ };
16199
+ for (const [keyword, pattern] of Object.entries(keywordPatterns)) {
16200
+ if (pattern.test(content)) {
16201
+ keywords.push(keyword);
16202
+ }
16203
+ }
16204
+ return { name, description, techStack, keywords };
16205
+ }
16206
+ function suggestSkills(context) {
16207
+ const suggestions = [];
16208
+ suggestions.push({
16209
+ name: "Find Skills",
16210
+ installCommand: "npx skills add vercel-labs/skills@find-skills --yes",
16211
+ reason: "Discover more skills for your workflow"
16212
+ });
16213
+ if (!context) {
16214
+ suggestions.push({
16215
+ name: "Writing Plans",
16216
+ installCommand: "npx skills add obra/superpowers@writing-plans --yes",
16217
+ reason: "Better planning before implementation"
16218
+ });
16219
+ return suggestions;
16220
+ }
16221
+ if (context.techStack.includes("react") || context.techStack.includes("next.js")) {
16222
+ suggestions.push({
16223
+ name: "React Best Practices",
16224
+ installCommand: "npx skills add vercel-labs/agent-skills@vercel-react-best-practices --yes",
16225
+ reason: `Detected React/Next.js in ${context.name}`
16226
+ });
16227
+ }
16228
+ if (context.techStack.includes("remotion")) {
16229
+ suggestions.push({
16230
+ name: "Remotion",
16231
+ installCommand: "npx skills add remotion-dev/skills@remotion-best-practices --yes",
16232
+ reason: "Detected Remotion video framework"
16233
+ });
16234
+ }
16235
+ if (context.keywords.includes("testing") || context.keywords.includes("tdd")) {
16236
+ suggestions.push({
16237
+ name: "Test-Driven Development",
16238
+ installCommand: "npx skills add obra/superpowers@test-driven-development --yes",
16239
+ reason: "Testing focus detected"
16240
+ });
16241
+ }
16242
+ if (context.keywords.includes("web")) {
16243
+ suggestions.push({
16244
+ name: "Web Design Guidelines",
16245
+ installCommand: "npx skills add vercel-labs/agent-skills@web-design-guidelines --yes",
16246
+ reason: "Web project detected"
16247
+ });
16248
+ }
16249
+ if (!suggestions.some((s2) => s2.name.includes("Plan"))) {
16250
+ suggestions.push({
16251
+ name: "Writing Plans",
16252
+ installCommand: "npx skills add obra/superpowers@writing-plans --yes",
16253
+ reason: "Helps structure work before coding"
16254
+ });
16255
+ }
16256
+ return suggestions.slice(0, 5);
16257
+ }
16258
+ function installSkill2(installCommand) {
16259
+ try {
16260
+ const result = spawnSync2("npx", installCommand.replace("npx ", "").split(" "), {
16261
+ stdio: "pipe",
16262
+ timeout: 60000,
16263
+ shell: true
16264
+ });
16265
+ if (result.status === 0) {
16266
+ return { success: true, message: "Installed successfully" };
16267
+ } else {
16268
+ const stderr = result.stderr?.toString() || "";
16269
+ return { success: false, message: stderr.slice(0, 100) || "Installation failed" };
16270
+ }
16271
+ } catch (error2) {
16272
+ return { success: false, message: error2 instanceof Error ? error2.message : "Unknown error" };
16273
+ }
16274
+ }
16133
16275
 
16134
16276
  // src/commands/scan.ts
16135
16277
  var scanCommand = defineCommand2({
16136
16278
  meta: {
16137
16279
  name: "scan",
16138
- description: "Analyze local data sources (git, AI sessions, tests) and compute your NaironAI score"
16280
+ description: "Scan your AI workflow and get optimization recommendations (interactive)"
16139
16281
  },
16140
16282
  args: {
16141
- since: {
16142
- type: "string",
16143
- description: 'Time range: 6h, 24h, 7d, 30d, or "7 days"',
16144
- default: "7d"
16145
- },
16146
- project: {
16147
- type: "string",
16148
- description: "Project directory to scan (defaults to cwd)"
16149
- },
16150
- install: {
16151
- type: "boolean",
16152
- alias: "i",
16153
- description: "Install recommended optimizations (interactive)",
16154
- default: false
16155
- },
16156
- "install-all": {
16157
- type: "boolean",
16158
- description: "Install all recommended optimizations (non-interactive)",
16159
- default: false
16160
- },
16161
- "install-essentials": {
16162
- type: "boolean",
16163
- description: "Install essential tools: Context7, Supermemory, Beads",
16164
- default: false
16165
- },
16166
- offline: {
16167
- type: "boolean",
16168
- description: "Skip uploading results to Convex",
16169
- default: false
16170
- },
16171
- "no-report": {
16172
- type: "boolean",
16173
- description: "Skip generating markdown report",
16174
- default: false
16175
- },
16176
- "report-dir": {
16177
- type: "string",
16178
- description: "Directory for reports (defaults to .nairon/reports)",
16179
- default: ".nairon/reports"
16180
- },
16181
- brief: {
16182
- type: "boolean",
16183
- description: "Show brief output (skip detailed analysis)",
16184
- default: false
16185
- },
16186
16283
  json: {
16187
16284
  type: "boolean",
16188
- description: "Output results as JSON (for CI/scripts)",
16285
+ description: "Output as JSON (for CI/scripts)",
16189
16286
  default: false
16190
16287
  },
16191
16288
  quiet: {
16192
16289
  type: "boolean",
16193
16290
  alias: "q",
16194
- description: "Quiet mode - only output the score number",
16195
- default: false
16196
- },
16197
- "no-cache": {
16198
- type: "boolean",
16199
- description: "Disable session caching (slower but ensures fresh data)",
16200
- default: false
16201
- },
16202
- watch: {
16203
- type: "boolean",
16204
- alias: "w",
16205
- description: "Watch mode - re-scan on file changes",
16291
+ description: "Only output score number",
16206
16292
  default: false
16207
- },
16208
- "watch-interval": {
16209
- type: "string",
16210
- description: "Watch interval in seconds (default: 30)",
16211
- default: "30"
16212
16293
  }
16213
16294
  },
16214
16295
  async run({ args }) {
16215
- const projectDir = args.project ?? process.cwd();
16216
- const since = parseSince(args.since);
16296
+ const projectDir = process.cwd();
16217
16297
  const jsonOutput = args.json;
16218
16298
  const quietMode = args.quiet;
16219
- const useCache = !args["no-cache"];
16220
- const watchMode = args.watch;
16221
- const watchInterval = parseInt(args["watch-interval"] || "30", 10) * 1000;
16222
16299
  const silent = jsonOutput || quietMode;
16300
+ let since;
16301
+ if (silent) {
16302
+ since = parseSince("7d");
16303
+ } else {
16304
+ console.log();
16305
+ console.log(" \uD83D\uDD0D " + "\x1B[1mNaironAI Workflow Scanner\x1B[0m");
16306
+ console.log(" " + "\x1B[2m" + "─".repeat(35) + "\x1B[0m");
16307
+ console.log();
16308
+ const period = await consola.prompt("Time period to analyze?", {
16309
+ type: "select",
16310
+ options: [
16311
+ { value: "7d", label: "Last 7 days (recommended)" },
16312
+ { value: "24h", label: "Last 24 hours" },
16313
+ { value: "30d", label: "Last 30 days" }
16314
+ ]
16315
+ });
16316
+ since = parseSince(period);
16317
+ if (isFirstScanInProject(projectDir)) {
16318
+ await showFirstScanWelcome(projectDir);
16319
+ markProjectScanned(projectDir);
16320
+ }
16321
+ }
16322
+ const useCache = true;
16323
+ const watchMode = false;
16324
+ const watchInterval = 30000;
16223
16325
  if (watchMode) {
16224
16326
  console.log();
16225
16327
  console.log(colors2.bold(colors2.primary(" NaironAI Watch Mode")));
@@ -16352,19 +16454,17 @@ var scanCommand = defineCommand2({
16352
16454
  console.log(` Projected monthly: ${colors2.primary(`$${scanCost.projectedMonthlyCostUsd.toFixed(2)}`)}`);
16353
16455
  console.log(colors2.dim(" " + "═".repeat(45)));
16354
16456
  console.log();
16355
- if (!args["no-report"]) {
16356
- const reportDir = join9(projectDir, args["report-dir"]);
16357
- const reportPath = generateReport(reportDir, score, git, agents, tests, args.since, sdlcAnalysis, scanCost, analysis);
16358
- console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
16359
- }
16360
- if (agents && agents.sessions.length > 0 && !args.brief) {
16457
+ const reportDir = join10(projectDir, ".nairon/reports");
16458
+ const reportPath = generateReport(reportDir, score, git, agents, tests, "7d", sdlcAnalysis, scanCost, analysis);
16459
+ console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
16460
+ if (agents && agents.sessions.length > 0) {
16361
16461
  const effortBreakdown = analyzeEffort(agents.sessions, git);
16362
16462
  const effortLines = formatEffortBreakdown(effortBreakdown);
16363
16463
  for (const line of effortLines) {
16364
16464
  console.log(line);
16365
16465
  }
16366
16466
  }
16367
- if (agents && agents.sessions.length > 0 && !args.brief) {
16467
+ if (agents && agents.sessions.length > 0) {
16368
16468
  const frustrationSummary = detectFrustrations(agents.sessions);
16369
16469
  if (frustrationSummary.totalFrustrations > 0) {
16370
16470
  const frustrationLines = formatFrustrationSummary(frustrationSummary);
@@ -16373,68 +16473,71 @@ var scanCommand = defineCommand2({
16373
16473
  }
16374
16474
  }
16375
16475
  }
16376
- if (!args.brief) {
16377
- const installedStatus = checkInstalledStatus(projectDir);
16378
- const projectContext = loadProjectContext(projectDir);
16379
- if (projectContext) {
16380
- console.log(` ${colors2.dim("Using project context from onboarding for personalized recommendations")}`);
16381
- }
16382
- const sdlc = analyzeSDLC(agents, git, tests, projectDir, analysis, projectContext);
16383
- for (const phase of [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review]) {
16384
- phase.recommendations = phase.recommendations.filter((rec) => {
16385
- const id = rec.title.toLowerCase().replace(/\s+/g, "-");
16386
- if (id.includes("context7") && installedStatus.context7)
16387
- return false;
16388
- if (id.includes("supermemory") && installedStatus.supermemory)
16389
- return false;
16390
- if (id.includes("nia") && installedStatus.nia)
16391
- return false;
16392
- if (id.includes("beads") && installedStatus.beads)
16393
- return false;
16394
- return true;
16395
- });
16396
- for (const tool of phase.tools) {
16397
- if (tool.name.toLowerCase().includes("context7") && installedStatus.context7) {
16398
- tool.installed = true;
16399
- }
16400
- if (tool.name.toLowerCase().includes("supermemory") && installedStatus.supermemory) {
16401
- tool.installed = true;
16402
- }
16403
- if (tool.name.toLowerCase().includes("nia") && installedStatus.nia) {
16404
- tool.installed = true;
16405
- }
16476
+ const installedStatus = checkInstalledStatus(projectDir);
16477
+ const projectContext = loadProjectContext(projectDir);
16478
+ if (projectContext) {
16479
+ console.log(` ${colors2.dim("Using project context from onboarding for personalized recommendations")}`);
16480
+ }
16481
+ const sdlc = analyzeSDLC(agents, git, tests, projectDir, analysis, projectContext);
16482
+ for (const phase of [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review]) {
16483
+ phase.recommendations = phase.recommendations.filter((rec) => {
16484
+ const id = rec.title.toLowerCase().replace(/\s+/g, "-");
16485
+ if (id.includes("context7") && installedStatus.context7)
16486
+ return false;
16487
+ if (id.includes("supermemory") && installedStatus.supermemory)
16488
+ return false;
16489
+ if (id.includes("nia") && installedStatus.nia)
16490
+ return false;
16491
+ if (id.includes("beads") && installedStatus.beads)
16492
+ return false;
16493
+ return true;
16494
+ });
16495
+ for (const tool of phase.tools) {
16496
+ if (tool.name.toLowerCase().includes("context7") && installedStatus.context7) {
16497
+ tool.installed = true;
16498
+ }
16499
+ if (tool.name.toLowerCase().includes("supermemory") && installedStatus.supermemory) {
16500
+ tool.installed = true;
16501
+ }
16502
+ if (tool.name.toLowerCase().includes("nia") && installedStatus.nia) {
16503
+ tool.installed = true;
16406
16504
  }
16407
16505
  }
16408
- const totalRecs = [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review].reduce((a2, p) => a2 + p.recommendations.length, 0);
16409
- if (totalRecs > 0) {
16410
- sdlc.overall.summary = sdlc.overall.summary.replace(/\d+ optimizations available\./, `${totalRecs} optimizations available.`);
16411
- } else if (sdlc.overall.summary.includes("optimizations available")) {
16412
- sdlc.overall.summary = sdlc.overall.summary.replace(/\d+ optimizations available\./, "All recommended tools installed.");
16413
- }
16414
- const sdlcLines = formatSDLCForTerminal(sdlc);
16415
- for (const line of sdlcLines) {
16416
- console.log(line);
16417
- }
16418
- let allRecs = [];
16419
- for (const phase of [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review]) {
16420
- for (const rec of phase.recommendations) {
16421
- if (rec.installCommand) {
16422
- allRecs.push(phaseRecToInstallable(rec));
16423
- }
16506
+ }
16507
+ const totalRecs = [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review].reduce((a2, p) => a2 + p.recommendations.length, 0);
16508
+ if (totalRecs > 0) {
16509
+ sdlc.overall.summary = sdlc.overall.summary.replace(/\d+ optimizations available\./, `${totalRecs} optimizations available.`);
16510
+ } else if (sdlc.overall.summary.includes("optimizations available")) {
16511
+ sdlc.overall.summary = sdlc.overall.summary.replace(/\d+ optimizations available\./, "All recommended tools installed.");
16512
+ }
16513
+ const sdlcLines = formatSDLCForTerminal(sdlc);
16514
+ for (const line of sdlcLines) {
16515
+ console.log(line);
16516
+ }
16517
+ let allRecs = [];
16518
+ for (const phase of [sdlc.requirements, sdlc.planning, sdlc.implementation, sdlc.review]) {
16519
+ for (const rec of phase.recommendations) {
16520
+ if (rec.installCommand) {
16521
+ allRecs.push(phaseRecToInstallable(rec));
16424
16522
  }
16425
16523
  }
16426
- allRecs = filterAlreadyInstalled(allRecs, installedStatus);
16427
- if (args["install-essentials"]) {
16428
- console.log();
16429
- console.log(` ${colors2.bold(colors2.primary("Installing Essential Tools"))}`);
16430
- console.log(colors2.dim(" " + "─".repeat(40)));
16431
- console.log();
16432
- const essentials = filterAlreadyInstalled(QUICK_INSTALL_PRESETS.essential, installedStatus);
16433
- if (essentials.length === 0) {
16434
- console.log(` ${colors2.success("✓")} All essential tools already installed!`);
16524
+ }
16525
+ allRecs = filterAlreadyInstalled(allRecs, installedStatus);
16526
+ if (allRecs.length > 0) {
16527
+ console.log();
16528
+ const optionLabels = allRecs.map((rec) => `${rec.name} - ${rec.description}`);
16529
+ const selected = await consola.prompt(`${allRecs.length} optimizations available. Select to install (space to select, enter to confirm):`, {
16530
+ type: "multiselect",
16531
+ options: optionLabels,
16532
+ required: false
16533
+ });
16534
+ if (selected && typeof selected !== "symbol" && Array.isArray(selected) && selected.length > 0) {
16535
+ const toInstall = allRecs.filter((rec) => selected.some((s2) => s2.startsWith(rec.name))).map((r3) => ({ ...r3, selected: true }));
16536
+ if (toInstall.length > 0) {
16435
16537
  console.log();
16436
- } else {
16437
- const results = await installOptimizations(essentials.map((e2) => ({ ...e2, selected: true })), projectDir, {});
16538
+ console.log(` ${colors2.bold("Installing...")}${colors2.dim("")}`);
16539
+ console.log();
16540
+ const results = await installOptimizations(toInstall, projectDir, {});
16438
16541
  for (const result of results) {
16439
16542
  if (result.success) {
16440
16543
  console.log(` ${colors2.success("✓")} ${result.name}: ${result.message}`);
@@ -16444,82 +16547,15 @@ var scanCommand = defineCommand2({
16444
16547
  }
16445
16548
  console.log();
16446
16549
  }
16447
- } else if (args["install-all"] && allRecs.length > 0) {
16448
- console.log();
16449
- console.log(` ${colors2.bold(colors2.primary("Installing All Optimizations"))}`);
16450
- console.log(colors2.dim(" " + "─".repeat(40)));
16451
- console.log();
16452
- const results = await installOptimizations(allRecs.map((r3) => ({ ...r3, selected: true })), projectDir, {});
16453
- for (const result of results) {
16454
- if (result.success) {
16455
- console.log(` ${colors2.success("✓")} ${result.name}: ${result.message}`);
16456
- } else {
16457
- console.log(` ${colors2.error("✗")} ${result.name}: ${result.error || result.message}`);
16458
- }
16459
- }
16460
- console.log();
16461
- } else if (allRecs.length > 0 && !args["install-essentials"] && !args["install-all"]) {
16550
+ } else {
16551
+ console.log(` ${colors2.dim("Skipped.")}`);
16462
16552
  console.log();
16463
- const optionLabels = allRecs.map((rec) => `${rec.name} - ${rec.description}`);
16464
- const selected = await consola.prompt(`${allRecs.length} optimizations available. Select to install (space to select, enter to confirm):`, {
16465
- type: "multiselect",
16466
- options: optionLabels,
16467
- required: false
16468
- });
16469
- if (selected && typeof selected !== "symbol" && Array.isArray(selected) && selected.length > 0) {
16470
- const toInstall = allRecs.filter((rec) => selected.some((s2) => s2.startsWith(rec.name))).map((r3) => ({ ...r3, selected: true }));
16471
- if (toInstall.length > 0) {
16472
- console.log();
16473
- console.log(` ${colors2.bold("Installing...")}${colors2.dim("")}`);
16474
- console.log();
16475
- const results = await installOptimizations(toInstall, projectDir, {});
16476
- for (const result of results) {
16477
- if (result.success) {
16478
- console.log(` ${colors2.success("✓")} ${result.name}: ${result.message}`);
16479
- } else {
16480
- console.log(` ${colors2.error("✗")} ${result.name}: ${result.error || result.message}`);
16481
- }
16482
- }
16483
- console.log();
16484
- }
16485
- } else {
16486
- console.log(` ${colors2.dim("Skipped. Run")} ${colors2.primary("nb scan --install-essentials")} ${colors2.dim("anytime to quick-install.")}`);
16487
- console.log();
16488
- }
16489
16553
  }
16490
16554
  }
16491
16555
  }
16492
- if (args.offline) {
16493
- if (!silent) {
16494
- console.log(` ${icons.info} Offline mode - skipping cloud sync`);
16495
- console.log();
16496
- }
16497
- if (jsonOutput) {
16498
- const output = {
16499
- score: score.overall,
16500
- tier: score.tier,
16501
- baseScore: score.baseScore,
16502
- tokenEfficiency: score.tokenEfficiency,
16503
- phases: score.phases,
16504
- cost: scanCost,
16505
- git: git ? { commitCount: git.commitCount, authors: git.authors.length } : null,
16506
- agents: agents ? { sessions: agents.totalSessions, tokens: agents.totalTokens } : null,
16507
- tests: tests ? { total: tests.totalTests, passRate: tests.passRate } : null,
16508
- scannedAt: score.scannedAt
16509
- };
16510
- console.log(JSON.stringify(output, null, 2));
16511
- } else if (quietMode) {
16512
- console.log(score.overall);
16513
- }
16514
- return;
16515
- }
16516
16556
  const client = getClient();
16517
16557
  const clerkId = getClerkId();
16518
16558
  if (!client || !clerkId) {
16519
- if (!silent) {
16520
- console.log(` ${icons.info} Run ${colors2.primary("nb init")} to enable cloud sync`);
16521
- console.log();
16522
- }
16523
16559
  if (jsonOutput) {
16524
16560
  const output = {
16525
16561
  score: score.overall,
@@ -16634,12 +16670,12 @@ var scanCommand = defineCommand2({
16634
16670
  }
16635
16671
  });
16636
16672
  function loadProjectContext(projectDir) {
16637
- const contextPath = join9(projectDir, ".nairon", "context.json");
16638
- if (!existsSync9(contextPath)) {
16673
+ const contextPath = join10(projectDir, ".nairon", "context.json");
16674
+ if (!existsSync10(contextPath)) {
16639
16675
  return null;
16640
16676
  }
16641
16677
  try {
16642
- const raw = readFileSync9(contextPath, "utf-8");
16678
+ const raw = readFileSync10(contextPath, "utf-8");
16643
16679
  const data = JSON.parse(raw);
16644
16680
  const context = {
16645
16681
  painPoints: data.painPoints || [],
@@ -16692,12 +16728,12 @@ function parseSince(since) {
16692
16728
  return new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
16693
16729
  }
16694
16730
  function generateReport(reportDir, score, git, agents, tests, since, sdlcAnalysis, scanCost, analysis) {
16695
- if (!existsSync9(reportDir)) {
16696
- mkdirSync4(reportDir, { recursive: true });
16731
+ if (!existsSync10(reportDir)) {
16732
+ mkdirSync5(reportDir, { recursive: true });
16697
16733
  }
16698
16734
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
16699
16735
  const filename = `scan-${timestamp}.md`;
16700
- const filepath = join9(reportDir, filename);
16736
+ const filepath = join10(reportDir, filename);
16701
16737
  const lines = [];
16702
16738
  lines.push(`# NaironAI Scan Report`);
16703
16739
  lines.push("");
@@ -16813,7 +16849,7 @@ function generateReport(reportDir, score, git, agents, tests, since, sdlcAnalysi
16813
16849
  lines.push("---");
16814
16850
  lines.push("");
16815
16851
  lines.push("*Generated by [nairon-bench](https://github.com/ObaidUr-Rahmaan/nairon-bench)*");
16816
- writeFileSync4(filepath, lines.join(`
16852
+ writeFileSync5(filepath, lines.join(`
16817
16853
  `));
16818
16854
  return filepath;
16819
16855
  }
@@ -16832,15 +16868,73 @@ function formatTokensPlain(tokens) {
16832
16868
  return `${Math.round(tokens / 1000)}K`;
16833
16869
  return tokens.toString();
16834
16870
  }
16871
+ async function showFirstScanWelcome(projectDir) {
16872
+ console.log();
16873
+ console.log(colors2.dim(" " + "═".repeat(50)));
16874
+ console.log(` ${colors2.primary("✨")} ${colors2.bold("First scan in this project!")}`);
16875
+ console.log(colors2.dim(" " + "═".repeat(50)));
16876
+ console.log();
16877
+ const agent = detectAgent();
16878
+ const agentNames = {
16879
+ "claude-code": "Claude Code",
16880
+ opencode: "OpenCode",
16881
+ cursor: "Cursor",
16882
+ unknown: "Unknown"
16883
+ };
16884
+ console.log(` ${icons.info} Detected agent: ${colors2.bold(agentNames[agent])}`);
16885
+ const context = analyzeReadme(projectDir);
16886
+ if (context) {
16887
+ console.log(` ${icons.info} Project: ${colors2.bold(context.name)}`);
16888
+ if (context.techStack.length > 0) {
16889
+ console.log(` ${colors2.dim(" Tech: " + context.techStack.slice(0, 4).join(", "))}`);
16890
+ }
16891
+ }
16892
+ console.log();
16893
+ const suggestions = suggestSkills(context);
16894
+ if (suggestions.length > 0) {
16895
+ console.log(` ${colors2.bold("Recommended skills for this project:")}`);
16896
+ console.log();
16897
+ for (let i3 = 0;i3 < Math.min(3, suggestions.length); i3++) {
16898
+ const s2 = suggestions[i3];
16899
+ console.log(` ${colors2.primary((i3 + 1).toString())}. ${colors2.bold(s2.name)}`);
16900
+ console.log(` ${colors2.dim(s2.reason)}`);
16901
+ }
16902
+ console.log();
16903
+ const installChoice = await consola.prompt("Install recommended skills?", {
16904
+ type: "select",
16905
+ options: [
16906
+ { value: "find-skills", label: "Just find-skills (discover more skills later)" },
16907
+ { value: "all", label: "All recommended skills" },
16908
+ { value: "skip", label: "Skip - continue to scan" }
16909
+ ]
16910
+ });
16911
+ if (installChoice === "find-skills" || installChoice === "all") {
16912
+ console.log();
16913
+ const toInstall = installChoice === "all" ? suggestions.slice(0, 3) : suggestions.slice(0, 1);
16914
+ for (const skill of toInstall) {
16915
+ process.stdout.write(` Installing ${skill.name}...`);
16916
+ const result = installSkill2(skill.installCommand);
16917
+ if (result.success) {
16918
+ console.log(` ${colors2.success("✓")}`);
16919
+ } else {
16920
+ console.log(` ${colors2.warning("⚠")} ${colors2.dim(`(${result.message.slice(0, 50)})`)}`);
16921
+ }
16922
+ }
16923
+ console.log();
16924
+ }
16925
+ }
16926
+ console.log(colors2.dim(" " + "═".repeat(50)));
16927
+ console.log();
16928
+ }
16835
16929
 
16836
16930
  // src/commands/report.ts
16837
16931
  init_dist();
16838
16932
  init_client();
16839
16933
 
16840
16934
  // src/collectors/report-data.ts
16841
- import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
16842
- import { homedir as homedir8 } from "node:os";
16843
- import { join as join10, basename as basename3 } from "node:path";
16935
+ import { existsSync as existsSync11, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync2 } from "node:fs";
16936
+ import { homedir as homedir9 } from "node:os";
16937
+ import { join as join11, basename as basename3 } from "node:path";
16844
16938
  async function collectReportData(projectDir, since, until = new Date, harness) {
16845
16939
  const projectName = basename3(projectDir);
16846
16940
  const [commits, allSessions, mcpConfigs] = await Promise.all([
@@ -16923,12 +17017,12 @@ function detectAIAssistedCommit(message) {
16923
17017
  }
16924
17018
  async function collectAllSessions(since, until, projectDir) {
16925
17019
  const sessions = [];
16926
- const claudeDir = join10(homedir8(), ".claude");
16927
- if (existsSync10(claudeDir)) {
17020
+ const claudeDir = join11(homedir9(), ".claude");
17021
+ if (existsSync11(claudeDir)) {
16928
17022
  sessions.push(...collectClaudeCodeSessions(claudeDir, since, until, projectDir));
16929
17023
  }
16930
- const openCodeDir = join10(homedir8(), ".local", "share", "opencode");
16931
- if (existsSync10(openCodeDir)) {
17024
+ const openCodeDir = join11(homedir9(), ".local", "share", "opencode");
17025
+ if (existsSync11(openCodeDir)) {
16932
17026
  sessions.push(...collectOpenCodeSessions2(openCodeDir, since, until));
16933
17027
  }
16934
17028
  sessions.sort((a2, b2) => a2.startTime.getTime() - b2.startTime.getTime());
@@ -16936,15 +17030,15 @@ async function collectAllSessions(since, until, projectDir) {
16936
17030
  }
16937
17031
  function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
16938
17032
  const sessions = [];
16939
- const transcriptsDir = join10(claudeDir, "transcripts");
16940
- if (!existsSync10(transcriptsDir))
17033
+ const transcriptsDir = join11(claudeDir, "transcripts");
17034
+ if (!existsSync11(transcriptsDir))
16941
17035
  return sessions;
16942
17036
  const projectHash = projectDir ? hashPath(projectDir) : null;
16943
- const projectsDir = join10(claudeDir, "projects");
17037
+ const projectsDir = join11(claudeDir, "projects");
16944
17038
  try {
16945
17039
  const transcriptFiles = readdirSync5(transcriptsDir).filter((f3) => f3.endsWith(".jsonl"));
16946
17040
  for (const file of transcriptFiles) {
16947
- const filePath = join10(transcriptsDir, file);
17041
+ const filePath = join11(transcriptsDir, file);
16948
17042
  const stat = statSync2(filePath);
16949
17043
  if (stat.mtime < since || stat.mtime > until)
16950
17044
  continue;
@@ -16955,12 +17049,12 @@ function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
16955
17049
  }
16956
17050
  } catch {}
16957
17051
  }
16958
- if (projectHash && existsSync10(projectsDir)) {
16959
- const projectSessionDir = join10(projectsDir, projectHash);
16960
- if (existsSync10(projectSessionDir)) {
17052
+ if (projectHash && existsSync11(projectsDir)) {
17053
+ const projectSessionDir = join11(projectsDir, projectHash);
17054
+ if (existsSync11(projectSessionDir)) {
16961
17055
  const projectFiles = readdirSync5(projectSessionDir).filter((f3) => f3.endsWith(".jsonl"));
16962
17056
  for (const file of projectFiles) {
16963
- const filePath = join10(projectSessionDir, file);
17057
+ const filePath = join11(projectSessionDir, file);
16964
17058
  const stat = statSync2(filePath);
16965
17059
  if (stat.mtime < since || stat.mtime > until)
16966
17060
  continue;
@@ -16979,7 +17073,7 @@ function collectClaudeCodeSessions(claudeDir, since, until, projectDir) {
16979
17073
  return sessions;
16980
17074
  }
16981
17075
  function parseClaudeTranscript(filePath, fileName) {
16982
- const content = readFileSync10(filePath, "utf-8");
17076
+ const content = readFileSync11(filePath, "utf-8");
16983
17077
  const lines = content.trim().split(`
16984
17078
  `).filter((l2) => l2.trim());
16985
17079
  if (lines.length === 0)
@@ -17069,25 +17163,25 @@ function parseClaudeTranscript(filePath, fileName) {
17069
17163
  }
17070
17164
  function collectOpenCodeSessions2(openCodeDir, since, until) {
17071
17165
  const sessions = [];
17072
- const sessionDir = join10(openCodeDir, "storage", "session");
17073
- const messageDir = join10(openCodeDir, "storage", "message");
17074
- const partDir = join10(openCodeDir, "storage", "part");
17075
- if (!existsSync10(sessionDir))
17166
+ const sessionDir = join11(openCodeDir, "storage", "session");
17167
+ const messageDir = join11(openCodeDir, "storage", "message");
17168
+ const partDir = join11(openCodeDir, "storage", "part");
17169
+ if (!existsSync11(sessionDir))
17076
17170
  return sessions;
17077
17171
  try {
17078
- const projectDirs = readdirSync5(sessionDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join10(sessionDir, d2.name));
17172
+ const projectDirs = readdirSync5(sessionDir, { withFileTypes: true }).filter((d2) => d2.isDirectory()).map((d2) => join11(sessionDir, d2.name));
17079
17173
  for (const projectDir of projectDirs) {
17080
17174
  const sessionFiles = readdirSync5(projectDir).filter((f3) => f3.endsWith(".json"));
17081
17175
  for (const sessionFile of sessionFiles) {
17082
17176
  try {
17083
- const sessionPath = join10(projectDir, sessionFile);
17084
- const sessionData = JSON.parse(readFileSync10(sessionPath, "utf-8"));
17177
+ const sessionPath = join11(projectDir, sessionFile);
17178
+ const sessionData = JSON.parse(readFileSync11(sessionPath, "utf-8"));
17085
17179
  const createdAt = new Date(sessionData.time?.created || 0);
17086
17180
  const updatedAt = new Date(sessionData.time?.updated || sessionData.time?.created || 0);
17087
17181
  if (updatedAt < since || createdAt > until)
17088
17182
  continue;
17089
17183
  const sessionId = sessionData.id;
17090
- const sessionMsgDir = join10(messageDir, sessionId);
17184
+ const sessionMsgDir = join11(messageDir, sessionId);
17091
17185
  const prompts = [];
17092
17186
  const responses = [];
17093
17187
  const toolsUsed = new Set;
@@ -17098,12 +17192,12 @@ function collectOpenCodeSessions2(openCodeDir, since, until) {
17098
17192
  let model = "unknown";
17099
17193
  let startTime = createdAt;
17100
17194
  let endTime = updatedAt;
17101
- if (existsSync10(sessionMsgDir)) {
17195
+ if (existsSync11(sessionMsgDir)) {
17102
17196
  const msgFiles = readdirSync5(sessionMsgDir).filter((f3) => f3.endsWith(".json")).sort();
17103
17197
  for (const msgFile of msgFiles) {
17104
17198
  try {
17105
- const msgPath = join10(sessionMsgDir, msgFile);
17106
- const msgData = JSON.parse(readFileSync10(msgPath, "utf-8"));
17199
+ const msgPath = join11(sessionMsgDir, msgFile);
17200
+ const msgData = JSON.parse(readFileSync11(msgPath, "utf-8"));
17107
17201
  const msgId = msgData.id;
17108
17202
  if (msgData.model?.modelID) {
17109
17203
  model = msgData.model.modelID;
@@ -17113,14 +17207,14 @@ function collectOpenCodeSessions2(openCodeDir, since, until) {
17113
17207
  startTime = msgTime;
17114
17208
  if (msgTime > endTime)
17115
17209
  endTime = msgTime;
17116
- const msgPartDir = join10(partDir, msgId);
17210
+ const msgPartDir = join11(partDir, msgId);
17117
17211
  let messageText = "";
17118
- if (existsSync10(msgPartDir)) {
17212
+ if (existsSync11(msgPartDir)) {
17119
17213
  const partFiles = readdirSync5(msgPartDir).filter((f3) => f3.endsWith(".json")).sort();
17120
17214
  for (const partFile of partFiles) {
17121
17215
  try {
17122
- const partPath = join10(msgPartDir, partFile);
17123
- const partData = JSON.parse(readFileSync10(partPath, "utf-8"));
17216
+ const partPath = join11(msgPartDir, partFile);
17217
+ const partData = JSON.parse(readFileSync11(partPath, "utf-8"));
17124
17218
  if (partData.type === "text" && partData.text && !partData.synthetic) {
17125
17219
  messageText += partData.text + `
17126
17220
  `;
@@ -17294,10 +17388,10 @@ function analyzeResponse(text, sessionId, index, promptId, timestamp) {
17294
17388
  }
17295
17389
  function collectMCPConfigs() {
17296
17390
  const configs = [];
17297
- const claudeConfig = join10(homedir8(), ".claude.json");
17298
- if (existsSync10(claudeConfig)) {
17391
+ const claudeConfig = join11(homedir9(), ".claude.json");
17392
+ if (existsSync11(claudeConfig)) {
17299
17393
  try {
17300
- const data = JSON.parse(readFileSync10(claudeConfig, "utf-8"));
17394
+ const data = JSON.parse(readFileSync11(claudeConfig, "utf-8"));
17301
17395
  const mcpServers = data.mcpServers || {};
17302
17396
  for (const [name, config] of Object.entries(mcpServers)) {
17303
17397
  configs.push({
@@ -17310,13 +17404,13 @@ function collectMCPConfigs() {
17310
17404
  } catch {}
17311
17405
  }
17312
17406
  const openCodeConfigs = [
17313
- join10(homedir8(), ".opencode.json"),
17314
- join10(homedir8(), ".config", "opencode", "opencode.json")
17407
+ join11(homedir9(), ".opencode.json"),
17408
+ join11(homedir9(), ".config", "opencode", "opencode.json")
17315
17409
  ];
17316
17410
  for (const configPath of openCodeConfigs) {
17317
- if (existsSync10(configPath)) {
17411
+ if (existsSync11(configPath)) {
17318
17412
  try {
17319
- const data = JSON.parse(readFileSync10(configPath, "utf-8"));
17413
+ const data = JSON.parse(readFileSync11(configPath, "utf-8"));
17320
17414
  const mcpServers = data.mcp || {};
17321
17415
  for (const [name, config] of Object.entries(mcpServers)) {
17322
17416
  if (!configs.find((c3) => c3.name === name)) {
@@ -17331,10 +17425,10 @@ function collectMCPConfigs() {
17331
17425
  } catch {}
17332
17426
  }
17333
17427
  }
17334
- const cursorConfig = join10(homedir8(), ".cursor", "mcp.json");
17335
- if (existsSync10(cursorConfig)) {
17428
+ const cursorConfig = join11(homedir9(), ".cursor", "mcp.json");
17429
+ if (existsSync11(cursorConfig)) {
17336
17430
  try {
17337
- const data = JSON.parse(readFileSync10(cursorConfig, "utf-8"));
17431
+ const data = JSON.parse(readFileSync11(cursorConfig, "utf-8"));
17338
17432
  const mcpServers = data.mcpServers || {};
17339
17433
  for (const [name, config] of Object.entries(mcpServers)) {
17340
17434
  if (!configs.find((c3) => c3.name === name)) {
@@ -19535,9 +19629,9 @@ function getSeverityEmoji(severity) {
19535
19629
  }
19536
19630
 
19537
19631
  // src/lib/tool-analyzer.ts
19538
- import { existsSync as existsSync11, readdirSync as readdirSync6 } from "node:fs";
19539
- import { homedir as homedir9 } from "node:os";
19540
- import { join as join11 } from "node:path";
19632
+ import { existsSync as existsSync12, readdirSync as readdirSync6 } from "node:fs";
19633
+ import { homedir as homedir10 } from "node:os";
19634
+ import { join as join12 } from "node:path";
19541
19635
  function analyzeToolUtilization(data) {
19542
19636
  const mcpServers = analyzeMCPServers(data);
19543
19637
  const mcpSummary = buildMCPSummary(mcpServers);
@@ -19687,24 +19781,24 @@ function categorizeTools(tools) {
19687
19781
  }
19688
19782
  function analyzeSkills() {
19689
19783
  const skills = [];
19690
- const home = homedir9();
19784
+ const home = homedir10();
19691
19785
  const skillsDirs = [
19692
- join11(home, ".claude", "skills"),
19693
- join11(home, ".agents", "skills"),
19694
- join11(home, ".config", "claude", "skills")
19786
+ join12(home, ".claude", "skills"),
19787
+ join12(home, ".agents", "skills"),
19788
+ join12(home, ".config", "claude", "skills")
19695
19789
  ];
19696
19790
  const projectSkillsDirs = [
19697
- join11(process.cwd(), ".claude", "skills"),
19698
- join11(process.cwd(), ".agents", "skills")
19791
+ join12(process.cwd(), ".claude", "skills"),
19792
+ join12(process.cwd(), ".agents", "skills")
19699
19793
  ];
19700
19794
  const openCodeSkillDirs = [
19701
- join11(home, ".config", "opencode", "skills"),
19702
- join11(home, ".local", "share", "opencode", "skills")
19795
+ join12(home, ".config", "opencode", "skills"),
19796
+ join12(home, ".local", "share", "opencode", "skills")
19703
19797
  ];
19704
19798
  const allSkillDirs = [...skillsDirs, ...projectSkillsDirs, ...openCodeSkillDirs];
19705
19799
  const seenSkills = new Set;
19706
19800
  for (const skillsDir of allSkillDirs) {
19707
- if (existsSync11(skillsDir)) {
19801
+ if (existsSync12(skillsDir)) {
19708
19802
  try {
19709
19803
  const entries = readdirSync6(skillsDir, { withFileTypes: true });
19710
19804
  for (const entry of entries) {
@@ -19715,7 +19809,7 @@ function analyzeSkills() {
19715
19809
  if (entry.isDirectory() || entry.isSymbolicLink() || entry.name.endsWith(".md")) {
19716
19810
  skills.push({
19717
19811
  name: skillName,
19718
- path: join11(skillsDir, entry.name),
19812
+ path: join12(skillsDir, entry.name),
19719
19813
  used: false,
19720
19814
  usageCount: 0
19721
19815
  });
@@ -20012,22 +20106,22 @@ function renderToolUtilizationMarkdown(analysis) {
20012
20106
  }
20013
20107
 
20014
20108
  // src/lib/coverage-parser.ts
20015
- import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
20016
- import { join as join12 } from "node:path";
20109
+ import { existsSync as existsSync13, readFileSync as readFileSync13 } from "node:fs";
20110
+ import { join as join13 } from "node:path";
20017
20111
  function parseCoverageReport(projectDir) {
20018
20112
  const coveragePaths = [
20019
- { path: join12(projectDir, "coverage", "lcov.info"), parser: parseLcov },
20020
- { path: join12(projectDir, "coverage", "coverage-summary.json"), parser: parseIstanbulSummary },
20021
- { path: join12(projectDir, "coverage", "coverage-final.json"), parser: parseIstanbulFinal },
20022
- { path: join12(projectDir, "coverage", "cobertura-coverage.xml"), parser: parseCobertura },
20023
- { path: join12(projectDir, "coverage", "clover.xml"), parser: parseClover },
20024
- { path: join12(projectDir, "lcov.info"), parser: parseLcov },
20025
- { path: join12(projectDir, ".nyc_output", "coverage-summary.json"), parser: parseIstanbulSummary }
20113
+ { path: join13(projectDir, "coverage", "lcov.info"), parser: parseLcov },
20114
+ { path: join13(projectDir, "coverage", "coverage-summary.json"), parser: parseIstanbulSummary },
20115
+ { path: join13(projectDir, "coverage", "coverage-final.json"), parser: parseIstanbulFinal },
20116
+ { path: join13(projectDir, "coverage", "cobertura-coverage.xml"), parser: parseCobertura },
20117
+ { path: join13(projectDir, "coverage", "clover.xml"), parser: parseClover },
20118
+ { path: join13(projectDir, "lcov.info"), parser: parseLcov },
20119
+ { path: join13(projectDir, ".nyc_output", "coverage-summary.json"), parser: parseIstanbulSummary }
20026
20120
  ];
20027
20121
  for (const { path, parser: parser4 } of coveragePaths) {
20028
- if (existsSync12(path)) {
20122
+ if (existsSync13(path)) {
20029
20123
  try {
20030
- const content = readFileSync12(path, "utf-8");
20124
+ const content = readFileSync13(path, "utf-8");
20031
20125
  return parser4(content, path);
20032
20126
  } catch {}
20033
20127
  }
@@ -21739,150 +21833,151 @@ function formatReportAsJSON(report) {
21739
21833
  }
21740
21834
 
21741
21835
  // src/commands/report.ts
21742
- import { writeFileSync as writeFileSync5 } from "node:fs";
21836
+ import { writeFileSync as writeFileSync6 } from "node:fs";
21743
21837
  import { resolve } from "node:path";
21744
21838
  var reportCommand = defineCommand2({
21745
21839
  meta: {
21746
21840
  name: "report",
21747
- description: "Generate AI-nativeness reports for hackathon submissions or regular benchmarks"
21841
+ description: "Generate AI-nativeness reports (interactive)"
21748
21842
  },
21749
21843
  args: {
21750
- hackathon: {
21751
- type: "boolean",
21752
- description: "Generate hackathon submission report (last 48 hours)",
21753
- default: false
21754
- },
21755
- harness: {
21756
- type: "string",
21757
- description: "AI harness to analyze: claude-code, opencode, cursor, or all"
21758
- },
21759
- since: {
21760
- type: "string",
21761
- description: "Time range: 48h, 7d, 30d, or ISO date"
21762
- },
21763
- output: {
21764
- type: "string",
21765
- description: "Save report to file (e.g., report.md)"
21766
- },
21767
- publish: {
21768
- type: "boolean",
21769
- description: "Publish report and get a shareable public link",
21770
- default: false
21771
- },
21772
- phase: {
21773
- type: "string",
21774
- description: "Drill into a specific phase (requirements, planning, implementation, testing, review)"
21775
- },
21776
- weekly: {
21777
- type: "boolean",
21778
- description: "Show latest scheduled weekly improvement report",
21779
- default: false
21780
- },
21781
- history: {
21782
- type: "boolean",
21783
- description: "Show score trend over time",
21784
- default: false
21785
- },
21786
- limit: {
21787
- type: "string",
21788
- description: "Number of historical scans to show",
21789
- default: "10"
21790
- },
21791
- format: {
21792
- type: "string",
21793
- description: "Output format: terminal, md, json, or text (legacy)",
21794
- default: "terminal"
21795
- },
21796
- leadership: {
21844
+ json: {
21797
21845
  type: "boolean",
21798
- description: "Generate leadership report with effort breakdown and hidden work visibility",
21846
+ description: "Output as JSON (for CI/scripts)",
21799
21847
  default: false
21800
21848
  }
21801
21849
  },
21802
21850
  async run({ args }) {
21803
- if (args.leadership) {
21804
- await runLeadershipReport(args);
21851
+ if (args.json) {
21852
+ const projectDir = process.cwd();
21853
+ const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
21854
+ const report = await generateHackathonReport(projectDir, since, new Date, "all");
21855
+ console.log(formatReportAsJSON(report));
21805
21856
  return;
21806
21857
  }
21807
- if (args.hackathon || args.since) {
21808
- await runHackathonReport(args);
21809
- return;
21810
- }
21811
- if (!args.weekly && !args.history && !args.phase) {
21812
- const choice = await consola.prompt("What would you like to report on?", {
21813
- type: "select",
21814
- options: [
21815
- { value: "hackathon", label: "Hackathon (last 48 hours)" },
21816
- { value: "week", label: "Last week" },
21817
- { value: "month", label: "Last month" },
21818
- { value: "cloud", label: "Cloud benchmark (requires nb init)" }
21819
- ]
21820
- });
21821
- if (choice === "hackathon" || choice === "week" || choice === "month") {
21822
- const sinceMap = {
21823
- hackathon: "48h",
21824
- week: "7d",
21825
- month: "30d"
21826
- };
21827
- args.since = sinceMap[choice];
21828
- const harness = await consola.prompt("Which AI harness did you use?", {
21829
- type: "select",
21830
- options: [
21831
- { value: "claude-code", label: "Claude Code" },
21832
- { value: "opencode", label: "OpenCode" },
21833
- { value: "cursor", label: "Cursor" },
21834
- { value: "all", label: "All (combine sessions from all harnesses)" }
21835
- ]
21836
- });
21837
- args.harness = harness;
21838
- const format2 = await consola.prompt("Output format?", {
21839
- type: "select",
21840
- options: [
21841
- { value: "terminal", label: "Terminal (view now)" },
21842
- { value: "md", label: "Markdown (for submission)" },
21843
- { value: "publish", label: "Public link (shareable URL)" },
21844
- { value: "json", label: "JSON (programmatic)" }
21845
- ]
21846
- });
21847
- if (format2 === "publish") {
21848
- args.publish = true;
21849
- args.format = "terminal";
21850
- } else {
21851
- args.format = format2;
21852
- }
21853
- if (format2 === "md") {
21854
- const filename = await consola.prompt("Save to file?", {
21855
- type: "text",
21856
- default: "ai-nativeness-report.md",
21857
- placeholder: "report.md"
21858
- });
21859
- if (filename && typeof filename === "string") {
21860
- args.output = filename;
21861
- }
21862
- }
21863
- await runHackathonReport(args);
21864
- return;
21865
- }
21866
- args.format = "text";
21867
- }
21868
- const client = getClient();
21869
- const clerkId = getClerkId();
21870
- if (!client || !clerkId) {
21871
- consola.error("Not connected to Convex. Run `nb init` to set up your profile.");
21872
- consola.info("Use `nb report --hackathon` for local AI-nativeness report.");
21858
+ console.log();
21859
+ console.log(" \uD83D\uDCCA " + "\x1B[1mNaironAI Report Generator\x1B[0m");
21860
+ console.log(" " + "\x1B[2m" + "─".repeat(35) + "\x1B[0m");
21861
+ console.log();
21862
+ const reportType = await consola.prompt("What kind of report?", {
21863
+ type: "select",
21864
+ options: [
21865
+ { value: "ai-nativeness", label: "AI-Nativeness Report (recommended)" },
21866
+ { value: "leadership", label: "Leadership Report (effort visibility)" },
21867
+ { value: "cloud", label: "Cloud Benchmark (requires nb init)" }
21868
+ ]
21869
+ });
21870
+ if (reportType === "leadership") {
21871
+ await runLeadershipReportInteractive();
21873
21872
  return;
21874
21873
  }
21875
- if (args.weekly) {
21876
- await showWeeklyReport(client, clerkId, args.format);
21874
+ if (reportType === "cloud") {
21875
+ await runCloudReport();
21877
21876
  return;
21878
21877
  }
21879
- if (args.history) {
21880
- await showHistory(client, clerkId, parseInt(args.limit, 10), args.format);
21881
- return;
21878
+ const period = await consola.prompt("Time period?", {
21879
+ type: "select",
21880
+ options: [
21881
+ { value: "48h", label: "Last 48 hours (hackathon)" },
21882
+ { value: "7d", label: "Last 7 days" },
21883
+ { value: "30d", label: "Last 30 days" }
21884
+ ]
21885
+ });
21886
+ const harness = await consola.prompt("Which AI tool?", {
21887
+ type: "select",
21888
+ options: [
21889
+ { value: "all", label: "All tools (recommended)" },
21890
+ { value: "claude-code", label: "Claude Code" },
21891
+ { value: "opencode", label: "OpenCode" },
21892
+ { value: "cursor", label: "Cursor" }
21893
+ ]
21894
+ });
21895
+ const format2 = await consola.prompt("Output format?", {
21896
+ type: "select",
21897
+ options: [
21898
+ { value: "publish", label: "Public link (shareable URL)" },
21899
+ { value: "terminal", label: "Terminal (view now)" },
21900
+ { value: "md", label: "Markdown file" },
21901
+ { value: "json", label: "JSON" }
21902
+ ]
21903
+ });
21904
+ const reportArgs = {
21905
+ since: period,
21906
+ harness,
21907
+ format: format2 === "publish" ? "terminal" : format2,
21908
+ publish: format2 === "publish"
21909
+ };
21910
+ if (format2 === "md") {
21911
+ const filename = await consola.prompt("Save to file?", {
21912
+ type: "text",
21913
+ default: "ai-nativeness-report.md",
21914
+ placeholder: "report.md"
21915
+ });
21916
+ if (filename && typeof filename === "string") {
21917
+ reportArgs.output = filename;
21918
+ }
21882
21919
  }
21883
- await showLatestScan(client, clerkId, args.phase, args.format);
21920
+ await runHackathonReport(reportArgs);
21884
21921
  }
21885
21922
  });
21923
+ async function runLeadershipReportInteractive() {
21924
+ const period = await consola.prompt("Time period?", {
21925
+ type: "select",
21926
+ options: [
21927
+ { value: "7d", label: "Last 7 days" },
21928
+ { value: "30d", label: "Last 30 days" },
21929
+ { value: "48h", label: "Last 48 hours" }
21930
+ ]
21931
+ });
21932
+ const format2 = await consola.prompt("Output format?", {
21933
+ type: "select",
21934
+ options: [
21935
+ { value: "terminal", label: "Terminal (view now)" },
21936
+ { value: "md", label: "Markdown file" }
21937
+ ]
21938
+ });
21939
+ let output;
21940
+ if (format2 === "md") {
21941
+ const filename = await consola.prompt("Save to file?", {
21942
+ type: "text",
21943
+ default: "leadership-report.md"
21944
+ });
21945
+ if (filename && typeof filename === "string") {
21946
+ output = filename;
21947
+ }
21948
+ }
21949
+ await runLeadershipReport({
21950
+ since: period,
21951
+ format: format2,
21952
+ output
21953
+ });
21954
+ }
21955
+ async function runCloudReport() {
21956
+ const client = getClient();
21957
+ const clerkId = getClerkId();
21958
+ if (!client || !clerkId) {
21959
+ consola.error("Not connected to Convex. Run `nb init` to set up your profile.");
21960
+ consola.info("Try the AI-Nativeness Report instead - it works locally.");
21961
+ return;
21962
+ }
21963
+ const cloudOption = await consola.prompt("What would you like to see?", {
21964
+ type: "select",
21965
+ options: [
21966
+ { value: "latest", label: "Latest scan results" },
21967
+ { value: "history", label: "Score history (trend)" },
21968
+ { value: "weekly", label: "Weekly improvement report" }
21969
+ ]
21970
+ });
21971
+ if (cloudOption === "weekly") {
21972
+ await showWeeklyReport(client, clerkId, "text");
21973
+ return;
21974
+ }
21975
+ if (cloudOption === "history") {
21976
+ await showHistory(client, clerkId, 10, "text");
21977
+ return;
21978
+ }
21979
+ await showLatestScan(client, clerkId, undefined, "text");
21980
+ }
21886
21981
  async function runHackathonReport(args) {
21887
21982
  const projectDir = process.cwd();
21888
21983
  let harness = args.harness;
@@ -21950,7 +22045,7 @@ async function runHackathonReport(args) {
21950
22045
  }
21951
22046
  if (args.output) {
21952
22047
  const filepath = resolve(projectDir, args.output);
21953
- writeFileSync5(filepath, output);
22048
+ writeFileSync6(filepath, output);
21954
22049
  consola.success(`Report saved to ${filepath}`);
21955
22050
  }
21956
22051
  console.log(output);
@@ -22119,7 +22214,7 @@ async function runLeadershipReport(args) {
22119
22214
  }
22120
22215
  if (args.output) {
22121
22216
  const filepath = resolve(projectDir, args.output);
22122
- writeFileSync5(filepath, output);
22217
+ writeFileSync6(filepath, output);
22123
22218
  consola.success(`Report saved to ${filepath}`);
22124
22219
  }
22125
22220
  console.log(output);
@@ -22281,9 +22376,9 @@ function renderBar4(value, width) {
22281
22376
  }
22282
22377
 
22283
22378
  // src/commands/doctor.ts
22284
- import { existsSync as existsSync13 } from "node:fs";
22285
- import { homedir as homedir10 } from "node:os";
22286
- import { join as join13 } from "node:path";
22379
+ import { existsSync as existsSync14 } from "node:fs";
22380
+ import { homedir as homedir11 } from "node:os";
22381
+ import { join as join14 } from "node:path";
22287
22382
  init_client();
22288
22383
 
22289
22384
  // src/lib/recommendations-db.ts
@@ -22409,6 +22504,18 @@ var PLUGINS = [
22409
22504
  }
22410
22505
  ];
22411
22506
  var SKILLS = [
22507
+ {
22508
+ id: "find-skills",
22509
+ name: "Find Skills",
22510
+ type: "skill",
22511
+ description: "Discover and install skills from the open agent skills ecosystem - essential for any AI workflow",
22512
+ installCommand: "npx skills add vercel-labs/skills@find-skills --yes",
22513
+ phases: ["requirements", "planning", "implementation", "review"],
22514
+ impact: "high",
22515
+ detectionKey: "find-skills",
22516
+ website: "https://skills.sh/vercel-labs/skills/find-skills",
22517
+ badge: "Essential"
22518
+ },
22412
22519
  {
22413
22520
  id: "tdd-skill",
22414
22521
  name: "Test-Driven Development",
@@ -22880,25 +22987,17 @@ function printVerificationResults(summary, verbose = false) {
22880
22987
  var doctorCommand = defineCommand2({
22881
22988
  meta: {
22882
22989
  name: "doctor",
22883
- description: "Check tool detection, data sources, and connectivity health"
22990
+ description: "Check system health and diagnose issues"
22884
22991
  },
22885
22992
  args: {
22886
22993
  "verify-recommendations": {
22887
22994
  type: "boolean",
22888
- description: "Verify all recommendations in the database are valid",
22889
- alias: "verify",
22995
+ description: "",
22890
22996
  default: false
22891
22997
  },
22892
22998
  thorough: {
22893
22999
  type: "boolean",
22894
- description: "Run thorough verification with network checks",
22895
- alias: "t",
22896
- default: false
22897
- },
22898
- verbose: {
22899
- type: "boolean",
22900
- description: "Show all results including valid recommendations",
22901
- alias: "v",
23000
+ description: "",
22902
23001
  default: false
22903
23002
  }
22904
23003
  },
@@ -22924,32 +23023,32 @@ var doctorCommand = defineCommand2({
22924
23023
  } else {
22925
23024
  spinner2.succeed("All recommendations verified");
22926
23025
  }
22927
- printVerificationResults(summary, args.verbose);
23026
+ printVerificationResults(summary, true);
22928
23027
  if (summary.invalid > 0) {
22929
23028
  process.exit(1);
22930
23029
  }
22931
23030
  return;
22932
23031
  }
22933
23032
  console.log();
22934
- console.log(colors2.bold(colors2.primary(" NaironAI Doctor")));
22935
- console.log(colors2.dim(` Checking system health...
22936
- `));
23033
+ console.log(" \uD83E\uDE7A " + "\x1B[1mNaironAI Doctor\x1B[0m");
23034
+ console.log(" " + "\x1B[2m" + "─".repeat(35) + "\x1B[0m");
23035
+ console.log();
22937
23036
  const spinner = createSpinner("Running diagnostics...");
22938
23037
  spinner.start();
22939
23038
  const results = [];
22940
23039
  results.push({ label: "OS", status: "info", value: `${process.platform} ${process.arch}` });
22941
23040
  results.push({ label: "Bun", status: "info", value: typeof Bun !== "undefined" ? Bun.version : "N/A" });
22942
23041
  results.push({ label: "Node", status: "info", value: process.version });
22943
- results.push({ label: "Home", status: "info", value: homedir10() });
22944
- const gitDir = join13(process.cwd(), ".git");
22945
- if (existsSync13(gitDir)) {
23042
+ results.push({ label: "Home", status: "info", value: homedir11() });
23043
+ const gitDir = join14(process.cwd(), ".git");
23044
+ if (existsSync14(gitDir)) {
22946
23045
  results.push({ label: "Git", status: "success", value: "Repository detected" });
22947
23046
  } else {
22948
23047
  results.push({ label: "Git", status: "warn", value: "No repository in current directory" });
22949
23048
  }
22950
23049
  for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
22951
- const resolvedPath = pathTemplate.replace("~", homedir10());
22952
- if (existsSync13(resolvedPath)) {
23050
+ const resolvedPath = pathTemplate.replace("~", homedir11());
23051
+ if (existsSync14(resolvedPath)) {
22953
23052
  results.push({ label: agent, status: "success", value: `found at ${resolvedPath}` });
22954
23053
  }
22955
23054
  }
@@ -23003,10 +23102,10 @@ var doctorCommand = defineCommand2({
23003
23102
  });
23004
23103
 
23005
23104
  // src/commands/setup.ts
23006
- import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, copyFileSync } from "node:fs";
23007
- import { join as join14, dirname as dirname2 } from "node:path";
23008
- import { homedir as homedir11, platform as platform2 } from "node:os";
23009
- import { execSync as execSync3 } from "node:child_process";
23105
+ import { existsSync as existsSync15, readFileSync as readFileSync14, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, copyFileSync } from "node:fs";
23106
+ import { join as join15, dirname as dirname2 } from "node:path";
23107
+ import { homedir as homedir12, platform as platform2 } from "node:os";
23108
+ import { execSync as execSync4 } from "node:child_process";
23010
23109
  import { createInterface } from "node:readline";
23011
23110
  var CONVEX_SITE_URL = "https://steady-bass-841.convex.site";
23012
23111
  async function fetchSetupData() {
@@ -23024,7 +23123,7 @@ var FALLBACK_HARNESSES = [
23024
23123
  id: "claude-code",
23025
23124
  name: "Claude Code",
23026
23125
  configPaths: [
23027
- join14(homedir11(), ".claude.json")
23126
+ join15(homedir12(), ".claude.json")
23028
23127
  ],
23029
23128
  mcpConfigKey: "mcpServers",
23030
23129
  detected: false
@@ -23033,8 +23132,8 @@ var FALLBACK_HARNESSES = [
23033
23132
  id: "opencode",
23034
23133
  name: "OpenCode",
23035
23134
  configPaths: [
23036
- join14(homedir11(), ".opencode.json"),
23037
- join14(homedir11(), ".config", "opencode", "opencode.json")
23135
+ join15(homedir12(), ".opencode.json"),
23136
+ join15(homedir12(), ".config", "opencode", "opencode.json")
23038
23137
  ],
23039
23138
  mcpConfigKey: "mcp",
23040
23139
  detected: false
@@ -23043,8 +23142,8 @@ var FALLBACK_HARNESSES = [
23043
23142
  id: "cursor",
23044
23143
  name: "Cursor",
23045
23144
  configPaths: [
23046
- join14(homedir11(), ".cursor", "mcp.json"),
23047
- join14(homedir11(), "Library", "Application Support", "Cursor", "User", "globalStorage", "mcp.json")
23145
+ join15(homedir12(), ".cursor", "mcp.json"),
23146
+ join15(homedir12(), "Library", "Application Support", "Cursor", "User", "globalStorage", "mcp.json")
23048
23147
  ],
23049
23148
  mcpConfigKey: "mcpServers",
23050
23149
  detected: false
@@ -23061,7 +23160,7 @@ function getConfigPathsForPlatform(harness) {
23061
23160
  } else {
23062
23161
  paths = pathMap.linux;
23063
23162
  }
23064
- return paths.map((p) => p.replace(/^~/, homedir11()).replace(/%USERPROFILE%/gi, homedir11()).replace(/%APPDATA%/gi, join14(homedir11(), "AppData", "Roaming")));
23163
+ return paths.map((p) => p.replace(/^~/, homedir12()).replace(/%USERPROFILE%/gi, homedir12()).replace(/%APPDATA%/gi, join15(homedir12(), "AppData", "Roaming")));
23065
23164
  }
23066
23165
  function convertAPIHarnessToConfig(harness) {
23067
23166
  return {
@@ -23076,7 +23175,7 @@ function detectHarnesses(harnesses) {
23076
23175
  const detected = [];
23077
23176
  for (const harness of harnesses) {
23078
23177
  for (const configPath of harness.configPaths) {
23079
- if (existsSync14(configPath)) {
23178
+ if (existsSync15(configPath)) {
23080
23179
  detected.push({
23081
23180
  ...harness,
23082
23181
  detected: true,
@@ -23089,19 +23188,19 @@ function detectHarnesses(harnesses) {
23089
23188
  return detected;
23090
23189
  }
23091
23190
  function backupConfig(configPath) {
23092
- const backupDir = join14(homedir11(), ".nairon", "backups");
23093
- if (!existsSync14(backupDir)) {
23094
- mkdirSync5(backupDir, { recursive: true });
23191
+ const backupDir = join15(homedir12(), ".nairon", "backups");
23192
+ if (!existsSync15(backupDir)) {
23193
+ mkdirSync6(backupDir, { recursive: true });
23095
23194
  }
23096
23195
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
23097
23196
  const filename = configPath.replace(/[/\\]/g, "_").replace(/^_/, "");
23098
- const backupPath = join14(backupDir, `${timestamp}_${filename}`);
23197
+ const backupPath = join15(backupDir, `${timestamp}_${filename}`);
23099
23198
  copyFileSync(configPath, backupPath);
23100
23199
  return backupPath;
23101
23200
  }
23102
23201
  function readJsonConfig(configPath) {
23103
23202
  try {
23104
- const content = readFileSync13(configPath, "utf-8");
23203
+ const content = readFileSync14(configPath, "utf-8");
23105
23204
  return JSON.parse(content);
23106
23205
  } catch {
23107
23206
  return {};
@@ -23109,10 +23208,10 @@ function readJsonConfig(configPath) {
23109
23208
  }
23110
23209
  function writeJsonConfig(configPath, config) {
23111
23210
  const dir = dirname2(configPath);
23112
- if (!existsSync14(dir)) {
23113
- mkdirSync5(dir, { recursive: true });
23211
+ if (!existsSync15(dir)) {
23212
+ mkdirSync6(dir, { recursive: true });
23114
23213
  }
23115
- writeFileSync6(configPath, JSON.stringify(config, null, 2));
23214
+ writeFileSync7(configPath, JSON.stringify(config, null, 2));
23116
23215
  }
23117
23216
  async function prompt2(question) {
23118
23217
  const rl = createInterface({
@@ -23153,7 +23252,7 @@ function getInstalledMCPs(harnesses) {
23153
23252
  const installed = new Set;
23154
23253
  for (const harness of harnesses) {
23155
23254
  for (const configPath of harness.configPaths) {
23156
- if (!existsSync14(configPath))
23255
+ if (!existsSync15(configPath))
23157
23256
  continue;
23158
23257
  const config = readJsonConfig(configPath);
23159
23258
  const mcpServers = config[harness.mcpConfigKey];
@@ -23177,7 +23276,7 @@ async function installMCPServer(serverName, instruction, harness) {
23177
23276
  if (!harness.configPath || !instruction.mcpServerConfig)
23178
23277
  return false;
23179
23278
  try {
23180
- if (existsSync14(harness.configPath)) {
23279
+ if (existsSync15(harness.configPath)) {
23181
23280
  backupConfig(harness.configPath);
23182
23281
  }
23183
23282
  const config = readJsonConfig(harness.configPath);
@@ -23205,16 +23304,16 @@ async function installMCPServer(serverName, instruction, harness) {
23205
23304
  async function installBeads(projectDir) {
23206
23305
  try {
23207
23306
  try {
23208
- execSync3("bd --version", { stdio: "ignore" });
23307
+ execSync4("bd --version", { stdio: "ignore" });
23209
23308
  } catch {
23210
23309
  const spinner = createSpinner("Installing Beads globally...");
23211
23310
  spinner.start();
23212
23311
  try {
23213
- execSync3("bun add -g beads", { stdio: "ignore" });
23312
+ execSync4("bun add -g beads", { stdio: "ignore" });
23214
23313
  spinner.succeed("Beads installed globally");
23215
23314
  } catch {
23216
23315
  try {
23217
- execSync3("npm install -g beads", { stdio: "ignore" });
23316
+ execSync4("npm install -g beads", { stdio: "ignore" });
23218
23317
  spinner.succeed("Beads installed globally (via npm)");
23219
23318
  } catch {
23220
23319
  spinner.fail("Failed to install Beads globally");
@@ -23222,13 +23321,13 @@ async function installBeads(projectDir) {
23222
23321
  }
23223
23322
  }
23224
23323
  }
23225
- const beadsDir = join14(projectDir, ".beads");
23226
- if (!existsSync14(beadsDir)) {
23324
+ const beadsDir = join15(projectDir, ".beads");
23325
+ if (!existsSync15(beadsDir)) {
23227
23326
  const projectName = projectDir.split(/[/\\]/).pop() ?? "project";
23228
23327
  const initSpinner = createSpinner(`Initializing Beads in ${projectName}...`);
23229
23328
  initSpinner.start();
23230
23329
  try {
23231
- execSync3(`bd init ${projectName}`, { cwd: projectDir, stdio: "ignore" });
23330
+ execSync4(`bd init ${projectName}`, { cwd: projectDir, stdio: "ignore" });
23232
23331
  initSpinner.succeed(`Beads initialized in ${projectName}`);
23233
23332
  } catch {
23234
23333
  initSpinner.fail("Failed to initialize Beads in project");
@@ -23242,9 +23341,9 @@ async function installBeads(projectDir) {
23242
23341
  }
23243
23342
  }
23244
23343
  async function createClaudeRules(projectDir) {
23245
- const claudeMdPath = join14(projectDir, "CLAUDE.md");
23246
- const agentsMdPath = join14(projectDir, "AGENTS.md");
23247
- if (existsSync14(claudeMdPath) || existsSync14(agentsMdPath)) {
23344
+ const claudeMdPath = join15(projectDir, "CLAUDE.md");
23345
+ const agentsMdPath = join15(projectDir, "AGENTS.md");
23346
+ if (existsSync15(claudeMdPath) || existsSync15(agentsMdPath)) {
23248
23347
  console.log(` ${icons.info} CLAUDE.md or AGENTS.md already exists`);
23249
23348
  return true;
23250
23349
  }
@@ -23278,7 +23377,7 @@ bun run build
23278
23377
  Use conventional commits: feat:, fix:, docs:, refactor:, test:, chore:
23279
23378
  `;
23280
23379
  try {
23281
- writeFileSync6(claudeMdPath, content);
23380
+ writeFileSync7(claudeMdPath, content);
23282
23381
  return true;
23283
23382
  } catch {
23284
23383
  return false;
@@ -23558,7 +23657,7 @@ var setupCommand = defineCommand2({
23558
23657
  });
23559
23658
 
23560
23659
  // src/commands/upgrade.ts
23561
- import { execSync as execSync4 } from "node:child_process";
23660
+ import { execSync as execSync5 } from "node:child_process";
23562
23661
  var CYAN = "\x1B[36m";
23563
23662
  var GREEN = "\x1B[32m";
23564
23663
  var YELLOW = "\x1B[33m";
@@ -23568,8 +23667,30 @@ var RESET = "\x1B[0m";
23568
23667
  var MAGENTA = "\x1B[35m";
23569
23668
  var CHANGELOG = [
23570
23669
  {
23571
- version: "0.4.0",
23670
+ version: "0.5.1",
23671
+ date: "2026-02-14",
23672
+ title: "Smart Project Onboarding",
23673
+ highlights: [
23674
+ "First scan detects your project and suggests relevant skills",
23675
+ "Auto-installs find-skills to discover more tools later",
23676
+ "Analyzes README to recommend skills for your tech stack",
23677
+ "Detects your AI agent (Claude Code, OpenCode, Cursor)"
23678
+ ]
23679
+ },
23680
+ {
23681
+ version: "0.5.0",
23572
23682
  date: "2026-02-14",
23683
+ title: "Interactive-First CLI",
23684
+ highlights: [
23685
+ "No more flags to remember - commands guide you through options",
23686
+ "Just run nb scan or nb report and follow the prompts",
23687
+ "Public link is now the first option for report sharing",
23688
+ "CI/scripts still supported via --json flag"
23689
+ ]
23690
+ },
23691
+ {
23692
+ version: "0.4.0",
23693
+ date: "2026-02-13",
23573
23694
  title: "AI Work Visibility",
23574
23695
  highlights: [
23575
23696
  "New: Effort breakdown shows generation/review/correction/integration time",
@@ -23604,7 +23725,7 @@ var CHANGELOG = [
23604
23725
  title: "Reliable Recommendations",
23605
23726
  highlights: [
23606
23727
  "Fixed skills.sh format - skills now install correctly",
23607
- "New: nb doctor --verify-recommendations to validate install commands"
23728
+ "Improved recommendation validation before release"
23608
23729
  ]
23609
23730
  },
23610
23731
  {
@@ -23872,7 +23993,7 @@ var upgradeCommand = defineCommand2({
23872
23993
  console.log(`${DIM} Checking for updates...${RESET}`);
23873
23994
  let latestVersion;
23874
23995
  try {
23875
- latestVersion = execSync4("npm view nairon-bench version", { encoding: "utf-8" }).trim();
23996
+ latestVersion = execSync5("npm view nairon-bench version", { encoding: "utf-8" }).trim();
23876
23997
  } catch {
23877
23998
  console.log(` ${YELLOW}⚠${RESET} Could not check npm registry`);
23878
23999
  console.log(` ${DIM}Run 'bun add -g nairon-bench@latest' manually${RESET}`);
@@ -23899,7 +24020,7 @@ var upgradeCommand = defineCommand2({
23899
24020
  console.log(` ${CYAN}↓${RESET} Upgrading to ${GREEN}v${latestVersion}${RESET}...`);
23900
24021
  console.log();
23901
24022
  try {
23902
- execSync4("npm install -g nairon-bench@latest", {
24023
+ execSync5("npm install -g nairon-bench@latest", {
23903
24024
  stdio: "inherit",
23904
24025
  env: { ...process.env, npm_config_fund: "false" }
23905
24026
  });
@@ -23917,7 +24038,7 @@ var upgradeCommand = defineCommand2({
23917
24038
  });
23918
24039
  function getCurrentVersion() {
23919
24040
  try {
23920
- const output = execSync4("nb --version 2>/dev/null || nairon-bench --version 2>/dev/null", {
24041
+ const output = execSync5("nb --version 2>/dev/null || nairon-bench --version 2>/dev/null", {
23921
24042
  encoding: "utf-8",
23922
24043
  stdio: ["pipe", "pipe", "pipe"]
23923
24044
  });
@@ -24006,7 +24127,7 @@ function showFullChangelog() {
24006
24127
  // package.json
24007
24128
  var package_default = {
24008
24129
  name: "nairon-bench",
24009
- version: "0.4.0",
24130
+ version: "0.5.1",
24010
24131
  description: "AI workflow benchmarking CLI",
24011
24132
  type: "module",
24012
24133
  bin: {
@@ -24014,7 +24135,8 @@ var package_default = {
24014
24135
  nb: "./dist/index.js"
24015
24136
  },
24016
24137
  files: [
24017
- "dist"
24138
+ "dist",
24139
+ "postinstall.js"
24018
24140
  ],
24019
24141
  repository: {
24020
24142
  type: "git",
@@ -24042,7 +24164,8 @@ var package_default = {
24042
24164
  "test:e2e": "vitest run tests/e2e/",
24043
24165
  "test:watch": "vitest",
24044
24166
  clean: "rm -rf dist",
24045
- prepublishOnly: "bun run build"
24167
+ prepublishOnly: "bun run build",
24168
+ postinstall: "node postinstall.js || true"
24046
24169
  },
24047
24170
  dependencies: {
24048
24171
  "cli-boxes": "^4.0.1",
@@ -24065,9 +24188,9 @@ var versionCommand = defineCommand2({
24065
24188
 
24066
24189
  // src/commands/init.ts
24067
24190
  init_dist();
24068
- import { existsSync as existsSync15 } from "node:fs";
24069
- import { homedir as homedir12, platform as platform3, arch } from "node:os";
24070
- import { join as join15 } from "node:path";
24191
+ import { existsSync as existsSync16 } from "node:fs";
24192
+ import { homedir as homedir13, platform as platform3, arch } from "node:os";
24193
+ import { join as join16 } from "node:path";
24071
24194
  init_client();
24072
24195
 
24073
24196
  // src/lib/github-auth.ts
@@ -24237,8 +24360,8 @@ var initCommand = defineCommand2({
24237
24360
  consola.start("Step 2/6: Detecting AI agents...");
24238
24361
  const detectedAgents = [];
24239
24362
  for (const [agent, pathTemplate] of Object.entries(AGENT_LOG_PATHS)) {
24240
- const resolvedPath = pathTemplate.replace("~", homedir12());
24241
- if (existsSync15(resolvedPath)) {
24363
+ const resolvedPath = pathTemplate.replace("~", homedir13());
24364
+ if (existsSync16(resolvedPath)) {
24242
24365
  detectedAgents.push(agent);
24243
24366
  consola.success(` Found: ${agent}`);
24244
24367
  }
@@ -24341,7 +24464,7 @@ var initCommand = defineCommand2({
24341
24464
  { name: "playwright", configs: ["playwright.config.ts", "playwright.config.js"] }
24342
24465
  ];
24343
24466
  for (const fw of testFrameworks) {
24344
- if (fw.configs.some((c3) => existsSync15(join15(process.cwd(), c3)))) {
24467
+ if (fw.configs.some((c3) => existsSync16(join16(process.cwd(), c3)))) {
24345
24468
  toolStack.testing.push(fw.name);
24346
24469
  }
24347
24470
  }
@@ -24410,13 +24533,13 @@ var initCommand = defineCommand2({
24410
24533
  });
24411
24534
  function detectPackageManager() {
24412
24535
  const cwd = process.cwd();
24413
- if (existsSync15(join15(cwd, "bun.lock")) || existsSync15(join15(cwd, "bun.lockb")))
24536
+ if (existsSync16(join16(cwd, "bun.lock")) || existsSync16(join16(cwd, "bun.lockb")))
24414
24537
  return "bun";
24415
- if (existsSync15(join15(cwd, "pnpm-lock.yaml")))
24538
+ if (existsSync16(join16(cwd, "pnpm-lock.yaml")))
24416
24539
  return "pnpm";
24417
- if (existsSync15(join15(cwd, "yarn.lock")))
24540
+ if (existsSync16(join16(cwd, "yarn.lock")))
24418
24541
  return "yarn";
24419
- if (existsSync15(join15(cwd, "package-lock.json")))
24542
+ if (existsSync16(join16(cwd, "package-lock.json")))
24420
24543
  return "npm";
24421
24544
  return;
24422
24545
  }
@@ -24431,15 +24554,15 @@ var CLAUDE_CODE_SKILLS = [
24431
24554
  ];
24432
24555
  function detectEasyWins(detectedAgents) {
24433
24556
  const wins = [];
24434
- const home = homedir12();
24557
+ const home = homedir13();
24435
24558
  const hasClaudeCode = detectedAgents.some((a2) => a2.toLowerCase().includes("claude") || a2.toLowerCase().includes("opencode"));
24436
- const claudeConfigDir = join15(home, ".claude");
24437
- const hasClaudeConfig = existsSync15(claudeConfigDir);
24559
+ const claudeConfigDir = join16(home, ".claude");
24560
+ const hasClaudeConfig = existsSync16(claudeConfigDir);
24438
24561
  if (hasClaudeCode || hasClaudeConfig) {
24439
- const skillsDir = join15(home, ".claude", "skills");
24562
+ const skillsDir = join16(home, ".claude", "skills");
24440
24563
  for (const skill of CLAUDE_CODE_SKILLS) {
24441
- const skillPath = join15(skillsDir, skill.id);
24442
- const isInstalled = existsSync15(skillPath);
24564
+ const skillPath = join16(skillsDir, skill.id);
24565
+ const isInstalled = existsSync16(skillPath);
24443
24566
  if (!isInstalled) {
24444
24567
  wins.push({
24445
24568
  icon: skill.icon,
@@ -24455,12 +24578,12 @@ function detectEasyWins(detectedAgents) {
24455
24578
  // src/commands/onboard.ts
24456
24579
  init_dist();
24457
24580
  init_client();
24458
- import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync7, readdirSync as readdirSync9 } from "node:fs";
24459
- import { join as join17 } from "node:path";
24581
+ import { existsSync as existsSync18, mkdirSync as mkdirSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync8, readdirSync as readdirSync9 } from "node:fs";
24582
+ import { join as join18 } from "node:path";
24460
24583
 
24461
24584
  // src/lib/project-context-detector.ts
24462
- import { existsSync as existsSync16, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "node:fs";
24463
- import { join as join16, basename as basename4 } from "node:path";
24585
+ import { existsSync as existsSync17, readFileSync as readFileSync15, readdirSync as readdirSync8 } from "node:fs";
24586
+ import { join as join17, basename as basename4 } from "node:path";
24464
24587
  function detectProjectContext(projectDir) {
24465
24588
  const projectName = basename4(projectDir);
24466
24589
  const projectId = generateProjectId(projectDir);
@@ -24496,11 +24619,11 @@ function detectTechStack(projectDir) {
24496
24619
  };
24497
24620
  }
24498
24621
  function readPackageJson(projectDir) {
24499
- const pkgPath = join16(projectDir, "package.json");
24500
- if (!existsSync16(pkgPath))
24622
+ const pkgPath = join17(projectDir, "package.json");
24623
+ if (!existsSync17(pkgPath))
24501
24624
  return {};
24502
24625
  try {
24503
- return JSON.parse(readFileSync14(pkgPath, "utf-8"));
24626
+ return JSON.parse(readFileSync15(pkgPath, "utf-8"));
24504
24627
  } catch {
24505
24628
  return {};
24506
24629
  }
@@ -24521,34 +24644,34 @@ function detectFramework(deps) {
24521
24644
  return null;
24522
24645
  }
24523
24646
  function detectMetaFramework(deps, projectDir) {
24524
- if (deps.next || existsSync16(join16(projectDir, "next.config.js")) || existsSync16(join16(projectDir, "next.config.mjs")))
24647
+ if (deps.next || existsSync17(join17(projectDir, "next.config.js")) || existsSync17(join17(projectDir, "next.config.mjs")))
24525
24648
  return "next";
24526
- if (deps.nuxt || existsSync16(join16(projectDir, "nuxt.config.ts")))
24649
+ if (deps.nuxt || existsSync17(join17(projectDir, "nuxt.config.ts")))
24527
24650
  return "nuxt";
24528
24651
  if (deps["@sveltejs/kit"])
24529
24652
  return "sveltekit";
24530
- if (deps.astro || existsSync16(join16(projectDir, "astro.config.mjs")))
24653
+ if (deps.astro || existsSync17(join17(projectDir, "astro.config.mjs")))
24531
24654
  return "astro";
24532
24655
  if (deps["@remix-run/react"])
24533
24656
  return "remix";
24534
24657
  if (deps.gatsby)
24535
24658
  return "gatsby";
24536
- if (deps.vite || existsSync16(join16(projectDir, "vite.config.ts")))
24659
+ if (deps.vite || existsSync17(join17(projectDir, "vite.config.ts")))
24537
24660
  return "vite";
24538
24661
  return null;
24539
24662
  }
24540
24663
  function detectRuntime(projectDir) {
24541
- if (existsSync16(join16(projectDir, "bun.lockb")) || existsSync16(join16(projectDir, "bun.lock")))
24664
+ if (existsSync17(join17(projectDir, "bun.lockb")) || existsSync17(join17(projectDir, "bun.lock")))
24542
24665
  return "bun";
24543
- if (existsSync16(join16(projectDir, "deno.json")) || existsSync16(join16(projectDir, "deno.lock")))
24666
+ if (existsSync17(join17(projectDir, "deno.json")) || existsSync17(join17(projectDir, "deno.lock")))
24544
24667
  return "deno";
24545
24668
  return "node";
24546
24669
  }
24547
24670
  function detectLanguage(projectDir) {
24548
- if (existsSync16(join16(projectDir, "tsconfig.json")))
24671
+ if (existsSync17(join17(projectDir, "tsconfig.json")))
24549
24672
  return "typescript";
24550
- const srcDir = join16(projectDir, "src");
24551
- if (existsSync16(srcDir)) {
24673
+ const srcDir = join17(projectDir, "src");
24674
+ if (existsSync17(srcDir)) {
24552
24675
  try {
24553
24676
  const files = readdirSync8(srcDir);
24554
24677
  if (files.some((f3) => f3.endsWith(".ts") || f3.endsWith(".tsx")))
@@ -24559,7 +24682,7 @@ function detectLanguage(projectDir) {
24559
24682
  }
24560
24683
  function detectStyling(deps, projectDir) {
24561
24684
  const tools = [];
24562
- if (deps.tailwindcss || existsSync16(join16(projectDir, "tailwind.config.js")) || existsSync16(join16(projectDir, "tailwind.config.ts")))
24685
+ if (deps.tailwindcss || existsSync17(join17(projectDir, "tailwind.config.js")) || existsSync17(join17(projectDir, "tailwind.config.ts")))
24563
24686
  tools.push("tailwind");
24564
24687
  if (deps["styled-components"])
24565
24688
  tools.push("styled-components");
@@ -24604,13 +24727,13 @@ function detectORM(deps) {
24604
24727
  }
24605
24728
  function detectTesting(deps, projectDir) {
24606
24729
  const tools = [];
24607
- if (deps.vitest || existsSync16(join16(projectDir, "vitest.config.ts")))
24730
+ if (deps.vitest || existsSync17(join17(projectDir, "vitest.config.ts")))
24608
24731
  tools.push("vitest");
24609
- if (deps.jest || existsSync16(join16(projectDir, "jest.config.js")))
24732
+ if (deps.jest || existsSync17(join17(projectDir, "jest.config.js")))
24610
24733
  tools.push("jest");
24611
- if (deps["@playwright/test"] || existsSync16(join16(projectDir, "playwright.config.ts")))
24734
+ if (deps["@playwright/test"] || existsSync17(join17(projectDir, "playwright.config.ts")))
24612
24735
  tools.push("playwright");
24613
- if (deps.cypress || existsSync16(join16(projectDir, "cypress.config.ts")))
24736
+ if (deps.cypress || existsSync17(join17(projectDir, "cypress.config.ts")))
24614
24737
  tools.push("cypress");
24615
24738
  if (deps["@testing-library/react"] || deps["@testing-library/vue"])
24616
24739
  tools.push("testing-library");
@@ -24642,7 +24765,7 @@ function detectBuildTools(deps, projectDir) {
24642
24765
  tools.push("esbuild");
24643
24766
  if (deps.webpack)
24644
24767
  tools.push("webpack");
24645
- if (deps.turbo || existsSync16(join16(projectDir, "turbo.json")))
24768
+ if (deps.turbo || existsSync17(join17(projectDir, "turbo.json")))
24646
24769
  tools.push("turborepo");
24647
24770
  if (deps.tsup)
24648
24771
  tools.push("tsup");
@@ -24652,17 +24775,17 @@ function detectBuildTools(deps, projectDir) {
24652
24775
  }
24653
24776
  function detectDeployment(projectDir) {
24654
24777
  const tools = [];
24655
- if (existsSync16(join16(projectDir, "vercel.json")) || existsSync16(join16(projectDir, ".vercel")))
24778
+ if (existsSync17(join17(projectDir, "vercel.json")) || existsSync17(join17(projectDir, ".vercel")))
24656
24779
  tools.push("vercel");
24657
- if (existsSync16(join16(projectDir, "netlify.toml")))
24780
+ if (existsSync17(join17(projectDir, "netlify.toml")))
24658
24781
  tools.push("netlify");
24659
- if (existsSync16(join16(projectDir, "fly.toml")))
24782
+ if (existsSync17(join17(projectDir, "fly.toml")))
24660
24783
  tools.push("fly");
24661
- if (existsSync16(join16(projectDir, "railway.json")))
24784
+ if (existsSync17(join17(projectDir, "railway.json")))
24662
24785
  tools.push("railway");
24663
- if (existsSync16(join16(projectDir, "Dockerfile")))
24786
+ if (existsSync17(join17(projectDir, "Dockerfile")))
24664
24787
  tools.push("docker");
24665
- if (existsSync16(join16(projectDir, ".github", "workflows")))
24788
+ if (existsSync17(join17(projectDir, ".github", "workflows")))
24666
24789
  tools.push("github-actions");
24667
24790
  return tools;
24668
24791
  }
@@ -24685,20 +24808,20 @@ function detectAIML(deps) {
24685
24808
  return tools;
24686
24809
  }
24687
24810
  function inferProjectType(projectDir, techStack) {
24688
- if (existsSync16(join16(projectDir, "packages")) || existsSync16(join16(projectDir, "apps"))) {
24811
+ if (existsSync17(join17(projectDir, "packages")) || existsSync17(join17(projectDir, "apps"))) {
24689
24812
  return "monorepo";
24690
24813
  }
24691
24814
  const pkg = readPackageJson(projectDir);
24692
- if (pkg.bin || existsSync16(join16(projectDir, "src", "cli.ts")) || existsSync16(join16(projectDir, "src", "index.ts"))) {
24815
+ if (pkg.bin || existsSync17(join17(projectDir, "src", "cli.ts")) || existsSync17(join17(projectDir, "src", "index.ts"))) {
24693
24816
  const hasBin = !!pkg.bin;
24694
- const hasCommands = existsSync16(join16(projectDir, "src", "commands"));
24817
+ const hasCommands = existsSync17(join17(projectDir, "src", "commands"));
24695
24818
  if (hasBin || hasCommands)
24696
24819
  return "cli";
24697
24820
  }
24698
24821
  if (!techStack.framework && !techStack.metaFramework && pkg.main) {
24699
24822
  return "library";
24700
24823
  }
24701
- if (existsSync16(join16(projectDir, "app.json")) || existsSync16(join16(projectDir, "expo"))) {
24824
+ if (existsSync17(join17(projectDir, "app.json")) || existsSync17(join17(projectDir, "expo"))) {
24702
24825
  return "mobile";
24703
24826
  }
24704
24827
  if (techStack.database.length > 0 && !techStack.framework) {
@@ -24715,10 +24838,10 @@ function extractDescription(projectDir) {
24715
24838
  return pkg.description;
24716
24839
  const readmePaths = ["README.md", "readme.md", "Readme.md"];
24717
24840
  for (const readme of readmePaths) {
24718
- const path = join16(projectDir, readme);
24719
- if (existsSync16(path)) {
24841
+ const path = join17(projectDir, readme);
24842
+ if (existsSync17(path)) {
24720
24843
  try {
24721
- const content = readFileSync14(path, "utf-8");
24844
+ const content = readFileSync15(path, "utf-8");
24722
24845
  const lines = content.split(`
24723
24846
  `);
24724
24847
  let foundTitle = false;
@@ -26136,9 +26259,9 @@ var onboardCommand = defineCommand2({
26136
26259
  },
26137
26260
  async run({ args }) {
26138
26261
  const projectPath = process.cwd();
26139
- const contextPath = join17(projectPath, ".nairon", "context.json");
26140
- if (existsSync17(contextPath) && !args.force) {
26141
- const existing = JSON.parse(readFileSync15(contextPath, "utf-8"));
26262
+ const contextPath = join18(projectPath, ".nairon", "context.json");
26263
+ if (existsSync18(contextPath) && !args.force) {
26264
+ const existing = JSON.parse(readFileSync16(contextPath, "utf-8"));
26142
26265
  consola.info("Project already onboarded. Use --force to re-run.");
26143
26266
  consola.info(`Business: ${existing.businessContext?.domain || existing.businessContext?.slice?.(0, 50) || "Not set"}...`);
26144
26267
  consola.info(`Stack: ${formatTechStackSummary(existing)}`);
@@ -26286,11 +26409,11 @@ var onboardCommand = defineCommand2({
26286
26409
  createdAt: new Date().toISOString(),
26287
26410
  updatedAt: new Date().toISOString()
26288
26411
  };
26289
- const naironDir = join17(projectPath, ".nairon");
26290
- if (!existsSync17(naironDir)) {
26291
- mkdirSync6(naironDir, { recursive: true });
26412
+ const naironDir = join18(projectPath, ".nairon");
26413
+ if (!existsSync18(naironDir)) {
26414
+ mkdirSync7(naironDir, { recursive: true });
26292
26415
  }
26293
- writeFileSync7(contextPath, JSON.stringify(fullContext, null, 2));
26416
+ writeFileSync8(contextPath, JSON.stringify(fullContext, null, 2));
26294
26417
  consola.success(`Saved to ${contextPath}`);
26295
26418
  if (isSupermemoryConfigured()) {
26296
26419
  consola.start("Storing in Supermemory...");
@@ -26378,51 +26501,51 @@ function formatTechStackSummary(context) {
26378
26501
  function detectInstalledTools(projectPath) {
26379
26502
  const tools = [];
26380
26503
  const home = process.env.HOME || "";
26381
- const userClaudeJson = join17(home, ".claude.json");
26382
- if (existsSync17(userClaudeJson)) {
26504
+ const userClaudeJson = join18(home, ".claude.json");
26505
+ if (existsSync18(userClaudeJson)) {
26383
26506
  try {
26384
- const config = JSON.parse(readFileSync15(userClaudeJson, "utf-8"));
26507
+ const config = JSON.parse(readFileSync16(userClaudeJson, "utf-8"));
26385
26508
  if (config.mcpServers) {
26386
26509
  tools.push(...Object.keys(config.mcpServers));
26387
26510
  }
26388
26511
  } catch {}
26389
26512
  }
26390
- const claudeDesktopConfig = join17(home, ".claude", "claude_desktop_config.json");
26391
- if (existsSync17(claudeDesktopConfig)) {
26513
+ const claudeDesktopConfig = join18(home, ".claude", "claude_desktop_config.json");
26514
+ if (existsSync18(claudeDesktopConfig)) {
26392
26515
  try {
26393
- const config = JSON.parse(readFileSync15(claudeDesktopConfig, "utf-8"));
26516
+ const config = JSON.parse(readFileSync16(claudeDesktopConfig, "utf-8"));
26394
26517
  if (config.mcpServers) {
26395
26518
  tools.push(...Object.keys(config.mcpServers));
26396
26519
  }
26397
26520
  } catch {}
26398
26521
  }
26399
- const projectMcpJson = join17(projectPath, ".mcp.json");
26400
- if (existsSync17(projectMcpJson)) {
26522
+ const projectMcpJson = join18(projectPath, ".mcp.json");
26523
+ if (existsSync18(projectMcpJson)) {
26401
26524
  try {
26402
- const config = JSON.parse(readFileSync15(projectMcpJson, "utf-8"));
26525
+ const config = JSON.parse(readFileSync16(projectMcpJson, "utf-8"));
26403
26526
  if (config.mcpServers) {
26404
26527
  tools.push(...Object.keys(config.mcpServers));
26405
26528
  }
26406
26529
  } catch {}
26407
26530
  }
26408
- const projectClaudeConfig = join17(projectPath, ".claude", "settings.json");
26409
- if (existsSync17(projectClaudeConfig)) {
26531
+ const projectClaudeConfig = join18(projectPath, ".claude", "settings.json");
26532
+ if (existsSync18(projectClaudeConfig)) {
26410
26533
  try {
26411
- const config = JSON.parse(readFileSync15(projectClaudeConfig, "utf-8"));
26534
+ const config = JSON.parse(readFileSync16(projectClaudeConfig, "utf-8"));
26412
26535
  if (config.mcpServers) {
26413
26536
  tools.push(...Object.keys(config.mcpServers));
26414
26537
  }
26415
26538
  } catch {}
26416
26539
  }
26417
- const skillsDir = join17(home, ".config", "opencode", "skills");
26418
- if (existsSync17(skillsDir)) {
26540
+ const skillsDir = join18(home, ".config", "opencode", "skills");
26541
+ if (existsSync18(skillsDir)) {
26419
26542
  try {
26420
26543
  const skills = readdirSync9(skillsDir);
26421
26544
  tools.push(...skills.filter((s2) => !s2.startsWith(".")));
26422
26545
  } catch {}
26423
26546
  }
26424
- const agentsSkillsDir = join17(home, ".agents", "skills");
26425
- if (existsSync17(agentsSkillsDir)) {
26547
+ const agentsSkillsDir = join18(home, ".agents", "skills");
26548
+ if (existsSync18(agentsSkillsDir)) {
26426
26549
  try {
26427
26550
  const skills = readdirSync9(agentsSkillsDir);
26428
26551
  tools.push(...skills.filter((s2) => !s2.startsWith(".")));
@@ -26432,11 +26555,11 @@ function detectInstalledTools(projectPath) {
26432
26555
  }
26433
26556
  function detectPrimaryAgent() {
26434
26557
  const home = process.env.HOME || "";
26435
- if (existsSync17(join17(home, ".claude")))
26558
+ if (existsSync18(join18(home, ".claude")))
26436
26559
  return "claude-code";
26437
- if (existsSync17(join17(home, ".cursor")))
26560
+ if (existsSync18(join18(home, ".cursor")))
26438
26561
  return "cursor";
26439
- if (existsSync17(join17(home, ".config", "opencode")))
26562
+ if (existsSync18(join18(home, ".config", "opencode")))
26440
26563
  return "opencode";
26441
26564
  return;
26442
26565
  }
@@ -26531,7 +26654,7 @@ function formatBytes(bytes) {
26531
26654
  // package.json
26532
26655
  var package_default2 = {
26533
26656
  name: "nairon-bench",
26534
- version: "0.4.0",
26657
+ version: "0.5.1",
26535
26658
  description: "AI workflow benchmarking CLI",
26536
26659
  type: "module",
26537
26660
  bin: {
@@ -26539,7 +26662,8 @@ var package_default2 = {
26539
26662
  nb: "./dist/index.js"
26540
26663
  },
26541
26664
  files: [
26542
- "dist"
26665
+ "dist",
26666
+ "postinstall.js"
26543
26667
  ],
26544
26668
  repository: {
26545
26669
  type: "git",
@@ -26567,7 +26691,8 @@ var package_default2 = {
26567
26691
  "test:e2e": "vitest run tests/e2e/",
26568
26692
  "test:watch": "vitest",
26569
26693
  clean: "rm -rf dist",
26570
- prepublishOnly: "bun run build"
26694
+ prepublishOnly: "bun run build",
26695
+ postinstall: "node postinstall.js || true"
26571
26696
  },
26572
26697
  dependencies: {
26573
26698
  "cli-boxes": "^4.0.1",
@@ -26632,7 +26757,7 @@ var hasVersion = args.includes("--version");
26632
26757
  if (!hasSubcommand && !hasHelp && !hasVersion && args.length === 0) {
26633
26758
  showBanner();
26634
26759
  } else {
26635
- const main = defineCommand({
26760
+ const cmd = defineCommand({
26636
26761
  meta: {
26637
26762
  name: "nairon-bench",
26638
26763
  version: VERSION2,
@@ -26650,5 +26775,5 @@ if (!hasSubcommand && !hasHelp && !hasVersion && args.length === 0) {
26650
26775
  cache: cacheCommand
26651
26776
  }
26652
26777
  });
26653
- runMain(main);
26778
+ runMain(cmd);
26654
26779
  }