standout 0.5.38 → 0.6.1

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 +19 -0
  2. package/dist/cli.js +457 -114
  3. package/package.json +6 -1
package/README.md CHANGED
@@ -11,3 +11,22 @@ 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 so your stats never
18
+ decay — local session logs only cover ~30 days, and the refresh keeps your
19
+ full history intact. It fires at the first hour your machine is awake in each
20
+ new month (an hourly launchd/cron tick guarded by a month stamp in
21
+ `~/.standout/last-refresh`, so sleeping through the 1st never skips a month).
22
+ Uses launchd on macOS and cron on Linux; not available on Windows.
23
+
24
+ ```bash
25
+ npx standout schedule status # is it on? command, log path, last run
26
+ npx standout schedule off # disable (won't be re-added automatically)
27
+ npx standout schedule [job] # re-enable / install manually
28
+ ```
29
+
30
+ Scheduled runs are unattended: they upload your refreshed stats and write the
31
+ wrapped link to the log (`~/Library/Logs/standout.log` on macOS,
32
+ `~/.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
  }
@@ -5144,6 +5148,333 @@ The JSON should follow this structure:
5144
5148
  - Ask interactive questions ONE AT A TIME.
5145
5149
  - If you receive a pause_turn stop reason, your turn will be continued automatically \u2014 just keep going.`;
5146
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 buildGuardedCommand(runCommand) {
5207
+ return `STAMP="$HOME/.standout/last-refresh"; MONTH=$(date +%Y-%m); [ "$(cat "$STAMP" 2>/dev/null)" = "$MONTH" ] || { mkdir -p "$HOME/.standout"; STANDOUT_SCHEDULED=1 ${runCommand} && echo "$MONTH" > "$STAMP"; }`;
5208
+ }
5209
+ function extractRunCommand(guarded) {
5210
+ const m = guarded.match(/STANDOUT_SCHEDULED=1 (.*?) && echo "\$MONTH"/);
5211
+ return m ? m[1] : null;
5212
+ }
5213
+ function buildLaunchdPlist(guardedCommand, logPath) {
5214
+ return `<?xml version="1.0" encoding="UTF-8"?>
5215
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
5216
+ <plist version="1.0">
5217
+ <dict>
5218
+ <key>Label</key><string>${LAUNCHD_LABEL}</string>
5219
+ <key>ProgramArguments</key>
5220
+ <array>
5221
+ <string>/bin/zsh</string>
5222
+ <string>-lc</string>
5223
+ <string>${escapeXml(guardedCommand)}</string>
5224
+ </array>
5225
+ <key>StartInterval</key><integer>3600</integer>
5226
+ <key>StandardOutPath</key><string>${escapeXml(logPath)}</string>
5227
+ <key>StandardErrorPath</key><string>${escapeXml(logPath)}</string>
5228
+ </dict>
5229
+ </plist>
5230
+ `;
5231
+ }
5232
+ function buildCronLine(guardedCommand, logPath) {
5233
+ const escaped = guardedCommand.replace(/%/g, "\\%");
5234
+ return `0 * * * * /bin/bash -lc '${escaped}' >> "${logPath}" 2>&1 ${CRON_MARKER}`;
5235
+ }
5236
+ function stripCronEntries(crontab) {
5237
+ return crontab.split("\n").filter((line) => !line.includes(CRON_MARKER)).join("\n");
5238
+ }
5239
+ function supportedPlatform() {
5240
+ return process.platform === "darwin" || process.platform === "linux";
5241
+ }
5242
+ function stateDir() {
5243
+ return join6(homedir6(), ".standout");
5244
+ }
5245
+ function optOutPath() {
5246
+ return join6(stateDir(), "no-schedule");
5247
+ }
5248
+ function plistPath() {
5249
+ return join6(homedir6(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
5250
+ }
5251
+ function schedLogPath() {
5252
+ return process.platform === "darwin" ? join6(homedir6(), "Library", "Logs", "standout.log") : join6(stateDir(), "standout.log");
5253
+ }
5254
+ function isOptedOut() {
5255
+ return existsSync6(optOutPath());
5256
+ }
5257
+ function writeOptOut() {
5258
+ mkdirSync2(stateDir(), { recursive: true });
5259
+ writeFileSync(
5260
+ optOutPath(),
5261
+ "created by `standout schedule off` \u2014 delete (or run `npx standout schedule`) to re-enable monthly runs\n"
5262
+ );
5263
+ }
5264
+ function clearOptOut() {
5265
+ rmSync2(optOutPath(), { force: true });
5266
+ }
5267
+ function refreshStampPath() {
5268
+ return join6(stateDir(), "last-refresh");
5269
+ }
5270
+ function readRefreshStamp() {
5271
+ try {
5272
+ return readFileSync6(refreshStampPath(), "utf8").trim() || null;
5273
+ } catch {
5274
+ return null;
5275
+ }
5276
+ }
5277
+ function writeRefreshStamp() {
5278
+ mkdirSync2(stateDir(), { recursive: true });
5279
+ const now = /* @__PURE__ */ new Date();
5280
+ const month = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
5281
+ writeFileSync(refreshStampPath(), `${month}
5282
+ `);
5283
+ }
5284
+ function launchctl(args) {
5285
+ const res = spawnSync2("launchctl", args, { encoding: "utf8" });
5286
+ return {
5287
+ status: res.status,
5288
+ stdout: res.stdout ?? "",
5289
+ stderr: res.stderr ?? ""
5290
+ };
5291
+ }
5292
+ function uid() {
5293
+ return process.getuid?.() ?? 0;
5294
+ }
5295
+ function readCrontab() {
5296
+ const res = spawnSync2("crontab", ["-l"], { encoding: "utf8" });
5297
+ return res.status === 0 ? res.stdout ?? "" : "";
5298
+ }
5299
+ function writeCrontab(content) {
5300
+ const res = spawnSync2("crontab", ["-"], {
5301
+ input: content,
5302
+ encoding: "utf8"
5303
+ });
5304
+ if (res.status !== 0) {
5305
+ throw new Error(`crontab update failed: ${res.stderr?.trim() || "error"}`);
5306
+ }
5307
+ }
5308
+ function installJob(command) {
5309
+ const guarded = buildGuardedCommand(command);
5310
+ if (process.platform === "darwin") {
5311
+ const path2 = plistPath();
5312
+ mkdirSync2(join6(homedir6(), "Library", "LaunchAgents"), { recursive: true });
5313
+ writeFileSync(path2, buildLaunchdPlist(guarded, schedLogPath()));
5314
+ launchctl(["bootout", `gui/${uid()}/${LAUNCHD_LABEL}`]);
5315
+ const res = launchctl(["bootstrap", `gui/${uid()}`, path2]);
5316
+ if (res.status !== 0) {
5317
+ throw new Error(
5318
+ `launchctl bootstrap failed (${res.stderr.trim() || res.status}) \u2014 plist written to ${path2}; load it manually with: launchctl bootstrap gui/$(id -u) ${path2}`
5319
+ );
5320
+ }
5321
+ } else {
5322
+ mkdirSync2(stateDir(), { recursive: true });
5323
+ const stripped = stripCronEntries(readCrontab());
5324
+ const base = stripped === "" || stripped.endsWith("\n") ? stripped : stripped + "\n";
5325
+ writeCrontab(base + buildCronLine(guarded, schedLogPath()) + "\n");
5326
+ }
5327
+ writeRefreshStamp();
5328
+ }
5329
+ function installedShellCommand() {
5330
+ if (process.platform === "darwin") {
5331
+ if (!existsSync6(plistPath())) return null;
5332
+ const xml = readFileSync6(plistPath(), "utf8");
5333
+ const m = xml.match(/<string>-lc<\/string>\s*<string>([^<]*)<\/string>/);
5334
+ return m ? unescapeXml(m[1]) : null;
5335
+ }
5336
+ if (process.platform === "linux") {
5337
+ const line = readCrontab().split("\n").find((l) => l.includes(CRON_MARKER));
5338
+ const m = line?.match(/-lc '([^']*)'/);
5339
+ return m ? m[1].replace(/\\%/g, "%") : null;
5340
+ }
5341
+ return null;
5342
+ }
5343
+ async function installSchedule(slug, flags) {
5344
+ if (!supportedPlatform()) {
5345
+ process.stderr.write(
5346
+ "\n scheduling isn't supported on this platform yet \u2014 run `npx standout` manually.\n\n"
5347
+ );
5348
+ process.exit(1);
5349
+ }
5350
+ clearOptOut();
5351
+ const command = buildRunCommand(slug, flags);
5352
+ installJob(command);
5353
+ process.stderr.write(
5354
+ `
5355
+ ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh scheduled")} ${chalk.dim("(first awake hour of each month)")}
5356
+ ` + chalk.dim(` runs: ${command}
5357
+ `) + chalk.dim(` log: ${schedLogPath()}
5358
+ `) + chalk.dim(" disable anytime with `npx standout schedule off`\n\n")
5359
+ );
5360
+ }
5361
+ async function removeSchedule() {
5362
+ if (!supportedPlatform()) {
5363
+ process.stderr.write(
5364
+ "\n scheduling isn't supported on this platform \u2014 nothing to remove.\n\n"
5365
+ );
5366
+ return;
5367
+ }
5368
+ writeOptOut();
5369
+ let removed = false;
5370
+ if (process.platform === "darwin") {
5371
+ launchctl(["bootout", `gui/${uid()}/${LAUNCHD_LABEL}`]);
5372
+ if (existsSync6(plistPath())) {
5373
+ rmSync2(plistPath(), { force: true });
5374
+ removed = true;
5375
+ }
5376
+ } else {
5377
+ const current = readCrontab();
5378
+ const stripped = stripCronEntries(current);
5379
+ if (stripped !== current) {
5380
+ writeCrontab(stripped);
5381
+ removed = true;
5382
+ }
5383
+ }
5384
+ process.stderr.write(
5385
+ removed ? `
5386
+ ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh disabled")}
5387
+ ` + chalk.dim(
5388
+ " it won't be scheduled again \u2014 re-enable with `npx standout schedule`\n\n"
5389
+ ) : `
5390
+ ${chalk.dim("no monthly schedule installed \u2014 future runs won't add one (re-enable with `npx standout schedule`)")}
5391
+
5392
+ `
5393
+ );
5394
+ }
5395
+ async function scheduleStatus() {
5396
+ if (!supportedPlatform()) {
5397
+ process.stderr.write(
5398
+ "\n scheduling isn't supported on this platform.\n\n"
5399
+ );
5400
+ return;
5401
+ }
5402
+ const shell = installedShellCommand();
5403
+ if (!shell) {
5404
+ process.stderr.write(
5405
+ `
5406
+ ${chalk.white("no monthly schedule installed")}` + (isOptedOut() ? chalk.dim(" (opted out)") : "") + "\n" + chalk.dim(" enable with `npx standout schedule`\n\n")
5407
+ );
5408
+ return;
5409
+ }
5410
+ const stamp = readRefreshStamp();
5411
+ process.stderr.write(
5412
+ `
5413
+ ${chalk.hex(ACCENT)("\u2713")} ${chalk.white("monthly refresh is on")} ${chalk.dim("(first awake hour of each month)")}
5414
+ ` + chalk.dim(` runs: ${extractRunCommand(shell) ?? shell}
5415
+ `) + chalk.dim(` log: ${schedLogPath()}
5416
+ `) + chalk.dim(` last refreshed month: ${stamp ?? "never"}
5417
+ `)
5418
+ );
5419
+ if (process.platform === "darwin") {
5420
+ const res = launchctl(["print", `gui/${uid()}/${LAUNCHD_LABEL}`]);
5421
+ const exitLine = res.stdout.split("\n").find((l) => l.includes("last exit code"));
5422
+ if (res.status !== 0) {
5423
+ process.stderr.write(
5424
+ chalk.dim(
5425
+ " warning: job not loaded in launchd \u2014 re-run `npx standout schedule`\n"
5426
+ )
5427
+ );
5428
+ } else if (exitLine) {
5429
+ process.stderr.write(chalk.dim(` ${exitLine.trim()}
5430
+ `));
5431
+ }
5432
+ }
5433
+ if (existsSync6(schedLogPath())) {
5434
+ const mtime = statSync5(schedLogPath()).mtime;
5435
+ const tail = readFileSync6(schedLogPath(), "utf8").trimEnd().split("\n").slice(-10);
5436
+ process.stderr.write(
5437
+ chalk.dim(` last log activity: ${mtime.toLocaleString()}
5438
+
5439
+ `) + tail.map((l) => chalk.dim(` \u2502 ${l}
5440
+ `)).join("")
5441
+ );
5442
+ }
5443
+ process.stderr.write("\n");
5444
+ }
5445
+ function autoRefreshNotice(headline) {
5446
+ const optOut = "npx standout schedule off";
5447
+ return `
5448
+ ${chalk.hex(ACCENT)("\u27F3")} ${chalk.bold.white(headline)}
5449
+ ${chalk.white("standout re-runs once a month so your wrapped keeps building")}
5450
+ ${chalk.white("(because local logs only keep ~30 days of history).")}
5451
+ ${chalk.white("opt out anytime:")} ${chalk.hex(ACCENT)(optOut)}
5452
+
5453
+ `;
5454
+ }
5455
+ async function maybeAutoInstallSchedule(slug) {
5456
+ try {
5457
+ if (process.env.STANDOUT_SCHEDULED === "1") return;
5458
+ if (!supportedPlatform() || isOptedOut()) return;
5459
+ const command = buildRunCommand(slug, []);
5460
+ if (installedShellCommand() === buildGuardedCommand(command)) {
5461
+ writeRefreshStamp();
5462
+ process.stderr.write(autoRefreshNotice("auto-refresh is on"));
5463
+ return;
5464
+ }
5465
+ installJob(command);
5466
+ process.stderr.write(autoRefreshNotice("auto-refresh scheduled"));
5467
+ } catch (error) {
5468
+ process.stderr.write(
5469
+ chalk.dim(
5470
+ ` (couldn't schedule the monthly refresh: ${error instanceof Error ? error.message : String(error)})
5471
+
5472
+ `
5473
+ )
5474
+ );
5475
+ }
5476
+ }
5477
+
5147
5478
  // src/tools.ts
5148
5479
  import { execSync as execSync3 } from "child_process";
5149
5480
  import { createInterface as createInterface2 } from "readline";
@@ -6014,7 +6345,7 @@ function computeConversation(aiUsage, line, themes) {
6014
6345
 
6015
6346
  // src/wrapped/render.ts
6016
6347
  import boxen from "boxen";
6017
- import chalk from "chalk";
6348
+ import chalk2 from "chalk";
6018
6349
  import gradient from "gradient-string";
6019
6350
 
6020
6351
  // src/wrapped/chrono-critters.ts
@@ -6191,8 +6522,8 @@ function hasHistogramData(view) {
6191
6522
  return view.hour_histogram.some((v) => v > 0);
6192
6523
  }
6193
6524
  function divider(label) {
6194
- return chalk.dim(`
6195
- ${label.padEnd(48, " ")} ${chalk.dim("[press \u21B5]")}
6525
+ return chalk2.dim(`
6526
+ ${label.padEnd(48, " ")} ${chalk2.dim("[press \u21B5]")}
6196
6527
  `);
6197
6528
  }
6198
6529
  function wrapWords(text, width) {
@@ -6230,7 +6561,7 @@ function centerBlock(art, color) {
6230
6561
  function bar2(value, max, width) {
6231
6562
  if (max === 0) return " ".repeat(width);
6232
6563
  const filled = Math.round(value / max * width);
6233
- return chalk.hex("#ff8a00")("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(width - filled));
6564
+ return chalk2.hex("#ff8a00")("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(width - filled));
6234
6565
  }
6235
6566
  function compactNum(n) {
6236
6567
  const sign = n < 0 ? "-" : "";
@@ -6240,12 +6571,12 @@ function compactNum(n) {
6240
6571
  return `${sign}${abs}`;
6241
6572
  }
6242
6573
  function coloredBar(value, max, width, hex) {
6243
- if (max <= 0) return chalk.gray("\u2591".repeat(width));
6574
+ if (max <= 0) return chalk2.gray("\u2591".repeat(width));
6244
6575
  const filled = Math.max(
6245
6576
  0,
6246
6577
  Math.min(width, Math.round(value / max * width))
6247
6578
  );
6248
- return chalk.hex(hex)("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(width - filled));
6579
+ return chalk2.hex(hex)("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(width - filled));
6249
6580
  }
6250
6581
  function sparkline2(buckets) {
6251
6582
  if (!buckets.length) return "";
@@ -6273,8 +6604,8 @@ function hourlyChart(buckets, critter = null) {
6273
6604
  }
6274
6605
  }
6275
6606
  const left = [
6276
- chalk.hex("#ad5cff")(spark.padEnd(width, " ")),
6277
- chalk.dim(axis.join(""))
6607
+ chalk2.hex("#ad5cff")(spark.padEnd(width, " ")),
6608
+ chalk2.dim(axis.join(""))
6278
6609
  ];
6279
6610
  if (!critter) return left.map((l) => ` ${l}`);
6280
6611
  const critterLines = critter.art.split("\n");
@@ -6316,12 +6647,12 @@ function card1Open(view) {
6316
6647
  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.";
6317
6648
  const bench = benchmarkInline(view, view.rank_summary.hours_percentile);
6318
6649
  const lines = [
6319
- chalk.dim("YOU MANAGED"),
6650
+ chalk2.dim("YOU MANAGED"),
6320
6651
  "",
6321
6652
  bigNumber(` ${hours} session-hours`) + (bench ? ` ${bench}` : ""),
6322
6653
  "",
6323
- chalk.white(` ${verdict}`),
6324
- chalk.dim(" across parallel agent sessions, last 30 days")
6654
+ chalk2.white(` ${verdict}`),
6655
+ chalk2.dim(" across parallel agent sessions, last 30 days")
6325
6656
  ].join("\n");
6326
6657
  return box(lines, { borderColor: "yellow" });
6327
6658
  }
@@ -6331,7 +6662,7 @@ function cardAllTime(view) {
6331
6662
  const max = Math.max(...months.map((m) => m.duration_hours), 1);
6332
6663
  const rows = months.map((m) => {
6333
6664
  const label = monthYearShort(m.month).padEnd(6);
6334
- return ` ${chalk.dim(label)} ${bar2(m.duration_hours, max, 20)} ${chalk.dim(`${Math.round(m.duration_hours)}h`)}`;
6665
+ return ` ${chalk2.dim(label)} ${bar2(m.duration_hours, max, 20)} ${chalk2.dim(`${Math.round(m.duration_hours)}h`)}`;
6335
6666
  });
6336
6667
  const since = view.all_time.first_session ? new Date(view.all_time.first_session).toISOString().slice(0, 7) : "your first local log";
6337
6668
  const latest = months[months.length - 1];
@@ -6341,14 +6672,14 @@ function cardAllTime(view) {
6341
6672
  )[0];
6342
6673
  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;
6343
6674
  const lines = [
6344
- chalk.dim("YOUR AI CODING YEAR"),
6675
+ chalk2.dim("YOUR AI CODING YEAR"),
6345
6676
  "",
6346
6677
  ` ${bigNumber(`~${Math.round(view.all_time.total_hours).toLocaleString()} hours`)} from local logs`,
6347
- ` ${chalk.white(view.all_time.total_sessions.toLocaleString())} sessions detected since ${since}`,
6348
- ...trend ? [` ${chalk.dim(trend)}`] : [],
6678
+ ` ${chalk2.white(view.all_time.total_sessions.toLocaleString())} sessions detected since ${since}`,
6679
+ ...trend ? [` ${chalk2.dim(trend)}`] : [],
6349
6680
  "",
6350
6681
  ...rows,
6351
- best ? ` ${chalk.dim(`best local-log month: ${monthShort(best.month)} \xB7 ~${Math.round(best.duration_hours)}h`)}` : ""
6682
+ best ? ` ${chalk2.dim(`best local-log month: ${monthShort(best.month)} \xB7 ~${Math.round(best.duration_hours)}h`)}` : ""
6352
6683
  ].join("\n");
6353
6684
  return box(lines, { borderColor: "cyan" });
6354
6685
  }
@@ -6367,25 +6698,25 @@ function cardRhythm(view) {
6367
6698
  const isNightOwl = critter?.key === "night_owl";
6368
6699
  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.";
6369
6700
  const lines = [
6370
- chalk.dim("WHEN YOU CODE"),
6701
+ chalk2.dim("WHEN YOU CODE"),
6371
6702
  "",
6372
6703
  ...hourlyChart(
6373
6704
  view.hour_histogram,
6374
- critter ? { art: critter.art, color: chalk.magenta } : null
6705
+ critter ? { art: critter.art, color: chalk2.magenta } : null
6375
6706
  ),
6376
6707
  "",
6377
- ` ${chalk.white(caption)}`,
6708
+ ` ${chalk2.white(caption)}`,
6378
6709
  "",
6379
- ` ${chalk.white("weekdays".padEnd(9))} ${bar2(weekdayPct, 100, 20)} ${chalk.dim(weekdayPct + "%")}`,
6380
- ` ${chalk.white("weekends".padEnd(9))} ${bar2(weekendPct, 100, 20)} ${chalk.dim(weekendPct + "%")}`,
6710
+ ` ${chalk2.white("weekdays".padEnd(9))} ${bar2(weekdayPct, 100, 20)} ${chalk2.dim(weekdayPct + "%")}`,
6711
+ ` ${chalk2.white("weekends".padEnd(9))} ${bar2(weekendPct, 100, 20)} ${chalk2.dim(weekendPct + "%")}`,
6381
6712
  ...weekendBench ? ["", ` ${weekendBench}`] : [],
6382
6713
  ""
6383
6714
  ];
6384
6715
  if (view.rhythm_comment) {
6385
6716
  for (const l of wrapWords(view.rhythm_comment, 47))
6386
- lines.push(chalk.italic.white(" " + l));
6717
+ lines.push(chalk2.italic.white(" " + l));
6387
6718
  } else {
6388
- lines.push(chalk.dim(" " + subhead));
6719
+ lines.push(chalk2.dim(" " + subhead));
6389
6720
  }
6390
6721
  return box(lines.join("\n"), { borderColor });
6391
6722
  }
@@ -6398,10 +6729,10 @@ function cardProjects(view) {
6398
6729
  if (!usageReady(view)) return "";
6399
6730
  const projects = view.top_projects.filter((p) => p.commits > 0 || p.sessions > 0).slice(0, 3);
6400
6731
  if (projects.length === 0) return "";
6401
- const lines = [chalk.dim("YOUR TOP PROJECTS"), ""];
6732
+ const lines = [chalk2.dim("YOUR TOP PROJECTS"), ""];
6402
6733
  const c = view.contributions;
6403
6734
  if (c && c.one_liner) {
6404
- for (const l of wrapWords(c.one_liner, 50)) lines.push(chalk.dim(l));
6735
+ for (const l of wrapWords(c.one_liner, 50)) lines.push(chalk2.dim(l));
6405
6736
  lines.push("");
6406
6737
  }
6407
6738
  projects.forEach((p, i) => {
@@ -6411,14 +6742,14 @@ function cardProjects(view) {
6411
6742
  lines.push(` ${bigNumber(`\u25B8 ${name}`)} ${TITLE_GRADIENT(count)}`);
6412
6743
  if (p.blurb)
6413
6744
  for (const l of wrapWords(p.blurb, 44))
6414
- lines.push(` ${chalk.white(l)}`);
6745
+ lines.push(` ${chalk2.white(l)}`);
6415
6746
  } else {
6416
6747
  const namePart = `\u25B8 ${name}`;
6417
6748
  const pad = " ".repeat(Math.max(2, 24 - namePart.length));
6418
- lines.push(` ${chalk.white(namePart)}${pad}${chalk.dim(count)}`);
6749
+ lines.push(` ${chalk2.white(namePart)}${pad}${chalk2.dim(count)}`);
6419
6750
  if (p.blurb)
6420
6751
  for (const l of wrapWords(p.blurb, 44))
6421
- lines.push(` ${chalk.dim(l)}`);
6752
+ lines.push(` ${chalk2.dim(l)}`);
6422
6753
  }
6423
6754
  lines.push("");
6424
6755
  });
@@ -6434,17 +6765,17 @@ function card7AINative(view) {
6434
6765
  );
6435
6766
  const streak = view.streak_days;
6436
6767
  const lines = [
6437
- chalk.dim("YOU'RE AI-NATIVE"),
6768
+ chalk2.dim("YOU'RE AI-NATIVE"),
6438
6769
  "",
6439
- ` commits since 2024: ${chalk.white(view.total_commits.toLocaleString())}`,
6440
- ` AI co-authored: ${chalk.white(view.ai_assisted_commits.toLocaleString())}`,
6770
+ ` commits since 2024: ${chalk2.white(view.total_commits.toLocaleString())}`,
6771
+ ` AI co-authored: ${chalk2.white(view.ai_assisted_commits.toLocaleString())}`,
6441
6772
  "",
6442
- ` ${bar2(pct, 100, 22)} ${chalk.white(pct + "%")}${bench ? ` ${bench}` : ""}`,
6773
+ ` ${bar2(pct, 100, 22)} ${chalk2.white(pct + "%")}${bench ? ` ${bench}` : ""}`,
6443
6774
  ...streak >= 2 ? [
6444
- ` ${chalk.white(`${streak}-day streak`)}${chalk.dim(", coding without missing a day.")}`
6775
+ ` ${chalk2.white(`${streak}-day streak`)}${chalk2.dim(", coding without missing a day.")}`
6445
6776
  ] : [],
6446
6777
  "",
6447
- chalk.dim(subhead)
6778
+ chalk2.dim(subhead)
6448
6779
  ].join("\n");
6449
6780
  return box(lines, { borderColor: "yellow" });
6450
6781
  }
@@ -6457,7 +6788,7 @@ function benchmarkInline(view, percentile) {
6457
6788
  const top = topPercentNumber(percentile);
6458
6789
  if (top === null) return "";
6459
6790
  const pct = `${top}%`;
6460
- return top < 10 ? chalk.dim("(top ") + TITLE_GRADIENT(pct) + chalk.dim(" of users)") : chalk.dim(`(top ${pct} of users)`);
6791
+ return top < 10 ? chalk2.dim("(top ") + TITLE_GRADIENT(pct) + chalk2.dim(" of users)") : chalk2.dim(`(top ${pct} of users)`);
6461
6792
  }
6462
6793
  function formatTokens(n) {
6463
6794
  if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
@@ -6477,24 +6808,24 @@ function cardTokens(view) {
6477
6808
  const max = Math.max(...t.by_tool.map((x) => x.tokens), 1);
6478
6809
  const toolRows = t.by_tool.length > 0 ? t.by_tool.slice(0, 3).map((x) => {
6479
6810
  const padTool = x.tool.padEnd(13, " ");
6480
- return ` ${chalk.white(padTool)} ${bar2(x.tokens, max, 16)} ${chalk.dim(formatTokens(x.tokens))}`;
6481
- }).join("\n") : chalk.dim(" (no tool breakdown available)");
6482
- const deltaLine = t.multiplier_vs_prior !== null && t.multiplier_vs_prior >= 1.05 ? chalk.hex("#ff8a00")(
6811
+ return ` ${chalk2.white(padTool)} ${bar2(x.tokens, max, 16)} ${chalk2.dim(formatTokens(x.tokens))}`;
6812
+ }).join("\n") : chalk2.dim(" (no tool breakdown available)");
6813
+ const deltaLine = t.multiplier_vs_prior !== null && t.multiplier_vs_prior >= 1.05 ? chalk2.hex("#ff8a00")(
6483
6814
  ` \u2191 ${t.multiplier_vs_prior.toFixed(1)}\xD7 vs last period`
6484
- ) : t.multiplier_vs_prior !== null && t.multiplier_vs_prior < 0.95 ? chalk.dim(
6815
+ ) : t.multiplier_vs_prior !== null && t.multiplier_vs_prior < 0.95 ? chalk2.dim(
6485
6816
  ` \u2193 ${(1 / t.multiplier_vs_prior).toFixed(1)}\xD7 less than last period`
6486
- ) : t.total_prior_30d > 0 ? chalk.dim(` \u2248 same as last period`) : chalk.dim(` rolling 30-day window`);
6817
+ ) : t.total_prior_30d > 0 ? chalk2.dim(` \u2248 same as last period`) : chalk2.dim(` rolling 30-day window`);
6487
6818
  const cost = t.estimated_retail_cost_usd;
6488
- const costLine = cost > 0 ? ` ${chalk.dim("would have cost")} ${TITLE_GRADIENT(`~$${cost.toLocaleString()}`)} ${chalk.dim("at retail API rates")}` : "";
6489
- const cursorNote = t.cursor_estimated ? t.cursor_token_share >= 0.9 ? chalk.dim(
6819
+ const costLine = cost > 0 ? ` ${chalk2.dim("would have cost")} ${TITLE_GRADIENT(`~$${cost.toLocaleString()}`)} ${chalk2.dim("at retail API rates")}` : "";
6820
+ const cursorNote = t.cursor_estimated ? t.cursor_token_share >= 0.9 ? chalk2.dim(
6490
6821
  " \u2248 estimated from time spent in Cursor (no local token data)"
6491
- ) : chalk.dim(
6822
+ ) : chalk2.dim(
6492
6823
  " includes Cursor (tokens + cost estimated from active time)"
6493
6824
  ) : "";
6494
- 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)") : "";
6495
- const generatedLine = t.work_tokens > 0 ? ` ${chalk.white(formatTokens(t.work_tokens))} ${chalk.dim("were actually generated fresh")}` : "";
6825
+ 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)") : "";
6826
+ const generatedLine = t.work_tokens > 0 ? ` ${chalk2.white(formatTokens(t.work_tokens))} ${chalk2.dim("were actually generated fresh")}` : "";
6496
6827
  const lines = [
6497
- chalk.dim("YOU BURNED THIS MANY TOKENS"),
6828
+ chalk2.dim("YOU BURNED THIS MANY TOKENS"),
6498
6829
  "",
6499
6830
  ` ${totalLine}${tokensBench ? ` ${tokensBench}` : ""}`,
6500
6831
  "",
@@ -6516,23 +6847,23 @@ function cardToolRelationship(view) {
6516
6847
  if (r.kind === "insufficient") {
6517
6848
  return "";
6518
6849
  }
6519
- const streakLine = view.streak_days >= 2 ? ` ${chalk.white(`${view.streak_days}-day streak, coding without missing a day.`)}` : null;
6850
+ const streakLine = view.streak_days >= 2 ? ` ${chalk2.white(`${view.streak_days}-day streak, coding without missing a day.`)}` : null;
6520
6851
  if (r.kind === "switch") {
6521
6852
  const rows = r.timeline.slice(-6).map((t) => {
6522
6853
  const monthShort2 = t.month.slice(2).replace("-", "/");
6523
6854
  const filled = Math.round(t.share * 10);
6524
- const bar3 = chalk.hex("#ff8a00")("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(10 - filled));
6525
- const label = t.dominant === r.from_tool ? chalk.dim(t.dominant) : chalk.white(t.dominant);
6526
- return ` ${chalk.dim(monthShort2)} ${bar3} ${label}`;
6855
+ const bar3 = chalk2.hex("#ff8a00")("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(10 - filled));
6856
+ const label = t.dominant === r.from_tool ? chalk2.dim(t.dominant) : chalk2.white(t.dominant);
6857
+ return ` ${chalk2.dim(monthShort2)} ${bar3} ${label}`;
6527
6858
  });
6528
6859
  return box(
6529
6860
  [
6530
- chalk.dim("YOU SWITCHED TOOLS"),
6861
+ chalk2.dim("YOU SWITCHED TOOLS"),
6531
6862
  "",
6532
6863
  ...rows,
6533
6864
  "",
6534
- chalk.dim(` joined the "${r.from_tool} \u2192 ${r.to_tool}" club`),
6535
- chalk.dim(` in ${r.switch_month}.`),
6865
+ chalk2.dim(` joined the "${r.from_tool} \u2192 ${r.to_tool}" club`),
6866
+ chalk2.dim(` in ${r.switch_month}.`),
6536
6867
  ...streakLine ? ["", streakLine] : []
6537
6868
  ].join("\n"),
6538
6869
  { borderColor: "cyan" }
@@ -6541,14 +6872,14 @@ function cardToolRelationship(view) {
6541
6872
  if (r.kind === "loyalist") {
6542
6873
  return box(
6543
6874
  [
6544
- chalk.dim("YOU'RE A LOYALIST"),
6875
+ chalk2.dim("YOU'RE A LOYALIST"),
6545
6876
  "",
6546
6877
  ` ${bigNumber(r.tool.toUpperCase())}`,
6547
6878
  "",
6548
- chalk.dim(
6879
+ chalk2.dim(
6549
6880
  ` ${r.sessions_count} sessions across ${r.months_count} month${r.months_count === 1 ? "" : "s"}.`
6550
6881
  ),
6551
- chalk.dim(" no second-guessing. you knew what you wanted."),
6882
+ chalk2.dim(" no second-guessing. you knew what you wanted."),
6552
6883
  ...streakLine ? ["", streakLine] : []
6553
6884
  ].join("\n"),
6554
6885
  { borderColor: "cyan" }
@@ -6557,16 +6888,16 @@ function cardToolRelationship(view) {
6557
6888
  if (r.kind === "polyglot") {
6558
6889
  const rows = r.tools.slice(0, 4).map((t) => {
6559
6890
  const filled = Math.round(t.share * 22);
6560
- const bar3 = chalk.hex("#ff8a00")("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(22 - filled));
6561
- return ` ${chalk.white(t.tool.padEnd(12, " "))} ${bar3} ${chalk.dim(Math.round(t.share * 100) + "%")}`;
6891
+ const bar3 = chalk2.hex("#ff8a00")("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(22 - filled));
6892
+ return ` ${chalk2.white(t.tool.padEnd(12, " "))} ${bar3} ${chalk2.dim(Math.round(t.share * 100) + "%")}`;
6562
6893
  });
6563
6894
  return box(
6564
6895
  [
6565
- chalk.dim("YOU'RE A POLYGLOT"),
6896
+ chalk2.dim("YOU'RE A POLYGLOT"),
6566
6897
  "",
6567
6898
  ...rows,
6568
6899
  "",
6569
- chalk.dim(" you don't pick favorites. respect."),
6900
+ chalk2.dim(" you don't pick favorites. respect."),
6570
6901
  ...streakLine ? ["", streakLine] : []
6571
6902
  ].join("\n"),
6572
6903
  { borderColor: "cyan" }
@@ -6628,17 +6959,17 @@ function cardModelsStack(view) {
6628
6959
  const column = (mm) => {
6629
6960
  if (!mm) return [];
6630
6961
  const c = modelProviderColor(mm.prov);
6631
- const hex = (s) => chalk.hex(c)(s);
6962
+ const hex = (s) => chalk2.hex(c)(s);
6632
6963
  const cap = " " + hex("\u256D" + "\u2500".repeat(8) + "\u256E") + " ";
6633
6964
  const base = " " + hex("\u2570" + "\u2500".repeat(8) + "\u256F") + " ";
6634
6965
  const side = " " + hex("\u2502") + " ".repeat(8) + hex("\u2502") + " ";
6635
- const num3 = " " + hex("\u2502") + chalk.bold.white(center("#" + mm.rank, 8)) + hex("\u2502") + " ";
6966
+ const num3 = " " + hex("\u2502") + chalk2.bold.white(center("#" + mm.rank, 8)) + hex("\u2502") + " ";
6636
6967
  const h = heights[mm.rank];
6637
6968
  const mid = Math.floor((h - 1) / 2);
6638
6969
  const body = [];
6639
6970
  for (let i = 0; i < h; i++) body.push(i === mid ? num3 : side);
6640
- const label = mm.rank === 1 ? bigNumber(center(mm.name)) : chalk.white(center(mm.name));
6641
- return [label, chalk.dim(center(mm.pct + "%")), cap, ...body, base];
6971
+ const label = mm.rank === 1 ? bigNumber(center(mm.name)) : chalk2.white(center(mm.name));
6972
+ return [label, chalk2.dim(center(mm.pct + "%")), cap, ...body, base];
6642
6973
  };
6643
6974
  const cols = [column(second), column(first), column(third)];
6644
6975
  const maxH = Math.max(...cols.map((col) => col.length));
@@ -6646,20 +6977,20 @@ function cardModelsStack(view) {
6646
6977
  (col) => Array(maxH - col.length).fill(blankCol).concat(col)
6647
6978
  );
6648
6979
  const header = ranked.length === 1 ? "YOUR MODEL & STACK" : "YOUR MODELS & STACK";
6649
- const lines = [chalk.dim(header), ""];
6980
+ const lines = [chalk2.dim(header), ""];
6650
6981
  lines.push(
6651
- " " + [blankCol, chalk.hex("#ffd700")(center("\u265B")), blankCol].join(" ")
6982
+ " " + [blankCol, chalk2.hex("#ffd700")(center("\u265B")), blankCol].join(" ")
6652
6983
  );
6653
6984
  for (let r = 0; r < maxH; r++)
6654
6985
  lines.push(" " + [padded[0][r], padded[1][r], padded[2][r]].join(" "));
6655
6986
  lines.push("");
6656
6987
  lines.push(
6657
- ` top pick: ${chalk.white(first.name)} ${chalk.dim(`\u2014 ${first.pct}% of sessions`)}`
6988
+ ` top pick: ${chalk2.white(first.name)} ${chalk2.dim(`\u2014 ${first.pct}% of sessions`)}`
6658
6989
  );
6659
6990
  if (view.stack_comment) {
6660
6991
  lines.push("");
6661
6992
  for (const l of wrapWords(view.stack_comment, 46))
6662
- lines.push(chalk.italic.white(" " + l));
6993
+ lines.push(chalk2.italic.white(" " + l));
6663
6994
  }
6664
6995
  return box(lines.join("\n"), { borderColor: "magenta" });
6665
6996
  }
@@ -6670,25 +7001,25 @@ function cardCodeFootprint(view) {
6670
7001
  const netLabel = `${f.net >= 0 ? "+" : ""}${compactNum(f.net)}`;
6671
7002
  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.";
6672
7003
  const lines = [
6673
- chalk.dim("YOUR CODE FOOTPRINT"),
7004
+ chalk2.dim("YOUR CODE FOOTPRINT"),
6674
7005
  "",
6675
7006
  ` ${bigNumber(f.label.toUpperCase())}`,
6676
7007
  "",
6677
- ` ${chalk.green("+ " + compactNum(f.lines_added).padEnd(7))} ${coloredBar(f.lines_added, max, 22, "#22c55e")} ${chalk.dim("added")}`,
6678
- ` ${chalk.red("- " + compactNum(f.lines_deleted).padEnd(7))} ${coloredBar(f.lines_deleted, max, 22, "#ef4444")} ${chalk.dim("deleted")}`,
7008
+ ` ${chalk2.green("+ " + compactNum(f.lines_added).padEnd(7))} ${coloredBar(f.lines_added, max, 22, "#22c55e")} ${chalk2.dim("added")}`,
7009
+ ` ${chalk2.red("- " + compactNum(f.lines_deleted).padEnd(7))} ${coloredBar(f.lines_deleted, max, 22, "#ef4444")} ${chalk2.dim("deleted")}`,
6679
7010
  "",
6680
- ` ${chalk.white("net " + netLabel)} ${chalk.dim("lines since 2024")}`
7011
+ ` ${chalk2.white("net " + netLabel)} ${chalk2.dim("lines since 2024")}`
6681
7012
  ];
6682
7013
  if (f.median_commit_size > 0)
6683
7014
  lines.push(
6684
- ` ${chalk.dim(`typical commit ~${compactNum(f.median_commit_size)} lines \xB7 biggest ${compactNum(f.biggest_commit_size)}`)}`
7015
+ ` ${chalk2.dim(`typical commit ~${compactNum(f.median_commit_size)} lines \xB7 biggest ${compactNum(f.biggest_commit_size)}`)}`
6685
7016
  );
6686
7017
  lines.push("");
6687
7018
  if (f.line) {
6688
7019
  for (const l of wrapWords(f.line, 47))
6689
- lines.push(chalk.italic.white(" " + l));
7020
+ lines.push(chalk2.italic.white(" " + l));
6690
7021
  } else {
6691
- lines.push(chalk.dim(" " + subhead));
7022
+ lines.push(chalk2.dim(" " + subhead));
6692
7023
  }
6693
7024
  return box(lines.join("\n"), { borderColor: "green" });
6694
7025
  }
@@ -6699,20 +7030,20 @@ function cardConcurrency(view) {
6699
7030
  const longest = days >= 1.5 ? `${days.toFixed(0)} days` : `${Math.round(c.longest_session_hours)}h`;
6700
7031
  const bench = benchmarkInline(view, view.rank_summary.avg_agents_percentile);
6701
7032
  const lines = [
6702
- chalk.dim("HOW MANY AGENTS YOU JUGGLE"),
7033
+ chalk2.dim("HOW MANY AGENTS YOU JUGGLE"),
6703
7034
  "",
6704
- ` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk.white("agents open at once")}${bench ? ` ${bench}` : ""}`,
7035
+ ` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk2.white("agents open at once")}${bench ? ` ${bench}` : ""}`,
6705
7036
  "",
6706
- ` ${chalk.white("peak".padEnd(14))} ${chalk.dim(`${c.open_tab_peak} at once`)}`,
6707
- ` ${chalk.white("longest tab".padEnd(14))} ${chalk.dim(`${longest} open`)}`,
6708
- ` ${chalk.white("2+ active".padEnd(14))} ${chalk.dim(`${Math.round(c.active_juggle_pct * 100)}% of your coding time`)}`,
7037
+ ` ${chalk2.white("peak".padEnd(14))} ${chalk2.dim(`${c.open_tab_peak} at once`)}`,
7038
+ ` ${chalk2.white("longest tab".padEnd(14))} ${chalk2.dim(`${longest} open`)}`,
7039
+ ` ${chalk2.white("2+ active".padEnd(14))} ${chalk2.dim(`${Math.round(c.active_juggle_pct * 100)}% of your coding time`)}`,
6709
7040
  ""
6710
7041
  ];
6711
7042
  if (c.line) {
6712
7043
  for (const l of wrapWords(c.line, 47))
6713
- lines.push(chalk.italic.white(" " + l));
7044
+ lines.push(chalk2.italic.white(" " + l));
6714
7045
  } else {
6715
- lines.push(chalk.dim(" a one-agent setup could never."));
7046
+ lines.push(chalk2.dim(" a one-agent setup could never."));
6716
7047
  }
6717
7048
  return box(lines.join("\n"), { borderColor: "blue" });
6718
7049
  }
@@ -6729,22 +7060,22 @@ function cardTalk(view) {
6729
7060
  const c = view.conversation;
6730
7061
  const v = view.collaboration;
6731
7062
  if ((!c || c.total_prompts < 10) && !v) return "";
6732
- const lines = [chalk.dim("HOW YOU TALK"), ""];
7063
+ const lines = [chalk2.dim("HOW YOU TALK"), ""];
6733
7064
  if (c?.line) {
6734
7065
  for (const l of wrapWords(c.line, 47))
6735
- lines.push(chalk.italic.white(" " + l));
7066
+ lines.push(chalk2.italic.white(" " + l));
6736
7067
  lines.push("");
6737
7068
  }
6738
7069
  if (v) {
6739
7070
  lines.push(` ${bigNumber(v.style_label)}`);
6740
7071
  if (v.summary)
6741
7072
  for (const l of wrapWords(v.summary, 47))
6742
- lines.push(chalk.white(" " + l));
7073
+ lines.push(chalk2.white(" " + l));
6743
7074
  const sigs = [
6744
7075
  ["reads", v.signals.fact_checking],
6745
7076
  ["drives", v.signals.autonomy]
6746
7077
  ].filter(([, t]) => t).map(
6747
- ([label, t]) => ` ${chalk.dim(label.padEnd(8))} ${chalk.white(briefSignal(t))}`
7078
+ ([label, t]) => ` ${chalk2.dim(label.padEnd(8))} ${chalk2.white(briefSignal(t))}`
6748
7079
  );
6749
7080
  if (sigs.length > 0) {
6750
7081
  lines.push("");
@@ -6754,11 +7085,11 @@ function cardTalk(view) {
6754
7085
  if (c) {
6755
7086
  lines.push("");
6756
7087
  lines.push(
6757
- ` ${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")}`
7088
+ ` ${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")}`
6758
7089
  );
6759
7090
  if (c.themes.length > 0)
6760
7091
  lines.push(
6761
- ` ${chalk.dim("recurring: " + c.themes.slice(0, 4).join(", "))}`
7092
+ ` ${chalk2.dim("recurring: " + c.themes.slice(0, 4).join(", "))}`
6762
7093
  );
6763
7094
  }
6764
7095
  return box(lines.join("\n"), { borderColor: "magenta" });
@@ -6770,13 +7101,13 @@ function cardProficiency(view) {
6770
7101
  if (!p) {
6771
7102
  return box(
6772
7103
  [
6773
- chalk.dim(" you are\u2026"),
7104
+ chalk2.dim(" you are\u2026"),
6774
7105
  "",
6775
- chalk.hex("#ff8a00")(mascot),
7106
+ chalk2.hex("#ff8a00")(mascot),
6776
7107
  "",
6777
7108
  ` ${archetype}`,
6778
7109
  "",
6779
- ` ${chalk.italic.white(`"${view.zinger}"`)}`
7110
+ ` ${chalk2.italic.white(`"${view.zinger}"`)}`
6780
7111
  ].join("\n"),
6781
7112
  { borderColor: "yellow" }
6782
7113
  );
@@ -6784,27 +7115,27 @@ function cardProficiency(view) {
6784
7115
  const scoreTop = topPercentNumber(p.score_percentile ?? null);
6785
7116
  const sampleSize = view.rank_summary.sample_size;
6786
7117
  const cohort = scoreTop != null && sampleSize > 0 ? `top ${scoreTop}% of ${sampleSize.toLocaleString()} users` : view.cohort_size > 0 ? `among ${view.cohort_size.toLocaleString()} users so far` : "";
6787
- const metricRow = (label, value) => value === null ? null : ` ${chalk.white(label.padEnd(12))} ${bar2(value, 100, 10)} ${chalk.dim(String(value))}`;
7118
+ const metricRow = (label, value) => value === null ? null : ` ${chalk2.white(label.padEnd(12))} ${bar2(value, 100, 10)} ${chalk2.dim(String(value))}`;
6788
7119
  const bars = [
6789
7120
  metricRow("intensity", p.intensity),
6790
7121
  metricRow("consistency", p.consistency),
6791
- p.craft_bonus > 0 ? ` ${chalk.white("craft".padEnd(12))} ${chalk.hex("#ff8a00")(`+${p.craft_bonus}`)} ${chalk.dim("clean prompting")}` : null
7122
+ p.craft_bonus > 0 ? ` ${chalk2.white("craft".padEnd(12))} ${chalk2.hex("#ff8a00")(`+${p.craft_bonus}`)} ${chalk2.dim("clean prompting")}` : null
6792
7123
  ].filter((r) => r !== null);
6793
7124
  const zingerLines = wrapWords(`"${view.zinger}"`, INNER_WIDTH).map(
6794
- (l) => centerText(l, chalk.italic.white)
7125
+ (l) => centerText(l, chalk2.italic.white)
6795
7126
  );
6796
7127
  const commentLines = p.comment ? wrapWords(p.comment, INNER_WIDTH - 4).map(
6797
- (l, i) => i === 0 ? ` ${chalk.hex("#ff8a00")(">")} ${chalk.white(l)}` : ` ${chalk.white(l)}`
7128
+ (l, i) => i === 0 ? ` ${chalk2.hex("#ff8a00")(">")} ${chalk2.white(l)}` : ` ${chalk2.white(l)}`
6798
7129
  ) : [];
6799
7130
  const tier = tierForScore2(p.score);
6800
7131
  const lines = [
6801
- centerBlock(tier.art, chalk.hex("#ff8a00")),
7132
+ centerBlock(tier.art, chalk2.hex("#ff8a00")),
6802
7133
  "",
6803
7134
  centerText(`\u2500\u2500 ${tier.label} \u2500\u2500`, bigNumber),
6804
7135
  centerText(`${p.score} / 100`, bigNumber),
6805
- cohort ? centerText(cohort, chalk.dim) : null,
7136
+ cohort ? centerText(cohort, chalk2.dim) : null,
6806
7137
  "",
6807
- centerText(view.archetype_label, chalk.bold.hex("#ad5cff")),
7138
+ centerText(view.archetype_label, chalk2.bold.hex("#ad5cff")),
6808
7139
  ...zingerLines,
6809
7140
  "",
6810
7141
  ...bars,
@@ -6842,7 +7173,7 @@ async function renderWrappedAll(view) {
6842
7173
  " " + TITLE_GRADIENT.multiline("\u2591\u2592\u2593 YOUR AI WRAPPED \u2593\u2592\u2591") + "\n"
6843
7174
  );
6844
7175
  process.stdout.write(
6845
- chalk.dim(` ${view.name.toLowerCase()} \xB7 last 30 days
7176
+ chalk2.dim(` ${view.name.toLowerCase()} \xB7 last 30 days
6846
7177
 
6847
7178
  `)
6848
7179
  );
@@ -7065,18 +7396,18 @@ var MODE_OPTIONS = [
7065
7396
  function renderModeMenu(selected) {
7066
7397
  const out = [
7067
7398
  "",
7068
- chalk2.white(" How do you want to run Standout?"),
7399
+ chalk3.white(" How do you want to run Standout?"),
7069
7400
  ""
7070
7401
  ];
7071
7402
  MODE_OPTIONS.forEach((opt, i) => {
7072
7403
  const active = i === selected;
7073
- const pointer = active ? chalk2.hex("#ad5cff")("\u276F ") : " ";
7074
- const title = active ? chalk2.bold.white(opt.title) + chalk2.dim(opt.tag) : chalk2.dim(opt.title + opt.tag);
7404
+ const pointer = active ? chalk3.hex("#ad5cff")("\u276F ") : " ";
7405
+ const title = active ? chalk3.bold.white(opt.title) + chalk3.dim(opt.tag) : chalk3.dim(opt.title + opt.tag);
7075
7406
  out.push(`${pointer}${title}`);
7076
- for (const line of opt.lines) out.push(chalk2.dim(` ${line}`));
7407
+ for (const line of opt.lines) out.push(chalk3.dim(` ${line}`));
7077
7408
  out.push("");
7078
7409
  });
7079
- out.push(chalk2.dim(" \u2191/\u2193 to move \xB7 enter to select"));
7410
+ out.push(chalk3.dim(" \u2191/\u2193 to move \xB7 enter to select"));
7080
7411
  return out.join("\n");
7081
7412
  }
7082
7413
  async function chooseMode() {
@@ -7179,8 +7510,8 @@ function startLoadingLine(label) {
7179
7510
  let frame = 0;
7180
7511
  const render = () => {
7181
7512
  const dots = frames[frame % frames.length];
7182
- const accent = chalk2.hex("#ad5cff")(dots);
7183
- const text = chalk2.white(label);
7513
+ const accent = chalk3.hex("#ad5cff")(dots);
7514
+ const text = chalk3.white(label);
7184
7515
  process.stderr.write(`\r\x1B[2K ${accent} ${text}`);
7185
7516
  frame += 1;
7186
7517
  };
@@ -7212,7 +7543,7 @@ async function maybeShareWrapped(wrapped) {
7212
7543
  return;
7213
7544
  }
7214
7545
  const ans = await askLine(
7215
- chalk2.white(" press enter to see your ai wrapped ")
7546
+ chalk3.white(" press enter to see your ai wrapped ")
7216
7547
  );
7217
7548
  process.stderr.write("\n");
7218
7549
  const shared = !ans || ans.toLowerCase() !== "n";
@@ -7308,8 +7639,8 @@ async function runLocal() {
7308
7639
  );
7309
7640
  return;
7310
7641
  }
7311
- const file = join6(tmpdir2(), `standout-wrapped-${Date.now()}.png`);
7312
- writeFileSync(file, png);
7642
+ const file = join7(tmpdir2(), `standout-wrapped-${Date.now()}.png`);
7643
+ writeFileSync2(file, png);
7313
7644
  process.stderr.write("\n");
7314
7645
  const shown = showImageInline(png);
7315
7646
  if (!shown) {
@@ -7400,6 +7731,7 @@ async function runAgent(jobId) {
7400
7731
  `
7401
7732
  );
7402
7733
  }
7734
+ await maybeAutoInstallSchedule(jobId);
7403
7735
  await maybeShareWrapped(wrapped);
7404
7736
  } else {
7405
7737
  process.stderr.write(
@@ -7533,6 +7865,17 @@ async function runAgent(jobId) {
7533
7865
  async function main() {
7534
7866
  ensureHeapHeadroom();
7535
7867
  const args = process.argv.slice(2);
7868
+ if (args[0] === "schedule" || args[0] === "unschedule") {
7869
+ const action = parseScheduleArgs(args);
7870
+ if (action.kind === "install") {
7871
+ await installSchedule(action.slug, action.flags);
7872
+ } else if (action.kind === "remove") {
7873
+ await removeSchedule();
7874
+ } else {
7875
+ await scheduleStatus();
7876
+ }
7877
+ return;
7878
+ }
7536
7879
  const proxy = configureProxyFromEnv();
7537
7880
  if (proxy) process.stderr.write(` (using proxy ${proxy})
7538
7881
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "standout",
3
- "version": "0.5.38",
3
+ "version": "0.6.1",
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
  }