standout 0.5.36 → 0.5.37
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/cli.js +211 -51
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,10 +9,18 @@ import { tmpdir as tmpdir2 } from "node:os";
|
|
|
9
9
|
import { join as join6 } from "node:path";
|
|
10
10
|
import { createInterface as createInterface3 } from "readline";
|
|
11
11
|
|
|
12
|
+
// src/sources.ts
|
|
13
|
+
var SOURCES = ["claude_code", "cowork", "codex", "cursor"];
|
|
14
|
+
var SOURCE_LABELS = {
|
|
15
|
+
claude_code: "Claude Code",
|
|
16
|
+
cowork: "Cowork",
|
|
17
|
+
codex: "Codex",
|
|
18
|
+
cursor: "Cursor"
|
|
19
|
+
};
|
|
20
|
+
|
|
12
21
|
// src/payload.ts
|
|
13
22
|
var MAX_BODY_BYTES = 4e6;
|
|
14
23
|
var MAX_UPLOAD_EXCHANGES = 500;
|
|
15
|
-
var TOOLS = ["claude_code", "codex", "cursor"];
|
|
16
24
|
function byteLength(value) {
|
|
17
25
|
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
18
26
|
}
|
|
@@ -26,7 +34,7 @@ function exchangesOf(aiUsage, tool) {
|
|
|
26
34
|
function totalExchanges(payload) {
|
|
27
35
|
const aiUsage = asRecord(payload.ai_usage);
|
|
28
36
|
if (!aiUsage) return 0;
|
|
29
|
-
return
|
|
37
|
+
return SOURCES.reduce((n, t) => n + exchangesOf(aiUsage, t).length, 0);
|
|
30
38
|
}
|
|
31
39
|
function evenSample(arr, keep) {
|
|
32
40
|
if (arr.length <= keep) return arr;
|
|
@@ -38,7 +46,7 @@ function evenSample(arr, keep) {
|
|
|
38
46
|
function capExchangesInPlace(payload, maxTotal) {
|
|
39
47
|
const aiUsage = asRecord(payload.ai_usage);
|
|
40
48
|
if (!aiUsage) return;
|
|
41
|
-
const present =
|
|
49
|
+
const present = SOURCES.map((t) => ({
|
|
42
50
|
t,
|
|
43
51
|
n: exchangesOf(aiUsage, t).length
|
|
44
52
|
})).filter((x) => x.n > 0);
|
|
@@ -71,7 +79,7 @@ function capPayload(payload) {
|
|
|
71
79
|
}
|
|
72
80
|
const over = () => byteLength(p) > MAX_BODY_BYTES;
|
|
73
81
|
if (over()) {
|
|
74
|
-
for (const t of
|
|
82
|
+
for (const t of SOURCES) {
|
|
75
83
|
const tool = asRecord(aiUsage[t]);
|
|
76
84
|
if (tool && Array.isArray(tool.conversation_samples) && tool.conversation_samples.length) {
|
|
77
85
|
tool.conversation_samples = [];
|
|
@@ -82,7 +90,7 @@ function capPayload(payload) {
|
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
92
|
if (over()) {
|
|
85
|
-
for (const t of
|
|
93
|
+
for (const t of SOURCES) {
|
|
86
94
|
const tool = asRecord(aiUsage[t]);
|
|
87
95
|
if (tool && Array.isArray(tool.prompt_samples) && tool.prompt_samples.length) {
|
|
88
96
|
tool.prompt_samples = [];
|
|
@@ -110,7 +118,7 @@ function capPayload(payload) {
|
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
while (over() && totalExchanges(p) > 0) {
|
|
113
|
-
for (const t of
|
|
121
|
+
for (const t of SOURCES) {
|
|
114
122
|
const tool = asRecord(aiUsage[t]);
|
|
115
123
|
const ex = tool?.exchanges;
|
|
116
124
|
if (tool && Array.isArray(ex) && ex.length > 0) {
|
|
@@ -244,8 +252,15 @@ function boostProficiencyForDisplay(p) {
|
|
|
244
252
|
return { ...p, score, craft_bonus: Math.max(0, score - p.base) };
|
|
245
253
|
}
|
|
246
254
|
|
|
255
|
+
// ../../lib/standout/wrapped/sources.ts
|
|
256
|
+
var WRAPPED_SOURCES = [
|
|
257
|
+
"claude_code",
|
|
258
|
+
"cowork",
|
|
259
|
+
"codex",
|
|
260
|
+
"cursor"
|
|
261
|
+
];
|
|
262
|
+
|
|
247
263
|
// ../../lib/standout/wrapped/stat-pack.ts
|
|
248
|
-
var SOURCES = ["claude_code", "codex", "cursor"];
|
|
249
264
|
var INFRA_FRAMEWORKS = /* @__PURE__ */ new Set([
|
|
250
265
|
"github-actions",
|
|
251
266
|
"terraform",
|
|
@@ -296,7 +311,7 @@ function buildStatPack(profile) {
|
|
|
296
311
|
const frameworkCounts = /* @__PURE__ */ new Map();
|
|
297
312
|
const languageCounts = /* @__PURE__ */ new Map();
|
|
298
313
|
const modelCounts = /* @__PURE__ */ new Map();
|
|
299
|
-
for (const src of
|
|
314
|
+
for (const src of WRAPPED_SOURCES) {
|
|
300
315
|
const stats = aiUsage[src];
|
|
301
316
|
if (!stats) continue;
|
|
302
317
|
const c = stats.concurrency;
|
|
@@ -394,7 +409,6 @@ function peakLabel(hour) {
|
|
|
394
409
|
}
|
|
395
410
|
|
|
396
411
|
// ../../lib/standout/wrapped/share-stats.ts
|
|
397
|
-
var SOURCES2 = ["claude_code", "codex", "cursor"];
|
|
398
412
|
var WRAPPED_COMMUNITY_BOOST = 1e3;
|
|
399
413
|
var boostCommunity = (n) => n > 0 ? n + WRAPPED_COMMUNITY_BOOST : 0;
|
|
400
414
|
var RETAIL_RATES = [
|
|
@@ -476,7 +490,7 @@ function computeTokens(aiUsage) {
|
|
|
476
490
|
let workTokens = 0;
|
|
477
491
|
let cacheTokens = 0;
|
|
478
492
|
let totalCost = 0;
|
|
479
|
-
for (const src of
|
|
493
|
+
for (const src of WRAPPED_SOURCES) {
|
|
480
494
|
const stats = aiUsage[src];
|
|
481
495
|
if (!stats) continue;
|
|
482
496
|
for (const b of stats.monthly_buckets ?? []) {
|
|
@@ -497,7 +511,7 @@ function computeTokens(aiUsage) {
|
|
|
497
511
|
function computeHours(aiUsage) {
|
|
498
512
|
let totalHours = 0;
|
|
499
513
|
let totalSessions = 0;
|
|
500
|
-
for (const src of
|
|
514
|
+
for (const src of WRAPPED_SOURCES) {
|
|
501
515
|
const stats = aiUsage[src];
|
|
502
516
|
if (!stats) continue;
|
|
503
517
|
totalHours += num2(stats.total_duration_hours);
|
|
@@ -507,7 +521,7 @@ function computeHours(aiUsage) {
|
|
|
507
521
|
}
|
|
508
522
|
function computeHistogram(aiUsage) {
|
|
509
523
|
const hours = new Array(24).fill(0);
|
|
510
|
-
for (const src of
|
|
524
|
+
for (const src of WRAPPED_SOURCES) {
|
|
511
525
|
const stats = aiUsage[src];
|
|
512
526
|
if (!stats) continue;
|
|
513
527
|
const allTime = stats.all_time;
|
|
@@ -1592,7 +1606,7 @@ async function renderWrappedCardLocally(prefetched, theme = "dark") {
|
|
|
1592
1606
|
|
|
1593
1607
|
// src/gather.ts
|
|
1594
1608
|
import { execSync as execSync2 } from "child_process";
|
|
1595
|
-
import { existsSync as existsSync5, readFileSync as
|
|
1609
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
1596
1610
|
import { join as join5 } from "path";
|
|
1597
1611
|
import { homedir as homedir5 } from "os";
|
|
1598
1612
|
|
|
@@ -1601,6 +1615,7 @@ import {
|
|
|
1601
1615
|
createReadStream,
|
|
1602
1616
|
existsSync as existsSync2,
|
|
1603
1617
|
readdirSync,
|
|
1618
|
+
readFileSync as readFileSync2,
|
|
1604
1619
|
realpathSync,
|
|
1605
1620
|
statSync
|
|
1606
1621
|
} from "fs";
|
|
@@ -2362,7 +2377,7 @@ function registerFile(raw, filePath) {
|
|
|
2362
2377
|
if (raw.filePaths.size > 5e3) return;
|
|
2363
2378
|
raw.filePaths.add(filePath);
|
|
2364
2379
|
}
|
|
2365
|
-
function recordSessionMeta(raw, sessionId, ts, cwd, model, version) {
|
|
2380
|
+
function recordSessionMeta(raw, sessionId, ts, cwd, model, version, projectLabel = null) {
|
|
2366
2381
|
const minute = Math.floor(ts / 6e4);
|
|
2367
2382
|
const existing = raw.sessions.get(sessionId);
|
|
2368
2383
|
if (existing) {
|
|
@@ -2370,6 +2385,8 @@ function recordSessionMeta(raw, sessionId, ts, cwd, model, version) {
|
|
|
2370
2385
|
if (ts > existing.lastTs) existing.lastTs = ts;
|
|
2371
2386
|
existing.eventTimestamps.add(minute);
|
|
2372
2387
|
if (!existing.cwd && cwd) existing.cwd = cwd;
|
|
2388
|
+
if (!existing.projectLabel && projectLabel)
|
|
2389
|
+
existing.projectLabel = projectLabel;
|
|
2373
2390
|
if (model) existing.models.add(model);
|
|
2374
2391
|
if (!existing.version && version) existing.version = version;
|
|
2375
2392
|
} else {
|
|
@@ -2378,6 +2395,7 @@ function recordSessionMeta(raw, sessionId, ts, cwd, model, version) {
|
|
|
2378
2395
|
lastTs: ts,
|
|
2379
2396
|
eventTimestamps: /* @__PURE__ */ new Set([minute]),
|
|
2380
2397
|
cwd,
|
|
2398
|
+
projectLabel,
|
|
2381
2399
|
models: model ? /* @__PURE__ */ new Set([model]) : /* @__PURE__ */ new Set(),
|
|
2382
2400
|
modelTurnCounts: /* @__PURE__ */ new Map(),
|
|
2383
2401
|
inputTokens: 0,
|
|
@@ -2463,6 +2481,93 @@ function listClaudeCodeFiles() {
|
|
|
2463
2481
|
}
|
|
2464
2482
|
return dedupeByRealpath(files);
|
|
2465
2483
|
}
|
|
2484
|
+
function coworkSessionsBaseDir() {
|
|
2485
|
+
const home = homedir2();
|
|
2486
|
+
if (process.platform === "darwin") {
|
|
2487
|
+
return join2(
|
|
2488
|
+
home,
|
|
2489
|
+
"Library",
|
|
2490
|
+
"Application Support",
|
|
2491
|
+
"Claude",
|
|
2492
|
+
"local-agent-mode-sessions"
|
|
2493
|
+
);
|
|
2494
|
+
}
|
|
2495
|
+
if (process.platform === "win32") {
|
|
2496
|
+
const appData = process.env.APPDATA;
|
|
2497
|
+
return appData ? join2(appData, "Claude", "local-agent-mode-sessions") : null;
|
|
2498
|
+
}
|
|
2499
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
2500
|
+
return join2(
|
|
2501
|
+
xdg || join2(home, ".config"),
|
|
2502
|
+
"Claude",
|
|
2503
|
+
"local-agent-mode-sessions"
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
function listCoworkFiles() {
|
|
2507
|
+
const base = coworkSessionsBaseDir();
|
|
2508
|
+
if (!base || !existsSync2(base)) return [];
|
|
2509
|
+
const found = [];
|
|
2510
|
+
const safeReaddir = (dir) => {
|
|
2511
|
+
try {
|
|
2512
|
+
return readdirSync(dir);
|
|
2513
|
+
} catch {
|
|
2514
|
+
return [];
|
|
2515
|
+
}
|
|
2516
|
+
};
|
|
2517
|
+
const isDir = (p) => {
|
|
2518
|
+
try {
|
|
2519
|
+
return statSync(p).isDirectory();
|
|
2520
|
+
} catch {
|
|
2521
|
+
return false;
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
for (const acct of safeReaddir(base)) {
|
|
2525
|
+
const acctDir = join2(base, acct);
|
|
2526
|
+
if (!isDir(acctDir)) continue;
|
|
2527
|
+
for (const org of safeReaddir(acctDir)) {
|
|
2528
|
+
const orgDir = join2(acctDir, org);
|
|
2529
|
+
if (!isDir(orgDir)) continue;
|
|
2530
|
+
for (const entry of safeReaddir(orgDir)) {
|
|
2531
|
+
if (!entry.startsWith("local_")) continue;
|
|
2532
|
+
const sessionDir = join2(orgDir, entry);
|
|
2533
|
+
if (!isDir(sessionDir)) continue;
|
|
2534
|
+
let title = "";
|
|
2535
|
+
try {
|
|
2536
|
+
const meta = JSON.parse(
|
|
2537
|
+
readFileSync2(join2(orgDir, `${entry}.json`), "utf-8")
|
|
2538
|
+
);
|
|
2539
|
+
if (typeof meta.title === "string") title = meta.title.trim();
|
|
2540
|
+
} catch {
|
|
2541
|
+
}
|
|
2542
|
+
const projectsDir = join2(sessionDir, ".claude", "projects");
|
|
2543
|
+
if (!existsSync2(projectsDir)) continue;
|
|
2544
|
+
for (const proj of safeReaddir(projectsDir)) {
|
|
2545
|
+
const projDir = join2(projectsDir, proj);
|
|
2546
|
+
if (!isDir(projDir)) continue;
|
|
2547
|
+
for (const file of safeReaddir(projDir)) {
|
|
2548
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
2549
|
+
const filePath = join2(projDir, file);
|
|
2550
|
+
try {
|
|
2551
|
+
const stat = statSync(filePath);
|
|
2552
|
+
if (!stat.isFile()) continue;
|
|
2553
|
+
found.push({
|
|
2554
|
+
path: filePath,
|
|
2555
|
+
mtime: stat.mtimeMs,
|
|
2556
|
+
title: title || "Cowork session"
|
|
2557
|
+
});
|
|
2558
|
+
} catch {
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const titleByPath = new Map(found.map((f) => [f.path, f.title]));
|
|
2566
|
+
return dedupeByRealpath(found).map((path2) => ({
|
|
2567
|
+
path: path2,
|
|
2568
|
+
title: titleByPath.get(path2) ?? "Cowork session"
|
|
2569
|
+
}));
|
|
2570
|
+
}
|
|
2466
2571
|
function listCodexFiles() {
|
|
2467
2572
|
const base = join2(homedir2(), ".codex", "sessions");
|
|
2468
2573
|
if (!existsSync2(base)) return [];
|
|
@@ -2509,7 +2614,9 @@ function makeTargets(targetInputs) {
|
|
|
2509
2614
|
seenUserPromptThisSession: false,
|
|
2510
2615
|
collectConversation: target.collectConversation ?? false,
|
|
2511
2616
|
sessionPromptCounts: /* @__PURE__ */ new Map(),
|
|
2512
|
-
pendingPrompt: /* @__PURE__ */ new Map()
|
|
2617
|
+
pendingPrompt: /* @__PURE__ */ new Map(),
|
|
2618
|
+
projectLabel: target.projectLabel ?? null,
|
|
2619
|
+
suppressFiles: target.suppressFiles ?? false
|
|
2513
2620
|
}));
|
|
2514
2621
|
}
|
|
2515
2622
|
async function parseClaudeCodeFileTargets(filePath, targetInputs) {
|
|
@@ -2553,12 +2660,28 @@ async function parseClaudeCodeFileTargets(filePath, targetInputs) {
|
|
|
2553
2660
|
}
|
|
2554
2661
|
}
|
|
2555
2662
|
if (shouldCount) {
|
|
2556
|
-
recordSessionMeta(
|
|
2663
|
+
recordSessionMeta(
|
|
2664
|
+
target.raw,
|
|
2665
|
+
sessionId,
|
|
2666
|
+
ts,
|
|
2667
|
+
cwd,
|
|
2668
|
+
null,
|
|
2669
|
+
version,
|
|
2670
|
+
target.projectLabel
|
|
2671
|
+
);
|
|
2557
2672
|
}
|
|
2558
2673
|
} else if (type === "assistant" && message) {
|
|
2559
2674
|
const model = typeof message.model === "string" ? message.model : null;
|
|
2560
2675
|
if (shouldCount) {
|
|
2561
|
-
recordSessionMeta(
|
|
2676
|
+
recordSessionMeta(
|
|
2677
|
+
target.raw,
|
|
2678
|
+
sessionId,
|
|
2679
|
+
ts,
|
|
2680
|
+
cwd,
|
|
2681
|
+
model,
|
|
2682
|
+
version,
|
|
2683
|
+
target.projectLabel
|
|
2684
|
+
);
|
|
2562
2685
|
}
|
|
2563
2686
|
const usage = message.usage;
|
|
2564
2687
|
const messageId = typeof message.id === "string" ? message.id : null;
|
|
@@ -2593,7 +2716,7 @@ async function parseClaudeCodeFileTargets(filePath, targetInputs) {
|
|
|
2593
2716
|
);
|
|
2594
2717
|
}
|
|
2595
2718
|
const input = item.input;
|
|
2596
|
-
if (input) {
|
|
2719
|
+
if (input && !target.suppressFiles) {
|
|
2597
2720
|
registerFile(
|
|
2598
2721
|
target.raw,
|
|
2599
2722
|
input.file_path
|
|
@@ -2917,8 +3040,10 @@ function finalize2(raw) {
|
|
|
2917
3040
|
if (s.lastTs > lastTs) lastTs = s.lastTs;
|
|
2918
3041
|
if (s.cwd) {
|
|
2919
3042
|
if (PARALLEL_WORKSPACE_RE.test(s.cwd)) parallelCwds.add(s.cwd);
|
|
2920
|
-
|
|
2921
|
-
|
|
3043
|
+
}
|
|
3044
|
+
const projectKey = s.projectLabel ?? (s.cwd ? compactCwd(s.cwd) : null);
|
|
3045
|
+
if (projectKey) {
|
|
3046
|
+
projectCounts.set(projectKey, (projectCounts.get(projectKey) || 0) + 1);
|
|
2922
3047
|
}
|
|
2923
3048
|
s.models.forEach((m) => {
|
|
2924
3049
|
if (m && !m.startsWith("<") && !m.includes("synthetic")) modelsSet.add(m);
|
|
@@ -3152,11 +3277,15 @@ function withAllTime(recent, allTime, previous = null) {
|
|
|
3152
3277
|
async function gatherAiUsage() {
|
|
3153
3278
|
const claudeFiles = listClaudeCodeFiles();
|
|
3154
3279
|
const codexFiles = listCodexFiles();
|
|
3280
|
+
const coworkFiles = listCoworkFiles();
|
|
3155
3281
|
const windowStartMs = Date.now() - USAGE_WINDOW_MS;
|
|
3156
3282
|
const previousWindowStartMs = windowStartMs - USAGE_WINDOW_MS;
|
|
3157
3283
|
const claudeRecentRaw = emptyRaw();
|
|
3158
3284
|
const claudePreviousRaw = emptyRaw();
|
|
3159
3285
|
const claudeAllRaw = emptyRaw();
|
|
3286
|
+
const coworkRecentRaw = emptyRaw();
|
|
3287
|
+
const coworkPreviousRaw = emptyRaw();
|
|
3288
|
+
const coworkAllRaw = emptyRaw();
|
|
3160
3289
|
const codexRecentRaw = emptyRaw();
|
|
3161
3290
|
const codexPreviousRaw = emptyRaw();
|
|
3162
3291
|
const codexAllRaw = emptyRaw();
|
|
@@ -3184,6 +3313,32 @@ async function gatherAiUsage() {
|
|
|
3184
3313
|
}
|
|
3185
3314
|
}
|
|
3186
3315
|
})(),
|
|
3316
|
+
(async () => {
|
|
3317
|
+
for (const { path: path2, title } of coworkFiles) {
|
|
3318
|
+
try {
|
|
3319
|
+
await parseClaudeCodeFileTargets(path2, [
|
|
3320
|
+
{
|
|
3321
|
+
raw: coworkRecentRaw,
|
|
3322
|
+
opts: { windowStartMs },
|
|
3323
|
+
collectConversation: true,
|
|
3324
|
+
projectLabel: title,
|
|
3325
|
+
suppressFiles: true
|
|
3326
|
+
},
|
|
3327
|
+
{
|
|
3328
|
+
raw: coworkPreviousRaw,
|
|
3329
|
+
opts: {
|
|
3330
|
+
windowStartMs: previousWindowStartMs,
|
|
3331
|
+
windowEndMs: windowStartMs
|
|
3332
|
+
},
|
|
3333
|
+
projectLabel: title,
|
|
3334
|
+
suppressFiles: true
|
|
3335
|
+
},
|
|
3336
|
+
{ raw: coworkAllRaw, projectLabel: title, suppressFiles: true }
|
|
3337
|
+
]);
|
|
3338
|
+
} catch {
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
})(),
|
|
3187
3342
|
(async () => {
|
|
3188
3343
|
for (const file of codexFiles) {
|
|
3189
3344
|
try {
|
|
@@ -3213,6 +3368,11 @@ async function gatherAiUsage() {
|
|
|
3213
3368
|
finalize2(claudeAllRaw),
|
|
3214
3369
|
finalize2(claudePreviousRaw)
|
|
3215
3370
|
),
|
|
3371
|
+
cowork: withAllTime(
|
|
3372
|
+
finalize2(coworkRecentRaw),
|
|
3373
|
+
finalize2(coworkAllRaw),
|
|
3374
|
+
finalize2(coworkPreviousRaw)
|
|
3375
|
+
),
|
|
3216
3376
|
codex: withAllTime(
|
|
3217
3377
|
finalize2(codexRecentRaw),
|
|
3218
3378
|
finalize2(codexAllRaw),
|
|
@@ -3224,7 +3384,7 @@ async function gatherAiUsage() {
|
|
|
3224
3384
|
|
|
3225
3385
|
// src/dev-env.ts
|
|
3226
3386
|
import { execSync } from "child_process";
|
|
3227
|
-
import { existsSync as existsSync3, readFileSync as
|
|
3387
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
3228
3388
|
import { homedir as homedir3 } from "os";
|
|
3229
3389
|
import { join as join3 } from "path";
|
|
3230
3390
|
var INTERESTING_TOOLS = [
|
|
@@ -3272,7 +3432,7 @@ function execSafe(cmd, timeout = 5e3) {
|
|
|
3272
3432
|
}
|
|
3273
3433
|
function readFileSafe(path2, maxBytes = 3e3) {
|
|
3274
3434
|
try {
|
|
3275
|
-
const content =
|
|
3435
|
+
const content = readFileSync3(path2, "utf-8");
|
|
3276
3436
|
return content.length > maxBytes ? content.slice(0, maxBytes) : content;
|
|
3277
3437
|
} catch {
|
|
3278
3438
|
return null;
|
|
@@ -3299,7 +3459,7 @@ function collectAgentsMd() {
|
|
|
3299
3459
|
function summarizeClaudeSettingsFile(path2, displayPath) {
|
|
3300
3460
|
if (!existsSync3(path2)) return null;
|
|
3301
3461
|
try {
|
|
3302
|
-
const raw = JSON.parse(
|
|
3462
|
+
const raw = JSON.parse(readFileSync3(path2, "utf-8"));
|
|
3303
3463
|
const hooksObj = raw.hooks;
|
|
3304
3464
|
const hooks = hooksObj ? Object.keys(hooksObj) : [];
|
|
3305
3465
|
const mcpServersObj = raw.mcpServers;
|
|
@@ -3474,7 +3634,7 @@ import {
|
|
|
3474
3634
|
mkdirSync,
|
|
3475
3635
|
copyFileSync,
|
|
3476
3636
|
rmSync,
|
|
3477
|
-
readFileSync as
|
|
3637
|
+
readFileSync as readFileSync4
|
|
3478
3638
|
} from "fs";
|
|
3479
3639
|
import { homedir as homedir4, platform as platform2, tmpdir } from "os";
|
|
3480
3640
|
import { join as join4 } from "path";
|
|
@@ -3558,7 +3718,7 @@ async function waitForDebugPort(userDataDir, timeoutMs) {
|
|
|
3558
3718
|
while (Date.now() < deadline) {
|
|
3559
3719
|
if (existsSync4(portFile)) {
|
|
3560
3720
|
try {
|
|
3561
|
-
const [port] =
|
|
3721
|
+
const [port] = readFileSync4(portFile, "utf-8").trim().split("\n");
|
|
3562
3722
|
if (port && /^\d+$/.test(port)) return parseInt(port, 10);
|
|
3563
3723
|
} catch {
|
|
3564
3724
|
}
|
|
@@ -3826,6 +3986,7 @@ function emptyGatheredProfile(readiness = {
|
|
|
3826
3986
|
},
|
|
3827
3987
|
ai_usage: {
|
|
3828
3988
|
claude_code: null,
|
|
3989
|
+
cowork: null,
|
|
3829
3990
|
codex: null,
|
|
3830
3991
|
cursor: null
|
|
3831
3992
|
},
|
|
@@ -3844,7 +4005,9 @@ function emptyGatheredProfile(readiness = {
|
|
|
3844
4005
|
};
|
|
3845
4006
|
}
|
|
3846
4007
|
function hasAnyUsage(aiUsage) {
|
|
3847
|
-
return Boolean(
|
|
4008
|
+
return Boolean(
|
|
4009
|
+
aiUsage.claude_code || aiUsage.cowork || aiUsage.codex || aiUsage.cursor
|
|
4010
|
+
);
|
|
3848
4011
|
}
|
|
3849
4012
|
function gatherLog(options, message) {
|
|
3850
4013
|
if (options.verbose === false) return;
|
|
@@ -3865,6 +4028,7 @@ async function gatherUsageStats() {
|
|
|
3865
4028
|
return {
|
|
3866
4029
|
ai_usage: {
|
|
3867
4030
|
claude_code: null,
|
|
4031
|
+
cowork: null,
|
|
3868
4032
|
codex: null,
|
|
3869
4033
|
cursor: null
|
|
3870
4034
|
},
|
|
@@ -3958,7 +4122,7 @@ function readRepoReadme(repoPath) {
|
|
|
3958
4122
|
const p = join5(repoPath, name);
|
|
3959
4123
|
if (!existsSync5(p)) continue;
|
|
3960
4124
|
try {
|
|
3961
|
-
const text =
|
|
4125
|
+
const text = readFileSync5(p, "utf-8").trim();
|
|
3962
4126
|
if (text.length > 0) return redactSecrets(text).slice(0, 1200);
|
|
3963
4127
|
} catch {
|
|
3964
4128
|
}
|
|
@@ -4306,6 +4470,7 @@ async function gather(options = {}) {
|
|
|
4306
4470
|
const githubEnrichPromise = safeUsername && !localOnly ? enrichApi("github", safeUsername).catch(() => null) : Promise.resolve(null);
|
|
4307
4471
|
const aiUsagePromise = options.aiUsage ? Promise.resolve(options.aiUsage) : gatherAiUsage().catch(() => ({
|
|
4308
4472
|
claude_code: null,
|
|
4473
|
+
cowork: null,
|
|
4309
4474
|
codex: null,
|
|
4310
4475
|
cursor: null
|
|
4311
4476
|
}));
|
|
@@ -4528,7 +4693,7 @@ async function gather(options = {}) {
|
|
|
4528
4693
|
);
|
|
4529
4694
|
commitSubjects = subjectsRaw ? subjectsRaw.split("\n").map((s) => s.trim()).filter(Boolean).slice(0, 200).map((s) => redactSecrets(s).slice(0, 160)) : [];
|
|
4530
4695
|
}
|
|
4531
|
-
const claudeMd = existsSync5("CLAUDE.md") ?
|
|
4696
|
+
const claudeMd = existsSync5("CLAUDE.md") ? readFileSync5("CLAUDE.md", "utf-8").slice(0, 3e3) : null;
|
|
4532
4697
|
const claudeDirsRaw = execSafe2(
|
|
4533
4698
|
`find ${homedir5()} -maxdepth 3 -path "*/.claude" -type d | head -5`
|
|
4534
4699
|
);
|
|
@@ -4542,7 +4707,7 @@ async function gather(options = {}) {
|
|
|
4542
4707
|
}
|
|
4543
4708
|
let cursorrules = null;
|
|
4544
4709
|
if (existsSync5(".cursorrules")) {
|
|
4545
|
-
cursorrules =
|
|
4710
|
+
cursorrules = readFileSync5(".cursorrules", "utf-8").slice(0, 2e3);
|
|
4546
4711
|
}
|
|
4547
4712
|
const [aiUsage, devEnvironment, githubEnrichment] = await Promise.all([
|
|
4548
4713
|
aiUsagePromise,
|
|
@@ -4707,7 +4872,7 @@ Your first user message contains a <prefetched_profile> block with a full JSON d
|
|
|
4707
4872
|
- \`twitter\`: handle + optional enrichment
|
|
4708
4873
|
- \`local\`: local git repos, commit patterns, collaborators
|
|
4709
4874
|
- \`ai_coding\`: AI-assisted commit counts, CLAUDE.md contents, .claude dirs, other AI tool presence, .cursorrules
|
|
4710
|
-
- \`ai_usage\`: Claude Code + Codex session parsing \u2014 total sessions, active days, tool-call frequency, projects (cwd), models used, peak hour, weekend ratio, languages inferred from file extensions, frameworks inferred from file paths, sanitized first-line prompt samples
|
|
4875
|
+
- \`ai_usage\`: Claude Code + Cowork + Codex session parsing \u2014 total sessions, active days, tool-call frequency, projects (cwd), models used, peak hour, weekend ratio, languages inferred from file extensions, frameworks inferred from file paths, sanitized first-line prompt samples. \`cowork\` is Claude Cowork (Anthropic's knowledge-work agent) \u2014 treat it as knowledge work, not coding (its projects are session titles, and it has no languages/frameworks).
|
|
4711
4876
|
- \`dev_environment\`: AGENTS.md contents, Claude hooks/MCPs/commands, cursor rules, VSCode extensions, global npm/brew/pip packages, installed dev CLIs with versions
|
|
4712
4877
|
|
|
4713
4878
|
TREAT PREFETCHED DATA AS AUTHORITATIVE. Do not re-run git log, curl GitHub, scan local files, parse ~/.claude, etc. That work is done. Read the block and proceed.
|
|
@@ -4902,6 +5067,7 @@ The JSON should follow this structure:
|
|
|
4902
5067
|
},
|
|
4903
5068
|
"ai_usage": {
|
|
4904
5069
|
"claude_code": null,
|
|
5070
|
+
"cowork": null,
|
|
4905
5071
|
"codex": null,
|
|
4906
5072
|
"cursor": null
|
|
4907
5073
|
},
|
|
@@ -5045,7 +5211,7 @@ ${question}
|
|
|
5045
5211
|
});
|
|
5046
5212
|
});
|
|
5047
5213
|
}
|
|
5048
|
-
var
|
|
5214
|
+
var TOOLS = [
|
|
5049
5215
|
{
|
|
5050
5216
|
name: "bash",
|
|
5051
5217
|
description: "Execute a bash command in a read-only sandbox. Destructive commands (rm, mv, git push, sudo, etc.) are blocked. Use for reading files, git log, curl GET, find, ls, etc.",
|
|
@@ -5134,12 +5300,6 @@ async function executeTool(name, input, options = {}) {
|
|
|
5134
5300
|
// src/wrapped/aggregate.ts
|
|
5135
5301
|
var WRAPPED_COMMUNITY_BOOST2 = 1e3;
|
|
5136
5302
|
var boostCommunity2 = (n) => n > 0 ? n + WRAPPED_COMMUNITY_BOOST2 : 0;
|
|
5137
|
-
var SOURCES3 = ["claude_code", "codex", "cursor"];
|
|
5138
|
-
var TOOL_LABELS = {
|
|
5139
|
-
claude_code: "Claude Code",
|
|
5140
|
-
codex: "Codex",
|
|
5141
|
-
cursor: "Cursor"
|
|
5142
|
-
};
|
|
5143
5303
|
var RETAIL_RATES2 = [
|
|
5144
5304
|
{
|
|
5145
5305
|
match: /claude.*opus/i,
|
|
@@ -5304,7 +5464,7 @@ function aggregateView(args) {
|
|
|
5304
5464
|
const allTimeMonthly = /* @__PURE__ */ new Map();
|
|
5305
5465
|
const allTimeHourBuckets = new Array(24).fill(0);
|
|
5306
5466
|
let allTimeStreak = 0;
|
|
5307
|
-
for (const src of
|
|
5467
|
+
for (const src of SOURCES) {
|
|
5308
5468
|
const stats = aiUsage[src];
|
|
5309
5469
|
if (!stats) continue;
|
|
5310
5470
|
usageStatsSeen = true;
|
|
@@ -5555,7 +5715,7 @@ function computeTokens2(aiUsage) {
|
|
|
5555
5715
|
const cursorStats = aiUsage["cursor"];
|
|
5556
5716
|
const cursorEstimated = !!(cursorStats && (cursorStats.total_sessions ?? 0) >= CURSOR_MIN_SESSIONS);
|
|
5557
5717
|
let cursorTokens = 0;
|
|
5558
|
-
for (const src of
|
|
5718
|
+
for (const src of SOURCES) {
|
|
5559
5719
|
const stats = aiUsage[src];
|
|
5560
5720
|
if (!stats) continue;
|
|
5561
5721
|
const buckets = stats.monthly_buckets ?? [];
|
|
@@ -5565,7 +5725,7 @@ function computeTokens2(aiUsage) {
|
|
|
5565
5725
|
workTokens += work;
|
|
5566
5726
|
cacheTokens += cache;
|
|
5567
5727
|
if (src === "cursor") cursorTokens += work + cache;
|
|
5568
|
-
byTool[
|
|
5728
|
+
byTool[SOURCE_LABELS[src] ?? src] = (byTool[SOURCE_LABELS[src] ?? src] ?? 0) + work + cache;
|
|
5569
5729
|
const rate = rateForModel2(b.primary_model);
|
|
5570
5730
|
const cacheRead = b.cache_read_tokens ?? (b.cache_write_tokens === void 0 ? b.cache_tokens ?? 0 : 0);
|
|
5571
5731
|
const cacheWrite = b.cache_write_tokens ?? 0;
|
|
@@ -5610,7 +5770,7 @@ function computeTokens2(aiUsage) {
|
|
|
5610
5770
|
}
|
|
5611
5771
|
function computeToolRelationship(aiUsage) {
|
|
5612
5772
|
const monthMap = /* @__PURE__ */ new Map();
|
|
5613
|
-
for (const src of
|
|
5773
|
+
for (const src of SOURCES) {
|
|
5614
5774
|
const stats = aiUsage[src];
|
|
5615
5775
|
if (!stats) continue;
|
|
5616
5776
|
for (const b of stats.monthly_buckets ?? []) {
|
|
@@ -5630,7 +5790,7 @@ function computeToolRelationship(aiUsage) {
|
|
|
5630
5790
|
if (best) {
|
|
5631
5791
|
return {
|
|
5632
5792
|
kind: "loyalist",
|
|
5633
|
-
tool:
|
|
5793
|
+
tool: SOURCE_LABELS[best[0]] ?? best[0],
|
|
5634
5794
|
months_count: 1,
|
|
5635
5795
|
sessions_count: best[1]
|
|
5636
5796
|
};
|
|
@@ -5665,12 +5825,12 @@ function computeToolRelationship(aiUsage) {
|
|
|
5665
5825
|
if (switchIdx > 0) {
|
|
5666
5826
|
return {
|
|
5667
5827
|
kind: "switch",
|
|
5668
|
-
from_tool:
|
|
5669
|
-
to_tool:
|
|
5828
|
+
from_tool: SOURCE_LABELS[firstDominant] ?? firstDominant,
|
|
5829
|
+
to_tool: SOURCE_LABELS[lastDominant] ?? lastDominant,
|
|
5670
5830
|
switch_month: timeline[switchIdx].month,
|
|
5671
5831
|
timeline: timeline.map((t) => ({
|
|
5672
5832
|
month: t.month,
|
|
5673
|
-
dominant:
|
|
5833
|
+
dominant: SOURCE_LABELS[t.dominant] ?? t.dominant,
|
|
5674
5834
|
share: t.share
|
|
5675
5835
|
}))
|
|
5676
5836
|
};
|
|
@@ -5684,7 +5844,7 @@ function computeToolRelationship(aiUsage) {
|
|
|
5684
5844
|
}
|
|
5685
5845
|
return {
|
|
5686
5846
|
kind: "loyalist",
|
|
5687
|
-
tool:
|
|
5847
|
+
tool: SOURCE_LABELS[firstDominant] ?? firstDominant,
|
|
5688
5848
|
months_count: months.length,
|
|
5689
5849
|
sessions_count: totalSessions
|
|
5690
5850
|
};
|
|
@@ -5697,14 +5857,14 @@ function computeToolRelationship(aiUsage) {
|
|
|
5697
5857
|
}
|
|
5698
5858
|
const grandTotal = [...totalsByTool.values()].reduce((a, b) => a + b, 0);
|
|
5699
5859
|
const tools = [...totalsByTool.entries()].sort(([, a], [, b]) => b - a).map(([tool, n]) => ({
|
|
5700
|
-
tool:
|
|
5860
|
+
tool: SOURCE_LABELS[tool] ?? tool,
|
|
5701
5861
|
share: grandTotal > 0 ? n / grandTotal : 0
|
|
5702
5862
|
}));
|
|
5703
5863
|
return { kind: "polyglot", tools };
|
|
5704
5864
|
}
|
|
5705
5865
|
function computeModels(aiUsage) {
|
|
5706
5866
|
const allModelCounts = /* @__PURE__ */ new Map();
|
|
5707
|
-
for (const src of
|
|
5867
|
+
for (const src of SOURCES) {
|
|
5708
5868
|
const stats = aiUsage[src];
|
|
5709
5869
|
if (!stats?.model_session_counts) continue;
|
|
5710
5870
|
for (const [model, n] of Object.entries(stats.model_session_counts)) {
|
|
@@ -5774,7 +5934,7 @@ function computeCodeFootprint(aiCoding, totalCommits, line) {
|
|
|
5774
5934
|
function computeConcurrency2(aiUsage, line) {
|
|
5775
5935
|
let best = null;
|
|
5776
5936
|
let bestSessions = -1;
|
|
5777
|
-
for (const src of
|
|
5937
|
+
for (const src of SOURCES) {
|
|
5778
5938
|
const stats = aiUsage[src];
|
|
5779
5939
|
if (!stats?.concurrency) continue;
|
|
5780
5940
|
const sessions = stats.total_sessions ?? 0;
|
|
@@ -5796,7 +5956,7 @@ function computeConversation(aiUsage, line, themes) {
|
|
|
5796
5956
|
let totalPrompts = 0;
|
|
5797
5957
|
let questionWeighted = 0;
|
|
5798
5958
|
let charsWeighted = 0;
|
|
5799
|
-
for (const src of
|
|
5959
|
+
for (const src of SOURCES) {
|
|
5800
5960
|
const stats = aiUsage[src];
|
|
5801
5961
|
const c = stats?.conversation;
|
|
5802
5962
|
if (!c) continue;
|
|
@@ -7234,7 +7394,7 @@ async function runAgent(jobId) {
|
|
|
7234
7394
|
messages,
|
|
7235
7395
|
// Cache the entire tools array via cache_control on the last tool definition.
|
|
7236
7396
|
tools: [
|
|
7237
|
-
...
|
|
7397
|
+
...TOOLS,
|
|
7238
7398
|
{
|
|
7239
7399
|
type: "web_search_20250305",
|
|
7240
7400
|
name: "web_search",
|