standout 0.5.24 → 0.5.26
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/cursor.d.ts +21 -18
- package/dist/cursor.js +95 -20
- package/dist/wrapped/aggregate.js +14 -6
- package/dist/wrapped/preview.js +1 -0
- package/dist/wrapped/render.js +40 -25
- package/dist/wrapped/types.d.ts +3 -1
- package/package.json +1 -1
package/dist/cursor.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface CursorStats {
|
|
|
11
11
|
total: number;
|
|
12
12
|
};
|
|
13
13
|
models: string[];
|
|
14
|
+
model_session_counts: Record<string, number>;
|
|
15
|
+
tool_call_counts: Record<string, number>;
|
|
14
16
|
hour_buckets: number[];
|
|
15
17
|
weekend_ratio: number;
|
|
16
18
|
languages: Record<string, number>;
|
|
@@ -18,15 +20,12 @@ export interface CursorStats {
|
|
|
18
20
|
prompt_frequency: PromptFrequency[];
|
|
19
21
|
exchanges: ConversationExchange[];
|
|
20
22
|
interaction: InteractionSignals;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cache_tokens: number;
|
|
28
|
-
primary_model: string | null;
|
|
29
|
-
}[];
|
|
23
|
+
total_input_tokens: number;
|
|
24
|
+
total_output_tokens: number;
|
|
25
|
+
total_cache_read_tokens: number;
|
|
26
|
+
total_cache_write_tokens: number;
|
|
27
|
+
tokens_estimated: true;
|
|
28
|
+
monthly_buckets: CursorMonthlyBucket[];
|
|
30
29
|
all_time?: {
|
|
31
30
|
total_sessions: number;
|
|
32
31
|
active_days: number;
|
|
@@ -37,18 +36,22 @@ export interface CursorStats {
|
|
|
37
36
|
total_output_tokens: number;
|
|
38
37
|
total_cache_read_tokens: number;
|
|
39
38
|
total_cache_write_tokens: number;
|
|
40
|
-
monthly_buckets:
|
|
41
|
-
month: string;
|
|
42
|
-
sessions: number;
|
|
43
|
-
duration_hours: number;
|
|
44
|
-
input_tokens: number;
|
|
45
|
-
output_tokens: number;
|
|
46
|
-
cache_tokens: number;
|
|
47
|
-
primary_model: string | null;
|
|
48
|
-
}[];
|
|
39
|
+
monthly_buckets: CursorMonthlyBucket[];
|
|
49
40
|
};
|
|
50
41
|
}
|
|
42
|
+
interface CursorMonthlyBucket {
|
|
43
|
+
month: string;
|
|
44
|
+
sessions: number;
|
|
45
|
+
duration_hours: number;
|
|
46
|
+
input_tokens: number;
|
|
47
|
+
output_tokens: number;
|
|
48
|
+
cache_read_tokens: number;
|
|
49
|
+
cache_write_tokens: number;
|
|
50
|
+
cache_tokens: number;
|
|
51
|
+
primary_model: string | null;
|
|
52
|
+
}
|
|
51
53
|
export declare function gatherCursor(opts?: {
|
|
52
54
|
dbPath?: string;
|
|
53
55
|
windowStartMs?: number;
|
|
54
56
|
}): Promise<CursorStats | null>;
|
|
57
|
+
export {};
|
package/dist/cursor.js
CHANGED
|
@@ -16,6 +16,27 @@ const CORRECTION_RE = /\b(no|nope|actually|wrong|incorrect|instead|revert|undo|d
|
|
|
16
16
|
// Mirrors ai-usage.ts — cannot import (would create a module cycle).
|
|
17
17
|
const TRIVIAL_ASK_RE = /^(y|n|yes|yep|yeah|ya|ok|okay|k|sure|go|go ahead|do it|continue|please continue|next|thanks|thank you|thx|ty|nice|cool|perfect|great|good|done|ship it|lgtm|👍|\.|\?)\b[\s.!]*$/i;
|
|
18
18
|
const SHELL_COMMAND_RE = /^(npx|npm|pnpm|yarn|bun|git|cd|ls|cat|node|python|pip|brew|sudo|docker|curl|echo|mkdir|rm|mv|cp)\b/i;
|
|
19
|
+
// Cursor persists no usable token/cost data locally — bubble `tokenCount` is
|
|
20
|
+
// zeroed and composer `usageData` is empty. We estimate both from active
|
|
21
|
+
// duration using the economics of our Claude Code cohort (Cursor runs the same
|
|
22
|
+
// Claude models). The per-hour token split is calibrated so that, priced at the
|
|
23
|
+
// generic retail fallback rate, it totals ~$20/active-hour — the cohort's
|
|
24
|
+
// blended retail $/hour. Surfaced with an "estimated" disclaimer on the cards.
|
|
25
|
+
const CURSOR_EST_TOKENS_PER_HOUR = {
|
|
26
|
+
input: 38_000,
|
|
27
|
+
output: 110_000,
|
|
28
|
+
cacheRead: 24_750_000,
|
|
29
|
+
cacheWrite: 750_000,
|
|
30
|
+
};
|
|
31
|
+
function estimateCursorTokens(hours) {
|
|
32
|
+
const h = hours > 0 ? hours : 0;
|
|
33
|
+
return {
|
|
34
|
+
input: Math.round(CURSOR_EST_TOKENS_PER_HOUR.input * h),
|
|
35
|
+
output: Math.round(CURSOR_EST_TOKENS_PER_HOUR.output * h),
|
|
36
|
+
cacheRead: Math.round(CURSOR_EST_TOKENS_PER_HOUR.cacheRead * h),
|
|
37
|
+
cacheWrite: Math.round(CURSOR_EST_TOKENS_PER_HOUR.cacheWrite * h),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
19
40
|
function isLowSignalAsk(sample) {
|
|
20
41
|
const t = sample.trim();
|
|
21
42
|
return t.length < 12 || TRIVIAL_ASK_RE.test(t) || SHELL_COMMAND_RE.test(t);
|
|
@@ -159,13 +180,20 @@ async function readState(dbPath, Driver) {
|
|
|
159
180
|
continue;
|
|
160
181
|
const composerId = parsed.composerId ??
|
|
161
182
|
row.key.slice("composerData:".length);
|
|
183
|
+
const modelConfig = parsed.modelConfig && typeof parsed.modelConfig === "object"
|
|
184
|
+
? parsed.modelConfig
|
|
185
|
+
: null;
|
|
186
|
+
const modelName = (modelConfig && typeof modelConfig.modelName === "string"
|
|
187
|
+
? modelConfig.modelName
|
|
188
|
+
: null) ??
|
|
189
|
+
(typeof parsed.latestSelectedModel === "string"
|
|
190
|
+
? parsed.latestSelectedModel
|
|
191
|
+
: null);
|
|
162
192
|
composers.set(composerId, {
|
|
163
193
|
composerId,
|
|
164
194
|
createdAt: numericTs(parsed.createdAt),
|
|
165
195
|
lastUpdatedAt: numericTs(parsed.lastUpdatedAt),
|
|
166
|
-
|
|
167
|
-
? parsed.latestSelectedModel
|
|
168
|
-
: null,
|
|
196
|
+
modelName: modelName && modelName !== "default" ? modelName : null,
|
|
169
197
|
});
|
|
170
198
|
}
|
|
171
199
|
if (rows.length < PAGE)
|
|
@@ -196,12 +224,22 @@ async function readState(dbPath, Driver) {
|
|
|
196
224
|
const text = typeof parsed.text === "string" ? parsed.text : null;
|
|
197
225
|
const createdAt = numericTs(parsed.createdAt);
|
|
198
226
|
const filePaths = extractFilePaths(parsed);
|
|
227
|
+
const tool = parsed.toolFormerData && typeof parsed.toolFormerData === "object"
|
|
228
|
+
? parsed.toolFormerData
|
|
229
|
+
: null;
|
|
230
|
+
const toolName = tool &&
|
|
231
|
+
(typeof tool.name === "string"
|
|
232
|
+
? tool.name
|
|
233
|
+
: typeof tool.tool === "string"
|
|
234
|
+
? tool.tool
|
|
235
|
+
: null);
|
|
199
236
|
bubbles.set(parts[2], {
|
|
200
237
|
conversationId,
|
|
201
238
|
type,
|
|
202
239
|
text,
|
|
203
240
|
createdAt,
|
|
204
241
|
filePaths,
|
|
242
|
+
toolName: toolName || null,
|
|
205
243
|
});
|
|
206
244
|
}
|
|
207
245
|
if (rows.length < PAGE)
|
|
@@ -294,10 +332,10 @@ export async function gatherCursor(opts = {}) {
|
|
|
294
332
|
first_session: allTime.first_session,
|
|
295
333
|
last_session: allTime.last_session,
|
|
296
334
|
total_duration_hours: allTime.total_duration_hours,
|
|
297
|
-
total_input_tokens:
|
|
298
|
-
total_output_tokens:
|
|
299
|
-
total_cache_read_tokens:
|
|
300
|
-
total_cache_write_tokens:
|
|
335
|
+
total_input_tokens: allTime.total_input_tokens,
|
|
336
|
+
total_output_tokens: allTime.total_output_tokens,
|
|
337
|
+
total_cache_read_tokens: allTime.total_cache_read_tokens,
|
|
338
|
+
total_cache_write_tokens: allTime.total_cache_write_tokens,
|
|
301
339
|
monthly_buckets: allTime.monthly_buckets,
|
|
302
340
|
};
|
|
303
341
|
return stats.total_sessions > 0 || allTime.total_sessions > 0 ? stats : null;
|
|
@@ -335,6 +373,9 @@ function finalize(raw, opts = {}) {
|
|
|
335
373
|
let userTurns = 0;
|
|
336
374
|
let correctionTurns = 0;
|
|
337
375
|
let countedSessions = 0;
|
|
376
|
+
let toolCalls = 0;
|
|
377
|
+
const modelSessionCounts = {};
|
|
378
|
+
const toolCallCounts = {};
|
|
338
379
|
const monthly = new Map();
|
|
339
380
|
for (const [composerId, composer] of raw.composers.entries()) {
|
|
340
381
|
const entry = sessionBubbles.get(composerId);
|
|
@@ -366,6 +407,10 @@ function finalize(raw, opts = {}) {
|
|
|
366
407
|
month.sessions += 1;
|
|
367
408
|
month.durationMs += durationMs;
|
|
368
409
|
monthly.set(monthKey, month);
|
|
410
|
+
if (composer.modelName) {
|
|
411
|
+
modelSessionCounts[composer.modelName] =
|
|
412
|
+
(modelSessionCounts[composer.modelName] ?? 0) + 1;
|
|
413
|
+
}
|
|
369
414
|
for (const ts of windowTs) {
|
|
370
415
|
const d = new Date(ts);
|
|
371
416
|
if (Number.isNaN(d.getTime()))
|
|
@@ -385,6 +430,11 @@ function finalize(raw, opts = {}) {
|
|
|
385
430
|
if (allFiles.size < MAX_FILE_PATHS)
|
|
386
431
|
allFiles.add(fp);
|
|
387
432
|
}
|
|
433
|
+
if (bubble.toolName) {
|
|
434
|
+
toolCalls += 1;
|
|
435
|
+
toolCallCounts[bubble.toolName] =
|
|
436
|
+
(toolCallCounts[bubble.toolName] ?? 0) + 1;
|
|
437
|
+
}
|
|
388
438
|
}
|
|
389
439
|
const windowSamples = windowBubbles
|
|
390
440
|
.filter((bubble) => bubble.type === 1 && bubble.text)
|
|
@@ -443,6 +493,34 @@ function finalize(raw, opts = {}) {
|
|
|
443
493
|
languages[lang] = (languages[lang] || 0) + 1;
|
|
444
494
|
}
|
|
445
495
|
}
|
|
496
|
+
const monthlyBuckets = [...monthly.entries()]
|
|
497
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
498
|
+
.map(([month, b]) => {
|
|
499
|
+
const hours = +(b.durationMs / 1000 / 3600).toFixed(1);
|
|
500
|
+
const est = estimateCursorTokens(hours);
|
|
501
|
+
return {
|
|
502
|
+
month,
|
|
503
|
+
sessions: b.sessions,
|
|
504
|
+
duration_hours: hours,
|
|
505
|
+
input_tokens: est.input,
|
|
506
|
+
output_tokens: est.output,
|
|
507
|
+
cache_read_tokens: est.cacheRead,
|
|
508
|
+
cache_write_tokens: est.cacheWrite,
|
|
509
|
+
cache_tokens: est.cacheRead + est.cacheWrite,
|
|
510
|
+
primary_model: null,
|
|
511
|
+
};
|
|
512
|
+
});
|
|
513
|
+
// Totals are the exact sum of the (rounded-per-month) buckets so the top-level
|
|
514
|
+
// figures metrics.ts reads match what the card sums from monthly_buckets.
|
|
515
|
+
const totals = monthlyBuckets.reduce((acc, b) => ({
|
|
516
|
+
input: acc.input + b.input_tokens,
|
|
517
|
+
output: acc.output + b.output_tokens,
|
|
518
|
+
cacheRead: acc.cacheRead + b.cache_read_tokens,
|
|
519
|
+
cacheWrite: acc.cacheWrite + b.cache_write_tokens,
|
|
520
|
+
}), { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
|
521
|
+
const models = Object.entries(modelSessionCounts)
|
|
522
|
+
.sort((a, b) => b[1] - a[1])
|
|
523
|
+
.map(([model]) => model);
|
|
446
524
|
return {
|
|
447
525
|
total_sessions: countedSessions,
|
|
448
526
|
active_days: activeDays.size,
|
|
@@ -458,7 +536,9 @@ function finalize(raw, opts = {}) {
|
|
|
458
536
|
assistant: assistantTotal,
|
|
459
537
|
total: userTotal + assistantTotal,
|
|
460
538
|
},
|
|
461
|
-
models
|
|
539
|
+
models,
|
|
540
|
+
model_session_counts: modelSessionCounts,
|
|
541
|
+
tool_call_counts: toolCallCounts,
|
|
462
542
|
hour_buckets: hourBuckets,
|
|
463
543
|
weekend_ratio: totalEvents > 0 ? +(weekendEvents / totalEvents).toFixed(2) : 0,
|
|
464
544
|
languages,
|
|
@@ -470,18 +550,13 @@ function finalize(raw, opts = {}) {
|
|
|
470
550
|
interaction: {
|
|
471
551
|
user_turns: userTurns,
|
|
472
552
|
correction_turns: correctionTurns,
|
|
473
|
-
tool_calls:
|
|
553
|
+
tool_calls: toolCalls,
|
|
474
554
|
},
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
input_tokens: 0,
|
|
482
|
-
output_tokens: 0,
|
|
483
|
-
cache_tokens: 0,
|
|
484
|
-
primary_model: null,
|
|
485
|
-
})),
|
|
555
|
+
total_input_tokens: totals.input,
|
|
556
|
+
total_output_tokens: totals.output,
|
|
557
|
+
total_cache_read_tokens: totals.cacheRead,
|
|
558
|
+
total_cache_write_tokens: totals.cacheWrite,
|
|
559
|
+
tokens_estimated: true,
|
|
560
|
+
monthly_buckets: monthlyBuckets,
|
|
486
561
|
};
|
|
487
562
|
}
|
|
@@ -469,6 +469,7 @@ export function aggregateView(args) {
|
|
|
469
469
|
total_tokens_percentile: rankSummary.total_tokens_percentile ?? null,
|
|
470
470
|
avg_agents_percentile: rankSummary.avg_agents_percentile ?? null,
|
|
471
471
|
weekend_pct_percentile: rankSummary.weekend_pct_percentile ?? null,
|
|
472
|
+
ai_assisted_pct_percentile: rankSummary.ai_assisted_pct_percentile ?? null,
|
|
472
473
|
},
|
|
473
474
|
proficiency: parseProficiency(computedObj.proficiency),
|
|
474
475
|
tokens,
|
|
@@ -494,13 +495,15 @@ function computeTokens(aiUsage) {
|
|
|
494
495
|
const cacheTotal = (b) => b.cache_read_tokens !== undefined || b.cache_write_tokens !== undefined
|
|
495
496
|
? (b.cache_read_tokens ?? 0) + (b.cache_write_tokens ?? 0)
|
|
496
497
|
: (b.cache_tokens ?? 0);
|
|
497
|
-
// Cursor exposes no
|
|
498
|
-
//
|
|
499
|
-
//
|
|
500
|
-
//
|
|
498
|
+
// Cursor exposes no local token counts — its tokens/cost are estimated from
|
|
499
|
+
// active time (see cursor.ts). Flag it so the card discloses the estimate, but
|
|
500
|
+
// only when it was actually used in THIS window (the card is "last 30 days").
|
|
501
|
+
// All-time Cursor sessions don't count: claiming "Cursor too" when they haven't
|
|
502
|
+
// opened it in a month is just wrong.
|
|
501
503
|
const CURSOR_MIN_SESSIONS = 3;
|
|
502
504
|
const cursorStats = aiUsage["cursor"];
|
|
503
|
-
const
|
|
505
|
+
const cursorEstimated = !!(cursorStats && (cursorStats.total_sessions ?? 0) >= CURSOR_MIN_SESSIONS);
|
|
506
|
+
let cursorTokens = 0;
|
|
504
507
|
for (const src of SOURCES) {
|
|
505
508
|
const stats = aiUsage[src];
|
|
506
509
|
if (!stats)
|
|
@@ -511,6 +514,8 @@ function computeTokens(aiUsage) {
|
|
|
511
514
|
const cache = cacheTotal(b);
|
|
512
515
|
workTokens += work;
|
|
513
516
|
cacheTokens += cache;
|
|
517
|
+
if (src === "cursor")
|
|
518
|
+
cursorTokens += work + cache;
|
|
514
519
|
// The per-tool bars + sparkline show the cache-inclusive total (matches the
|
|
515
520
|
// rainbow headline); the "generated" callout is the work-only number.
|
|
516
521
|
byTool[TOOL_LABELS[src] ?? src] =
|
|
@@ -569,7 +574,10 @@ function computeTokens(aiUsage) {
|
|
|
569
574
|
by_tool: sortedByTool,
|
|
570
575
|
six_month_buckets: sixMonth,
|
|
571
576
|
estimated_retail_cost_usd: Math.round(totalCost),
|
|
572
|
-
|
|
577
|
+
cursor_estimated: cursorEstimated,
|
|
578
|
+
cursor_token_share: cursorEstimated && workTokens + cacheTokens > 0
|
|
579
|
+
? cursorTokens / (workTokens + cacheTokens)
|
|
580
|
+
: 0,
|
|
573
581
|
};
|
|
574
582
|
}
|
|
575
583
|
function computeToolRelationship(aiUsage) {
|
package/dist/wrapped/preview.js
CHANGED
package/dist/wrapped/render.js
CHANGED
|
@@ -200,12 +200,11 @@ function card1Open(view) {
|
|
|
200
200
|
: perDay >= 1
|
|
201
201
|
? "a daily habit at this point."
|
|
202
202
|
: "just getting warmed up.";
|
|
203
|
-
const
|
|
203
|
+
const bench = benchmarkInline(view, view.rank_summary.hours_percentile);
|
|
204
204
|
const lines = [
|
|
205
205
|
chalk.dim("YOU MANAGED"),
|
|
206
206
|
"",
|
|
207
|
-
bigNumber(` ${hours} session-hours`),
|
|
208
|
-
...(badge ? ["", badge] : []),
|
|
207
|
+
bigNumber(` ${hours} session-hours`) + (bench ? ` ${bench}` : ""),
|
|
209
208
|
"",
|
|
210
209
|
chalk.white(` ${verdict}`),
|
|
211
210
|
chalk.dim(" across parallel agent sessions, last 30 days"),
|
|
@@ -261,7 +260,7 @@ function cardRhythm(view) {
|
|
|
261
260
|
: "no clear peak";
|
|
262
261
|
const weekendPct = Math.round(view.weekend_ratio * 100);
|
|
263
262
|
const weekdayPct = 100 - weekendPct;
|
|
264
|
-
const
|
|
263
|
+
const weekendBench = benchmarkInline(view, view.rank_summary.weekend_pct_percentile);
|
|
265
264
|
const isNightOwl = critter?.key === "night_owl";
|
|
266
265
|
const subhead = isNightOwl
|
|
267
266
|
? "while the 9-5ers sleep, you ship."
|
|
@@ -281,7 +280,7 @@ function cardRhythm(view) {
|
|
|
281
280
|
"",
|
|
282
281
|
` ${chalk.white("weekdays".padEnd(9))} ${bar(weekdayPct, 100, 20)} ${chalk.dim(weekdayPct + "%")}`,
|
|
283
282
|
` ${chalk.white("weekends".padEnd(9))} ${bar(weekendPct, 100, 20)} ${chalk.dim(weekendPct + "%")}`,
|
|
284
|
-
...(
|
|
283
|
+
...(weekendBench ? ["", ` ${weekendBench}`] : []),
|
|
285
284
|
"",
|
|
286
285
|
];
|
|
287
286
|
if (view.rhythm_comment) {
|
|
@@ -350,13 +349,20 @@ function card7AINative(view) {
|
|
|
350
349
|
: pct > 0
|
|
351
350
|
? "Claude's in your commit history. no going back."
|
|
352
351
|
: "no AI-co-authored commits yet.";
|
|
352
|
+
const bench = benchmarkInline(view, view.rank_summary.ai_assisted_pct_percentile);
|
|
353
|
+
const streak = view.streak_days;
|
|
353
354
|
const lines = [
|
|
354
355
|
chalk.dim("YOU'RE AI-NATIVE"),
|
|
355
356
|
"",
|
|
356
357
|
` commits since 2024: ${chalk.white(view.total_commits.toLocaleString())}`,
|
|
357
358
|
` AI co-authored: ${chalk.white(view.ai_assisted_commits.toLocaleString())}`,
|
|
358
359
|
"",
|
|
359
|
-
` ${bar(pct, 100,
|
|
360
|
+
` ${bar(pct, 100, 22)} ${chalk.white(pct + "%")}${bench ? ` ${bench}` : ""}`,
|
|
361
|
+
...(streak >= 2
|
|
362
|
+
? [
|
|
363
|
+
` ${chalk.white(`${streak}-day streak`)}${chalk.dim(", coding without missing a day.")}`,
|
|
364
|
+
]
|
|
365
|
+
: []),
|
|
360
366
|
"",
|
|
361
367
|
chalk.dim(subhead),
|
|
362
368
|
].join("\n");
|
|
@@ -399,16 +405,20 @@ function topPercent(percentile) {
|
|
|
399
405
|
const top = topPercentNumber(percentile);
|
|
400
406
|
return top === null ? null : `top ${top}%`;
|
|
401
407
|
}
|
|
402
|
-
// Inline "top X%" benchmark
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
|
|
408
|
+
// Inline "(top X% of users)" cohort benchmark appended to a metric. Faint, except
|
|
409
|
+
// when the user lands in the top <10% the percentile renders in the rainbow
|
|
410
|
+
// gradient (the rest stays faint). Returns "" (omitted) for early adopters or
|
|
411
|
+
// until the metric has a percentile, so callers can append conditionally.
|
|
412
|
+
function benchmarkInline(view, percentile) {
|
|
406
413
|
if (view.early_adopter)
|
|
407
|
-
return
|
|
414
|
+
return "";
|
|
408
415
|
const top = topPercentNumber(percentile);
|
|
409
416
|
if (top === null)
|
|
410
|
-
return
|
|
411
|
-
|
|
417
|
+
return "";
|
|
418
|
+
const pct = `${top}%`;
|
|
419
|
+
return top < 10
|
|
420
|
+
? chalk.dim("(top ") + TITLE_GRADIENT(pct) + chalk.dim(" of users)")
|
|
421
|
+
: chalk.dim(`(top ${pct} of users)`);
|
|
412
422
|
}
|
|
413
423
|
function cardRankSummary(view) {
|
|
414
424
|
if (!usageReady(view) || view.total_sessions <= 0)
|
|
@@ -443,9 +453,9 @@ function cardRankSummary(view) {
|
|
|
443
453
|
.filter((stat) => stat.topNumber !== null)
|
|
444
454
|
.sort((a, b) => a.topNumber - b.topNumber)[0];
|
|
445
455
|
const cohort = rank.sample_size > 0
|
|
446
|
-
? `across ${rank.sample_size.toLocaleString()}
|
|
456
|
+
? `across ${rank.sample_size.toLocaleString()} users`
|
|
447
457
|
: view.cohort_size > 0
|
|
448
|
-
? `among ${view.cohort_size.toLocaleString()}
|
|
458
|
+
? `among ${view.cohort_size.toLocaleString()} users so far`
|
|
449
459
|
: "Standout profile summary";
|
|
450
460
|
const row = (stat) => {
|
|
451
461
|
const top = "top" in stat && stat.top
|
|
@@ -485,6 +495,7 @@ function cardTokens(view) {
|
|
|
485
495
|
if (!usageReady(view) || (t.work_tokens <= 0 && t.cache_tokens <= 0))
|
|
486
496
|
return "";
|
|
487
497
|
const totalLine = bigNumber(formatTokens(t.total_30d) + " tokens");
|
|
498
|
+
const tokensBench = benchmarkInline(view, view.rank_summary.total_tokens_percentile);
|
|
488
499
|
const max = Math.max(...t.by_tool.map((x) => x.tokens), 1);
|
|
489
500
|
const toolRows = t.by_tool.length > 0
|
|
490
501
|
? t.by_tool
|
|
@@ -506,6 +517,14 @@ function cardTokens(view) {
|
|
|
506
517
|
const costLine = cost > 0
|
|
507
518
|
? ` ${chalk.dim("would have cost")} ${TITLE_GRADIENT(`~$${cost.toLocaleString()}`)} ${chalk.dim("at retail API rates")}`
|
|
508
519
|
: "";
|
|
520
|
+
// Cursor stores no token counts locally, so its tokens + cost are estimated
|
|
521
|
+
// from active time. Disclose it — wording scales with how much of this card is
|
|
522
|
+
// Cursor (cursor-only users: the whole number is an estimate).
|
|
523
|
+
const cursorNote = t.cursor_estimated
|
|
524
|
+
? t.cursor_token_share >= 0.9
|
|
525
|
+
? chalk.dim(" ≈ estimated from time spent in Cursor (no local token data)")
|
|
526
|
+
: chalk.dim(" includes Cursor (tokens + cost estimated from active time)")
|
|
527
|
+
: "";
|
|
509
528
|
const cachedNote = t.cache_tokens > t.work_tokens
|
|
510
529
|
? chalk.dim(" thankfully, most of them were cached :)")
|
|
511
530
|
: t.cache_tokens > 0
|
|
@@ -517,18 +536,15 @@ function cardTokens(view) {
|
|
|
517
536
|
const lines = [
|
|
518
537
|
chalk.dim("YOU BURNED THIS MANY TOKENS"),
|
|
519
538
|
"",
|
|
520
|
-
` ${totalLine}`,
|
|
521
|
-
benchmarkBadge(view, view.rank_summary.total_tokens_percentile, "of devs by tokens") ?? "",
|
|
539
|
+
` ${totalLine}${tokensBench ? ` ${tokensBench}` : ""}`,
|
|
522
540
|
"",
|
|
523
541
|
cachedNote,
|
|
524
542
|
generatedLine,
|
|
525
543
|
costLine,
|
|
544
|
+
cursorNote,
|
|
526
545
|
"",
|
|
527
546
|
" last 30 days, across:",
|
|
528
547
|
toolRows,
|
|
529
|
-
view.tokens.cursor_untracked
|
|
530
|
-
? chalk.dim(" Cursor used too (its tokens aren't tracked locally)")
|
|
531
|
-
: "",
|
|
532
548
|
"",
|
|
533
549
|
deltaLine,
|
|
534
550
|
]
|
|
@@ -754,12 +770,11 @@ function cardConcurrency(view) {
|
|
|
754
770
|
const longest = days >= 1.5
|
|
755
771
|
? `${days.toFixed(0)} days`
|
|
756
772
|
: `${Math.round(c.longest_session_hours)}h`;
|
|
757
|
-
const
|
|
773
|
+
const bench = benchmarkInline(view, view.rank_summary.avg_agents_percentile);
|
|
758
774
|
const lines = [
|
|
759
775
|
chalk.dim("HOW MANY AGENTS YOU JUGGLE"),
|
|
760
776
|
"",
|
|
761
|
-
` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk.white("agents open at once")}`,
|
|
762
|
-
...(badge ? ["", badge] : []),
|
|
777
|
+
` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk.white("agents open at once")}${bench ? ` ${bench}` : ""}`,
|
|
763
778
|
"",
|
|
764
779
|
` ${chalk.white("peak".padEnd(14))} ${chalk.dim(`${c.open_tab_peak} at once`)}`,
|
|
765
780
|
` ${chalk.white("longest tab".padEnd(14))} ${chalk.dim(`${longest} open`)}`,
|
|
@@ -866,9 +881,9 @@ export function cardProficiency(view) {
|
|
|
866
881
|
const scoreTop = topPercentNumber(p.score_percentile ?? null);
|
|
867
882
|
const sampleSize = view.rank_summary.sample_size;
|
|
868
883
|
const cohort = scoreTop != null && sampleSize > 0
|
|
869
|
-
? `top ${scoreTop}% of ${sampleSize.toLocaleString()}
|
|
884
|
+
? `top ${scoreTop}% of ${sampleSize.toLocaleString()} users`
|
|
870
885
|
: view.cohort_size > 0
|
|
871
|
-
? `among ${view.cohort_size.toLocaleString()}
|
|
886
|
+
? `among ${view.cohort_size.toLocaleString()} users so far`
|
|
872
887
|
: "";
|
|
873
888
|
const metricRow = (label, value) => value === null
|
|
874
889
|
? null
|
package/dist/wrapped/types.d.ts
CHANGED
|
@@ -97,6 +97,7 @@ export interface WrappedAggregateView {
|
|
|
97
97
|
total_tokens_percentile: number | null;
|
|
98
98
|
avg_agents_percentile: number | null;
|
|
99
99
|
weekend_pct_percentile: number | null;
|
|
100
|
+
ai_assisted_pct_percentile: number | null;
|
|
100
101
|
};
|
|
101
102
|
proficiency: ProficiencyView | null;
|
|
102
103
|
tokens: {
|
|
@@ -114,7 +115,8 @@ export interface WrappedAggregateView {
|
|
|
114
115
|
tokens: number;
|
|
115
116
|
}[];
|
|
116
117
|
estimated_retail_cost_usd: number;
|
|
117
|
-
|
|
118
|
+
cursor_estimated: boolean;
|
|
119
|
+
cursor_token_share: number;
|
|
118
120
|
};
|
|
119
121
|
tool_relationship: {
|
|
120
122
|
kind: "switch";
|