nairon-bench 0.0.40 → 0.2.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.
- package/dist/index.js +1227 -29
- 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();
|
|
@@ -23629,10 +23698,1135 @@ var setupCommand = defineCommand2({
|
|
|
23629
23698
|
}
|
|
23630
23699
|
}
|
|
23631
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
|
+
import { execSync as execSync5 } from "node:child_process";
|
|
24188
|
+
var CYAN2 = "\x1B[36m";
|
|
24189
|
+
var YELLOW2 = "\x1B[33m";
|
|
24190
|
+
var GREEN2 = "\x1B[32m";
|
|
24191
|
+
var RED = "\x1B[31m";
|
|
24192
|
+
var DIM2 = "\x1B[2m";
|
|
24193
|
+
var BOLD2 = "\x1B[1m";
|
|
24194
|
+
var RESET2 = "\x1B[0m";
|
|
24195
|
+
var MAGENTA2 = "\x1B[35m";
|
|
24196
|
+
var SKILLS_SH_MAPPINGS = {
|
|
24197
|
+
testing: [
|
|
24198
|
+
{ skill: "obra/superpowers/test-driven-development", name: "Test-Driven Development", reason: "Agentic TDD workflow for writing tests first", installs: 8400 },
|
|
24199
|
+
{ skill: "anthropics/skills/webapp-testing", name: "Web App Testing", reason: "E2E testing patterns for web applications", installs: 9400 }
|
|
24200
|
+
],
|
|
24201
|
+
debugging: [
|
|
24202
|
+
{ skill: "obra/superpowers/systematic-debugging", name: "Systematic Debugging", reason: "Structured approach to finding and fixing bugs", installs: 10100 }
|
|
24203
|
+
],
|
|
24204
|
+
planning: [
|
|
24205
|
+
{ skill: "obra/superpowers/writing-plans", name: "Writing Plans", reason: "Better planning before implementation", installs: 8700 },
|
|
24206
|
+
{ skill: "obra/superpowers/executing-plans", name: "Executing Plans", reason: "Follow through on plans systematically", installs: 7700 }
|
|
24207
|
+
],
|
|
24208
|
+
code_review: [
|
|
24209
|
+
{ skill: "obra/superpowers/requesting-code-review", name: "Requesting Code Review", reason: "Get better feedback on your code", installs: 7100 },
|
|
24210
|
+
{ skill: "obra/superpowers/receiving-code-review", name: "Receiving Code Review", reason: "Process and apply code review feedback", installs: 5900 }
|
|
24211
|
+
],
|
|
24212
|
+
browser_testing: [
|
|
24213
|
+
{ skill: "vercel-labs/agent-browser/agent-browser", name: "Agent Browser", reason: "Browser automation for AI agents", installs: 33600 },
|
|
24214
|
+
{ skill: "browser-use/browser-use/browser-use", name: "Browser Use", reason: "Let AI control the browser", installs: 28500 }
|
|
24215
|
+
],
|
|
24216
|
+
frontend: [
|
|
24217
|
+
{ skill: "anthropics/skills/frontend-design", name: "Frontend Design", reason: "Better UI/UX patterns for AI-generated code", installs: 65500 },
|
|
24218
|
+
{ skill: "vercel-labs/agent-skills/web-design-guidelines", name: "Web Design Guidelines", reason: "Design best practices", installs: 96100 }
|
|
24219
|
+
],
|
|
24220
|
+
react: [
|
|
24221
|
+
{ skill: "vercel-labs/agent-skills/vercel-react-best-practices", name: "React Best Practices", reason: "Production-ready React patterns", installs: 128100 }
|
|
24222
|
+
],
|
|
24223
|
+
nextjs: [
|
|
24224
|
+
{ skill: "vercel-labs/next-skills/next-best-practices", name: "Next.js Best Practices", reason: "App router patterns and optimizations", installs: 11200 }
|
|
24225
|
+
],
|
|
24226
|
+
supabase: [
|
|
24227
|
+
{ skill: "supabase/agent-skills/supabase-postgres-best-practices", name: "Supabase Best Practices", reason: "Database and auth patterns", installs: 16700 }
|
|
24228
|
+
],
|
|
24229
|
+
expo: [
|
|
24230
|
+
{ skill: "expo/skills/building-native-ui", name: "Building Native UI", reason: "React Native UI patterns", installs: 9500 }
|
|
24231
|
+
],
|
|
24232
|
+
parallel_agents: [
|
|
24233
|
+
{ skill: "obra/superpowers/dispatching-parallel-agents", name: "Parallel Agents", reason: "Run multiple agents for faster work", installs: 5600 },
|
|
24234
|
+
{ skill: "obra/superpowers/subagent-driven-development", name: "Subagent Development", reason: "Delegate work to specialized agents", installs: 6400 }
|
|
24235
|
+
],
|
|
24236
|
+
brainstorming: [
|
|
24237
|
+
{ skill: "obra/superpowers/brainstorming", name: "Brainstorming", reason: "Generate and evaluate ideas systematically", installs: 18300 }
|
|
24238
|
+
],
|
|
24239
|
+
memory: [
|
|
24240
|
+
{ skill: "obra/episodic-memory/remembering-conversations", name: "Remembering Conversations", reason: "Persistent memory across sessions", installs: 4800 }
|
|
24241
|
+
]
|
|
24242
|
+
};
|
|
24243
|
+
var PAIN_POINT_TOOLS = {
|
|
24244
|
+
context: [
|
|
24245
|
+
{ tool: "context7", reason: "Fetches up-to-date library docs directly into AI context", installCmd: "nb tools install context7" },
|
|
24246
|
+
{ tool: "nia", reason: "Index and search external repos, docs, and packages" }
|
|
24247
|
+
],
|
|
24248
|
+
session_length: [
|
|
24249
|
+
{ tool: "supermemory", reason: "Persistent memory across sessions - never lose context", installCmd: "nb tools install supermemory" },
|
|
24250
|
+
{ tool: "beads", reason: "Task tracking that persists between sessions" }
|
|
24251
|
+
],
|
|
24252
|
+
hallucinations: [
|
|
24253
|
+
{ tool: "context7", reason: "Real docs reduce hallucinations by 40%+", installCmd: "nb tools install context7" },
|
|
24254
|
+
{ tool: "repoprompt", reason: "Better context building for accurate code generation" }
|
|
24255
|
+
],
|
|
24256
|
+
testing: [
|
|
24257
|
+
{ tool: "stagehand", reason: "AI-powered E2E testing with Playwright" },
|
|
24258
|
+
{ tool: "agentic-tdd", reason: "Test-driven development workflow for AI coding" }
|
|
24259
|
+
],
|
|
24260
|
+
prompting: [
|
|
24261
|
+
{ tool: "skills", reason: "Pre-built prompt patterns for common tasks" },
|
|
24262
|
+
{ tool: "structured-prompts", reason: "Templates that get better AI outputs" }
|
|
24263
|
+
],
|
|
24264
|
+
tool_management: [
|
|
24265
|
+
{ tool: "oh-my-opencode", reason: "Unified agent harness - one config for all tools" },
|
|
24266
|
+
{ tool: "mcp-installer", reason: "Easy MCP server management" }
|
|
24267
|
+
],
|
|
24268
|
+
cost: [
|
|
24269
|
+
{ tool: "openrouter", reason: "Route to cheapest model per task", installCmd: "nb tools install openrouter" },
|
|
24270
|
+
{ tool: "token-counter", reason: "Track and optimize token usage" }
|
|
24271
|
+
]
|
|
24272
|
+
};
|
|
24273
|
+
var STACK_TOOLS = {
|
|
24274
|
+
nextjs: [{ tool: "vercel-mcp", reason: "Deploy and manage Vercel projects from AI" }],
|
|
24275
|
+
react: [{ tool: "storybook-mcp", reason: "AI-assisted component development" }],
|
|
24276
|
+
typescript: [{ tool: "typescript-mcp", reason: "Better type inference in AI context" }],
|
|
24277
|
+
prisma: [{ tool: "prisma-mcp", reason: "Database schema and queries in AI context" }],
|
|
24278
|
+
convex: [{ tool: "convex-mcp", reason: "Convex functions and schema in AI context" }],
|
|
24279
|
+
python: [{ tool: "python-mcp", reason: "Python environment and packages in AI context" }]
|
|
24280
|
+
};
|
|
24281
|
+
var recommendCommand = defineCommand2({
|
|
24282
|
+
meta: {
|
|
24283
|
+
name: "recommend",
|
|
24284
|
+
description: "Get personalized AI workflow recommendations based on your project"
|
|
24285
|
+
},
|
|
24286
|
+
args: {
|
|
24287
|
+
scan: {
|
|
24288
|
+
type: "boolean",
|
|
24289
|
+
description: "Re-analyze project before recommending",
|
|
24290
|
+
default: false
|
|
24291
|
+
},
|
|
24292
|
+
limit: {
|
|
24293
|
+
type: "string",
|
|
24294
|
+
description: "Max recommendations to show",
|
|
24295
|
+
default: "5"
|
|
24296
|
+
},
|
|
24297
|
+
all: {
|
|
24298
|
+
type: "boolean",
|
|
24299
|
+
description: "Show all recommendations (not just top)",
|
|
24300
|
+
default: false
|
|
24301
|
+
},
|
|
24302
|
+
skills: {
|
|
24303
|
+
type: "boolean",
|
|
24304
|
+
description: "Show skills.sh recommendations based on workflow issues",
|
|
24305
|
+
default: false
|
|
24306
|
+
},
|
|
24307
|
+
install: {
|
|
24308
|
+
type: "boolean",
|
|
24309
|
+
description: "Auto-install recommended skills from skills.sh",
|
|
24310
|
+
default: false
|
|
24311
|
+
}
|
|
24312
|
+
},
|
|
24313
|
+
async run({ args }) {
|
|
24314
|
+
const projectPath = process.cwd();
|
|
24315
|
+
const contextPath = join16(projectPath, ".nairon", "context.json");
|
|
24316
|
+
const limit = parseInt(args.limit) || 5;
|
|
24317
|
+
consola.log("");
|
|
24318
|
+
if (args.skills || args.install) {
|
|
24319
|
+
await showSkillsRecommendations(projectPath, args.install);
|
|
24320
|
+
return;
|
|
24321
|
+
}
|
|
24322
|
+
console.log(`${YELLOW2}\uD83D\uDCCA Analyzing your project...${RESET2}`);
|
|
24323
|
+
let context = null;
|
|
24324
|
+
if (existsSync16(contextPath)) {
|
|
24325
|
+
try {
|
|
24326
|
+
context = JSON.parse(readFileSync13(contextPath, "utf-8"));
|
|
24327
|
+
console.log(`${DIM2}Loaded context from ${contextPath}${RESET2}`);
|
|
24328
|
+
} catch {}
|
|
24329
|
+
}
|
|
24330
|
+
if (!context) {
|
|
24331
|
+
consola.warn("No project context found. Run 'nb onboard' first for personalized recommendations.");
|
|
24332
|
+
consola.log("");
|
|
24333
|
+
await showGenericRecommendations();
|
|
24334
|
+
return;
|
|
24335
|
+
}
|
|
24336
|
+
const recommendations = [];
|
|
24337
|
+
for (const painPoint of context.painPoints) {
|
|
24338
|
+
const tools = PAIN_POINT_TOOLS[painPoint] || [];
|
|
24339
|
+
for (const tool of tools) {
|
|
24340
|
+
if (!context.installedTools.includes(tool.tool)) {
|
|
24341
|
+
recommendations.push({
|
|
24342
|
+
tool: tool.tool,
|
|
24343
|
+
reason: tool.reason,
|
|
24344
|
+
source: "pain_point",
|
|
24345
|
+
relevanceScore: 0.9,
|
|
24346
|
+
installCmd: tool.installCmd
|
|
24347
|
+
});
|
|
24348
|
+
}
|
|
24349
|
+
}
|
|
24350
|
+
}
|
|
24351
|
+
for (const tech of context.techStack) {
|
|
24352
|
+
const tools = STACK_TOOLS[tech.toLowerCase()] || [];
|
|
24353
|
+
for (const tool of tools) {
|
|
24354
|
+
if (!context.installedTools.includes(tool.tool)) {
|
|
24355
|
+
recommendations.push({
|
|
24356
|
+
tool: tool.tool,
|
|
24357
|
+
reason: tool.reason,
|
|
24358
|
+
source: "stack",
|
|
24359
|
+
relevanceScore: 0.7
|
|
24360
|
+
});
|
|
24361
|
+
}
|
|
24362
|
+
}
|
|
24363
|
+
}
|
|
24364
|
+
const client = getClient();
|
|
24365
|
+
if (client) {
|
|
24366
|
+
try {
|
|
24367
|
+
const trending = await client.query(api.nightcrawler.getRecommendationsForProject, {
|
|
24368
|
+
projectPath,
|
|
24369
|
+
limit: 10
|
|
24370
|
+
});
|
|
24371
|
+
for (const t2 of trending) {
|
|
24372
|
+
if (recommendations.some((r3) => r3.tool === t2.toolSlug))
|
|
24373
|
+
continue;
|
|
24374
|
+
if (context.installedTools.includes(t2.toolSlug))
|
|
24375
|
+
continue;
|
|
24376
|
+
recommendations.push({
|
|
24377
|
+
tool: t2.toolSlug,
|
|
24378
|
+
reason: t2.reason,
|
|
24379
|
+
source: "trending",
|
|
24380
|
+
relevanceScore: t2.relevanceScore,
|
|
24381
|
+
trendingMentions: t2.mentions
|
|
24382
|
+
});
|
|
24383
|
+
}
|
|
24384
|
+
} catch (err) {}
|
|
24385
|
+
}
|
|
24386
|
+
const seen = new Set;
|
|
24387
|
+
const uniqueRecs = recommendations.filter((r3) => {
|
|
24388
|
+
if (seen.has(r3.tool))
|
|
24389
|
+
return false;
|
|
24390
|
+
seen.add(r3.tool);
|
|
24391
|
+
return true;
|
|
24392
|
+
}).sort((a2, b2) => b2.relevanceScore - a2.relevanceScore);
|
|
24393
|
+
consola.log("");
|
|
24394
|
+
console.log(`${BOLD2}${CYAN2}RECOMMENDATIONS FOR ${context.projectName.toUpperCase()}${RESET2}`);
|
|
24395
|
+
console.log(`${DIM2}Based on: ${context.painPoints.length} pain points, ${context.techStack.length} stack items${RESET2}`);
|
|
24396
|
+
console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
|
|
24397
|
+
const toShow = args.all ? uniqueRecs : uniqueRecs.slice(0, limit);
|
|
24398
|
+
if (toShow.length === 0) {
|
|
24399
|
+
consola.success("Your setup looks great! No new recommendations at this time.");
|
|
24400
|
+
consola.log("");
|
|
24401
|
+
console.log(`${DIM2}Run 'nb trending' to see what's new in the community${RESET2}`);
|
|
24402
|
+
return;
|
|
24403
|
+
}
|
|
24404
|
+
for (let i3 = 0;i3 < toShow.length; i3++) {
|
|
24405
|
+
const rec = toShow[i3];
|
|
24406
|
+
const num = i3 + 1;
|
|
24407
|
+
const sourceIcon = getSourceIcon(rec.source);
|
|
24408
|
+
const trendingBadge = rec.trendingMentions ? `${MAGENTA2}↑${rec.trendingMentions}${RESET2} ` : "";
|
|
24409
|
+
consola.log("");
|
|
24410
|
+
console.log(`${BOLD2}${num}. ${CYAN2}${rec.tool}${RESET2} ${sourceIcon}`);
|
|
24411
|
+
console.log(` ${trendingBadge}${rec.reason}`);
|
|
24412
|
+
if (rec.installCmd) {
|
|
24413
|
+
console.log(` ${DIM2}Install: ${GREEN2}${rec.installCmd}${RESET2}`);
|
|
24414
|
+
}
|
|
24415
|
+
}
|
|
24416
|
+
consola.log("");
|
|
24417
|
+
console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
|
|
24418
|
+
const painPointRecs = toShow.filter((r3) => r3.source === "pain_point").length;
|
|
24419
|
+
const stackRecs = toShow.filter((r3) => r3.source === "stack").length;
|
|
24420
|
+
const trendingRecs = toShow.filter((r3) => r3.source === "trending").length;
|
|
24421
|
+
console.log(`${DIM2}${painPointRecs} for pain points · ${stackRecs} for your stack · ${trendingRecs} trending${RESET2}`);
|
|
24422
|
+
if (!args.all && uniqueRecs.length > limit) {
|
|
24423
|
+
console.log(`${DIM2}Run 'nb recommend --all' to see ${uniqueRecs.length - limit} more${RESET2}`);
|
|
24424
|
+
}
|
|
24425
|
+
if (context.installedTools.length > 0) {
|
|
24426
|
+
consola.log("");
|
|
24427
|
+
console.log(`${GREEN2}✓ Already installed:${RESET2} ${context.installedTools.slice(0, 5).join(", ")}${context.installedTools.length > 5 ? ` +${context.installedTools.length - 5} more` : ""}`);
|
|
24428
|
+
}
|
|
24429
|
+
}
|
|
24430
|
+
});
|
|
24431
|
+
async function showGenericRecommendations() {
|
|
24432
|
+
console.log(`${BOLD2}${CYAN2}TOP COMMUNITY RECOMMENDATIONS${RESET2}`);
|
|
24433
|
+
console.log(`${DIM2}Run 'nb onboard' for personalized suggestions${RESET2}`);
|
|
24434
|
+
console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
|
|
24435
|
+
consola.log("");
|
|
24436
|
+
const genericRecs = [
|
|
24437
|
+
{ tool: "context7", reason: "Up-to-date library docs in AI context - reduces hallucinations" },
|
|
24438
|
+
{ tool: "supermemory", reason: "Persistent memory across sessions" },
|
|
24439
|
+
{ tool: "repoprompt", reason: "Better context building for code generation" },
|
|
24440
|
+
{ tool: "beads", reason: "Task tracking that persists between sessions" },
|
|
24441
|
+
{ tool: "stagehand", reason: "AI-powered E2E testing" }
|
|
24442
|
+
];
|
|
24443
|
+
for (let i3 = 0;i3 < genericRecs.length; i3++) {
|
|
24444
|
+
const rec = genericRecs[i3];
|
|
24445
|
+
console.log(`${BOLD2}${i3 + 1}. ${CYAN2}${rec.tool}${RESET2}`);
|
|
24446
|
+
console.log(` ${rec.reason}`);
|
|
24447
|
+
consola.log("");
|
|
24448
|
+
}
|
|
24449
|
+
}
|
|
24450
|
+
function getSourceIcon(source) {
|
|
24451
|
+
switch (source) {
|
|
24452
|
+
case "pain_point":
|
|
24453
|
+
return `${YELLOW2}[pain point]${RESET2}`;
|
|
24454
|
+
case "stack":
|
|
24455
|
+
return `${GREEN2}[your stack]${RESET2}`;
|
|
24456
|
+
case "trending":
|
|
24457
|
+
return `${MAGENTA2}[trending]${RESET2}`;
|
|
24458
|
+
case "community":
|
|
24459
|
+
return `${DIM2}[community]${RESET2}`;
|
|
24460
|
+
default:
|
|
24461
|
+
return "";
|
|
24462
|
+
}
|
|
24463
|
+
}
|
|
24464
|
+
async function showSkillsRecommendations(projectPath, autoInstall) {
|
|
24465
|
+
console.log(`${BOLD2}${CYAN2}SKILLS.SH RECOMMENDATIONS${RESET2}`);
|
|
24466
|
+
console.log(`${DIM2}Based on your workflow patterns${RESET2}`);
|
|
24467
|
+
console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
|
|
24468
|
+
consola.log("");
|
|
24469
|
+
const contextPath = join16(projectPath, ".nairon", "context.json");
|
|
24470
|
+
let techStack = [];
|
|
24471
|
+
let painPoints = [];
|
|
24472
|
+
if (existsSync16(contextPath)) {
|
|
24473
|
+
try {
|
|
24474
|
+
const context = JSON.parse(readFileSync13(contextPath, "utf-8"));
|
|
24475
|
+
techStack = context.techStack || [];
|
|
24476
|
+
painPoints = context.painPoints || [];
|
|
24477
|
+
} catch {}
|
|
24478
|
+
}
|
|
24479
|
+
const reportsDir = join16(projectPath, ".nairon", "reports");
|
|
24480
|
+
let detectedIssues = [];
|
|
24481
|
+
if (existsSync16(reportsDir)) {
|
|
24482
|
+
try {
|
|
24483
|
+
const files = __require("fs").readdirSync(reportsDir).filter((f3) => f3.endsWith(".md"));
|
|
24484
|
+
if (files.length > 0) {
|
|
24485
|
+
const latestReport = readFileSync13(join16(reportsDir, files[files.length - 1]), "utf-8");
|
|
24486
|
+
if (latestReport.includes("Testing") && latestReport.includes("0/100")) {
|
|
24487
|
+
detectedIssues.push("testing");
|
|
24488
|
+
}
|
|
24489
|
+
if (latestReport.includes("frustration") || latestReport.includes("Frustration")) {
|
|
24490
|
+
detectedIssues.push("debugging");
|
|
24491
|
+
}
|
|
24492
|
+
if (latestReport.includes("Planning") && latestReport.match(/Planning.*[0-4]\d\/100/)) {
|
|
24493
|
+
detectedIssues.push("planning");
|
|
24494
|
+
}
|
|
24495
|
+
}
|
|
24496
|
+
} catch {}
|
|
24497
|
+
}
|
|
24498
|
+
if (painPoints.includes("testing"))
|
|
24499
|
+
detectedIssues.push("testing");
|
|
24500
|
+
if (painPoints.includes("context"))
|
|
24501
|
+
detectedIssues.push("memory");
|
|
24502
|
+
if (painPoints.includes("prompting"))
|
|
24503
|
+
detectedIssues.push("planning");
|
|
24504
|
+
const recommendations = [];
|
|
24505
|
+
for (const tech of techStack) {
|
|
24506
|
+
const key = tech.toLowerCase();
|
|
24507
|
+
const skills = SKILLS_SH_MAPPINGS[key] || [];
|
|
24508
|
+
for (const skill of skills) {
|
|
24509
|
+
recommendations.push({
|
|
24510
|
+
skill: skill.skill,
|
|
24511
|
+
name: skill.name,
|
|
24512
|
+
reason: skill.reason,
|
|
24513
|
+
category: tech,
|
|
24514
|
+
installs: skill.installs
|
|
24515
|
+
});
|
|
24516
|
+
}
|
|
24517
|
+
}
|
|
24518
|
+
for (const issue of detectedIssues) {
|
|
24519
|
+
const skills = SKILLS_SH_MAPPINGS[issue] || [];
|
|
24520
|
+
for (const skill of skills) {
|
|
24521
|
+
if (!recommendations.some((r3) => r3.skill === skill.skill)) {
|
|
24522
|
+
recommendations.push({
|
|
24523
|
+
skill: skill.skill,
|
|
24524
|
+
name: skill.name,
|
|
24525
|
+
reason: skill.reason,
|
|
24526
|
+
category: issue,
|
|
24527
|
+
installs: skill.installs
|
|
24528
|
+
});
|
|
24529
|
+
}
|
|
24530
|
+
}
|
|
24531
|
+
}
|
|
24532
|
+
if (recommendations.length < 3) {
|
|
24533
|
+
const genericSkills = [
|
|
24534
|
+
...SKILLS_SH_MAPPINGS["frontend"] || [],
|
|
24535
|
+
...SKILLS_SH_MAPPINGS["brainstorming"] || [],
|
|
24536
|
+
...SKILLS_SH_MAPPINGS["debugging"] || []
|
|
24537
|
+
];
|
|
24538
|
+
for (const skill of genericSkills) {
|
|
24539
|
+
if (!recommendations.some((r3) => r3.skill === skill.skill)) {
|
|
24540
|
+
recommendations.push({
|
|
24541
|
+
skill: skill.skill,
|
|
24542
|
+
name: skill.name,
|
|
24543
|
+
reason: skill.reason,
|
|
24544
|
+
category: "general",
|
|
24545
|
+
installs: skill.installs
|
|
24546
|
+
});
|
|
24547
|
+
}
|
|
24548
|
+
}
|
|
24549
|
+
}
|
|
24550
|
+
recommendations.sort((a2, b2) => (b2.installs || 0) - (a2.installs || 0));
|
|
24551
|
+
const toShow = recommendations.slice(0, 5);
|
|
24552
|
+
if (toShow.length === 0) {
|
|
24553
|
+
console.log(`${DIM2}No skill recommendations at this time.${RESET2}`);
|
|
24554
|
+
console.log(`${DIM2}Run 'nb onboard' to set up your project context.${RESET2}`);
|
|
24555
|
+
return;
|
|
24556
|
+
}
|
|
24557
|
+
const toInstall = [];
|
|
24558
|
+
for (let i3 = 0;i3 < toShow.length; i3++) {
|
|
24559
|
+
const rec = toShow[i3];
|
|
24560
|
+
const installsStr = rec.installs ? `${DIM2}${(rec.installs / 1000).toFixed(1)}K installs${RESET2}` : "";
|
|
24561
|
+
console.log(`${BOLD2}${i3 + 1}. ${CYAN2}${rec.name}${RESET2} ${installsStr}`);
|
|
24562
|
+
console.log(` ${rec.reason}`);
|
|
24563
|
+
console.log(` ${DIM2}npx skills add ${rec.skill}${RESET2}`);
|
|
24564
|
+
consola.log("");
|
|
24565
|
+
toInstall.push(rec.skill);
|
|
24566
|
+
}
|
|
24567
|
+
if (autoInstall && toInstall.length > 0) {
|
|
24568
|
+
console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
|
|
24569
|
+
console.log(`${YELLOW2}Installing ${toInstall.length} skills...${RESET2}`);
|
|
24570
|
+
consola.log("");
|
|
24571
|
+
for (const skill of toInstall) {
|
|
24572
|
+
const skillName = skill.split("/").pop();
|
|
24573
|
+
try {
|
|
24574
|
+
console.log(`${DIM2}→ Installing ${skillName}...${RESET2}`);
|
|
24575
|
+
execSync5(`npx skills add ${skill}`, { stdio: "inherit" });
|
|
24576
|
+
console.log(`${GREEN2}✓${RESET2} Installed ${skillName}`);
|
|
24577
|
+
} catch (err) {
|
|
24578
|
+
console.log(`${RED}✗${RESET2} Failed to install ${skillName}`);
|
|
24579
|
+
}
|
|
24580
|
+
consola.log("");
|
|
24581
|
+
}
|
|
24582
|
+
console.log(`${GREEN2}Done!${RESET2} Skills are now available in your agent.`);
|
|
24583
|
+
} else if (!autoInstall) {
|
|
24584
|
+
console.log(`${DIM2}${"─".repeat(50)}${RESET2}`);
|
|
24585
|
+
console.log(`${DIM2}Run ${CYAN2}nb recommend --install${RESET2}${DIM2} to auto-install these skills${RESET2}`);
|
|
24586
|
+
}
|
|
24587
|
+
}
|
|
24588
|
+
|
|
24589
|
+
// src/commands/upgrade.ts
|
|
24590
|
+
import { execSync as execSync6 } from "node:child_process";
|
|
24591
|
+
var CYAN3 = "\x1B[36m";
|
|
24592
|
+
var GREEN3 = "\x1B[32m";
|
|
24593
|
+
var YELLOW3 = "\x1B[33m";
|
|
24594
|
+
var DIM3 = "\x1B[2m";
|
|
24595
|
+
var BOLD3 = "\x1B[1m";
|
|
24596
|
+
var RESET3 = "\x1B[0m";
|
|
24597
|
+
var MAGENTA3 = "\x1B[35m";
|
|
24598
|
+
var CHANGELOG = [
|
|
24599
|
+
{
|
|
24600
|
+
version: "0.2.0",
|
|
24601
|
+
date: "2026-02-14",
|
|
24602
|
+
title: "Skills.sh Auto-Install",
|
|
24603
|
+
highlights: [
|
|
24604
|
+
"nb recommend --skills: Show skills.sh recommendations",
|
|
24605
|
+
"nb recommend --install: Auto-install skills with one command",
|
|
24606
|
+
"Maps pain points & tech stack to 50+ skills",
|
|
24607
|
+
"Slack notifications on new releases"
|
|
24608
|
+
]
|
|
24609
|
+
},
|
|
24610
|
+
{
|
|
24611
|
+
version: "0.1.0",
|
|
24612
|
+
date: "2026-02-13",
|
|
24613
|
+
title: "Nightcrawler & Recommendations",
|
|
24614
|
+
highlights: [
|
|
24615
|
+
"nb onboard - Set up project context for personalized recommendations",
|
|
24616
|
+
"nb trending - See hot topics in AI dev tools community",
|
|
24617
|
+
"nb recommend - Get personalized tool suggestions",
|
|
24618
|
+
"Auto-recommendations after nb scan",
|
|
24619
|
+
"Background X/Twitter scraper with hype filter",
|
|
24620
|
+
"Cost-optimized scraping (~$35/month)"
|
|
24621
|
+
]
|
|
24622
|
+
},
|
|
24623
|
+
{
|
|
24624
|
+
version: "0.0.40",
|
|
24625
|
+
date: "2026-02-10",
|
|
24626
|
+
title: "Frustration Detection & Smarter Reports",
|
|
24627
|
+
highlights: [
|
|
24628
|
+
"Detect frustration patterns in AI sessions",
|
|
24629
|
+
"Improved report generation",
|
|
24630
|
+
"Better token waste analysis"
|
|
24631
|
+
]
|
|
24632
|
+
},
|
|
24633
|
+
{
|
|
24634
|
+
version: "0.0.34",
|
|
24635
|
+
date: "2026-02-08",
|
|
24636
|
+
title: "Accurate Token & Commit Tracking",
|
|
24637
|
+
highlights: [
|
|
24638
|
+
"Fixed token counting accuracy",
|
|
24639
|
+
"Improved commit attribution",
|
|
24640
|
+
"Better session correlation"
|
|
24641
|
+
]
|
|
24642
|
+
},
|
|
24643
|
+
{
|
|
24644
|
+
version: "0.0.31",
|
|
24645
|
+
date: "2026-02-08",
|
|
24646
|
+
title: "Multi-Agent Support & Report Sharing",
|
|
24647
|
+
highlights: [
|
|
24648
|
+
"Support for multiple AI agents",
|
|
24649
|
+
"Shareable reports with nb publish",
|
|
24650
|
+
"Improved dashboard"
|
|
24651
|
+
]
|
|
24652
|
+
},
|
|
24653
|
+
{
|
|
24654
|
+
version: "0.0.26",
|
|
24655
|
+
date: "2026-02-07",
|
|
24656
|
+
title: "Hackathon Reports & Publish Links",
|
|
24657
|
+
highlights: [
|
|
24658
|
+
"nb report for hackathon submissions",
|
|
24659
|
+
"Publishable report links",
|
|
24660
|
+
"SDLC phase analysis"
|
|
24661
|
+
]
|
|
24662
|
+
},
|
|
24663
|
+
{
|
|
24664
|
+
version: "0.0.22",
|
|
24665
|
+
date: "2026-02-07",
|
|
24666
|
+
title: "Multi-Agent & Team Features",
|
|
24667
|
+
highlights: [
|
|
24668
|
+
"Team workflow support",
|
|
24669
|
+
"Multi-agent session tracking",
|
|
24670
|
+
"Improved cost analysis"
|
|
24671
|
+
]
|
|
24672
|
+
},
|
|
24673
|
+
{
|
|
24674
|
+
version: "0.0.18",
|
|
24675
|
+
date: "2026-02-07",
|
|
24676
|
+
title: "CI/CD & Analytics",
|
|
24677
|
+
highlights: [
|
|
24678
|
+
"CI/CD integration support",
|
|
24679
|
+
"Enhanced analytics dashboard",
|
|
24680
|
+
"Badge system"
|
|
24681
|
+
]
|
|
24682
|
+
}
|
|
24683
|
+
];
|
|
24684
|
+
var upgradeCommand = defineCommand2({
|
|
24685
|
+
meta: {
|
|
24686
|
+
name: "upgrade",
|
|
24687
|
+
description: "Upgrade nairon-bench to the latest version and see what's new"
|
|
24688
|
+
},
|
|
24689
|
+
args: {
|
|
24690
|
+
check: {
|
|
24691
|
+
type: "boolean",
|
|
24692
|
+
description: "Only check for updates without installing",
|
|
24693
|
+
default: false
|
|
24694
|
+
},
|
|
24695
|
+
changelog: {
|
|
24696
|
+
type: "boolean",
|
|
24697
|
+
description: "Show full changelog",
|
|
24698
|
+
default: false
|
|
24699
|
+
}
|
|
24700
|
+
},
|
|
24701
|
+
async run({ args }) {
|
|
24702
|
+
const currentVersion = getCurrentVersion();
|
|
24703
|
+
console.log();
|
|
24704
|
+
console.log(`${BOLD3}${CYAN3} nairon-bench upgrade${RESET3}`);
|
|
24705
|
+
console.log(`${DIM3} ${"─".repeat(40)}${RESET3}`);
|
|
24706
|
+
console.log();
|
|
24707
|
+
if (args.changelog) {
|
|
24708
|
+
showFullChangelog();
|
|
24709
|
+
return;
|
|
24710
|
+
}
|
|
24711
|
+
console.log(`${DIM3} Checking for updates...${RESET3}`);
|
|
24712
|
+
let latestVersion;
|
|
24713
|
+
try {
|
|
24714
|
+
latestVersion = execSync6("npm view nairon-bench version", { encoding: "utf-8" }).trim();
|
|
24715
|
+
} catch {
|
|
24716
|
+
console.log(` ${YELLOW3}⚠${RESET3} Could not check npm registry`);
|
|
24717
|
+
console.log(` ${DIM3}Run 'bun upgrade -g nairon-bench' manually${RESET3}`);
|
|
24718
|
+
return;
|
|
24719
|
+
}
|
|
24720
|
+
console.log();
|
|
24721
|
+
console.log(` ${DIM3}Current:${RESET3} ${currentVersion}`);
|
|
24722
|
+
console.log(` ${DIM3}Latest:${RESET3} ${latestVersion}`);
|
|
24723
|
+
console.log();
|
|
24724
|
+
if (compareVersions(currentVersion, latestVersion) >= 0) {
|
|
24725
|
+
console.log(` ${GREEN3}✓${RESET3} You're on the latest version!`);
|
|
24726
|
+
console.log();
|
|
24727
|
+
console.log(` ${DIM3}Run 'nb upgrade --changelog' to see past releases${RESET3}`);
|
|
24728
|
+
return;
|
|
24729
|
+
}
|
|
24730
|
+
const newReleases = getNewReleases(currentVersion, latestVersion);
|
|
24731
|
+
if (newReleases.length > 0) {
|
|
24732
|
+
showWhatsNew(newReleases);
|
|
24733
|
+
}
|
|
24734
|
+
if (args.check) {
|
|
24735
|
+
console.log();
|
|
24736
|
+
console.log(` ${DIM3}Run 'nb upgrade' to install${RESET3}`);
|
|
24737
|
+
return;
|
|
24738
|
+
}
|
|
24739
|
+
console.log();
|
|
24740
|
+
console.log(`${DIM3} ${"─".repeat(40)}${RESET3}`);
|
|
24741
|
+
console.log(` ${CYAN3}↓${RESET3} Upgrading to ${GREEN3}v${latestVersion}${RESET3}...`);
|
|
24742
|
+
console.log();
|
|
24743
|
+
try {
|
|
24744
|
+
try {
|
|
24745
|
+
execSync6("bun upgrade -g nairon-bench", { stdio: "inherit" });
|
|
24746
|
+
} catch {
|
|
24747
|
+
execSync6("npm install -g nairon-bench@latest", { stdio: "inherit" });
|
|
24748
|
+
}
|
|
24749
|
+
console.log();
|
|
24750
|
+
console.log(` ${GREEN3}✓${RESET3} Upgraded to ${GREEN3}v${latestVersion}${RESET3}`);
|
|
24751
|
+
console.log();
|
|
24752
|
+
console.log(` ${DIM3}Run 'nb doctor' to verify installation${RESET3}`);
|
|
24753
|
+
} catch (err) {
|
|
24754
|
+
console.log();
|
|
24755
|
+
console.log(` ${YELLOW3}⚠${RESET3} Upgrade failed`);
|
|
24756
|
+
console.log(` ${DIM3}Try running manually:${RESET3}`);
|
|
24757
|
+
console.log(` ${CYAN3}bun upgrade -g nairon-bench${RESET3}`);
|
|
24758
|
+
console.log(` ${DIM3}or${RESET3}`);
|
|
24759
|
+
console.log(` ${CYAN3}npm install -g nairon-bench@latest${RESET3}`);
|
|
24760
|
+
}
|
|
24761
|
+
}
|
|
24762
|
+
});
|
|
24763
|
+
function getCurrentVersion() {
|
|
24764
|
+
try {
|
|
24765
|
+
const output = execSync6("nb --version 2>/dev/null || nairon-bench --version 2>/dev/null", {
|
|
24766
|
+
encoding: "utf-8",
|
|
24767
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
24768
|
+
});
|
|
24769
|
+
const match = output.match(/v?(\d+\.\d+\.\d+)/);
|
|
24770
|
+
return match ? match[1] : "0.0.0";
|
|
24771
|
+
} catch {
|
|
24772
|
+
return "0.0.0";
|
|
24773
|
+
}
|
|
24774
|
+
}
|
|
24775
|
+
function compareVersions(a2, b2) {
|
|
24776
|
+
const partsA = a2.split(".").map(Number);
|
|
24777
|
+
const partsB = b2.split(".").map(Number);
|
|
24778
|
+
for (let i3 = 0;i3 < 3; i3++) {
|
|
24779
|
+
const diff = (partsA[i3] || 0) - (partsB[i3] || 0);
|
|
24780
|
+
if (diff !== 0)
|
|
24781
|
+
return diff;
|
|
24782
|
+
}
|
|
24783
|
+
return 0;
|
|
24784
|
+
}
|
|
24785
|
+
function getNewReleases(currentVersion, latestVersion) {
|
|
24786
|
+
return CHANGELOG.filter((entry) => {
|
|
24787
|
+
return compareVersions(entry.version, currentVersion) > 0 && compareVersions(entry.version, latestVersion) <= 0;
|
|
24788
|
+
});
|
|
24789
|
+
}
|
|
24790
|
+
function showWhatsNew(releases) {
|
|
24791
|
+
console.log(` ${BOLD3}${MAGENTA3}What's New${RESET3}`);
|
|
24792
|
+
console.log();
|
|
24793
|
+
for (const release of releases) {
|
|
24794
|
+
const isMajor = release.version.endsWith(".0") && !release.version.startsWith("0.0.");
|
|
24795
|
+
const isMinor = release.version.split(".")[1] !== "0" && release.version.endsWith(".0");
|
|
24796
|
+
const versionColor = isMajor ? GREEN3 : isMinor ? CYAN3 : DIM3;
|
|
24797
|
+
console.log(` ${versionColor}${BOLD3}v${release.version}${RESET3} ${DIM3}(${release.date})${RESET3}`);
|
|
24798
|
+
console.log(` ${release.title}`);
|
|
24799
|
+
console.log();
|
|
24800
|
+
for (const highlight of release.highlights.slice(0, 4)) {
|
|
24801
|
+
console.log(` ${GREEN3}•${RESET3} ${highlight}`);
|
|
24802
|
+
}
|
|
24803
|
+
if (release.highlights.length > 4) {
|
|
24804
|
+
console.log(` ${DIM3}+ ${release.highlights.length - 4} more improvements${RESET3}`);
|
|
24805
|
+
}
|
|
24806
|
+
console.log();
|
|
24807
|
+
}
|
|
24808
|
+
}
|
|
24809
|
+
function showFullChangelog() {
|
|
24810
|
+
console.log(` ${BOLD3}${MAGENTA3}Full Changelog${RESET3}`);
|
|
24811
|
+
console.log();
|
|
24812
|
+
for (const release of CHANGELOG) {
|
|
24813
|
+
console.log(` ${CYAN3}${BOLD3}v${release.version}${RESET3} ${DIM3}(${release.date})${RESET3}`);
|
|
24814
|
+
console.log(` ${release.title}`);
|
|
24815
|
+
console.log();
|
|
24816
|
+
for (const highlight of release.highlights) {
|
|
24817
|
+
console.log(` ${GREEN3}•${RESET3} ${highlight}`);
|
|
24818
|
+
}
|
|
24819
|
+
console.log();
|
|
24820
|
+
console.log(` ${DIM3}${"─".repeat(38)}${RESET3}`);
|
|
24821
|
+
console.log();
|
|
24822
|
+
}
|
|
24823
|
+
console.log(` ${DIM3}See full history at:${RESET3}`);
|
|
24824
|
+
console.log(` ${CYAN3}https://nairon.ai/changelog${RESET3}`);
|
|
24825
|
+
}
|
|
23632
24826
|
// package.json
|
|
23633
24827
|
var package_default = {
|
|
23634
24828
|
name: "nairon-bench",
|
|
23635
|
-
version: "0.0
|
|
24829
|
+
version: "0.2.0",
|
|
23636
24830
|
description: "AI workflow benchmarking CLI",
|
|
23637
24831
|
type: "module",
|
|
23638
24832
|
bin: {
|
|
@@ -23677,52 +24871,52 @@ var package_default = {
|
|
|
23677
24871
|
};
|
|
23678
24872
|
|
|
23679
24873
|
// src/lib/mascot.ts
|
|
23680
|
-
var
|
|
24874
|
+
var CYAN4 = "\x1B[36m";
|
|
23681
24875
|
var WHITE = "\x1B[97m";
|
|
23682
|
-
var
|
|
23683
|
-
var
|
|
23684
|
-
var
|
|
24876
|
+
var DIM4 = "\x1B[2m";
|
|
24877
|
+
var BOLD4 = "\x1B[1m";
|
|
24878
|
+
var RESET4 = "\x1B[0m";
|
|
23685
24879
|
function renderMascot(version2, cwd) {
|
|
23686
24880
|
const projectName = cwd ? cwd.split("/").pop() || cwd : "";
|
|
23687
|
-
const projectLine = projectName ? ` ${
|
|
24881
|
+
const projectLine = projectName ? ` ${DIM4}${projectName}${RESET4}` : "";
|
|
23688
24882
|
return `
|
|
23689
|
-
${WHITE} ▗▄▄▄▄▄▄▄▄▖${
|
|
23690
|
-
${WHITE} ▐${
|
|
23691
|
-
${WHITE} ▐${
|
|
23692
|
-
${WHITE} ▐${
|
|
23693
|
-
${WHITE} ▝▀▀▀▀▀▀▀▀▘${
|
|
24883
|
+
${WHITE} ▗▄▄▄▄▄▄▄▄▖${RESET4}
|
|
24884
|
+
${WHITE} ▐${CYAN4}█▀▀▀▀▀▀█${WHITE}▌${RESET4} ${BOLD4}NBench${RESET4} ${DIM4}v${version2}${RESET4}
|
|
24885
|
+
${WHITE} ▐${CYAN4}█ ${WHITE}▲${CYAN4} █${WHITE}▌${RESET4} ${DIM4}Benchmark AI workflows${RESET4}
|
|
24886
|
+
${WHITE} ▐${CYAN4}█▄▄▄▄▄▄█${WHITE}▌${RESET4}${projectLine}
|
|
24887
|
+
${WHITE} ▝▀▀▀▀▀▀▀▀▘${RESET4}
|
|
23694
24888
|
`;
|
|
23695
24889
|
}
|
|
23696
24890
|
|
|
23697
24891
|
// src/index.ts
|
|
23698
24892
|
var VERSION = package_default.version;
|
|
23699
|
-
var
|
|
23700
|
-
var
|
|
23701
|
-
var
|
|
23702
|
-
var
|
|
24893
|
+
var YELLOW4 = "\x1B[33m";
|
|
24894
|
+
var DIM5 = "\x1B[2m";
|
|
24895
|
+
var BOLD5 = "\x1B[1m";
|
|
24896
|
+
var RESET5 = "\x1B[0m";
|
|
23703
24897
|
function showBanner() {
|
|
23704
24898
|
const log = (msg) => process.stdout.write(msg + `
|
|
23705
24899
|
`);
|
|
23706
|
-
const
|
|
23707
|
-
const
|
|
24900
|
+
const GREEN4 = "\x1B[32m";
|
|
24901
|
+
const CYAN5 = "\x1B[36m";
|
|
23708
24902
|
log(renderMascot(VERSION, process.cwd()));
|
|
23709
|
-
log(`${
|
|
24903
|
+
log(`${YELLOW4}\u2501\u2501\u2501 Quick Start \u2501\u2501\u2501${RESET5}`);
|
|
23710
24904
|
log("");
|
|
23711
|
-
log(` ${
|
|
23712
|
-
log(` ${
|
|
23713
|
-
log(` ${
|
|
24905
|
+
log(` ${GREEN4}1.${RESET5} ${BOLD5}nb init${RESET5} Set up your profile (first time only)`);
|
|
24906
|
+
log(` ${GREEN4}2.${RESET5} ${BOLD5}nb scan${RESET5} Analyze your AI coding sessions`);
|
|
24907
|
+
log(` ${GREEN4}3.${RESET5} ${BOLD5}nb report --publish${RESET5} Generate & share your report`);
|
|
23714
24908
|
log("");
|
|
23715
|
-
log(`${
|
|
23716
|
-
log(`${
|
|
23717
|
-
log(`${
|
|
23718
|
-
log(`${
|
|
23719
|
-
log(`${
|
|
24909
|
+
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}`);
|
|
24910
|
+
log(`${DIM5}\u2502${RESET5} ${CYAN5}How often?${RESET5} ${DIM5}\u2502${RESET5}`);
|
|
24911
|
+
log(`${DIM5}\u2502${RESET5} \u2022 Run ${BOLD5}nb scan${RESET5} after coding sessions ${DIM5}\u2502${RESET5}`);
|
|
24912
|
+
log(`${DIM5}\u2502${RESET5} \u2022 Run ${BOLD5}nb report${RESET5} weekly to track progress ${DIM5}\u2502${RESET5}`);
|
|
24913
|
+
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}`);
|
|
23720
24914
|
log("");
|
|
23721
|
-
log(`${
|
|
24915
|
+
log(`${DIM5}Run ${BOLD5}nb --help${RESET5}${DIM5} for all commands${RESET5}`);
|
|
23722
24916
|
log("");
|
|
23723
24917
|
}
|
|
23724
24918
|
var args = process.argv.slice(2);
|
|
23725
|
-
var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges", "cache", "setup"];
|
|
24919
|
+
var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges", "cache", "setup", "onboard", "trending", "recommend"];
|
|
23726
24920
|
var hasSubcommand = args.some((arg) => subcommands.includes(arg));
|
|
23727
24921
|
var hasHelp = args.includes("--help") || args.includes("-h");
|
|
23728
24922
|
var hasVersion = args.includes("--version");
|
|
@@ -23751,7 +24945,11 @@ if (!hasSubcommand && !hasHelp && !hasVersion && args.length === 0) {
|
|
|
23751
24945
|
pr: prCommand,
|
|
23752
24946
|
badges: badgesCommand,
|
|
23753
24947
|
cache: cacheCommand,
|
|
23754
|
-
setup: setupCommand
|
|
24948
|
+
setup: setupCommand,
|
|
24949
|
+
onboard: onboardCommand,
|
|
24950
|
+
trending: trendingCommand,
|
|
24951
|
+
recommend: recommendCommand,
|
|
24952
|
+
upgrade: upgradeCommand
|
|
23755
24953
|
}
|
|
23756
24954
|
});
|
|
23757
24955
|
runMain(main);
|