nairon-bench 0.0.39 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1068 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14181,6 +14181,11 @@ var scanCommand = defineCommand2({
14181
14181
  description: "Disable session caching (slower but ensures fresh data)",
14182
14182
  default: false
14183
14183
  },
14184
+ "no-recommend": {
14185
+ type: "boolean",
14186
+ description: "Skip showing recommendations after scan",
14187
+ default: false
14188
+ },
14184
14189
  watch: {
14185
14190
  type: "boolean",
14186
14191
  alias: "w",
@@ -14360,6 +14365,9 @@ var scanCommand = defineCommand2({
14360
14365
  const reportPath = generateReport(reportDir, score, git, agents, tests, args.since, sdlcAnalysis, scanCost, analysis);
14361
14366
  console.log(` ${icons.success} Report saved: ${colors2.dim(reportPath)}`);
14362
14367
  }
14368
+ if (!args["no-recommend"]) {
14369
+ await showQuickRecommendations(analysis, agents, tests, projectDir);
14370
+ }
14363
14371
  }
14364
14372
  if (args.offline) {
14365
14373
  if (!silent) {
@@ -14656,6 +14664,67 @@ function formatTokensPlain(tokens) {
14656
14664
  return `${Math.round(tokens / 1000)}K`;
14657
14665
  return tokens.toString();
14658
14666
  }
14667
+ async function showQuickRecommendations(analysis, agents, tests, projectDir) {
14668
+ const suggestions = [];
14669
+ if (analysis.sessionPatterns.memoryLossCount > 2 || analysis.sessionPatterns.contextCompactions > 3) {
14670
+ suggestions.push({
14671
+ icon: "\uD83E\uDDE0",
14672
+ text: "Frequent context loss detected",
14673
+ tool: "Supermemory"
14674
+ });
14675
+ }
14676
+ if (!analysis.environment.hasContext7 && analysis.sessionPatterns.toolFailures > 2) {
14677
+ suggestions.push({
14678
+ icon: "\uD83D\uDCDA",
14679
+ text: "API hallucinations likely",
14680
+ tool: "Context7"
14681
+ });
14682
+ }
14683
+ if (!tests || tests.totalTests === 0) {
14684
+ suggestions.push({
14685
+ icon: "\uD83E\uDDEA",
14686
+ text: "No tests detected",
14687
+ tool: "Agentic TDD workflow"
14688
+ });
14689
+ }
14690
+ if (analysis.sessionPatterns.longBackForth > 2) {
14691
+ suggestions.push({
14692
+ icon: "\uD83D\uDCAC",
14693
+ text: "Excessive back-and-forth in sessions",
14694
+ tool: "Skills/structured prompts"
14695
+ });
14696
+ }
14697
+ if (!analysis.environment.hasBeads && agents && agents.totalSessions > 5) {
14698
+ suggestions.push({
14699
+ icon: "\uD83D\uDCCB",
14700
+ text: "No persistent task tracking",
14701
+ tool: "Beads"
14702
+ });
14703
+ }
14704
+ if (analysis.efficiency.tokensWastedWeekly > 50000) {
14705
+ suggestions.push({
14706
+ icon: "\uD83D\uDCB0",
14707
+ text: `~${formatTokensPlain(analysis.efficiency.tokensWastedWeekly)} tokens wasted/week`,
14708
+ tool: "Token optimization"
14709
+ });
14710
+ }
14711
+ if (suggestions.length === 0) {
14712
+ return;
14713
+ }
14714
+ const toShow = suggestions.slice(0, 3);
14715
+ console.log();
14716
+ console.log(` ${colors2.bold(colors2.primary("Quick Recommendations"))}`);
14717
+ console.log(colors2.dim(" " + "─".repeat(40)));
14718
+ console.log();
14719
+ for (const suggestion of toShow) {
14720
+ const toolHint = suggestion.tool ? colors2.success(` → ${suggestion.tool}`) : "";
14721
+ console.log(` ${suggestion.icon} ${suggestion.text}${toolHint}`);
14722
+ await sleep(100);
14723
+ }
14724
+ console.log();
14725
+ console.log(` ${colors2.dim(`Run ${colors2.primary("nb recommend")} for personalized tool suggestions`)}`);
14726
+ console.log(` ${colors2.dim(`Use ${colors2.primary("nb scan --no-recommend")} to skip this`)}`);
14727
+ }
14659
14728
 
14660
14729
  // src/commands/report.ts
14661
14730
  init_dist();
@@ -19719,6 +19788,30 @@ async function runHackathonReport(args) {
19719
19788
  consola.start(`Generating AI-nativeness report for ${harnessLabel} (last ${hoursAgo} hours)...`);
19720
19789
  try {
19721
19790
  const report = await generateHackathonReport(projectDir, since, until, harness);
19791
+ if (report.summary.totalSessions === 0 && harness && harness !== "all") {
19792
+ consola.warn(`No sessions found for harness "${harness}".`);
19793
+ consola.info(`Try running with --harness all to include sessions from all AI tools.`);
19794
+ consola.info(`Or check that your AI tool's session logs exist in the expected location.`);
19795
+ const allReport = await generateHackathonReport(projectDir, since, until, "all");
19796
+ if (allReport.summary.totalSessions > 0) {
19797
+ consola.info(`Found ${allReport.summary.totalSessions} sessions from other harnesses.`);
19798
+ const useAll = await consola.prompt("Use all sessions instead?", {
19799
+ type: "confirm",
19800
+ initial: true
19801
+ });
19802
+ if (useAll) {
19803
+ const newReport = allReport;
19804
+ if (args.publish) {
19805
+ await publishReport(newReport);
19806
+ return;
19807
+ }
19808
+ const format3 = args.format || "terminal";
19809
+ const output2 = format3 === "md" || format3 === "markdown" ? formatReportAsMarkdown(newReport) : format3 === "json" ? formatReportAsJSON(newReport) : formatReportAsTerminal(newReport);
19810
+ console.log(output2);
19811
+ return;
19812
+ }
19813
+ }
19814
+ }
19722
19815
  if (args.publish) {
19723
19816
  await publishReport(report);
19724
19817
  return;
@@ -19764,6 +19857,21 @@ async function publishReport(report) {
19764
19857
  consola.info("Alternatively, use `nb report --hackathon --format md` for local markdown.");
19765
19858
  return;
19766
19859
  }
19860
+ if (report.summary.totalSessions === 0) {
19861
+ consola.warn("Publishing report with 0 sessions detected.");
19862
+ consola.info("This may result in incomplete data. Common causes:");
19863
+ consola.info(" - Selected harness doesn't match your AI tool");
19864
+ consola.info(" - Session logs not found in expected location");
19865
+ consola.info(" - Time range doesn't include recent sessions");
19866
+ const proceed = await consola.prompt("Continue publishing anyway?", {
19867
+ type: "confirm",
19868
+ initial: false
19869
+ });
19870
+ if (!proceed) {
19871
+ consola.info("Aborted. Try running with --harness all");
19872
+ return;
19873
+ }
19874
+ }
19767
19875
  consola.start("Publishing report...");
19768
19876
  const markdownContent = formatReportAsMarkdown(report);
19769
19877
  const basePayload = {
@@ -23590,10 +23698,937 @@ var setupCommand = defineCommand2({
23590
23698
  }
23591
23699
  }
23592
23700
  });
23701
+
23702
+ // src/commands/onboard.ts
23703
+ init_dist();
23704
+ init_client();
23705
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
23706
+ import { join as join15, basename as basename5 } from "node:path";
23707
+ var TECH_PATTERNS = {
23708
+ typescript: ["tsconfig.json", "*.ts", "*.tsx"],
23709
+ javascript: ["*.js", "*.jsx", "*.mjs"],
23710
+ python: ["*.py", "requirements.txt", "pyproject.toml", "setup.py"],
23711
+ rust: ["Cargo.toml", "*.rs"],
23712
+ go: ["go.mod", "*.go"],
23713
+ nextjs: ["next.config.js", "next.config.ts", "next.config.mjs"],
23714
+ react: ["package.json:react"],
23715
+ vue: ["vue.config.js", "nuxt.config.ts", "package.json:vue"],
23716
+ svelte: ["svelte.config.js", "package.json:svelte"],
23717
+ astro: ["astro.config.mjs", "astro.config.ts"],
23718
+ express: ["package.json:express"],
23719
+ fastify: ["package.json:fastify"],
23720
+ hono: ["package.json:hono"],
23721
+ django: ["manage.py", "settings.py"],
23722
+ flask: ["app.py:Flask"],
23723
+ prisma: ["prisma/schema.prisma"],
23724
+ drizzle: ["drizzle.config.ts"],
23725
+ convex: ["convex/"],
23726
+ supabase: [".supabase/", "supabase/"],
23727
+ vitest: ["vitest.config.ts", "vitest.config.js"],
23728
+ jest: ["jest.config.ts", "jest.config.js"],
23729
+ playwright: ["playwright.config.ts"],
23730
+ langchain: ["package.json:langchain", "requirements.txt:langchain"],
23731
+ openai: ["package.json:openai", "requirements.txt:openai"],
23732
+ anthropic: ["package.json:@anthropic-ai"]
23733
+ };
23734
+ var PROJECT_TYPES = {
23735
+ "Web App": ["nextjs", "react", "vue", "svelte", "astro"],
23736
+ "API/Backend": ["express", "fastify", "hono", "django", "flask"],
23737
+ "CLI Tool": ["citty", "commander", "yargs"],
23738
+ Library: ["tsup", "rollup", "esbuild"],
23739
+ "Mobile App": ["react-native", "expo"],
23740
+ "AI/ML": ["langchain", "openai", "anthropic"]
23741
+ };
23742
+ var onboardCommand = defineCommand2({
23743
+ meta: {
23744
+ name: "onboard",
23745
+ description: "Set up project context for personalized AI workflow recommendations"
23746
+ },
23747
+ args: {
23748
+ force: {
23749
+ type: "boolean",
23750
+ description: "Re-run onboarding even if already configured",
23751
+ default: false
23752
+ }
23753
+ },
23754
+ async run({ args }) {
23755
+ const projectPath = process.cwd();
23756
+ const contextPath = join15(projectPath, ".nairon", "context.json");
23757
+ if (existsSync15(contextPath) && !args.force) {
23758
+ const existing = JSON.parse(readFileSync12(contextPath, "utf-8"));
23759
+ consola.info("Project already onboarded. Use --force to re-run.");
23760
+ consola.info(`Business: ${existing.businessContext?.slice(0, 50)}...`);
23761
+ consola.info(`Stack: ${existing.techStack?.join(", ")}`);
23762
+ return;
23763
+ }
23764
+ consola.log("");
23765
+ consola.box({
23766
+ title: "Project Onboarding",
23767
+ message: [
23768
+ "Let's set up your project context for personalized recommendations.",
23769
+ "",
23770
+ "This helps Nairon Bench suggest tools and workflows",
23771
+ "that are specifically relevant to YOUR project."
23772
+ ].join(`
23773
+ `)
23774
+ });
23775
+ consola.log("");
23776
+ consola.start("Step 1/4: Detecting tech stack...");
23777
+ const { techStack, languages } = detectTechStack(projectPath);
23778
+ const projectType = detectProjectType(techStack);
23779
+ consola.success(`Detected: ${techStack.slice(0, 5).join(", ")}${techStack.length > 5 ? ` +${techStack.length - 5} more` : ""}`);
23780
+ consola.info(`Project type: ${projectType}`);
23781
+ const confirmStack = await consola.prompt("Is this correct?", {
23782
+ type: "confirm",
23783
+ initial: true
23784
+ });
23785
+ let finalStack = techStack;
23786
+ if (confirmStack === false) {
23787
+ const stackInput = await consola.prompt("Enter your tech stack (comma-separated):", {
23788
+ type: "text",
23789
+ default: techStack.join(", ")
23790
+ });
23791
+ if (typeof stackInput === "string") {
23792
+ finalStack = stackInput.split(",").map((s2) => s2.trim()).filter(Boolean);
23793
+ }
23794
+ }
23795
+ consola.log("");
23796
+ consola.info("Step 2/4: Tell us about your project");
23797
+ const businessContext = await consola.prompt("What are you building? (e.g., 'B2B SaaS for invoice automation', 'Personal blog with AI features')", {
23798
+ type: "text",
23799
+ placeholder: "Describe your project in 1-2 sentences"
23800
+ });
23801
+ if (typeof businessContext === "symbol" || !businessContext) {
23802
+ consola.warn("Onboarding cancelled.");
23803
+ return;
23804
+ }
23805
+ const targetUsers = await consola.prompt("Who are your target users? (e.g., 'SMB accountants', 'Developers', 'General consumers')", {
23806
+ type: "text",
23807
+ placeholder: "Describe your target audience"
23808
+ });
23809
+ if (typeof targetUsers === "symbol" || !targetUsers) {
23810
+ consola.warn("Onboarding cancelled.");
23811
+ return;
23812
+ }
23813
+ consola.log("");
23814
+ consola.info("Step 3/4: What are your biggest workflow challenges?");
23815
+ const painPointOptions = [
23816
+ { label: "Finding the right context/docs for AI", value: "context" },
23817
+ { label: "Long sessions that lose context", value: "session_length" },
23818
+ { label: "AI hallucinations / wrong code", value: "hallucinations" },
23819
+ { label: "Testing and debugging AI-generated code", value: "testing" },
23820
+ { label: "Prompt engineering / getting good outputs", value: "prompting" },
23821
+ { label: "Managing multiple AI tools", value: "tool_management" },
23822
+ { label: "Cost management / token efficiency", value: "cost" }
23823
+ ];
23824
+ const selectedPainPoints = await consola.prompt("Select your top challenges:", {
23825
+ type: "multiselect",
23826
+ options: painPointOptions
23827
+ });
23828
+ const painPoints = Array.isArray(selectedPainPoints) ? selectedPainPoints.map((p) => typeof p === "string" ? p : p.value) : [];
23829
+ consola.log("");
23830
+ consola.start("Step 4/4: Detecting installed AI tools...");
23831
+ const installedTools = detectInstalledTools(projectPath);
23832
+ consola.success(`Found: ${installedTools.length > 0 ? installedTools.join(", ") : "None detected"}`);
23833
+ const context = {
23834
+ projectPath,
23835
+ projectName: basename5(projectPath),
23836
+ businessContext,
23837
+ targetUsers,
23838
+ techStack: finalStack,
23839
+ detectedLanguages: languages,
23840
+ painPoints,
23841
+ installedTools,
23842
+ primaryAgent: detectPrimaryAgent(),
23843
+ createdAt: new Date().toISOString(),
23844
+ updatedAt: new Date().toISOString()
23845
+ };
23846
+ const naironDir = join15(projectPath, ".nairon");
23847
+ if (!existsSync15(naironDir)) {
23848
+ mkdirSync5(naironDir, { recursive: true });
23849
+ }
23850
+ writeFileSync7(contextPath, JSON.stringify(context, null, 2));
23851
+ consola.success(`Saved to ${contextPath}`);
23852
+ const client = getClient();
23853
+ const config = getConfig();
23854
+ if (client) {
23855
+ consola.start("Syncing to cloud...");
23856
+ try {
23857
+ await client.mutation(api.nightcrawler.saveProjectContext, {
23858
+ projectPath,
23859
+ businessContext: context.businessContext,
23860
+ targetUsers: context.targetUsers,
23861
+ techStack: context.techStack,
23862
+ detectedLanguages: context.detectedLanguages,
23863
+ painPoints: context.painPoints,
23864
+ installedTools: context.installedTools,
23865
+ ...context.primaryAgent ? { primaryAgent: context.primaryAgent } : {}
23866
+ });
23867
+ consola.success("Project context synced to cloud");
23868
+ } catch (err) {
23869
+ consola.warn(`Cloud sync failed: ${err instanceof Error ? err.message : String(err)}`);
23870
+ }
23871
+ }
23872
+ const recommendations = getQuickRecommendations(context);
23873
+ consola.log("");
23874
+ consola.box({
23875
+ title: "Onboarding Complete!",
23876
+ message: [
23877
+ `Project: ${context.projectName}`,
23878
+ `Type: ${projectType}`,
23879
+ `Stack: ${finalStack.slice(0, 4).join(", ")}`,
23880
+ `Pain points: ${painPoints.length} identified`,
23881
+ "",
23882
+ "Quick recommendations based on your profile:",
23883
+ ...recommendations.map((r3) => ` ${r3.icon} ${r3.title}`),
23884
+ "",
23885
+ "Run 'nb recommend' for detailed suggestions",
23886
+ "Run 'nb trending' to see what's hot in the community"
23887
+ ].join(`
23888
+ `)
23889
+ });
23890
+ }
23891
+ });
23892
+ function detectTechStack(projectPath) {
23893
+ const techStack = [];
23894
+ const languages = [];
23895
+ const pkgPath = join15(projectPath, "package.json");
23896
+ let pkgJson = {};
23897
+ if (existsSync15(pkgPath)) {
23898
+ try {
23899
+ pkgJson = JSON.parse(readFileSync12(pkgPath, "utf-8"));
23900
+ } catch {}
23901
+ }
23902
+ const allDeps = {
23903
+ ...pkgJson.dependencies,
23904
+ ...pkgJson.devDependencies
23905
+ };
23906
+ for (const [tech, patterns] of Object.entries(TECH_PATTERNS)) {
23907
+ for (const pattern of patterns) {
23908
+ if (pattern.startsWith("package.json:")) {
23909
+ const dep = pattern.replace("package.json:", "");
23910
+ if (allDeps[dep]) {
23911
+ techStack.push(tech);
23912
+ break;
23913
+ }
23914
+ } else if (pattern.includes("/")) {
23915
+ if (existsSync15(join15(projectPath, pattern))) {
23916
+ techStack.push(tech);
23917
+ break;
23918
+ }
23919
+ } else if (!pattern.includes("*")) {
23920
+ if (existsSync15(join15(projectPath, pattern))) {
23921
+ techStack.push(tech);
23922
+ break;
23923
+ }
23924
+ }
23925
+ }
23926
+ }
23927
+ if (techStack.includes("typescript") || existsSync15(join15(projectPath, "tsconfig.json"))) {
23928
+ languages.push("TypeScript");
23929
+ }
23930
+ if (techStack.includes("javascript") || pkgJson.name) {
23931
+ languages.push("JavaScript");
23932
+ }
23933
+ if (techStack.includes("python") || existsSync15(join15(projectPath, "requirements.txt"))) {
23934
+ languages.push("Python");
23935
+ }
23936
+ if (techStack.includes("rust"))
23937
+ languages.push("Rust");
23938
+ if (techStack.includes("go"))
23939
+ languages.push("Go");
23940
+ return { techStack, languages };
23941
+ }
23942
+ function detectProjectType(techStack) {
23943
+ for (const [type, techs] of Object.entries(PROJECT_TYPES)) {
23944
+ if (techs.some((t2) => techStack.includes(t2))) {
23945
+ return type;
23946
+ }
23947
+ }
23948
+ return "General";
23949
+ }
23950
+ function detectInstalledTools(projectPath) {
23951
+ const tools = [];
23952
+ const home = process.env.HOME || "";
23953
+ const claudeConfigPath = join15(home, ".claude", "claude_desktop_config.json");
23954
+ if (existsSync15(claudeConfigPath)) {
23955
+ try {
23956
+ const config = JSON.parse(readFileSync12(claudeConfigPath, "utf-8"));
23957
+ if (config.mcpServers) {
23958
+ tools.push(...Object.keys(config.mcpServers));
23959
+ }
23960
+ } catch {}
23961
+ }
23962
+ const projectClaudeConfig = join15(projectPath, ".claude", "settings.json");
23963
+ if (existsSync15(projectClaudeConfig)) {
23964
+ try {
23965
+ const config = JSON.parse(readFileSync12(projectClaudeConfig, "utf-8"));
23966
+ if (config.mcpServers) {
23967
+ tools.push(...Object.keys(config.mcpServers));
23968
+ }
23969
+ } catch {}
23970
+ }
23971
+ const skillsDir = join15(home, ".claude", "skills");
23972
+ if (existsSync15(skillsDir)) {
23973
+ try {
23974
+ const { readdirSync: readdirSync8 } = __require("fs");
23975
+ const skills = readdirSync8(skillsDir);
23976
+ tools.push(...skills.filter((s2) => !s2.startsWith(".")));
23977
+ } catch {}
23978
+ }
23979
+ return [...new Set(tools)];
23980
+ }
23981
+ function detectPrimaryAgent() {
23982
+ const home = process.env.HOME || "";
23983
+ if (existsSync15(join15(home, ".claude")))
23984
+ return "claude-code";
23985
+ if (existsSync15(join15(home, ".cursor")))
23986
+ return "cursor";
23987
+ if (existsSync15(join15(home, ".config", "opencode")))
23988
+ return "opencode";
23989
+ return;
23990
+ }
23991
+ function getQuickRecommendations(context) {
23992
+ const recs = [];
23993
+ if (context.painPoints.includes("context") && !context.installedTools.includes("context7")) {
23994
+ recs.push({ icon: "\uD83D\uDCDA", title: "Context7 MCP - Better library docs in context" });
23995
+ }
23996
+ if (context.painPoints.includes("session_length") && !context.installedTools.includes("supermemory")) {
23997
+ recs.push({ icon: "\uD83E\uDDE0", title: "Supermemory - Persistent memory across sessions" });
23998
+ }
23999
+ if (context.painPoints.includes("testing")) {
24000
+ recs.push({ icon: "\uD83E\uDDEA", title: "Agentic TDD workflow - AI-assisted test-driven dev" });
24001
+ }
24002
+ if (context.painPoints.includes("cost")) {
24003
+ recs.push({ icon: "\uD83D\uDCB0", title: "OpenRouter - Route to cheapest model per task" });
24004
+ }
24005
+ if (context.painPoints.includes("prompting")) {
24006
+ recs.push({ icon: "✨", title: "Structured prompting patterns from community" });
24007
+ }
24008
+ if (recs.length === 0) {
24009
+ recs.push({ icon: "\uD83D\uDD25", title: "Run 'nb trending' to see what's hot" });
24010
+ }
24011
+ return recs.slice(0, 3);
24012
+ }
24013
+
24014
+ // src/commands/trending.ts
24015
+ init_dist();
24016
+ init_client();
24017
+ var CYAN = "\x1B[36m";
24018
+ var YELLOW = "\x1B[33m";
24019
+ var GREEN = "\x1B[32m";
24020
+ var DIM = "\x1B[2m";
24021
+ var BOLD = "\x1B[1m";
24022
+ var RESET = "\x1B[0m";
24023
+ var MAGENTA = "\x1B[35m";
24024
+ var CATEGORY_ICONS = {
24025
+ announcement: "\uD83D\uDCE2",
24026
+ launch: "\uD83D\uDE80",
24027
+ usage_pattern: "\uD83D\uDCA1",
24028
+ tutorial: "\uD83D\uDCFA",
24029
+ tip: "⚡"
24030
+ };
24031
+ var CATEGORY_LABELS = {
24032
+ announcement: "ANNOUNCEMENTS",
24033
+ launch: "LAUNCHES",
24034
+ usage_pattern: "WORKFLOWS",
24035
+ tutorial: "TUTORIALS",
24036
+ tip: "TIPS"
24037
+ };
24038
+ var trendingCommand = defineCommand2({
24039
+ meta: {
24040
+ name: "trending",
24041
+ description: "See what's hot in the AI dev tools community"
24042
+ },
24043
+ args: {
24044
+ days: {
24045
+ type: "string",
24046
+ description: "Time range (1, 7, 30)",
24047
+ default: "7"
24048
+ },
24049
+ category: {
24050
+ type: "string",
24051
+ description: "Filter by category (announcement, launch, usage_pattern, tutorial, tip)"
24052
+ },
24053
+ limit: {
24054
+ type: "string",
24055
+ description: "Max results per category",
24056
+ default: "5"
24057
+ },
24058
+ tools: {
24059
+ type: "boolean",
24060
+ description: "Show top tools only",
24061
+ default: false
24062
+ }
24063
+ },
24064
+ async run({ args }) {
24065
+ const client = getClient();
24066
+ if (!client) {
24067
+ consola.error("Not connected to cloud. Run 'nb doctor' to check connection.");
24068
+ return;
24069
+ }
24070
+ const days = parseInt(args.days) || 7;
24071
+ const limit = parseInt(args.limit) || 5;
24072
+ consola.log("");
24073
+ console.log(`${YELLOW}\uD83D\uDD25 Hot Topics — Last ${days} Day${days > 1 ? "s" : ""}${RESET}`);
24074
+ console.log(`${DIM}${"─".repeat(50)}${RESET}`);
24075
+ try {
24076
+ if (args.tools) {
24077
+ await showTopTools(client, days, limit * 2);
24078
+ return;
24079
+ }
24080
+ const insights = await client.query(api.nightcrawler.getRecentInsights, {
24081
+ limit: 50,
24082
+ maxHypeScore: 70,
24083
+ ...args.category ? { category: args.category } : {}
24084
+ });
24085
+ if (insights.length === 0) {
24086
+ consola.log("");
24087
+ consola.warn("No trending insights found. The Nightcrawler may not have run yet.");
24088
+ consola.info("Run the Nightcrawler manually or wait for the scheduled job.");
24089
+ showFallbackMessage();
24090
+ return;
24091
+ }
24092
+ const byCategory = {};
24093
+ for (const insight of insights) {
24094
+ const cat = insight.category;
24095
+ if (!byCategory[cat]) {
24096
+ byCategory[cat] = [];
24097
+ }
24098
+ byCategory[cat].push(insight);
24099
+ }
24100
+ const categoryOrder = ["announcement", "launch", "usage_pattern", "tutorial", "tip"];
24101
+ for (const category of categoryOrder) {
24102
+ const categoryInsights = byCategory[category];
24103
+ if (!categoryInsights || categoryInsights.length === 0)
24104
+ continue;
24105
+ const icon = CATEGORY_ICONS[category] || "\uD83D\uDCCC";
24106
+ const label = CATEGORY_LABELS[category] || category.toUpperCase();
24107
+ consola.log("");
24108
+ console.log(`${BOLD}${icon} ${label}${RESET}`);
24109
+ for (const insight of categoryInsights.slice(0, limit)) {
24110
+ const likes = formatNumber2(insight.engagement.likes);
24111
+ const hasVideo = insight.hasVideo ? `${MAGENTA}▶${RESET} ` : "";
24112
+ const tools = insight.toolsMentioned.length > 0 ? `${DIM}[${insight.toolsMentioned.slice(0, 2).join(", ")}]${RESET}` : "";
24113
+ console.log(`${DIM}├─${RESET} ${hasVideo}${insight.keyInsight.slice(0, 60)}${insight.keyInsight.length > 60 ? "..." : ""}`);
24114
+ console.log(`${DIM}│ ${RESET}${GREEN}${likes}❤${RESET} @${insight.authorHandle} ${tools}`);
24115
+ }
24116
+ console.log(`${DIM}└─${RESET}`);
24117
+ }
24118
+ consola.log("");
24119
+ await showTopTools(client, days, 5);
24120
+ consola.log("");
24121
+ console.log(`${DIM}Run 'nb trending --tools' for detailed tool rankings${RESET}`);
24122
+ console.log(`${DIM}Run 'nb recommend' for personalized suggestions${RESET}`);
24123
+ } catch (err) {
24124
+ consola.error(`Failed to fetch trending: ${err instanceof Error ? err.message : String(err)}`);
24125
+ showFallbackMessage();
24126
+ }
24127
+ }
24128
+ });
24129
+ async function showTopTools(client, days, limit) {
24130
+ try {
24131
+ const trending = await client.query(api.nightcrawler.getTrendingTools, {
24132
+ days,
24133
+ limit
24134
+ });
24135
+ if (trending.length === 0) {
24136
+ console.log(`${DIM}No tool data available yet.${RESET}`);
24137
+ return;
24138
+ }
24139
+ console.log(`${BOLD}\uD83D\uDEE0️ TOP TOOLS${RESET}`);
24140
+ for (let i3 = 0;i3 < trending.length; i3++) {
24141
+ const tool = trending[i3];
24142
+ const rank = i3 + 1;
24143
+ const bar = createBar(tool.mentions, trending[0].mentions);
24144
+ const avgLikes = formatNumber2(tool.avgLikes);
24145
+ console.log(`${DIM}${rank}.${RESET} ${CYAN}${tool.tool}${RESET}`);
24146
+ console.log(` ${bar} ${tool.mentions} mentions · ${avgLikes} avg❤`);
24147
+ }
24148
+ } catch (err) {
24149
+ console.log(`${DIM}Could not fetch tool rankings.${RESET}`);
24150
+ }
24151
+ }
24152
+ function showFallbackMessage() {
24153
+ consola.log("");
24154
+ consola.box({
24155
+ title: "Nightcrawler Not Running",
24156
+ message: [
24157
+ "The trending data comes from the Nightcrawler background job",
24158
+ "which scrapes X/Twitter for high-engagement dev tool posts.",
24159
+ "",
24160
+ "To populate data manually, run:",
24161
+ " cd skills/x-research",
24162
+ ' bun run x-search.ts search "MCP server" --quick',
24163
+ "",
24164
+ "Or wait for the scheduled Nightcrawler job."
24165
+ ].join(`
24166
+ `)
24167
+ });
24168
+ }
24169
+ function formatNumber2(num) {
24170
+ if (num >= 1e6)
24171
+ return `${(num / 1e6).toFixed(1)}M`;
24172
+ if (num >= 1000)
24173
+ return `${(num / 1000).toFixed(1)}K`;
24174
+ return num.toString();
24175
+ }
24176
+ function createBar(value, max, width = 15) {
24177
+ const filled = Math.round(value / max * width);
24178
+ const empty = width - filled;
24179
+ return `${GREEN}${"█".repeat(filled)}${RESET}${DIM}${"░".repeat(empty)}${RESET}`;
24180
+ }
24181
+
24182
+ // src/commands/recommend.ts
24183
+ init_dist();
24184
+ init_client();
24185
+ import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
24186
+ import { join as join16 } from "node:path";
24187
+ var CYAN2 = "\x1B[36m";
24188
+ var YELLOW2 = "\x1B[33m";
24189
+ var GREEN2 = "\x1B[32m";
24190
+ var DIM2 = "\x1B[2m";
24191
+ var BOLD2 = "\x1B[1m";
24192
+ var RESET2 = "\x1B[0m";
24193
+ var MAGENTA2 = "\x1B[35m";
24194
+ var PAIN_POINT_TOOLS = {
24195
+ context: [
24196
+ { tool: "context7", reason: "Fetches up-to-date library docs directly into AI context", installCmd: "nb tools install context7" },
24197
+ { tool: "nia", reason: "Index and search external repos, docs, and packages" }
24198
+ ],
24199
+ session_length: [
24200
+ { tool: "supermemory", reason: "Persistent memory across sessions - never lose context", installCmd: "nb tools install supermemory" },
24201
+ { tool: "beads", reason: "Task tracking that persists between sessions" }
24202
+ ],
24203
+ hallucinations: [
24204
+ { tool: "context7", reason: "Real docs reduce hallucinations by 40%+", installCmd: "nb tools install context7" },
24205
+ { tool: "repoprompt", reason: "Better context building for accurate code generation" }
24206
+ ],
24207
+ testing: [
24208
+ { tool: "stagehand", reason: "AI-powered E2E testing with Playwright" },
24209
+ { tool: "agentic-tdd", reason: "Test-driven development workflow for AI coding" }
24210
+ ],
24211
+ prompting: [
24212
+ { tool: "skills", reason: "Pre-built prompt patterns for common tasks" },
24213
+ { tool: "structured-prompts", reason: "Templates that get better AI outputs" }
24214
+ ],
24215
+ tool_management: [
24216
+ { tool: "oh-my-opencode", reason: "Unified agent harness - one config for all tools" },
24217
+ { tool: "mcp-installer", reason: "Easy MCP server management" }
24218
+ ],
24219
+ cost: [
24220
+ { tool: "openrouter", reason: "Route to cheapest model per task", installCmd: "nb tools install openrouter" },
24221
+ { tool: "token-counter", reason: "Track and optimize token usage" }
24222
+ ]
24223
+ };
24224
+ var STACK_TOOLS = {
24225
+ nextjs: [{ tool: "vercel-mcp", reason: "Deploy and manage Vercel projects from AI" }],
24226
+ react: [{ tool: "storybook-mcp", reason: "AI-assisted component development" }],
24227
+ typescript: [{ tool: "typescript-mcp", reason: "Better type inference in AI context" }],
24228
+ prisma: [{ tool: "prisma-mcp", reason: "Database schema and queries in AI context" }],
24229
+ convex: [{ tool: "convex-mcp", reason: "Convex functions and schema in AI context" }],
24230
+ python: [{ tool: "python-mcp", reason: "Python environment and packages in AI context" }]
24231
+ };
24232
+ var recommendCommand = defineCommand2({
24233
+ meta: {
24234
+ name: "recommend",
24235
+ description: "Get personalized AI workflow recommendations based on your project"
24236
+ },
24237
+ args: {
24238
+ scan: {
24239
+ type: "boolean",
24240
+ description: "Re-analyze project before recommending",
24241
+ default: false
24242
+ },
24243
+ limit: {
24244
+ type: "string",
24245
+ description: "Max recommendations to show",
24246
+ default: "5"
24247
+ },
24248
+ all: {
24249
+ type: "boolean",
24250
+ description: "Show all recommendations (not just top)",
24251
+ default: false
24252
+ }
24253
+ },
24254
+ async run({ args }) {
24255
+ const projectPath = process.cwd();
24256
+ const contextPath = join16(projectPath, ".nairon", "context.json");
24257
+ const limit = parseInt(args.limit) || 5;
24258
+ consola.log("");
24259
+ console.log(`${YELLOW2}\uD83D\uDCCA Analyzing your project...${RESET2}`);
24260
+ let context = null;
24261
+ if (existsSync16(contextPath)) {
24262
+ try {
24263
+ context = JSON.parse(readFileSync13(contextPath, "utf-8"));
24264
+ console.log(`${DIM2}Loaded context from ${contextPath}${RESET2}`);
24265
+ } catch {}
24266
+ }
24267
+ if (!context) {
24268
+ consola.warn("No project context found. Run 'nb onboard' first for personalized recommendations.");
24269
+ consola.log("");
24270
+ await showGenericRecommendations();
24271
+ return;
24272
+ }
24273
+ const recommendations = [];
24274
+ for (const painPoint of context.painPoints) {
24275
+ const tools = PAIN_POINT_TOOLS[painPoint] || [];
24276
+ for (const tool of tools) {
24277
+ if (!context.installedTools.includes(tool.tool)) {
24278
+ recommendations.push({
24279
+ tool: tool.tool,
24280
+ reason: tool.reason,
24281
+ source: "pain_point",
24282
+ relevanceScore: 0.9,
24283
+ installCmd: tool.installCmd
24284
+ });
24285
+ }
24286
+ }
24287
+ }
24288
+ for (const tech of context.techStack) {
24289
+ const tools = STACK_TOOLS[tech.toLowerCase()] || [];
24290
+ for (const tool of tools) {
24291
+ if (!context.installedTools.includes(tool.tool)) {
24292
+ recommendations.push({
24293
+ tool: tool.tool,
24294
+ reason: tool.reason,
24295
+ source: "stack",
24296
+ relevanceScore: 0.7
24297
+ });
24298
+ }
24299
+ }
24300
+ }
24301
+ const client = getClient();
24302
+ if (client) {
24303
+ try {
24304
+ const trending = await client.query(api.nightcrawler.getRecommendationsForProject, {
24305
+ projectPath,
24306
+ limit: 10
24307
+ });
24308
+ for (const t2 of trending) {
24309
+ if (recommendations.some((r3) => r3.tool === t2.toolSlug))
24310
+ continue;
24311
+ if (context.installedTools.includes(t2.toolSlug))
24312
+ continue;
24313
+ recommendations.push({
24314
+ tool: t2.toolSlug,
24315
+ reason: t2.reason,
24316
+ source: "trending",
24317
+ relevanceScore: t2.relevanceScore,
24318
+ trendingMentions: t2.mentions
24319
+ });
24320
+ }
24321
+ } catch (err) {}
24322
+ }
24323
+ const seen = new Set;
24324
+ const uniqueRecs = recommendations.filter((r3) => {
24325
+ if (seen.has(r3.tool))
24326
+ return false;
24327
+ seen.add(r3.tool);
24328
+ return true;
24329
+ }).sort((a2, b2) => b2.relevanceScore - a2.relevanceScore);
24330
+ consola.log("");
24331
+ console.log(`${BOLD2}${CYAN2}RECOMMENDATIONS FOR ${context.projectName.toUpperCase()}${RESET2}`);
24332
+ console.log(`${DIM2}Based on: ${context.painPoints.length} pain points, ${context.techStack.length} stack items${RESET2}`);
24333
+ console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
24334
+ const toShow = args.all ? uniqueRecs : uniqueRecs.slice(0, limit);
24335
+ if (toShow.length === 0) {
24336
+ consola.success("Your setup looks great! No new recommendations at this time.");
24337
+ consola.log("");
24338
+ console.log(`${DIM2}Run 'nb trending' to see what's new in the community${RESET2}`);
24339
+ return;
24340
+ }
24341
+ for (let i3 = 0;i3 < toShow.length; i3++) {
24342
+ const rec = toShow[i3];
24343
+ const num = i3 + 1;
24344
+ const sourceIcon = getSourceIcon(rec.source);
24345
+ const trendingBadge = rec.trendingMentions ? `${MAGENTA2}↑${rec.trendingMentions}${RESET2} ` : "";
24346
+ consola.log("");
24347
+ console.log(`${BOLD2}${num}. ${CYAN2}${rec.tool}${RESET2} ${sourceIcon}`);
24348
+ console.log(` ${trendingBadge}${rec.reason}`);
24349
+ if (rec.installCmd) {
24350
+ console.log(` ${DIM2}Install: ${GREEN2}${rec.installCmd}${RESET2}`);
24351
+ }
24352
+ }
24353
+ consola.log("");
24354
+ console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
24355
+ const painPointRecs = toShow.filter((r3) => r3.source === "pain_point").length;
24356
+ const stackRecs = toShow.filter((r3) => r3.source === "stack").length;
24357
+ const trendingRecs = toShow.filter((r3) => r3.source === "trending").length;
24358
+ console.log(`${DIM2}${painPointRecs} for pain points · ${stackRecs} for your stack · ${trendingRecs} trending${RESET2}`);
24359
+ if (!args.all && uniqueRecs.length > limit) {
24360
+ console.log(`${DIM2}Run 'nb recommend --all' to see ${uniqueRecs.length - limit} more${RESET2}`);
24361
+ }
24362
+ if (context.installedTools.length > 0) {
24363
+ consola.log("");
24364
+ console.log(`${GREEN2}✓ Already installed:${RESET2} ${context.installedTools.slice(0, 5).join(", ")}${context.installedTools.length > 5 ? ` +${context.installedTools.length - 5} more` : ""}`);
24365
+ }
24366
+ }
24367
+ });
24368
+ async function showGenericRecommendations() {
24369
+ console.log(`${BOLD2}${CYAN2}TOP COMMUNITY RECOMMENDATIONS${RESET2}`);
24370
+ console.log(`${DIM2}Run 'nb onboard' for personalized suggestions${RESET2}`);
24371
+ console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
24372
+ consola.log("");
24373
+ const genericRecs = [
24374
+ { tool: "context7", reason: "Up-to-date library docs in AI context - reduces hallucinations" },
24375
+ { tool: "supermemory", reason: "Persistent memory across sessions" },
24376
+ { tool: "repoprompt", reason: "Better context building for code generation" },
24377
+ { tool: "beads", reason: "Task tracking that persists between sessions" },
24378
+ { tool: "stagehand", reason: "AI-powered E2E testing" }
24379
+ ];
24380
+ for (let i3 = 0;i3 < genericRecs.length; i3++) {
24381
+ const rec = genericRecs[i3];
24382
+ console.log(`${BOLD2}${i3 + 1}. ${CYAN2}${rec.tool}${RESET2}`);
24383
+ console.log(` ${rec.reason}`);
24384
+ consola.log("");
24385
+ }
24386
+ }
24387
+ function getSourceIcon(source) {
24388
+ switch (source) {
24389
+ case "pain_point":
24390
+ return `${YELLOW2}[pain point]${RESET2}`;
24391
+ case "stack":
24392
+ return `${GREEN2}[your stack]${RESET2}`;
24393
+ case "trending":
24394
+ return `${MAGENTA2}[trending]${RESET2}`;
24395
+ case "community":
24396
+ return `${DIM2}[community]${RESET2}`;
24397
+ default:
24398
+ return "";
24399
+ }
24400
+ }
24401
+
24402
+ // src/commands/upgrade.ts
24403
+ import { execSync as execSync5 } from "node:child_process";
24404
+ var CYAN3 = "\x1B[36m";
24405
+ var GREEN3 = "\x1B[32m";
24406
+ var YELLOW3 = "\x1B[33m";
24407
+ var DIM3 = "\x1B[2m";
24408
+ var BOLD3 = "\x1B[1m";
24409
+ var RESET3 = "\x1B[0m";
24410
+ var MAGENTA3 = "\x1B[35m";
24411
+ var CHANGELOG = [
24412
+ {
24413
+ version: "0.1.0",
24414
+ date: "2026-02-13",
24415
+ title: "Nightcrawler & Recommendations",
24416
+ highlights: [
24417
+ "nb onboard - Set up project context for personalized recommendations",
24418
+ "nb trending - See hot topics in AI dev tools community",
24419
+ "nb recommend - Get personalized tool suggestions",
24420
+ "Auto-recommendations after nb scan",
24421
+ "Background X/Twitter scraper with hype filter",
24422
+ "Cost-optimized scraping (~$35/month)"
24423
+ ]
24424
+ },
24425
+ {
24426
+ version: "0.0.40",
24427
+ date: "2026-02-10",
24428
+ title: "Frustration Detection & Smarter Reports",
24429
+ highlights: [
24430
+ "Detect frustration patterns in AI sessions",
24431
+ "Improved report generation",
24432
+ "Better token waste analysis"
24433
+ ]
24434
+ },
24435
+ {
24436
+ version: "0.0.34",
24437
+ date: "2026-02-08",
24438
+ title: "Accurate Token & Commit Tracking",
24439
+ highlights: [
24440
+ "Fixed token counting accuracy",
24441
+ "Improved commit attribution",
24442
+ "Better session correlation"
24443
+ ]
24444
+ },
24445
+ {
24446
+ version: "0.0.31",
24447
+ date: "2026-02-08",
24448
+ title: "Multi-Agent Support & Report Sharing",
24449
+ highlights: [
24450
+ "Support for multiple AI agents",
24451
+ "Shareable reports with nb publish",
24452
+ "Improved dashboard"
24453
+ ]
24454
+ },
24455
+ {
24456
+ version: "0.0.26",
24457
+ date: "2026-02-07",
24458
+ title: "Hackathon Reports & Publish Links",
24459
+ highlights: [
24460
+ "nb report for hackathon submissions",
24461
+ "Publishable report links",
24462
+ "SDLC phase analysis"
24463
+ ]
24464
+ },
24465
+ {
24466
+ version: "0.0.22",
24467
+ date: "2026-02-07",
24468
+ title: "Multi-Agent & Team Features",
24469
+ highlights: [
24470
+ "Team workflow support",
24471
+ "Multi-agent session tracking",
24472
+ "Improved cost analysis"
24473
+ ]
24474
+ },
24475
+ {
24476
+ version: "0.0.18",
24477
+ date: "2026-02-07",
24478
+ title: "CI/CD & Analytics",
24479
+ highlights: [
24480
+ "CI/CD integration support",
24481
+ "Enhanced analytics dashboard",
24482
+ "Badge system"
24483
+ ]
24484
+ }
24485
+ ];
24486
+ var upgradeCommand = defineCommand2({
24487
+ meta: {
24488
+ name: "upgrade",
24489
+ description: "Upgrade nairon-bench to the latest version and see what's new"
24490
+ },
24491
+ args: {
24492
+ check: {
24493
+ type: "boolean",
24494
+ description: "Only check for updates without installing",
24495
+ default: false
24496
+ },
24497
+ changelog: {
24498
+ type: "boolean",
24499
+ description: "Show full changelog",
24500
+ default: false
24501
+ }
24502
+ },
24503
+ async run({ args }) {
24504
+ const currentVersion = getCurrentVersion();
24505
+ console.log();
24506
+ console.log(`${BOLD3}${CYAN3} nairon-bench upgrade${RESET3}`);
24507
+ console.log(`${DIM3} ${"─".repeat(40)}${RESET3}`);
24508
+ console.log();
24509
+ if (args.changelog) {
24510
+ showFullChangelog();
24511
+ return;
24512
+ }
24513
+ console.log(`${DIM3} Checking for updates...${RESET3}`);
24514
+ let latestVersion;
24515
+ try {
24516
+ latestVersion = execSync5("npm view nairon-bench version", { encoding: "utf-8" }).trim();
24517
+ } catch {
24518
+ console.log(` ${YELLOW3}⚠${RESET3} Could not check npm registry`);
24519
+ console.log(` ${DIM3}Run 'bun upgrade -g nairon-bench' manually${RESET3}`);
24520
+ return;
24521
+ }
24522
+ console.log();
24523
+ console.log(` ${DIM3}Current:${RESET3} ${currentVersion}`);
24524
+ console.log(` ${DIM3}Latest:${RESET3} ${latestVersion}`);
24525
+ console.log();
24526
+ if (compareVersions(currentVersion, latestVersion) >= 0) {
24527
+ console.log(` ${GREEN3}✓${RESET3} You're on the latest version!`);
24528
+ console.log();
24529
+ console.log(` ${DIM3}Run 'nb upgrade --changelog' to see past releases${RESET3}`);
24530
+ return;
24531
+ }
24532
+ const newReleases = getNewReleases(currentVersion, latestVersion);
24533
+ if (newReleases.length > 0) {
24534
+ showWhatsNew(newReleases);
24535
+ }
24536
+ if (args.check) {
24537
+ console.log();
24538
+ console.log(` ${DIM3}Run 'nb upgrade' to install${RESET3}`);
24539
+ return;
24540
+ }
24541
+ console.log();
24542
+ console.log(`${DIM3} ${"─".repeat(40)}${RESET3}`);
24543
+ console.log(` ${CYAN3}↓${RESET3} Upgrading to ${GREEN3}v${latestVersion}${RESET3}...`);
24544
+ console.log();
24545
+ try {
24546
+ try {
24547
+ execSync5("bun upgrade -g nairon-bench", { stdio: "inherit" });
24548
+ } catch {
24549
+ execSync5("npm install -g nairon-bench@latest", { stdio: "inherit" });
24550
+ }
24551
+ console.log();
24552
+ console.log(` ${GREEN3}✓${RESET3} Upgraded to ${GREEN3}v${latestVersion}${RESET3}`);
24553
+ console.log();
24554
+ console.log(` ${DIM3}Run 'nb doctor' to verify installation${RESET3}`);
24555
+ } catch (err) {
24556
+ console.log();
24557
+ console.log(` ${YELLOW3}⚠${RESET3} Upgrade failed`);
24558
+ console.log(` ${DIM3}Try running manually:${RESET3}`);
24559
+ console.log(` ${CYAN3}bun upgrade -g nairon-bench${RESET3}`);
24560
+ console.log(` ${DIM3}or${RESET3}`);
24561
+ console.log(` ${CYAN3}npm install -g nairon-bench@latest${RESET3}`);
24562
+ }
24563
+ }
24564
+ });
24565
+ function getCurrentVersion() {
24566
+ try {
24567
+ const output = execSync5("nb --version 2>/dev/null || nairon-bench --version 2>/dev/null", {
24568
+ encoding: "utf-8",
24569
+ stdio: ["pipe", "pipe", "pipe"]
24570
+ });
24571
+ const match = output.match(/v?(\d+\.\d+\.\d+)/);
24572
+ return match ? match[1] : "0.0.0";
24573
+ } catch {
24574
+ return "0.0.0";
24575
+ }
24576
+ }
24577
+ function compareVersions(a2, b2) {
24578
+ const partsA = a2.split(".").map(Number);
24579
+ const partsB = b2.split(".").map(Number);
24580
+ for (let i3 = 0;i3 < 3; i3++) {
24581
+ const diff = (partsA[i3] || 0) - (partsB[i3] || 0);
24582
+ if (diff !== 0)
24583
+ return diff;
24584
+ }
24585
+ return 0;
24586
+ }
24587
+ function getNewReleases(currentVersion, latestVersion) {
24588
+ return CHANGELOG.filter((entry) => {
24589
+ return compareVersions(entry.version, currentVersion) > 0 && compareVersions(entry.version, latestVersion) <= 0;
24590
+ });
24591
+ }
24592
+ function showWhatsNew(releases) {
24593
+ console.log(` ${BOLD3}${MAGENTA3}What's New${RESET3}`);
24594
+ console.log();
24595
+ for (const release of releases) {
24596
+ const isMajor = release.version.endsWith(".0") && !release.version.startsWith("0.0.");
24597
+ const isMinor = release.version.split(".")[1] !== "0" && release.version.endsWith(".0");
24598
+ const versionColor = isMajor ? GREEN3 : isMinor ? CYAN3 : DIM3;
24599
+ console.log(` ${versionColor}${BOLD3}v${release.version}${RESET3} ${DIM3}(${release.date})${RESET3}`);
24600
+ console.log(` ${release.title}`);
24601
+ console.log();
24602
+ for (const highlight of release.highlights.slice(0, 4)) {
24603
+ console.log(` ${GREEN3}•${RESET3} ${highlight}`);
24604
+ }
24605
+ if (release.highlights.length > 4) {
24606
+ console.log(` ${DIM3}+ ${release.highlights.length - 4} more improvements${RESET3}`);
24607
+ }
24608
+ console.log();
24609
+ }
24610
+ }
24611
+ function showFullChangelog() {
24612
+ console.log(` ${BOLD3}${MAGENTA3}Full Changelog${RESET3}`);
24613
+ console.log();
24614
+ for (const release of CHANGELOG) {
24615
+ console.log(` ${CYAN3}${BOLD3}v${release.version}${RESET3} ${DIM3}(${release.date})${RESET3}`);
24616
+ console.log(` ${release.title}`);
24617
+ console.log();
24618
+ for (const highlight of release.highlights) {
24619
+ console.log(` ${GREEN3}•${RESET3} ${highlight}`);
24620
+ }
24621
+ console.log();
24622
+ console.log(` ${DIM3}${"─".repeat(38)}${RESET3}`);
24623
+ console.log();
24624
+ }
24625
+ console.log(` ${DIM3}See full history at:${RESET3}`);
24626
+ console.log(` ${CYAN3}https://nairon.ai/changelog${RESET3}`);
24627
+ }
23593
24628
  // package.json
23594
24629
  var package_default = {
23595
24630
  name: "nairon-bench",
23596
- version: "0.0.39",
24631
+ version: "0.1.0",
23597
24632
  description: "AI workflow benchmarking CLI",
23598
24633
  type: "module",
23599
24634
  bin: {
@@ -23638,52 +24673,52 @@ var package_default = {
23638
24673
  };
23639
24674
 
23640
24675
  // src/lib/mascot.ts
23641
- var CYAN = "\x1B[36m";
24676
+ var CYAN4 = "\x1B[36m";
23642
24677
  var WHITE = "\x1B[97m";
23643
- var DIM = "\x1B[2m";
23644
- var BOLD = "\x1B[1m";
23645
- var RESET = "\x1B[0m";
24678
+ var DIM4 = "\x1B[2m";
24679
+ var BOLD4 = "\x1B[1m";
24680
+ var RESET4 = "\x1B[0m";
23646
24681
  function renderMascot(version2, cwd) {
23647
24682
  const projectName = cwd ? cwd.split("/").pop() || cwd : "";
23648
- const projectLine = projectName ? ` ${DIM}${projectName}${RESET}` : "";
24683
+ const projectLine = projectName ? ` ${DIM4}${projectName}${RESET4}` : "";
23649
24684
  return `
23650
- ${WHITE} ▗▄▄▄▄▄▄▄▄▖${RESET}
23651
- ${WHITE} ▐${CYAN}█▀▀▀▀▀▀█${WHITE}▌${RESET} ${BOLD}NBench${RESET} ${DIM}v${version2}${RESET}
23652
- ${WHITE} ▐${CYAN}█ ${WHITE}▲${CYAN} █${WHITE}▌${RESET} ${DIM}Benchmark AI workflows${RESET}
23653
- ${WHITE} ▐${CYAN}█▄▄▄▄▄▄█${WHITE}▌${RESET}${projectLine}
23654
- ${WHITE} ▝▀▀▀▀▀▀▀▀▘${RESET}
24685
+ ${WHITE} ▗▄▄▄▄▄▄▄▄▖${RESET4}
24686
+ ${WHITE} ▐${CYAN4}█▀▀▀▀▀▀█${WHITE}▌${RESET4} ${BOLD4}NBench${RESET4} ${DIM4}v${version2}${RESET4}
24687
+ ${WHITE} ▐${CYAN4}█ ${WHITE}▲${CYAN4} █${WHITE}▌${RESET4} ${DIM4}Benchmark AI workflows${RESET4}
24688
+ ${WHITE} ▐${CYAN4}█▄▄▄▄▄▄█${WHITE}▌${RESET4}${projectLine}
24689
+ ${WHITE} ▝▀▀▀▀▀▀▀▀▘${RESET4}
23655
24690
  `;
23656
24691
  }
23657
24692
 
23658
24693
  // src/index.ts
23659
24694
  var VERSION = package_default.version;
23660
- var YELLOW = "\x1B[33m";
23661
- var DIM2 = "\x1B[2m";
23662
- var BOLD2 = "\x1B[1m";
23663
- var RESET2 = "\x1B[0m";
24695
+ var YELLOW4 = "\x1B[33m";
24696
+ var DIM5 = "\x1B[2m";
24697
+ var BOLD5 = "\x1B[1m";
24698
+ var RESET5 = "\x1B[0m";
23664
24699
  function showBanner() {
23665
24700
  const log = (msg) => process.stdout.write(msg + `
23666
24701
  `);
23667
- const GREEN = "\x1B[32m";
23668
- const CYAN2 = "\x1B[36m";
24702
+ const GREEN4 = "\x1B[32m";
24703
+ const CYAN5 = "\x1B[36m";
23669
24704
  log(renderMascot(VERSION, process.cwd()));
23670
- log(`${YELLOW}\u2501\u2501\u2501 Quick Start \u2501\u2501\u2501${RESET2}`);
24705
+ log(`${YELLOW4}\u2501\u2501\u2501 Quick Start \u2501\u2501\u2501${RESET5}`);
23671
24706
  log("");
23672
- log(` ${GREEN}1.${RESET2} ${BOLD2}nb init${RESET2} Set up your profile (first time only)`);
23673
- log(` ${GREEN}2.${RESET2} ${BOLD2}nb scan${RESET2} Analyze your AI coding sessions`);
23674
- log(` ${GREEN}3.${RESET2} ${BOLD2}nb report --publish${RESET2} Generate & share your report`);
24707
+ log(` ${GREEN4}1.${RESET5} ${BOLD5}nb init${RESET5} Set up your profile (first time only)`);
24708
+ log(` ${GREEN4}2.${RESET5} ${BOLD5}nb scan${RESET5} Analyze your AI coding sessions`);
24709
+ log(` ${GREEN4}3.${RESET5} ${BOLD5}nb report --publish${RESET5} Generate & share your report`);
23675
24710
  log("");
23676
- log(`${DIM2}\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510${RESET2}`);
23677
- log(`${DIM2}\u2502${RESET2} ${CYAN2}How often?${RESET2} ${DIM2}\u2502${RESET2}`);
23678
- log(`${DIM2}\u2502${RESET2} \u2022 Run ${BOLD2}nb scan${RESET2} after coding sessions ${DIM2}\u2502${RESET2}`);
23679
- log(`${DIM2}\u2502${RESET2} \u2022 Run ${BOLD2}nb report${RESET2} weekly to track progress ${DIM2}\u2502${RESET2}`);
23680
- log(`${DIM2}\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518${RESET2}`);
24711
+ log(`${DIM5}\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510${RESET5}`);
24712
+ log(`${DIM5}\u2502${RESET5} ${CYAN5}How often?${RESET5} ${DIM5}\u2502${RESET5}`);
24713
+ log(`${DIM5}\u2502${RESET5} \u2022 Run ${BOLD5}nb scan${RESET5} after coding sessions ${DIM5}\u2502${RESET5}`);
24714
+ log(`${DIM5}\u2502${RESET5} \u2022 Run ${BOLD5}nb report${RESET5} weekly to track progress ${DIM5}\u2502${RESET5}`);
24715
+ log(`${DIM5}\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518${RESET5}`);
23681
24716
  log("");
23682
- log(`${DIM2}Run ${BOLD2}nb --help${RESET2}${DIM2} for all commands${RESET2}`);
24717
+ log(`${DIM5}Run ${BOLD5}nb --help${RESET5}${DIM5} for all commands${RESET5}`);
23683
24718
  log("");
23684
24719
  }
23685
24720
  var args = process.argv.slice(2);
23686
- var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges", "cache", "setup"];
24721
+ var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges", "cache", "setup", "onboard", "trending", "recommend"];
23687
24722
  var hasSubcommand = args.some((arg) => subcommands.includes(arg));
23688
24723
  var hasHelp = args.includes("--help") || args.includes("-h");
23689
24724
  var hasVersion = args.includes("--version");
@@ -23712,7 +24747,11 @@ if (!hasSubcommand && !hasHelp && !hasVersion && args.length === 0) {
23712
24747
  pr: prCommand,
23713
24748
  badges: badgesCommand,
23714
24749
  cache: cacheCommand,
23715
- setup: setupCommand
24750
+ setup: setupCommand,
24751
+ onboard: onboardCommand,
24752
+ trending: trendingCommand,
24753
+ recommend: recommendCommand,
24754
+ upgrade: upgradeCommand
23716
24755
  }
23717
24756
  });
23718
24757
  runMain(main);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nairon-bench",
3
- "version": "0.0.39",
3
+ "version": "0.1.0",
4
4
  "description": "AI workflow benchmarking CLI",
5
5
  "type": "module",
6
6
  "bin": {