nairon-bench 0.0.19 → 0.0.20
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 +474 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16954,6 +16954,476 @@ function generateCSV(data) {
|
|
|
16954
16954
|
`);
|
|
16955
16955
|
}
|
|
16956
16956
|
|
|
16957
|
+
// src/commands/pr.ts
|
|
16958
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
16959
|
+
var prCommand = defineCommand2({
|
|
16960
|
+
meta: {
|
|
16961
|
+
name: "pr",
|
|
16962
|
+
description: "Analyze AI usage in a pull request"
|
|
16963
|
+
},
|
|
16964
|
+
args: {
|
|
16965
|
+
pr: {
|
|
16966
|
+
type: "positional",
|
|
16967
|
+
description: "PR number or URL (defaults to current branch's PR)",
|
|
16968
|
+
required: false
|
|
16969
|
+
},
|
|
16970
|
+
json: {
|
|
16971
|
+
type: "boolean",
|
|
16972
|
+
description: "Output as JSON",
|
|
16973
|
+
default: false
|
|
16974
|
+
}
|
|
16975
|
+
},
|
|
16976
|
+
async run({ args }) {
|
|
16977
|
+
const jsonOutput = args.json;
|
|
16978
|
+
if (!jsonOutput) {
|
|
16979
|
+
console.log();
|
|
16980
|
+
console.log(colors2.bold(colors2.primary(" PR AI Analysis")));
|
|
16981
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
16982
|
+
console.log();
|
|
16983
|
+
}
|
|
16984
|
+
const spinner = jsonOutput ? null : createSpinner("Analyzing PR...");
|
|
16985
|
+
if (spinner)
|
|
16986
|
+
spinner.start();
|
|
16987
|
+
try {
|
|
16988
|
+
const prInfo = await getPRInfo(args.pr);
|
|
16989
|
+
if (!prInfo) {
|
|
16990
|
+
if (spinner)
|
|
16991
|
+
spinner.fail("Could not find PR");
|
|
16992
|
+
if (!jsonOutput) {
|
|
16993
|
+
console.log(` ${icons.error} No PR found. Provide a PR number or run from a branch with an open PR.`);
|
|
16994
|
+
}
|
|
16995
|
+
return;
|
|
16996
|
+
}
|
|
16997
|
+
if (spinner)
|
|
16998
|
+
spinner.succeed(`Found PR #${prInfo.number}`);
|
|
16999
|
+
const commits = await getPRCommits(prInfo.number);
|
|
17000
|
+
const prStart = new Date(prInfo.createdAt);
|
|
17001
|
+
const prEnd = prInfo.mergedAt ? new Date(prInfo.mergedAt) : new Date;
|
|
17002
|
+
const sessions = await collectAgentSessions(prStart);
|
|
17003
|
+
const prSessions = sessions?.sessions.filter((s2) => {
|
|
17004
|
+
const sessionStart = new Date(s2.startedAt);
|
|
17005
|
+
return sessionStart >= prStart && sessionStart <= prEnd;
|
|
17006
|
+
}) || [];
|
|
17007
|
+
const totalTokens = prSessions.reduce((sum, s2) => sum + s2.totalTokens, 0);
|
|
17008
|
+
const totalCost = prSessions.reduce((sum, s2) => sum + s2.costUsd, 0);
|
|
17009
|
+
const totalDuration = prSessions.reduce((sum, s2) => sum + s2.durationMinutes, 0);
|
|
17010
|
+
const wastedTokens = prSessions.reduce((sum, s2) => sum + s2.patterns.reduce((ps, p) => ps + p.estimatedWastedTokens, 0), 0);
|
|
17011
|
+
const modelUsage = {};
|
|
17012
|
+
for (const session of prSessions) {
|
|
17013
|
+
if (session.model) {
|
|
17014
|
+
modelUsage[session.model] = (modelUsage[session.model] || 0) + 1;
|
|
17015
|
+
}
|
|
17016
|
+
}
|
|
17017
|
+
const frustrationCount = prSessions.reduce((sum, s2) => sum + s2.patterns.filter((p) => p.type !== "smooth_flow").reduce((ps, p) => ps + p.count, 0), 0);
|
|
17018
|
+
const result = {
|
|
17019
|
+
pr: {
|
|
17020
|
+
number: prInfo.number,
|
|
17021
|
+
title: prInfo.title,
|
|
17022
|
+
author: prInfo.author,
|
|
17023
|
+
createdAt: prInfo.createdAt,
|
|
17024
|
+
mergedAt: prInfo.mergedAt,
|
|
17025
|
+
commits: commits.length,
|
|
17026
|
+
additions: prInfo.additions,
|
|
17027
|
+
deletions: prInfo.deletions
|
|
17028
|
+
},
|
|
17029
|
+
aiUsage: {
|
|
17030
|
+
sessions: prSessions.length,
|
|
17031
|
+
totalTokens,
|
|
17032
|
+
totalCost: Math.round(totalCost * 100) / 100,
|
|
17033
|
+
durationMinutes: totalDuration,
|
|
17034
|
+
wastedTokens,
|
|
17035
|
+
wastePercentage: totalTokens > 0 ? Math.round(wastedTokens / totalTokens * 100) : 0,
|
|
17036
|
+
frustrationEvents: frustrationCount,
|
|
17037
|
+
modelUsage
|
|
17038
|
+
},
|
|
17039
|
+
efficiency: {
|
|
17040
|
+
tokensPerCommit: commits.length > 0 ? Math.round(totalTokens / commits.length) : 0,
|
|
17041
|
+
tokensPerLine: prInfo.additions + prInfo.deletions > 0 ? Math.round(totalTokens / (prInfo.additions + prInfo.deletions)) : 0,
|
|
17042
|
+
costPerCommit: commits.length > 0 ? Math.round(totalCost / commits.length * 100) / 100 : 0
|
|
17043
|
+
}
|
|
17044
|
+
};
|
|
17045
|
+
if (jsonOutput) {
|
|
17046
|
+
console.log(JSON.stringify(result, null, 2));
|
|
17047
|
+
return;
|
|
17048
|
+
}
|
|
17049
|
+
console.log();
|
|
17050
|
+
console.log(` ${colors2.bold("PR Details")}`);
|
|
17051
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17052
|
+
console.log(` ${colors2.dim("Number:")} #${prInfo.number}`);
|
|
17053
|
+
console.log(` ${colors2.dim("Title:")} ${prInfo.title.slice(0, 50)}${prInfo.title.length > 50 ? "..." : ""}`);
|
|
17054
|
+
console.log(` ${colors2.dim("Author:")} @${prInfo.author}`);
|
|
17055
|
+
console.log(` ${colors2.dim("Commits:")} ${commits.length}`);
|
|
17056
|
+
console.log(` ${colors2.dim("Changes:")} ${colors2.success(`+${prInfo.additions}`)} ${colors2.error(`-${prInfo.deletions}`)}`);
|
|
17057
|
+
console.log();
|
|
17058
|
+
console.log(` ${colors2.bold("AI Usage During PR")}`);
|
|
17059
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17060
|
+
console.log(` ${colors2.dim("Sessions:")} ${prSessions.length}`);
|
|
17061
|
+
console.log(` ${colors2.dim("Tokens:")} ${formatNumber(totalTokens)}`);
|
|
17062
|
+
console.log(` ${colors2.dim("Cost:")} ${colors2.success(`$${totalCost.toFixed(2)}`)}`);
|
|
17063
|
+
console.log(` ${colors2.dim("Duration:")} ${totalDuration} minutes`);
|
|
17064
|
+
if (wastedTokens > 0) {
|
|
17065
|
+
const wastePercent = Math.round(wastedTokens / totalTokens * 100);
|
|
17066
|
+
console.log(` ${colors2.dim("Wasted:")} ${colors2.error(`${formatNumber(wastedTokens)} tokens (${wastePercent}%)`)}`);
|
|
17067
|
+
}
|
|
17068
|
+
if (frustrationCount > 0) {
|
|
17069
|
+
console.log(` ${colors2.dim("Frustration events:")} ${colors2.warning(frustrationCount.toString())}`);
|
|
17070
|
+
}
|
|
17071
|
+
console.log();
|
|
17072
|
+
console.log(` ${colors2.bold("Efficiency")}`);
|
|
17073
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17074
|
+
console.log(` ${colors2.dim("Tokens/commit:")} ${formatNumber(result.efficiency.tokensPerCommit)}`);
|
|
17075
|
+
console.log(` ${colors2.dim("Tokens/line:")} ${result.efficiency.tokensPerLine}`);
|
|
17076
|
+
console.log(` ${colors2.dim("Cost/commit:")} $${result.efficiency.costPerCommit.toFixed(2)}`);
|
|
17077
|
+
console.log();
|
|
17078
|
+
if (Object.keys(modelUsage).length > 0) {
|
|
17079
|
+
console.log(` ${colors2.bold("Models Used")}`);
|
|
17080
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17081
|
+
for (const [model, count] of Object.entries(modelUsage).sort((a2, b2) => b2[1] - a2[1])) {
|
|
17082
|
+
const shortModel = model.length > 25 ? model.slice(0, 22) + "..." : model;
|
|
17083
|
+
console.log(` ${colors2.dim(shortModel.padEnd(25))} ${count} sessions`);
|
|
17084
|
+
}
|
|
17085
|
+
console.log();
|
|
17086
|
+
}
|
|
17087
|
+
} catch (error2) {
|
|
17088
|
+
if (spinner)
|
|
17089
|
+
spinner.fail("Analysis failed");
|
|
17090
|
+
if (!jsonOutput) {
|
|
17091
|
+
console.log(` ${icons.error} ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
17092
|
+
}
|
|
17093
|
+
}
|
|
17094
|
+
}
|
|
17095
|
+
});
|
|
17096
|
+
async function getPRInfo(prArg) {
|
|
17097
|
+
try {
|
|
17098
|
+
let prNumber;
|
|
17099
|
+
if (prArg) {
|
|
17100
|
+
const match = prArg.match(/\/pull\/(\d+)/);
|
|
17101
|
+
prNumber = match ? match[1] : prArg;
|
|
17102
|
+
} else {
|
|
17103
|
+
const branch = execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
|
|
17104
|
+
const result2 = execSync2(`gh pr list --head "${branch}" --json number --limit 1`, { encoding: "utf-8" });
|
|
17105
|
+
const prs = JSON.parse(result2);
|
|
17106
|
+
if (prs.length === 0)
|
|
17107
|
+
return null;
|
|
17108
|
+
prNumber = prs[0].number.toString();
|
|
17109
|
+
}
|
|
17110
|
+
const result = execSync2(`gh pr view ${prNumber} --json number,title,author,createdAt,mergedAt,additions,deletions`, { encoding: "utf-8" });
|
|
17111
|
+
const pr = JSON.parse(result);
|
|
17112
|
+
return {
|
|
17113
|
+
number: pr.number,
|
|
17114
|
+
title: pr.title,
|
|
17115
|
+
author: pr.author.login,
|
|
17116
|
+
createdAt: pr.createdAt,
|
|
17117
|
+
mergedAt: pr.mergedAt,
|
|
17118
|
+
additions: pr.additions,
|
|
17119
|
+
deletions: pr.deletions
|
|
17120
|
+
};
|
|
17121
|
+
} catch {
|
|
17122
|
+
return null;
|
|
17123
|
+
}
|
|
17124
|
+
}
|
|
17125
|
+
async function getPRCommits(prNumber) {
|
|
17126
|
+
try {
|
|
17127
|
+
const result = execSync2(`gh pr view ${prNumber} --json commits --jq '.commits[].oid'`, { encoding: "utf-8" });
|
|
17128
|
+
return result.trim().split(`
|
|
17129
|
+
`).filter(Boolean);
|
|
17130
|
+
} catch {
|
|
17131
|
+
return [];
|
|
17132
|
+
}
|
|
17133
|
+
}
|
|
17134
|
+
function formatNumber(num) {
|
|
17135
|
+
if (num >= 1e6)
|
|
17136
|
+
return `${(num / 1e6).toFixed(1)}M`;
|
|
17137
|
+
if (num >= 1000)
|
|
17138
|
+
return `${Math.round(num / 1000)}K`;
|
|
17139
|
+
return num.toString();
|
|
17140
|
+
}
|
|
17141
|
+
|
|
17142
|
+
// src/lib/badges.ts
|
|
17143
|
+
function computeBadges(score, agents, git) {
|
|
17144
|
+
const badges = [];
|
|
17145
|
+
badges.push({
|
|
17146
|
+
id: "score-50",
|
|
17147
|
+
name: "Getting Started",
|
|
17148
|
+
description: "Reach a score of 50",
|
|
17149
|
+
icon: "\uD83C\uDFAF",
|
|
17150
|
+
tier: "bronze",
|
|
17151
|
+
category: "score",
|
|
17152
|
+
earned: score.overall >= 50,
|
|
17153
|
+
progress: Math.min(100, score.overall / 50 * 100)
|
|
17154
|
+
});
|
|
17155
|
+
badges.push({
|
|
17156
|
+
id: "score-70",
|
|
17157
|
+
name: "Proficient",
|
|
17158
|
+
description: "Reach a score of 70",
|
|
17159
|
+
icon: "⭐",
|
|
17160
|
+
tier: "silver",
|
|
17161
|
+
category: "score",
|
|
17162
|
+
earned: score.overall >= 70,
|
|
17163
|
+
progress: Math.min(100, score.overall / 70 * 100)
|
|
17164
|
+
});
|
|
17165
|
+
badges.push({
|
|
17166
|
+
id: "score-85",
|
|
17167
|
+
name: "Expert",
|
|
17168
|
+
description: "Reach a score of 85",
|
|
17169
|
+
icon: "\uD83C\uDFC6",
|
|
17170
|
+
tier: "gold",
|
|
17171
|
+
category: "score",
|
|
17172
|
+
earned: score.overall >= 85,
|
|
17173
|
+
progress: Math.min(100, score.overall / 85 * 100)
|
|
17174
|
+
});
|
|
17175
|
+
badges.push({
|
|
17176
|
+
id: "score-95",
|
|
17177
|
+
name: "Elite",
|
|
17178
|
+
description: "Reach a score of 95+",
|
|
17179
|
+
icon: "\uD83D\uDC8E",
|
|
17180
|
+
tier: "diamond",
|
|
17181
|
+
category: "score",
|
|
17182
|
+
earned: score.overall >= 95,
|
|
17183
|
+
progress: Math.min(100, score.overall / 95 * 100)
|
|
17184
|
+
});
|
|
17185
|
+
const wastePercent = score.tokenEfficiency.wastePercentage;
|
|
17186
|
+
badges.push({
|
|
17187
|
+
id: "low-waste",
|
|
17188
|
+
name: "Token Saver",
|
|
17189
|
+
description: "Keep token waste below 10%",
|
|
17190
|
+
icon: "\uD83D\uDCB0",
|
|
17191
|
+
tier: "silver",
|
|
17192
|
+
category: "efficiency",
|
|
17193
|
+
earned: wastePercent < 10,
|
|
17194
|
+
progress: wastePercent < 10 ? 100 : Math.max(0, 100 - (wastePercent - 10) * 5)
|
|
17195
|
+
});
|
|
17196
|
+
badges.push({
|
|
17197
|
+
id: "ultra-efficient",
|
|
17198
|
+
name: "Ultra Efficient",
|
|
17199
|
+
description: "Keep token waste below 5%",
|
|
17200
|
+
icon: "\uD83D\uDD25",
|
|
17201
|
+
tier: "gold",
|
|
17202
|
+
category: "efficiency",
|
|
17203
|
+
earned: wastePercent < 5,
|
|
17204
|
+
progress: wastePercent < 5 ? 100 : Math.max(0, 100 - wastePercent * 10)
|
|
17205
|
+
});
|
|
17206
|
+
if (agents) {
|
|
17207
|
+
const sessionCount = agents.totalSessions;
|
|
17208
|
+
badges.push({
|
|
17209
|
+
id: "first-session",
|
|
17210
|
+
name: "First Steps",
|
|
17211
|
+
description: "Complete your first AI session",
|
|
17212
|
+
icon: "\uD83D\uDC63",
|
|
17213
|
+
tier: "bronze",
|
|
17214
|
+
category: "consistency",
|
|
17215
|
+
earned: sessionCount >= 1,
|
|
17216
|
+
progress: sessionCount >= 1 ? 100 : 0
|
|
17217
|
+
});
|
|
17218
|
+
badges.push({
|
|
17219
|
+
id: "power-user",
|
|
17220
|
+
name: "Power User",
|
|
17221
|
+
description: "Complete 100 AI sessions",
|
|
17222
|
+
icon: "⚡",
|
|
17223
|
+
tier: "silver",
|
|
17224
|
+
category: "consistency",
|
|
17225
|
+
earned: sessionCount >= 100,
|
|
17226
|
+
progress: Math.min(100, sessionCount / 100 * 100)
|
|
17227
|
+
});
|
|
17228
|
+
badges.push({
|
|
17229
|
+
id: "ai-native",
|
|
17230
|
+
name: "AI Native",
|
|
17231
|
+
description: "Complete 500 AI sessions",
|
|
17232
|
+
icon: "\uD83E\uDD16",
|
|
17233
|
+
tier: "gold",
|
|
17234
|
+
category: "consistency",
|
|
17235
|
+
earned: sessionCount >= 500,
|
|
17236
|
+
progress: Math.min(100, sessionCount / 500 * 100)
|
|
17237
|
+
});
|
|
17238
|
+
const frustrationCount = agents.sessions.reduce((acc, s2) => acc + s2.patterns.filter((p) => p.type !== "smooth_flow").reduce((a2, p) => a2 + p.count, 0), 0);
|
|
17239
|
+
const smoothRatio = sessionCount > 0 ? 1 - frustrationCount / sessionCount / 5 : 0;
|
|
17240
|
+
badges.push({
|
|
17241
|
+
id: "smooth-operator",
|
|
17242
|
+
name: "Smooth Operator",
|
|
17243
|
+
description: "Maintain low frustration across sessions",
|
|
17244
|
+
icon: "\uD83D\uDE0E",
|
|
17245
|
+
tier: "gold",
|
|
17246
|
+
category: "efficiency",
|
|
17247
|
+
earned: smoothRatio > 0.8 && sessionCount >= 10,
|
|
17248
|
+
progress: Math.min(100, smoothRatio * 100)
|
|
17249
|
+
});
|
|
17250
|
+
const modelCount = Object.keys(agents.modelBreakdown).length;
|
|
17251
|
+
badges.push({
|
|
17252
|
+
id: "model-explorer",
|
|
17253
|
+
name: "Model Explorer",
|
|
17254
|
+
description: "Use 3+ different AI models",
|
|
17255
|
+
icon: "\uD83D\uDD2C",
|
|
17256
|
+
tier: "bronze",
|
|
17257
|
+
category: "tools",
|
|
17258
|
+
earned: modelCount >= 3,
|
|
17259
|
+
progress: Math.min(100, modelCount / 3 * 100)
|
|
17260
|
+
});
|
|
17261
|
+
}
|
|
17262
|
+
if (git) {
|
|
17263
|
+
badges.push({
|
|
17264
|
+
id: "commit-streak",
|
|
17265
|
+
name: "Commit Machine",
|
|
17266
|
+
description: "Average 5+ commits per day",
|
|
17267
|
+
icon: "\uD83D\uDCDD",
|
|
17268
|
+
tier: "silver",
|
|
17269
|
+
category: "consistency",
|
|
17270
|
+
earned: git.commitsPerDay >= 5,
|
|
17271
|
+
progress: Math.min(100, git.commitsPerDay / 5 * 100)
|
|
17272
|
+
});
|
|
17273
|
+
badges.push({
|
|
17274
|
+
id: "small-commits",
|
|
17275
|
+
name: "Atomic Commits",
|
|
17276
|
+
description: "Keep average commit size under 50 lines",
|
|
17277
|
+
icon: "\uD83C\uDFAF",
|
|
17278
|
+
tier: "silver",
|
|
17279
|
+
category: "efficiency",
|
|
17280
|
+
earned: git.avgCommitSize < 50,
|
|
17281
|
+
progress: git.avgCommitSize < 50 ? 100 : Math.max(0, 100 - (git.avgCommitSize - 50))
|
|
17282
|
+
});
|
|
17283
|
+
}
|
|
17284
|
+
const allPhasesAbove60 = score.phases.every((p) => p.score >= 60);
|
|
17285
|
+
badges.push({
|
|
17286
|
+
id: "full-coverage",
|
|
17287
|
+
name: "Full Coverage",
|
|
17288
|
+
description: "Score 60+ in all SDLC phases",
|
|
17289
|
+
icon: "\uD83C\uDF1F",
|
|
17290
|
+
tier: "gold",
|
|
17291
|
+
category: "score",
|
|
17292
|
+
earned: allPhasesAbove60,
|
|
17293
|
+
progress: Math.min(100, score.phases.filter((p) => p.score >= 60).length / score.phases.length * 100)
|
|
17294
|
+
});
|
|
17295
|
+
const implementationPhase = score.phases.find((p) => p.phase === "implementation");
|
|
17296
|
+
if (implementationPhase && implementationPhase.score >= 80) {
|
|
17297
|
+
badges.push({
|
|
17298
|
+
id: "code-wizard",
|
|
17299
|
+
name: "Code Wizard",
|
|
17300
|
+
description: "Score 80+ in implementation phase",
|
|
17301
|
+
icon: "\uD83E\uDDD9",
|
|
17302
|
+
tier: "platinum",
|
|
17303
|
+
category: "score",
|
|
17304
|
+
earned: true
|
|
17305
|
+
});
|
|
17306
|
+
}
|
|
17307
|
+
const testingPhase = score.phases.find((p) => p.phase === "testing");
|
|
17308
|
+
if (testingPhase && testingPhase.score >= 80) {
|
|
17309
|
+
badges.push({
|
|
17310
|
+
id: "test-champion",
|
|
17311
|
+
name: "Test Champion",
|
|
17312
|
+
description: "Score 80+ in testing phase",
|
|
17313
|
+
icon: "✅",
|
|
17314
|
+
tier: "platinum",
|
|
17315
|
+
category: "score",
|
|
17316
|
+
earned: true
|
|
17317
|
+
});
|
|
17318
|
+
}
|
|
17319
|
+
return badges;
|
|
17320
|
+
}
|
|
17321
|
+
function formatBadge(badge) {
|
|
17322
|
+
const tierColors = {
|
|
17323
|
+
bronze: "\x1B[33m",
|
|
17324
|
+
silver: "\x1B[37m",
|
|
17325
|
+
gold: "\x1B[93m",
|
|
17326
|
+
platinum: "\x1B[96m",
|
|
17327
|
+
diamond: "\x1B[95m"
|
|
17328
|
+
};
|
|
17329
|
+
const reset2 = "\x1B[0m";
|
|
17330
|
+
const dim2 = "\x1B[2m";
|
|
17331
|
+
const color = tierColors[badge.tier] || "";
|
|
17332
|
+
const checkmark = badge.earned ? "✓" : "○";
|
|
17333
|
+
const progressStr = badge.earned ? "" : ` ${dim2}(${Math.round(badge.progress || 0)}%)${reset2}`;
|
|
17334
|
+
return `${checkmark} ${badge.icon} ${color}${badge.name}${reset2}${progressStr}`;
|
|
17335
|
+
}
|
|
17336
|
+
function getEarnedBadges(badges) {
|
|
17337
|
+
return badges.filter((b2) => b2.earned);
|
|
17338
|
+
}
|
|
17339
|
+
function getBadgesByCategory(badges, category) {
|
|
17340
|
+
return badges.filter((b2) => b2.category === category);
|
|
17341
|
+
}
|
|
17342
|
+
|
|
17343
|
+
// src/commands/badges.ts
|
|
17344
|
+
var badgesCommand = defineCommand2({
|
|
17345
|
+
meta: {
|
|
17346
|
+
name: "badges",
|
|
17347
|
+
description: "View your earned badges and achievements"
|
|
17348
|
+
},
|
|
17349
|
+
args: {
|
|
17350
|
+
all: {
|
|
17351
|
+
type: "boolean",
|
|
17352
|
+
description: "Show all badges including unearned",
|
|
17353
|
+
default: false
|
|
17354
|
+
},
|
|
17355
|
+
json: {
|
|
17356
|
+
type: "boolean",
|
|
17357
|
+
description: "Output as JSON",
|
|
17358
|
+
default: false
|
|
17359
|
+
}
|
|
17360
|
+
},
|
|
17361
|
+
async run({ args }) {
|
|
17362
|
+
const jsonOutput = args.json;
|
|
17363
|
+
const showAll = args.all;
|
|
17364
|
+
if (!jsonOutput) {
|
|
17365
|
+
console.log();
|
|
17366
|
+
console.log(colors2.bold(colors2.primary(" Badges & Achievements")));
|
|
17367
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17368
|
+
console.log();
|
|
17369
|
+
}
|
|
17370
|
+
const spinner = jsonOutput ? null : createSpinner("Computing badges...");
|
|
17371
|
+
if (spinner)
|
|
17372
|
+
spinner.start();
|
|
17373
|
+
const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
17374
|
+
const projectDir = process.cwd();
|
|
17375
|
+
const git = await collectGit(projectDir, since);
|
|
17376
|
+
const agents = await collectAgentSessions(since);
|
|
17377
|
+
const tests = await collectTestResults(projectDir);
|
|
17378
|
+
const score = computeNaironScore(git ?? undefined, agents ?? undefined, tests ?? undefined);
|
|
17379
|
+
const allBadges = computeBadges(score, agents, git);
|
|
17380
|
+
const earnedBadges = getEarnedBadges(allBadges);
|
|
17381
|
+
if (spinner)
|
|
17382
|
+
spinner.succeed(`${earnedBadges.length}/${allBadges.length} badges earned`);
|
|
17383
|
+
if (jsonOutput) {
|
|
17384
|
+
console.log(JSON.stringify({
|
|
17385
|
+
earned: earnedBadges.length,
|
|
17386
|
+
total: allBadges.length,
|
|
17387
|
+
badges: showAll ? allBadges : earnedBadges
|
|
17388
|
+
}, null, 2));
|
|
17389
|
+
return;
|
|
17390
|
+
}
|
|
17391
|
+
console.log();
|
|
17392
|
+
const categories = ["score", "efficiency", "consistency", "tools", "special"];
|
|
17393
|
+
const categoryNames = {
|
|
17394
|
+
score: "Score Milestones",
|
|
17395
|
+
efficiency: "Efficiency",
|
|
17396
|
+
consistency: "Consistency",
|
|
17397
|
+
tools: "Tools & Models",
|
|
17398
|
+
special: "Special"
|
|
17399
|
+
};
|
|
17400
|
+
for (const category of categories) {
|
|
17401
|
+
const categoryBadges = getBadgesByCategory(allBadges, category);
|
|
17402
|
+
const badgesToShow = showAll ? categoryBadges : categoryBadges.filter((b2) => b2.earned);
|
|
17403
|
+
if (badgesToShow.length === 0)
|
|
17404
|
+
continue;
|
|
17405
|
+
console.log(` ${colors2.bold(categoryNames[category])}`);
|
|
17406
|
+
console.log();
|
|
17407
|
+
for (const badge of badgesToShow) {
|
|
17408
|
+
console.log(` ${formatBadge(badge)}`);
|
|
17409
|
+
if (showAll && !badge.earned) {
|
|
17410
|
+
console.log(` ${colors2.dim(badge.description)}`);
|
|
17411
|
+
}
|
|
17412
|
+
}
|
|
17413
|
+
console.log();
|
|
17414
|
+
}
|
|
17415
|
+
const earnedCount = earnedBadges.length;
|
|
17416
|
+
const totalCount = allBadges.length;
|
|
17417
|
+
const percentage = Math.round(earnedCount / totalCount * 100);
|
|
17418
|
+
console.log(colors2.dim(" " + "─".repeat(40)));
|
|
17419
|
+
console.log(` ${icons.success} ${colors2.bold(`${earnedCount}/${totalCount}`)} badges earned ${colors2.dim(`(${percentage}%)`)}`);
|
|
17420
|
+
if (!showAll && earnedCount < totalCount) {
|
|
17421
|
+
console.log(` ${colors2.dim(`Run ${colors2.primary("nb badges --all")} to see all available badges`)}`);
|
|
17422
|
+
}
|
|
17423
|
+
console.log();
|
|
17424
|
+
}
|
|
17425
|
+
});
|
|
17426
|
+
|
|
16957
17427
|
// src/index.ts
|
|
16958
17428
|
var VERSION = "0.0.13";
|
|
16959
17429
|
var CYAN = "\x1B[36m";
|
|
@@ -16984,7 +17454,7 @@ function showBanner() {
|
|
|
16984
17454
|
log("");
|
|
16985
17455
|
}
|
|
16986
17456
|
var args = process.argv.slice(2);
|
|
16987
|
-
var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export"];
|
|
17457
|
+
var subcommands = ["init", "scan", "report", "dashboard", "insights", "tools", "doctor", "publish", "history", "cost", "session", "export", "pr", "badges"];
|
|
16988
17458
|
var hasSubcommand = args.some((arg) => subcommands.includes(arg));
|
|
16989
17459
|
var hasHelp = args.includes("--help") || args.includes("-h");
|
|
16990
17460
|
var hasVersion = args.includes("--version");
|
|
@@ -17009,7 +17479,9 @@ if (!hasSubcommand && !hasHelp && !hasVersion && args.length === 0) {
|
|
|
17009
17479
|
history: historyCommand,
|
|
17010
17480
|
cost: costCommand,
|
|
17011
17481
|
session: sessionCommand,
|
|
17012
|
-
export: exportCommand
|
|
17482
|
+
export: exportCommand,
|
|
17483
|
+
pr: prCommand,
|
|
17484
|
+
badges: badgesCommand
|
|
17013
17485
|
}
|
|
17014
17486
|
});
|
|
17015
17487
|
runMain(main);
|