standout 0.5.37 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +17 -0
  2. package/dist/cli.js +484 -118
  3. package/package.json +6 -1
package/README.md CHANGED
@@ -11,3 +11,20 @@ get matched with roles through [Standout](https://standout.work).
11
11
 
12
12
  Optional: pass a job slug to apply directly (`npx standout <job>`), or `--chat`
13
13
  to build a full profile interactively.
14
+
15
+ ## Runs monthly, automatically
16
+
17
+ After a full run, standout schedules a monthly refresh (1st of each month) so
18
+ your stats never decay — local session logs only cover ~30 days, and the
19
+ refresh keeps your full history intact. Uses launchd on macOS and cron on
20
+ Linux; not available on Windows.
21
+
22
+ ```bash
23
+ npx standout schedule status # is it on? command, log path, last run
24
+ npx standout schedule off # disable (won't be re-added automatically)
25
+ npx standout schedule [job] # re-enable / install manually
26
+ ```
27
+
28
+ Scheduled runs are unattended: they upload your refreshed stats and write the
29
+ wrapped link to the log (`~/Library/Logs/standout.log` on macOS,
30
+ `~/.standout/standout.log` on Linux) instead of opening a browser.
package/dist/cli.js CHANGED
@@ -3,10 +3,10 @@ var __toBinaryNode = Uint8Array.fromBase64 || ((base64) => new Uint8Array(Buffer
3
3
 
4
4
  // src/cli.ts
5
5
  import Anthropic from "@anthropic-ai/sdk";
6
- import chalk2 from "chalk";
7
- import { writeFileSync } from "node:fs";
6
+ import chalk3 from "chalk";
7
+ import { writeFileSync as writeFileSync2 } from "node:fs";
8
8
  import { tmpdir as tmpdir2 } from "node:os";
9
- import { join as join6 } from "node:path";
9
+ import { join as join7 } from "node:path";
10
10
  import { createInterface as createInterface3 } from "readline";
11
11
 
12
12
  // src/sources.ts
@@ -514,8 +514,12 @@ function computeHours(aiUsage) {
514
514
  for (const src of WRAPPED_SOURCES) {
515
515
  const stats = aiUsage[src];
516
516
  if (!stats) continue;
517
- totalHours += num2(stats.total_duration_hours);
518
- totalSessions += num2(stats.total_sessions);
517
+ totalHours += num2(
518
+ stats.all_time?.total_duration_hours ?? stats.total_duration_hours
519
+ );
520
+ totalSessions += num2(
521
+ stats.all_time?.total_sessions ?? stats.total_sessions
522
+ );
519
523
  }
520
524
  return { total_hours: totalHours, total_sessions: totalSessions };
521
525
  }
@@ -3274,6 +3278,35 @@ function withAllTime(recent, allTime, previous = null) {
3274
3278
  if (previous) stats.previous_30d = summarizeUsage(previous);
3275
3279
  return stats;
3276
3280
  }
3281
+ function extractSessionRecords(raw, source) {
3282
+ const out = [];
3283
+ for (const [sessionId, s] of raw.sessions) {
3284
+ if (!sessionId) continue;
3285
+ const d = new Date(s.firstTs);
3286
+ if (isNaN(d.getTime())) continue;
3287
+ const month = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
3288
+ let primary = null;
3289
+ let best = 0;
3290
+ for (const [m, c] of s.modelTurnCounts) {
3291
+ if (c > best && m && !m.startsWith("<") && !m.includes("synthetic")) {
3292
+ best = c;
3293
+ primary = m;
3294
+ }
3295
+ }
3296
+ out.push({
3297
+ source,
3298
+ session_id: sessionId,
3299
+ month,
3300
+ duration_hours: +(estimateActiveDurationMs2(s.eventTimestamps) / 36e5).toFixed(3),
3301
+ input_tokens: s.inputTokens,
3302
+ output_tokens: s.outputTokens,
3303
+ cache_read_tokens: s.cacheReadTokens,
3304
+ cache_write_tokens: s.cacheWriteTokens,
3305
+ primary_model: primary
3306
+ });
3307
+ }
3308
+ return out;
3309
+ }
3277
3310
  async function gatherAiUsage() {
3278
3311
  const claudeFiles = listClaudeCodeFiles();
3279
3312
  const codexFiles = listCodexFiles();
@@ -3378,7 +3411,12 @@ async function gatherAiUsage() {
3378
3411
  finalize2(codexAllRaw),
3379
3412
  finalize2(codexPreviousRaw)
3380
3413
  ),
3381
- cursor
3414
+ cursor,
3415
+ sessions: [
3416
+ ...extractSessionRecords(claudeAllRaw, "claude_code"),
3417
+ ...extractSessionRecords(coworkAllRaw, "cowork"),
3418
+ ...extractSessionRecords(codexAllRaw, "codex")
3419
+ ]
3382
3420
  };
3383
3421
  }
3384
3422
 
@@ -3988,7 +4026,8 @@ function emptyGatheredProfile(readiness = {
3988
4026
  claude_code: null,
3989
4027
  cowork: null,
3990
4028
  codex: null,
3991
- cursor: null
4029
+ cursor: null,
4030
+ sessions: []
3992
4031
  },
3993
4032
  dev_environment: {
3994
4033
  agents_md: [],
@@ -4030,7 +4069,8 @@ async function gatherUsageStats() {
4030
4069
  claude_code: null,
4031
4070
  cowork: null,
4032
4071
  codex: null,
4033
- cursor: null
4072
+ cursor: null,
4073
+ sessions: []
4034
4074
  },
4035
4075
  readiness: {
4036
4076
  usage_stats_status: "error",
@@ -4472,7 +4512,8 @@ async function gather(options = {}) {
4472
4512
  claude_code: null,
4473
4513
  cowork: null,
4474
4514
  codex: null,
4475
- cursor: null
4515
+ cursor: null,
4516
+ sessions: []
4476
4517
  }));
4477
4518
  const devEnvPromise = gatherDevEnvironment().catch(
4478
4519
  () => ({
@@ -5107,6 +5148,305 @@ The JSON should follow this structure:
5107
5148
  - Ask interactive questions ONE AT A TIME.
5108
5149
  - If you receive a pause_turn stop reason, your turn will be continued automatically \u2014 just keep going.`;
5109
5150
 
5151
+ // src/schedule.ts
5152
+ import chalk from "chalk";
5153
+ import { spawnSync as spawnSync2 } from "node:child_process";
5154
+ import {
5155
+ existsSync as existsSync6,
5156
+ mkdirSync as mkdirSync2,
5157
+ readFileSync as readFileSync6,
5158
+ rmSync as rmSync2,
5159
+ statSync as statSync5,
5160
+ writeFileSync
5161
+ } from "node:fs";
5162
+ import { homedir as homedir6 } from "node:os";
5163
+ import { join as join6 } from "node:path";
5164
+ var LAUNCHD_LABEL = "work.standout.monthly";
5165
+ var CRON_MARKER = "# standout-monthly";
5166
+ var ACCENT = "#ad5cff";
5167
+ var SLUG_RE = /^[a-z0-9][a-z0-9_-]*$/i;
5168
+ var FLAG_ALLOWLIST = ["--local", "--no-upload", "--terminal"];
5169
+ function parseScheduleArgs(args) {
5170
+ if (args[0] === "unschedule") return { kind: "remove" };
5171
+ const rest = args.slice(1);
5172
+ if (rest[0] === "off" || rest[0] === "remove") return { kind: "remove" };
5173
+ if (rest[0] === "status") return { kind: "status" };
5174
+ let slug;
5175
+ const flags = [];
5176
+ for (const arg of rest) {
5177
+ if (arg === "--chat") {
5178
+ throw new Error(
5179
+ "--chat needs an interactive terminal and can't be scheduled"
5180
+ );
5181
+ }
5182
+ if (arg.startsWith("-")) {
5183
+ if (!FLAG_ALLOWLIST.includes(arg)) {
5184
+ throw new Error(
5185
+ `unknown flag for schedule: ${arg} (allowed: ${FLAG_ALLOWLIST.join(", ")})`
5186
+ );
5187
+ }
5188
+ if (!flags.includes(arg)) flags.push(arg);
5189
+ continue;
5190
+ }
5191
+ if (slug) throw new Error(`unexpected argument: ${arg}`);
5192
+ if (!SLUG_RE.test(arg)) throw new Error(`invalid job slug: ${arg}`);
5193
+ slug = arg;
5194
+ }
5195
+ return { kind: "install", slug, flags };
5196
+ }
5197
+ function buildRunCommand(slug, flags) {
5198
+ return ["npx", "-y", "standout@latest", ...slug ? [slug] : [], ...flags].join(" ").trim();
5199
+ }
5200
+ function escapeXml(text) {
5201
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5202
+ }
5203
+ function unescapeXml(text) {
5204
+ return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
5205
+ }
5206
+ function buildLaunchdPlist(command, logPath) {
5207
+ return `<?xml version="1.0" encoding="UTF-8"?>
5208
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
5209
+ <plist version="1.0">
5210
+ <dict>
5211
+ <key>Label</key><string>${LAUNCHD_LABEL}</string>
5212
+ <key>ProgramArguments</key>
5213
+ <array>
5214
+ <string>/bin/zsh</string>
5215
+ <string>-lc</string>
5216
+ <string>${escapeXml(command)}</string>
5217
+ </array>
5218
+ <key>StartCalendarInterval</key>
5219
+ <dict>
5220
+ <key>Day</key><integer>1</integer>
5221
+ <key>Hour</key><integer>10</integer>
5222
+ <key>Minute</key><integer>0</integer>
5223
+ </dict>
5224
+ <key>StandardOutPath</key><string>${escapeXml(logPath)}</string>
5225
+ <key>StandardErrorPath</key><string>${escapeXml(logPath)}</string>
5226
+ </dict>
5227
+ </plist>
5228
+ `;
5229
+ }
5230
+ function buildCronLine(command, logPath) {
5231
+ return `0 10 1 * * /bin/bash -lc '${command}' >> "${logPath}" 2>&1 ${CRON_MARKER}`;
5232
+ }
5233
+ function stripCronEntries(crontab) {
5234
+ return crontab.split("\n").filter((line) => !line.includes(CRON_MARKER)).join("\n");
5235
+ }
5236
+ function supportedPlatform() {
5237
+ return process.platform === "darwin" || process.platform === "linux";
5238
+ }
5239
+ function stateDir() {
5240
+ return join6(homedir6(), ".standout");
5241
+ }
5242
+ function optOutPath() {
5243
+ return join6(stateDir(), "no-schedule");
5244
+ }
5245
+ function plistPath() {
5246
+ return join6(homedir6(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
5247
+ }
5248
+ function schedLogPath() {
5249
+ return process.platform === "darwin" ? join6(homedir6(), "Library", "Logs", "standout.log") : join6(stateDir(), "standout.log");
5250
+ }
5251
+ function isOptedOut() {
5252
+ return existsSync6(optOutPath());
5253
+ }
5254
+ function writeOptOut() {
5255
+ mkdirSync2(stateDir(), { recursive: true });
5256
+ writeFileSync(
5257
+ optOutPath(),
5258
+ "created by `standout schedule off` \u2014 delete (or run `npx standout schedule`) to re-enable monthly runs\n"
5259
+ );
5260
+ }
5261
+ function clearOptOut() {
5262
+ rmSync2(optOutPath(), { force: true });
5263
+ }
5264
+ function launchctl(args) {
5265
+ const res = spawnSync2("launchctl", args, { encoding: "utf8" });
5266
+ return {
5267
+ status: res.status,
5268
+ stdout: res.stdout ?? "",
5269
+ stderr: res.stderr ?? ""
5270
+ };
5271
+ }
5272
+ function uid() {
5273
+ return process.getuid?.() ?? 0;
5274
+ }
5275
+ function readCrontab() {
5276
+ const res = spawnSync2("crontab", ["-l"], { encoding: "utf8" });
5277
+ return res.status === 0 ? res.stdout ?? "" : "";
5278
+ }
5279
+ function writeCrontab(content) {
5280
+ const res = spawnSync2("crontab", ["-"], {
5281
+ input: content,
5282
+ encoding: "utf8"
5283
+ });
5284
+ if (res.status !== 0) {
5285
+ throw new Error(`crontab update failed: ${res.stderr?.trim() || "error"}`);
5286
+ }
5287
+ }
5288
+ function installJob(command) {
5289
+ if (process.platform === "darwin") {
5290
+ const path2 = plistPath();
5291
+ mkdirSync2(join6(homedir6(), "Library", "LaunchAgents"), { recursive: true });
5292
+ writeFileSync(path2, buildLaunchdPlist(command, schedLogPath()));
5293
+ launchctl(["bootout", `gui/${uid()}/${LAUNCHD_LABEL}`]);
5294
+ const res = launchctl(["bootstrap", `gui/${uid()}`, path2]);
5295
+ if (res.status !== 0) {
5296
+ throw new Error(
5297
+ `launchctl bootstrap failed (${res.stderr.trim() || res.status}) \u2014 plist written to ${path2}; load it manually with: launchctl bootstrap gui/$(id -u) ${path2}`
5298
+ );
5299
+ }
5300
+ } else {
5301
+ mkdirSync2(stateDir(), { recursive: true });
5302
+ const stripped = stripCronEntries(readCrontab());
5303
+ const base = stripped === "" || stripped.endsWith("\n") ? stripped : stripped + "\n";
5304
+ writeCrontab(base + buildCronLine(command, schedLogPath()) + "\n");
5305
+ }
5306
+ }
5307
+ function installedCommand() {
5308
+ if (process.platform === "darwin") {
5309
+ if (!existsSync6(plistPath())) return null;
5310
+ const xml = readFileSync6(plistPath(), "utf8");
5311
+ const m = xml.match(/<string>-lc<\/string>\s*<string>([^<]*)<\/string>/);
5312
+ return m ? unescapeXml(m[1]) : null;
5313
+ }
5314
+ if (process.platform === "linux") {
5315
+ const line = readCrontab().split("\n").find((l) => l.includes(CRON_MARKER));
5316
+ const m = line?.match(/-lc '([^']*)'/);
5317
+ return m ? m[1] : null;
5318
+ }
5319
+ return null;
5320
+ }
5321
+ async function installSchedule(slug, flags) {
5322
+ if (!supportedPlatform()) {
5323
+ process.stderr.write(
5324
+ "\n scheduling isn't supported on this platform yet \u2014 run `npx standout` manually.\n\n"
5325
+ );
5326
+ process.exit(1);
5327
+ }
5328
+ clearOptOut();
5329
+ const command = buildRunCommand(slug, flags);
5330
+ installJob(command);
5331
+ process.stderr.write(
5332
+ `
5333
+ ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh scheduled")} ${chalk.dim("(1st of each month, 10:00)")}
5334
+ ` + chalk.dim(` runs: ${command}
5335
+ `) + chalk.dim(` log: ${schedLogPath()}
5336
+ `) + chalk.dim(" disable anytime with `npx standout schedule off`\n\n")
5337
+ );
5338
+ }
5339
+ async function removeSchedule() {
5340
+ if (!supportedPlatform()) {
5341
+ process.stderr.write(
5342
+ "\n scheduling isn't supported on this platform \u2014 nothing to remove.\n\n"
5343
+ );
5344
+ return;
5345
+ }
5346
+ writeOptOut();
5347
+ let removed = false;
5348
+ if (process.platform === "darwin") {
5349
+ launchctl(["bootout", `gui/${uid()}/${LAUNCHD_LABEL}`]);
5350
+ if (existsSync6(plistPath())) {
5351
+ rmSync2(plistPath(), { force: true });
5352
+ removed = true;
5353
+ }
5354
+ } else {
5355
+ const current = readCrontab();
5356
+ const stripped = stripCronEntries(current);
5357
+ if (stripped !== current) {
5358
+ writeCrontab(stripped);
5359
+ removed = true;
5360
+ }
5361
+ }
5362
+ process.stderr.write(
5363
+ removed ? `
5364
+ ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh disabled")}
5365
+ ` + chalk.dim(
5366
+ " it won't be scheduled again \u2014 re-enable with `npx standout schedule`\n\n"
5367
+ ) : `
5368
+ ${chalk.dim("no monthly schedule installed \u2014 future runs won't add one (re-enable with `npx standout schedule`)")}
5369
+
5370
+ `
5371
+ );
5372
+ }
5373
+ async function scheduleStatus() {
5374
+ if (!supportedPlatform()) {
5375
+ process.stderr.write(
5376
+ "\n scheduling isn't supported on this platform.\n\n"
5377
+ );
5378
+ return;
5379
+ }
5380
+ const command = installedCommand();
5381
+ if (!command) {
5382
+ process.stderr.write(
5383
+ `
5384
+ ${chalk.white("no monthly schedule installed")}` + (isOptedOut() ? chalk.dim(" (opted out)") : "") + "\n" + chalk.dim(" enable with `npx standout schedule`\n\n")
5385
+ );
5386
+ return;
5387
+ }
5388
+ process.stderr.write(
5389
+ `
5390
+ ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh is on")} ${chalk.dim("(1st of each month, 10:00)")}
5391
+ ` + chalk.dim(` runs: ${command}
5392
+ `) + chalk.dim(` log: ${schedLogPath()}
5393
+ `)
5394
+ );
5395
+ if (process.platform === "darwin") {
5396
+ const res = launchctl(["print", `gui/${uid()}/${LAUNCHD_LABEL}`]);
5397
+ const exitLine = res.stdout.split("\n").find((l) => l.includes("last exit code"));
5398
+ if (res.status !== 0) {
5399
+ process.stderr.write(
5400
+ chalk.dim(
5401
+ " warning: job not loaded in launchd \u2014 re-run `npx standout schedule`\n"
5402
+ )
5403
+ );
5404
+ } else if (exitLine) {
5405
+ process.stderr.write(chalk.dim(` ${exitLine.trim()}
5406
+ `));
5407
+ }
5408
+ }
5409
+ if (existsSync6(schedLogPath())) {
5410
+ const mtime = statSync5(schedLogPath()).mtime;
5411
+ const tail = readFileSync6(schedLogPath(), "utf8").trimEnd().split("\n").slice(-10);
5412
+ process.stderr.write(
5413
+ chalk.dim(` last log activity: ${mtime.toLocaleString()}
5414
+
5415
+ `) + tail.map((l) => chalk.dim(` \u2502 ${l}
5416
+ `)).join("")
5417
+ );
5418
+ }
5419
+ process.stderr.write("\n");
5420
+ }
5421
+ async function maybeAutoInstallSchedule(slug) {
5422
+ try {
5423
+ if (!supportedPlatform() || isOptedOut()) return;
5424
+ const command = buildRunCommand(slug, []);
5425
+ if (installedCommand() === command) {
5426
+ process.stderr.write(
5427
+ chalk.dim(
5428
+ " monthly refresh is on \u2014 `npx standout schedule off` to disable\n\n"
5429
+ )
5430
+ );
5431
+ return;
5432
+ }
5433
+ installJob(command);
5434
+ process.stderr.write(
5435
+ ` ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh scheduled")} ${chalk.dim("(1st of each month) \u2014 disable with `npx standout schedule off`")}
5436
+
5437
+ `
5438
+ );
5439
+ } catch (error) {
5440
+ process.stderr.write(
5441
+ chalk.dim(
5442
+ ` (couldn't schedule the monthly refresh: ${error instanceof Error ? error.message : String(error)})
5443
+
5444
+ `
5445
+ )
5446
+ );
5447
+ }
5448
+ }
5449
+
5110
5450
  // src/tools.ts
5111
5451
  import { execSync as execSync3 } from "child_process";
5112
5452
  import { createInterface as createInterface2 } from "readline";
@@ -5977,7 +6317,7 @@ function computeConversation(aiUsage, line, themes) {
5977
6317
 
5978
6318
  // src/wrapped/render.ts
5979
6319
  import boxen from "boxen";
5980
- import chalk from "chalk";
6320
+ import chalk2 from "chalk";
5981
6321
  import gradient from "gradient-string";
5982
6322
 
5983
6323
  // src/wrapped/chrono-critters.ts
@@ -6154,8 +6494,8 @@ function hasHistogramData(view) {
6154
6494
  return view.hour_histogram.some((v) => v > 0);
6155
6495
  }
6156
6496
  function divider(label) {
6157
- return chalk.dim(`
6158
- ${label.padEnd(48, " ")} ${chalk.dim("[press \u21B5]")}
6497
+ return chalk2.dim(`
6498
+ ${label.padEnd(48, " ")} ${chalk2.dim("[press \u21B5]")}
6159
6499
  `);
6160
6500
  }
6161
6501
  function wrapWords(text, width) {
@@ -6193,7 +6533,7 @@ function centerBlock(art, color) {
6193
6533
  function bar2(value, max, width) {
6194
6534
  if (max === 0) return " ".repeat(width);
6195
6535
  const filled = Math.round(value / max * width);
6196
- return chalk.hex("#ff8a00")("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(width - filled));
6536
+ return chalk2.hex("#ff8a00")("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(width - filled));
6197
6537
  }
6198
6538
  function compactNum(n) {
6199
6539
  const sign = n < 0 ? "-" : "";
@@ -6203,12 +6543,12 @@ function compactNum(n) {
6203
6543
  return `${sign}${abs}`;
6204
6544
  }
6205
6545
  function coloredBar(value, max, width, hex) {
6206
- if (max <= 0) return chalk.gray("\u2591".repeat(width));
6546
+ if (max <= 0) return chalk2.gray("\u2591".repeat(width));
6207
6547
  const filled = Math.max(
6208
6548
  0,
6209
6549
  Math.min(width, Math.round(value / max * width))
6210
6550
  );
6211
- return chalk.hex(hex)("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(width - filled));
6551
+ return chalk2.hex(hex)("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(width - filled));
6212
6552
  }
6213
6553
  function sparkline2(buckets) {
6214
6554
  if (!buckets.length) return "";
@@ -6236,8 +6576,8 @@ function hourlyChart(buckets, critter = null) {
6236
6576
  }
6237
6577
  }
6238
6578
  const left = [
6239
- chalk.hex("#ad5cff")(spark.padEnd(width, " ")),
6240
- chalk.dim(axis.join(""))
6579
+ chalk2.hex("#ad5cff")(spark.padEnd(width, " ")),
6580
+ chalk2.dim(axis.join(""))
6241
6581
  ];
6242
6582
  if (!critter) return left.map((l) => ` ${l}`);
6243
6583
  const critterLines = critter.art.split("\n");
@@ -6279,12 +6619,12 @@ function card1Open(view) {
6279
6619
  const verdict = perDay >= 6 ? "that's a second full-time job. 996 who?" : perDay >= 3 ? "basically a part-time gig with the machines." : perDay >= 1 ? "a daily habit at this point." : "just getting warmed up.";
6280
6620
  const bench = benchmarkInline(view, view.rank_summary.hours_percentile);
6281
6621
  const lines = [
6282
- chalk.dim("YOU MANAGED"),
6622
+ chalk2.dim("YOU MANAGED"),
6283
6623
  "",
6284
6624
  bigNumber(` ${hours} session-hours`) + (bench ? ` ${bench}` : ""),
6285
6625
  "",
6286
- chalk.white(` ${verdict}`),
6287
- chalk.dim(" across parallel agent sessions, last 30 days")
6626
+ chalk2.white(` ${verdict}`),
6627
+ chalk2.dim(" across parallel agent sessions, last 30 days")
6288
6628
  ].join("\n");
6289
6629
  return box(lines, { borderColor: "yellow" });
6290
6630
  }
@@ -6294,7 +6634,7 @@ function cardAllTime(view) {
6294
6634
  const max = Math.max(...months.map((m) => m.duration_hours), 1);
6295
6635
  const rows = months.map((m) => {
6296
6636
  const label = monthYearShort(m.month).padEnd(6);
6297
- return ` ${chalk.dim(label)} ${bar2(m.duration_hours, max, 20)} ${chalk.dim(`${Math.round(m.duration_hours)}h`)}`;
6637
+ return ` ${chalk2.dim(label)} ${bar2(m.duration_hours, max, 20)} ${chalk2.dim(`${Math.round(m.duration_hours)}h`)}`;
6298
6638
  });
6299
6639
  const since = view.all_time.first_session ? new Date(view.all_time.first_session).toISOString().slice(0, 7) : "your first local log";
6300
6640
  const latest = months[months.length - 1];
@@ -6304,14 +6644,14 @@ function cardAllTime(view) {
6304
6644
  )[0];
6305
6645
  const trend = latest && prior && prior.duration_hours > 0 ? `${monthShort(latest.month)}: ${latest.duration_hours >= prior.duration_hours ? "+" : ""}${Math.round((latest.duration_hours - prior.duration_hours) / prior.duration_hours * 100)}% vs previous month` : latest && prior && latest.duration_hours > 0 ? `${monthShort(latest.month)}: active after a quiet month` : null;
6306
6646
  const lines = [
6307
- chalk.dim("YOUR AI CODING YEAR"),
6647
+ chalk2.dim("YOUR AI CODING YEAR"),
6308
6648
  "",
6309
6649
  ` ${bigNumber(`~${Math.round(view.all_time.total_hours).toLocaleString()} hours`)} from local logs`,
6310
- ` ${chalk.white(view.all_time.total_sessions.toLocaleString())} sessions detected since ${since}`,
6311
- ...trend ? [` ${chalk.dim(trend)}`] : [],
6650
+ ` ${chalk2.white(view.all_time.total_sessions.toLocaleString())} sessions detected since ${since}`,
6651
+ ...trend ? [` ${chalk2.dim(trend)}`] : [],
6312
6652
  "",
6313
6653
  ...rows,
6314
- best ? ` ${chalk.dim(`best local-log month: ${monthShort(best.month)} \xB7 ~${Math.round(best.duration_hours)}h`)}` : ""
6654
+ best ? ` ${chalk2.dim(`best local-log month: ${monthShort(best.month)} \xB7 ~${Math.round(best.duration_hours)}h`)}` : ""
6315
6655
  ].join("\n");
6316
6656
  return box(lines, { borderColor: "cyan" });
6317
6657
  }
@@ -6330,25 +6670,25 @@ function cardRhythm(view) {
6330
6670
  const isNightOwl = critter?.key === "night_owl";
6331
6671
  const subhead = isNightOwl ? "while the 9-5ers sleep, you ship." : weekendPct < 10 ? "locked in on weekdays, weekends are yours." : weekendPct >= 35 ? "weekends are just more reps for you." : peak !== null && peak < 9 ? "up before standup. menace." : "you keep your own hours.";
6332
6672
  const lines = [
6333
- chalk.dim("WHEN YOU CODE"),
6673
+ chalk2.dim("WHEN YOU CODE"),
6334
6674
  "",
6335
6675
  ...hourlyChart(
6336
6676
  view.hour_histogram,
6337
- critter ? { art: critter.art, color: chalk.magenta } : null
6677
+ critter ? { art: critter.art, color: chalk2.magenta } : null
6338
6678
  ),
6339
6679
  "",
6340
- ` ${chalk.white(caption)}`,
6680
+ ` ${chalk2.white(caption)}`,
6341
6681
  "",
6342
- ` ${chalk.white("weekdays".padEnd(9))} ${bar2(weekdayPct, 100, 20)} ${chalk.dim(weekdayPct + "%")}`,
6343
- ` ${chalk.white("weekends".padEnd(9))} ${bar2(weekendPct, 100, 20)} ${chalk.dim(weekendPct + "%")}`,
6682
+ ` ${chalk2.white("weekdays".padEnd(9))} ${bar2(weekdayPct, 100, 20)} ${chalk2.dim(weekdayPct + "%")}`,
6683
+ ` ${chalk2.white("weekends".padEnd(9))} ${bar2(weekendPct, 100, 20)} ${chalk2.dim(weekendPct + "%")}`,
6344
6684
  ...weekendBench ? ["", ` ${weekendBench}`] : [],
6345
6685
  ""
6346
6686
  ];
6347
6687
  if (view.rhythm_comment) {
6348
6688
  for (const l of wrapWords(view.rhythm_comment, 47))
6349
- lines.push(chalk.italic.white(" " + l));
6689
+ lines.push(chalk2.italic.white(" " + l));
6350
6690
  } else {
6351
- lines.push(chalk.dim(" " + subhead));
6691
+ lines.push(chalk2.dim(" " + subhead));
6352
6692
  }
6353
6693
  return box(lines.join("\n"), { borderColor });
6354
6694
  }
@@ -6361,10 +6701,10 @@ function cardProjects(view) {
6361
6701
  if (!usageReady(view)) return "";
6362
6702
  const projects = view.top_projects.filter((p) => p.commits > 0 || p.sessions > 0).slice(0, 3);
6363
6703
  if (projects.length === 0) return "";
6364
- const lines = [chalk.dim("YOUR TOP PROJECTS"), ""];
6704
+ const lines = [chalk2.dim("YOUR TOP PROJECTS"), ""];
6365
6705
  const c = view.contributions;
6366
6706
  if (c && c.one_liner) {
6367
- for (const l of wrapWords(c.one_liner, 50)) lines.push(chalk.dim(l));
6707
+ for (const l of wrapWords(c.one_liner, 50)) lines.push(chalk2.dim(l));
6368
6708
  lines.push("");
6369
6709
  }
6370
6710
  projects.forEach((p, i) => {
@@ -6374,14 +6714,14 @@ function cardProjects(view) {
6374
6714
  lines.push(` ${bigNumber(`\u25B8 ${name}`)} ${TITLE_GRADIENT(count)}`);
6375
6715
  if (p.blurb)
6376
6716
  for (const l of wrapWords(p.blurb, 44))
6377
- lines.push(` ${chalk.white(l)}`);
6717
+ lines.push(` ${chalk2.white(l)}`);
6378
6718
  } else {
6379
6719
  const namePart = `\u25B8 ${name}`;
6380
6720
  const pad = " ".repeat(Math.max(2, 24 - namePart.length));
6381
- lines.push(` ${chalk.white(namePart)}${pad}${chalk.dim(count)}`);
6721
+ lines.push(` ${chalk2.white(namePart)}${pad}${chalk2.dim(count)}`);
6382
6722
  if (p.blurb)
6383
6723
  for (const l of wrapWords(p.blurb, 44))
6384
- lines.push(` ${chalk.dim(l)}`);
6724
+ lines.push(` ${chalk2.dim(l)}`);
6385
6725
  }
6386
6726
  lines.push("");
6387
6727
  });
@@ -6397,17 +6737,17 @@ function card7AINative(view) {
6397
6737
  );
6398
6738
  const streak = view.streak_days;
6399
6739
  const lines = [
6400
- chalk.dim("YOU'RE AI-NATIVE"),
6740
+ chalk2.dim("YOU'RE AI-NATIVE"),
6401
6741
  "",
6402
- ` commits since 2024: ${chalk.white(view.total_commits.toLocaleString())}`,
6403
- ` AI co-authored: ${chalk.white(view.ai_assisted_commits.toLocaleString())}`,
6742
+ ` commits since 2024: ${chalk2.white(view.total_commits.toLocaleString())}`,
6743
+ ` AI co-authored: ${chalk2.white(view.ai_assisted_commits.toLocaleString())}`,
6404
6744
  "",
6405
- ` ${bar2(pct, 100, 22)} ${chalk.white(pct + "%")}${bench ? ` ${bench}` : ""}`,
6745
+ ` ${bar2(pct, 100, 22)} ${chalk2.white(pct + "%")}${bench ? ` ${bench}` : ""}`,
6406
6746
  ...streak >= 2 ? [
6407
- ` ${chalk.white(`${streak}-day streak`)}${chalk.dim(", coding without missing a day.")}`
6747
+ ` ${chalk2.white(`${streak}-day streak`)}${chalk2.dim(", coding without missing a day.")}`
6408
6748
  ] : [],
6409
6749
  "",
6410
- chalk.dim(subhead)
6750
+ chalk2.dim(subhead)
6411
6751
  ].join("\n");
6412
6752
  return box(lines, { borderColor: "yellow" });
6413
6753
  }
@@ -6420,7 +6760,7 @@ function benchmarkInline(view, percentile) {
6420
6760
  const top = topPercentNumber(percentile);
6421
6761
  if (top === null) return "";
6422
6762
  const pct = `${top}%`;
6423
- return top < 10 ? chalk.dim("(top ") + TITLE_GRADIENT(pct) + chalk.dim(" of users)") : chalk.dim(`(top ${pct} of users)`);
6763
+ return top < 10 ? chalk2.dim("(top ") + TITLE_GRADIENT(pct) + chalk2.dim(" of users)") : chalk2.dim(`(top ${pct} of users)`);
6424
6764
  }
6425
6765
  function formatTokens(n) {
6426
6766
  if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
@@ -6440,24 +6780,24 @@ function cardTokens(view) {
6440
6780
  const max = Math.max(...t.by_tool.map((x) => x.tokens), 1);
6441
6781
  const toolRows = t.by_tool.length > 0 ? t.by_tool.slice(0, 3).map((x) => {
6442
6782
  const padTool = x.tool.padEnd(13, " ");
6443
- return ` ${chalk.white(padTool)} ${bar2(x.tokens, max, 16)} ${chalk.dim(formatTokens(x.tokens))}`;
6444
- }).join("\n") : chalk.dim(" (no tool breakdown available)");
6445
- const deltaLine = t.multiplier_vs_prior !== null && t.multiplier_vs_prior >= 1.05 ? chalk.hex("#ff8a00")(
6783
+ return ` ${chalk2.white(padTool)} ${bar2(x.tokens, max, 16)} ${chalk2.dim(formatTokens(x.tokens))}`;
6784
+ }).join("\n") : chalk2.dim(" (no tool breakdown available)");
6785
+ const deltaLine = t.multiplier_vs_prior !== null && t.multiplier_vs_prior >= 1.05 ? chalk2.hex("#ff8a00")(
6446
6786
  ` \u2191 ${t.multiplier_vs_prior.toFixed(1)}\xD7 vs last period`
6447
- ) : t.multiplier_vs_prior !== null && t.multiplier_vs_prior < 0.95 ? chalk.dim(
6787
+ ) : t.multiplier_vs_prior !== null && t.multiplier_vs_prior < 0.95 ? chalk2.dim(
6448
6788
  ` \u2193 ${(1 / t.multiplier_vs_prior).toFixed(1)}\xD7 less than last period`
6449
- ) : t.total_prior_30d > 0 ? chalk.dim(` \u2248 same as last period`) : chalk.dim(` rolling 30-day window`);
6789
+ ) : t.total_prior_30d > 0 ? chalk2.dim(` \u2248 same as last period`) : chalk2.dim(` rolling 30-day window`);
6450
6790
  const cost = t.estimated_retail_cost_usd;
6451
- const costLine = cost > 0 ? ` ${chalk.dim("would have cost")} ${TITLE_GRADIENT(`~$${cost.toLocaleString()}`)} ${chalk.dim("at retail API rates")}` : "";
6452
- const cursorNote = t.cursor_estimated ? t.cursor_token_share >= 0.9 ? chalk.dim(
6791
+ const costLine = cost > 0 ? ` ${chalk2.dim("would have cost")} ${TITLE_GRADIENT(`~$${cost.toLocaleString()}`)} ${chalk2.dim("at retail API rates")}` : "";
6792
+ const cursorNote = t.cursor_estimated ? t.cursor_token_share >= 0.9 ? chalk2.dim(
6453
6793
  " \u2248 estimated from time spent in Cursor (no local token data)"
6454
- ) : chalk.dim(
6794
+ ) : chalk2.dim(
6455
6795
  " includes Cursor (tokens + cost estimated from active time)"
6456
6796
  ) : "";
6457
- const cachedNote = t.cache_tokens > t.work_tokens ? chalk.dim(" thankfully, most of them were cached :)") : t.cache_tokens > 0 ? chalk.dim(" (includes cached context, re-read each turn)") : "";
6458
- const generatedLine = t.work_tokens > 0 ? ` ${chalk.white(formatTokens(t.work_tokens))} ${chalk.dim("were actually generated fresh")}` : "";
6797
+ const cachedNote = t.cache_tokens > t.work_tokens ? chalk2.dim(" thankfully, most of them were cached :)") : t.cache_tokens > 0 ? chalk2.dim(" (includes cached context, re-read each turn)") : "";
6798
+ const generatedLine = t.work_tokens > 0 ? ` ${chalk2.white(formatTokens(t.work_tokens))} ${chalk2.dim("were actually generated fresh")}` : "";
6459
6799
  const lines = [
6460
- chalk.dim("YOU BURNED THIS MANY TOKENS"),
6800
+ chalk2.dim("YOU BURNED THIS MANY TOKENS"),
6461
6801
  "",
6462
6802
  ` ${totalLine}${tokensBench ? ` ${tokensBench}` : ""}`,
6463
6803
  "",
@@ -6479,23 +6819,23 @@ function cardToolRelationship(view) {
6479
6819
  if (r.kind === "insufficient") {
6480
6820
  return "";
6481
6821
  }
6482
- const streakLine = view.streak_days >= 2 ? ` ${chalk.white(`${view.streak_days}-day streak, coding without missing a day.`)}` : null;
6822
+ const streakLine = view.streak_days >= 2 ? ` ${chalk2.white(`${view.streak_days}-day streak, coding without missing a day.`)}` : null;
6483
6823
  if (r.kind === "switch") {
6484
6824
  const rows = r.timeline.slice(-6).map((t) => {
6485
6825
  const monthShort2 = t.month.slice(2).replace("-", "/");
6486
6826
  const filled = Math.round(t.share * 10);
6487
- const bar3 = chalk.hex("#ff8a00")("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(10 - filled));
6488
- const label = t.dominant === r.from_tool ? chalk.dim(t.dominant) : chalk.white(t.dominant);
6489
- return ` ${chalk.dim(monthShort2)} ${bar3} ${label}`;
6827
+ const bar3 = chalk2.hex("#ff8a00")("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(10 - filled));
6828
+ const label = t.dominant === r.from_tool ? chalk2.dim(t.dominant) : chalk2.white(t.dominant);
6829
+ return ` ${chalk2.dim(monthShort2)} ${bar3} ${label}`;
6490
6830
  });
6491
6831
  return box(
6492
6832
  [
6493
- chalk.dim("YOU SWITCHED TOOLS"),
6833
+ chalk2.dim("YOU SWITCHED TOOLS"),
6494
6834
  "",
6495
6835
  ...rows,
6496
6836
  "",
6497
- chalk.dim(` joined the "${r.from_tool} \u2192 ${r.to_tool}" club`),
6498
- chalk.dim(` in ${r.switch_month}.`),
6837
+ chalk2.dim(` joined the "${r.from_tool} \u2192 ${r.to_tool}" club`),
6838
+ chalk2.dim(` in ${r.switch_month}.`),
6499
6839
  ...streakLine ? ["", streakLine] : []
6500
6840
  ].join("\n"),
6501
6841
  { borderColor: "cyan" }
@@ -6504,14 +6844,14 @@ function cardToolRelationship(view) {
6504
6844
  if (r.kind === "loyalist") {
6505
6845
  return box(
6506
6846
  [
6507
- chalk.dim("YOU'RE A LOYALIST"),
6847
+ chalk2.dim("YOU'RE A LOYALIST"),
6508
6848
  "",
6509
6849
  ` ${bigNumber(r.tool.toUpperCase())}`,
6510
6850
  "",
6511
- chalk.dim(
6851
+ chalk2.dim(
6512
6852
  ` ${r.sessions_count} sessions across ${r.months_count} month${r.months_count === 1 ? "" : "s"}.`
6513
6853
  ),
6514
- chalk.dim(" no second-guessing. you knew what you wanted."),
6854
+ chalk2.dim(" no second-guessing. you knew what you wanted."),
6515
6855
  ...streakLine ? ["", streakLine] : []
6516
6856
  ].join("\n"),
6517
6857
  { borderColor: "cyan" }
@@ -6520,16 +6860,16 @@ function cardToolRelationship(view) {
6520
6860
  if (r.kind === "polyglot") {
6521
6861
  const rows = r.tools.slice(0, 4).map((t) => {
6522
6862
  const filled = Math.round(t.share * 22);
6523
- const bar3 = chalk.hex("#ff8a00")("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(22 - filled));
6524
- return ` ${chalk.white(t.tool.padEnd(12, " "))} ${bar3} ${chalk.dim(Math.round(t.share * 100) + "%")}`;
6863
+ const bar3 = chalk2.hex("#ff8a00")("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(22 - filled));
6864
+ return ` ${chalk2.white(t.tool.padEnd(12, " "))} ${bar3} ${chalk2.dim(Math.round(t.share * 100) + "%")}`;
6525
6865
  });
6526
6866
  return box(
6527
6867
  [
6528
- chalk.dim("YOU'RE A POLYGLOT"),
6868
+ chalk2.dim("YOU'RE A POLYGLOT"),
6529
6869
  "",
6530
6870
  ...rows,
6531
6871
  "",
6532
- chalk.dim(" you don't pick favorites. respect."),
6872
+ chalk2.dim(" you don't pick favorites. respect."),
6533
6873
  ...streakLine ? ["", streakLine] : []
6534
6874
  ].join("\n"),
6535
6875
  { borderColor: "cyan" }
@@ -6591,17 +6931,17 @@ function cardModelsStack(view) {
6591
6931
  const column = (mm) => {
6592
6932
  if (!mm) return [];
6593
6933
  const c = modelProviderColor(mm.prov);
6594
- const hex = (s) => chalk.hex(c)(s);
6934
+ const hex = (s) => chalk2.hex(c)(s);
6595
6935
  const cap = " " + hex("\u256D" + "\u2500".repeat(8) + "\u256E") + " ";
6596
6936
  const base = " " + hex("\u2570" + "\u2500".repeat(8) + "\u256F") + " ";
6597
6937
  const side = " " + hex("\u2502") + " ".repeat(8) + hex("\u2502") + " ";
6598
- const num3 = " " + hex("\u2502") + chalk.bold.white(center("#" + mm.rank, 8)) + hex("\u2502") + " ";
6938
+ const num3 = " " + hex("\u2502") + chalk2.bold.white(center("#" + mm.rank, 8)) + hex("\u2502") + " ";
6599
6939
  const h = heights[mm.rank];
6600
6940
  const mid = Math.floor((h - 1) / 2);
6601
6941
  const body = [];
6602
6942
  for (let i = 0; i < h; i++) body.push(i === mid ? num3 : side);
6603
- const label = mm.rank === 1 ? bigNumber(center(mm.name)) : chalk.white(center(mm.name));
6604
- return [label, chalk.dim(center(mm.pct + "%")), cap, ...body, base];
6943
+ const label = mm.rank === 1 ? bigNumber(center(mm.name)) : chalk2.white(center(mm.name));
6944
+ return [label, chalk2.dim(center(mm.pct + "%")), cap, ...body, base];
6605
6945
  };
6606
6946
  const cols = [column(second), column(first), column(third)];
6607
6947
  const maxH = Math.max(...cols.map((col) => col.length));
@@ -6609,20 +6949,20 @@ function cardModelsStack(view) {
6609
6949
  (col) => Array(maxH - col.length).fill(blankCol).concat(col)
6610
6950
  );
6611
6951
  const header = ranked.length === 1 ? "YOUR MODEL & STACK" : "YOUR MODELS & STACK";
6612
- const lines = [chalk.dim(header), ""];
6952
+ const lines = [chalk2.dim(header), ""];
6613
6953
  lines.push(
6614
- " " + [blankCol, chalk.hex("#ffd700")(center("\u265B")), blankCol].join(" ")
6954
+ " " + [blankCol, chalk2.hex("#ffd700")(center("\u265B")), blankCol].join(" ")
6615
6955
  );
6616
6956
  for (let r = 0; r < maxH; r++)
6617
6957
  lines.push(" " + [padded[0][r], padded[1][r], padded[2][r]].join(" "));
6618
6958
  lines.push("");
6619
6959
  lines.push(
6620
- ` top pick: ${chalk.white(first.name)} ${chalk.dim(`\u2014 ${first.pct}% of sessions`)}`
6960
+ ` top pick: ${chalk2.white(first.name)} ${chalk2.dim(`\u2014 ${first.pct}% of sessions`)}`
6621
6961
  );
6622
6962
  if (view.stack_comment) {
6623
6963
  lines.push("");
6624
6964
  for (const l of wrapWords(view.stack_comment, 46))
6625
- lines.push(chalk.italic.white(" " + l));
6965
+ lines.push(chalk2.italic.white(" " + l));
6626
6966
  }
6627
6967
  return box(lines.join("\n"), { borderColor: "magenta" });
6628
6968
  }
@@ -6633,25 +6973,25 @@ function cardCodeFootprint(view) {
6633
6973
  const netLabel = `${f.net >= 0 ? "+" : ""}${compactNum(f.net)}`;
6634
6974
  const subhead = f.label === "Cleaner" ? "you delete more than you add. the slop stops with you." : f.label === "Shipper" ? `${f.additive_ratio >= 0 ? Math.round(f.additive_ratio * 100) : 0}% of your churn is brand-new code.` : "you rework as much as you write. nothing ships half-baked.";
6635
6975
  const lines = [
6636
- chalk.dim("YOUR CODE FOOTPRINT"),
6976
+ chalk2.dim("YOUR CODE FOOTPRINT"),
6637
6977
  "",
6638
6978
  ` ${bigNumber(f.label.toUpperCase())}`,
6639
6979
  "",
6640
- ` ${chalk.green("+ " + compactNum(f.lines_added).padEnd(7))} ${coloredBar(f.lines_added, max, 22, "#22c55e")} ${chalk.dim("added")}`,
6641
- ` ${chalk.red("- " + compactNum(f.lines_deleted).padEnd(7))} ${coloredBar(f.lines_deleted, max, 22, "#ef4444")} ${chalk.dim("deleted")}`,
6980
+ ` ${chalk2.green("+ " + compactNum(f.lines_added).padEnd(7))} ${coloredBar(f.lines_added, max, 22, "#22c55e")} ${chalk2.dim("added")}`,
6981
+ ` ${chalk2.red("- " + compactNum(f.lines_deleted).padEnd(7))} ${coloredBar(f.lines_deleted, max, 22, "#ef4444")} ${chalk2.dim("deleted")}`,
6642
6982
  "",
6643
- ` ${chalk.white("net " + netLabel)} ${chalk.dim("lines since 2024")}`
6983
+ ` ${chalk2.white("net " + netLabel)} ${chalk2.dim("lines since 2024")}`
6644
6984
  ];
6645
6985
  if (f.median_commit_size > 0)
6646
6986
  lines.push(
6647
- ` ${chalk.dim(`typical commit ~${compactNum(f.median_commit_size)} lines \xB7 biggest ${compactNum(f.biggest_commit_size)}`)}`
6987
+ ` ${chalk2.dim(`typical commit ~${compactNum(f.median_commit_size)} lines \xB7 biggest ${compactNum(f.biggest_commit_size)}`)}`
6648
6988
  );
6649
6989
  lines.push("");
6650
6990
  if (f.line) {
6651
6991
  for (const l of wrapWords(f.line, 47))
6652
- lines.push(chalk.italic.white(" " + l));
6992
+ lines.push(chalk2.italic.white(" " + l));
6653
6993
  } else {
6654
- lines.push(chalk.dim(" " + subhead));
6994
+ lines.push(chalk2.dim(" " + subhead));
6655
6995
  }
6656
6996
  return box(lines.join("\n"), { borderColor: "green" });
6657
6997
  }
@@ -6662,20 +7002,20 @@ function cardConcurrency(view) {
6662
7002
  const longest = days >= 1.5 ? `${days.toFixed(0)} days` : `${Math.round(c.longest_session_hours)}h`;
6663
7003
  const bench = benchmarkInline(view, view.rank_summary.avg_agents_percentile);
6664
7004
  const lines = [
6665
- chalk.dim("HOW MANY AGENTS YOU JUGGLE"),
7005
+ chalk2.dim("HOW MANY AGENTS YOU JUGGLE"),
6666
7006
  "",
6667
- ` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk.white("agents open at once")}${bench ? ` ${bench}` : ""}`,
7007
+ ` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk2.white("agents open at once")}${bench ? ` ${bench}` : ""}`,
6668
7008
  "",
6669
- ` ${chalk.white("peak".padEnd(14))} ${chalk.dim(`${c.open_tab_peak} at once`)}`,
6670
- ` ${chalk.white("longest tab".padEnd(14))} ${chalk.dim(`${longest} open`)}`,
6671
- ` ${chalk.white("2+ active".padEnd(14))} ${chalk.dim(`${Math.round(c.active_juggle_pct * 100)}% of your coding time`)}`,
7009
+ ` ${chalk2.white("peak".padEnd(14))} ${chalk2.dim(`${c.open_tab_peak} at once`)}`,
7010
+ ` ${chalk2.white("longest tab".padEnd(14))} ${chalk2.dim(`${longest} open`)}`,
7011
+ ` ${chalk2.white("2+ active".padEnd(14))} ${chalk2.dim(`${Math.round(c.active_juggle_pct * 100)}% of your coding time`)}`,
6672
7012
  ""
6673
7013
  ];
6674
7014
  if (c.line) {
6675
7015
  for (const l of wrapWords(c.line, 47))
6676
- lines.push(chalk.italic.white(" " + l));
7016
+ lines.push(chalk2.italic.white(" " + l));
6677
7017
  } else {
6678
- lines.push(chalk.dim(" a one-agent setup could never."));
7018
+ lines.push(chalk2.dim(" a one-agent setup could never."));
6679
7019
  }
6680
7020
  return box(lines.join("\n"), { borderColor: "blue" });
6681
7021
  }
@@ -6692,22 +7032,22 @@ function cardTalk(view) {
6692
7032
  const c = view.conversation;
6693
7033
  const v = view.collaboration;
6694
7034
  if ((!c || c.total_prompts < 10) && !v) return "";
6695
- const lines = [chalk.dim("HOW YOU TALK"), ""];
7035
+ const lines = [chalk2.dim("HOW YOU TALK"), ""];
6696
7036
  if (c?.line) {
6697
7037
  for (const l of wrapWords(c.line, 47))
6698
- lines.push(chalk.italic.white(" " + l));
7038
+ lines.push(chalk2.italic.white(" " + l));
6699
7039
  lines.push("");
6700
7040
  }
6701
7041
  if (v) {
6702
7042
  lines.push(` ${bigNumber(v.style_label)}`);
6703
7043
  if (v.summary)
6704
7044
  for (const l of wrapWords(v.summary, 47))
6705
- lines.push(chalk.white(" " + l));
7045
+ lines.push(chalk2.white(" " + l));
6706
7046
  const sigs = [
6707
7047
  ["reads", v.signals.fact_checking],
6708
7048
  ["drives", v.signals.autonomy]
6709
7049
  ].filter(([, t]) => t).map(
6710
- ([label, t]) => ` ${chalk.dim(label.padEnd(8))} ${chalk.white(briefSignal(t))}`
7050
+ ([label, t]) => ` ${chalk2.dim(label.padEnd(8))} ${chalk2.white(briefSignal(t))}`
6711
7051
  );
6712
7052
  if (sigs.length > 0) {
6713
7053
  lines.push("");
@@ -6717,11 +7057,11 @@ function cardTalk(view) {
6717
7057
  if (c) {
6718
7058
  lines.push("");
6719
7059
  lines.push(
6720
- ` ${chalk.white(c.total_prompts.toLocaleString())} ${chalk.dim("prompts")} \xB7 ${chalk.white(Math.round(c.question_ratio * 100) + "%")} ${chalk.dim("questions")} \xB7 ${chalk.white("~" + c.avg_prompt_chars)} ${chalk.dim("chars")}`
7060
+ ` ${chalk2.white(c.total_prompts.toLocaleString())} ${chalk2.dim("prompts")} \xB7 ${chalk2.white(Math.round(c.question_ratio * 100) + "%")} ${chalk2.dim("questions")} \xB7 ${chalk2.white("~" + c.avg_prompt_chars)} ${chalk2.dim("chars")}`
6721
7061
  );
6722
7062
  if (c.themes.length > 0)
6723
7063
  lines.push(
6724
- ` ${chalk.dim("recurring: " + c.themes.slice(0, 4).join(", "))}`
7064
+ ` ${chalk2.dim("recurring: " + c.themes.slice(0, 4).join(", "))}`
6725
7065
  );
6726
7066
  }
6727
7067
  return box(lines.join("\n"), { borderColor: "magenta" });
@@ -6733,13 +7073,13 @@ function cardProficiency(view) {
6733
7073
  if (!p) {
6734
7074
  return box(
6735
7075
  [
6736
- chalk.dim(" you are\u2026"),
7076
+ chalk2.dim(" you are\u2026"),
6737
7077
  "",
6738
- chalk.hex("#ff8a00")(mascot),
7078
+ chalk2.hex("#ff8a00")(mascot),
6739
7079
  "",
6740
7080
  ` ${archetype}`,
6741
7081
  "",
6742
- ` ${chalk.italic.white(`"${view.zinger}"`)}`
7082
+ ` ${chalk2.italic.white(`"${view.zinger}"`)}`
6743
7083
  ].join("\n"),
6744
7084
  { borderColor: "yellow" }
6745
7085
  );
@@ -6747,27 +7087,27 @@ function cardProficiency(view) {
6747
7087
  const scoreTop = topPercentNumber(p.score_percentile ?? null);
6748
7088
  const sampleSize = view.rank_summary.sample_size;
6749
7089
  const cohort = scoreTop != null && sampleSize > 0 ? `top ${scoreTop}% of ${sampleSize.toLocaleString()} users` : view.cohort_size > 0 ? `among ${view.cohort_size.toLocaleString()} users so far` : "";
6750
- const metricRow = (label, value) => value === null ? null : ` ${chalk.white(label.padEnd(12))} ${bar2(value, 100, 10)} ${chalk.dim(String(value))}`;
7090
+ const metricRow = (label, value) => value === null ? null : ` ${chalk2.white(label.padEnd(12))} ${bar2(value, 100, 10)} ${chalk2.dim(String(value))}`;
6751
7091
  const bars = [
6752
7092
  metricRow("intensity", p.intensity),
6753
7093
  metricRow("consistency", p.consistency),
6754
- p.craft_bonus > 0 ? ` ${chalk.white("craft".padEnd(12))} ${chalk.hex("#ff8a00")(`+${p.craft_bonus}`)} ${chalk.dim("clean prompting")}` : null
7094
+ p.craft_bonus > 0 ? ` ${chalk2.white("craft".padEnd(12))} ${chalk2.hex("#ff8a00")(`+${p.craft_bonus}`)} ${chalk2.dim("clean prompting")}` : null
6755
7095
  ].filter((r) => r !== null);
6756
7096
  const zingerLines = wrapWords(`"${view.zinger}"`, INNER_WIDTH).map(
6757
- (l) => centerText(l, chalk.italic.white)
7097
+ (l) => centerText(l, chalk2.italic.white)
6758
7098
  );
6759
7099
  const commentLines = p.comment ? wrapWords(p.comment, INNER_WIDTH - 4).map(
6760
- (l, i) => i === 0 ? ` ${chalk.hex("#ff8a00")(">")} ${chalk.white(l)}` : ` ${chalk.white(l)}`
7100
+ (l, i) => i === 0 ? ` ${chalk2.hex("#ff8a00")(">")} ${chalk2.white(l)}` : ` ${chalk2.white(l)}`
6761
7101
  ) : [];
6762
7102
  const tier = tierForScore2(p.score);
6763
7103
  const lines = [
6764
- centerBlock(tier.art, chalk.hex("#ff8a00")),
7104
+ centerBlock(tier.art, chalk2.hex("#ff8a00")),
6765
7105
  "",
6766
7106
  centerText(`\u2500\u2500 ${tier.label} \u2500\u2500`, bigNumber),
6767
7107
  centerText(`${p.score} / 100`, bigNumber),
6768
- cohort ? centerText(cohort, chalk.dim) : null,
7108
+ cohort ? centerText(cohort, chalk2.dim) : null,
6769
7109
  "",
6770
- centerText(view.archetype_label, chalk.bold.hex("#ad5cff")),
7110
+ centerText(view.archetype_label, chalk2.bold.hex("#ad5cff")),
6771
7111
  ...zingerLines,
6772
7112
  "",
6773
7113
  ...bars,
@@ -6805,7 +7145,7 @@ async function renderWrappedAll(view) {
6805
7145
  " " + TITLE_GRADIENT.multiline("\u2591\u2592\u2593 YOUR AI WRAPPED \u2593\u2592\u2591") + "\n"
6806
7146
  );
6807
7147
  process.stdout.write(
6808
- chalk.dim(` ${view.name.toLowerCase()} \xB7 last 30 days
7148
+ chalk2.dim(` ${view.name.toLowerCase()} \xB7 last 30 days
6809
7149
 
6810
7150
  `)
6811
7151
  );
@@ -6825,6 +7165,19 @@ async function renderWrappedAll(view) {
6825
7165
  }
6826
7166
 
6827
7167
  // src/wrapped-client.ts
7168
+ function applyLedgerMonthly(profile, ledgerMonthly) {
7169
+ if (!ledgerMonthly) return;
7170
+ const ai = profile?.ai_usage;
7171
+ if (!ai) return;
7172
+ for (const [src, buckets] of Object.entries(ledgerMonthly)) {
7173
+ if (!Array.isArray(buckets) || buckets.length === 0) continue;
7174
+ const stats = ai[src] ?? (ai[src] = {});
7175
+ const at = stats.all_time ?? (stats.all_time = {});
7176
+ at.monthly_buckets = buckets;
7177
+ at.total_duration_hours = +buckets.reduce((s, b) => s + (b.duration_hours ?? 0), 0).toFixed(1);
7178
+ at.total_sessions = buckets.reduce((s, b) => s + (b.sessions ?? 0), 0);
7179
+ }
7180
+ }
6828
7181
  var HEADERS = {
6829
7182
  "Content-Type": "application/json",
6830
7183
  // Vercel BotID flagged Anthropic/JS as bot traffic; same goes for default
@@ -6953,6 +7306,7 @@ async function createWrapped(args) {
6953
7306
  );
6954
7307
  return null;
6955
7308
  }
7309
+ applyLedgerMonthly(args.profile, created.ledger_monthly);
6956
7310
  const view = aggregateView({
6957
7311
  profile: args.profile,
6958
7312
  computed,
@@ -7014,18 +7368,18 @@ var MODE_OPTIONS = [
7014
7368
  function renderModeMenu(selected) {
7015
7369
  const out = [
7016
7370
  "",
7017
- chalk2.white(" How do you want to run Standout?"),
7371
+ chalk3.white(" How do you want to run Standout?"),
7018
7372
  ""
7019
7373
  ];
7020
7374
  MODE_OPTIONS.forEach((opt, i) => {
7021
7375
  const active = i === selected;
7022
- const pointer = active ? chalk2.hex("#ad5cff")("\u276F ") : " ";
7023
- const title = active ? chalk2.bold.white(opt.title) + chalk2.dim(opt.tag) : chalk2.dim(opt.title + opt.tag);
7376
+ const pointer = active ? chalk3.hex("#ad5cff")("\u276F ") : " ";
7377
+ const title = active ? chalk3.bold.white(opt.title) + chalk3.dim(opt.tag) : chalk3.dim(opt.title + opt.tag);
7024
7378
  out.push(`${pointer}${title}`);
7025
- for (const line of opt.lines) out.push(chalk2.dim(` ${line}`));
7379
+ for (const line of opt.lines) out.push(chalk3.dim(` ${line}`));
7026
7380
  out.push("");
7027
7381
  });
7028
- out.push(chalk2.dim(" \u2191/\u2193 to move \xB7 enter to select"));
7382
+ out.push(chalk3.dim(" \u2191/\u2193 to move \xB7 enter to select"));
7029
7383
  return out.join("\n");
7030
7384
  }
7031
7385
  async function chooseMode() {
@@ -7128,8 +7482,8 @@ function startLoadingLine(label) {
7128
7482
  let frame = 0;
7129
7483
  const render = () => {
7130
7484
  const dots = frames[frame % frames.length];
7131
- const accent = chalk2.hex("#ad5cff")(dots);
7132
- const text = chalk2.white(label);
7485
+ const accent = chalk3.hex("#ad5cff")(dots);
7486
+ const text = chalk3.white(label);
7133
7487
  process.stderr.write(`\r\x1B[2K ${accent} ${text}`);
7134
7488
  frame += 1;
7135
7489
  };
@@ -7161,7 +7515,7 @@ async function maybeShareWrapped(wrapped) {
7161
7515
  return;
7162
7516
  }
7163
7517
  const ans = await askLine(
7164
- chalk2.white(" press enter to see your ai wrapped ")
7518
+ chalk3.white(" press enter to see your ai wrapped ")
7165
7519
  );
7166
7520
  process.stderr.write("\n");
7167
7521
  const shared = !ans || ans.toLowerCase() !== "n";
@@ -7257,8 +7611,8 @@ async function runLocal() {
7257
7611
  );
7258
7612
  return;
7259
7613
  }
7260
- const file = join6(tmpdir2(), `standout-wrapped-${Date.now()}.png`);
7261
- writeFileSync(file, png);
7614
+ const file = join7(tmpdir2(), `standout-wrapped-${Date.now()}.png`);
7615
+ writeFileSync2(file, png);
7262
7616
  process.stderr.write("\n");
7263
7617
  const shown = showImageInline(png);
7264
7618
  if (!shown) {
@@ -7482,6 +7836,17 @@ async function runAgent(jobId) {
7482
7836
  async function main() {
7483
7837
  ensureHeapHeadroom();
7484
7838
  const args = process.argv.slice(2);
7839
+ if (args[0] === "schedule" || args[0] === "unschedule") {
7840
+ const action = parseScheduleArgs(args);
7841
+ if (action.kind === "install") {
7842
+ await installSchedule(action.slug, action.flags);
7843
+ } else if (action.kind === "remove") {
7844
+ await removeSchedule();
7845
+ } else {
7846
+ await scheduleStatus();
7847
+ }
7848
+ return;
7849
+ }
7485
7850
  const proxy = configureProxyFromEnv();
7486
7851
  if (proxy) process.stderr.write(` (using proxy ${proxy})
7487
7852
  `);
@@ -7507,6 +7872,7 @@ async function main() {
7507
7872
  } else {
7508
7873
  if (jobId) process.env.STANDOUT_JOB_ID = jobId;
7509
7874
  await runAgent(jobId);
7875
+ await maybeAutoInstallSchedule(jobId);
7510
7876
  }
7511
7877
  }
7512
7878
  main().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "standout",
3
- "version": "0.5.37",
3
+ "version": "0.6.0",
4
4
  "description": "Build your developer profile with AI. One command, zero friction.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -63,5 +63,10 @@
63
63
  "license": "MIT",
64
64
  "optionalDependencies": {
65
65
  "better-sqlite3": "^12.9.0"
66
+ },
67
+ "allowScripts": {
68
+ "better-sqlite3": true,
69
+ "esbuild": true,
70
+ "fsevents": true
66
71
  }
67
72
  }