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.
- package/README.md +19 -0
- package/dist/cli.js +457 -114
- 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
|
|
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
|
|
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(
|
|
518
|
-
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
5202
|
+
}
|
|
5203
|
+
function unescapeXml(text) {
|
|
5204
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/&/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
|
|
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
|
|
6195
|
-
${label.padEnd(48, " ")} ${
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
6277
|
-
|
|
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
|
-
|
|
6650
|
+
chalk2.dim("YOU MANAGED"),
|
|
6320
6651
|
"",
|
|
6321
6652
|
bigNumber(` ${hours} session-hours`) + (bench ? ` ${bench}` : ""),
|
|
6322
6653
|
"",
|
|
6323
|
-
|
|
6324
|
-
|
|
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 ` ${
|
|
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
|
-
|
|
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
|
-
` ${
|
|
6348
|
-
...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 ? ` ${
|
|
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
|
-
|
|
6701
|
+
chalk2.dim("WHEN YOU CODE"),
|
|
6371
6702
|
"",
|
|
6372
6703
|
...hourlyChart(
|
|
6373
6704
|
view.hour_histogram,
|
|
6374
|
-
critter ? { art: critter.art, color:
|
|
6705
|
+
critter ? { art: critter.art, color: chalk2.magenta } : null
|
|
6375
6706
|
),
|
|
6376
6707
|
"",
|
|
6377
|
-
` ${
|
|
6708
|
+
` ${chalk2.white(caption)}`,
|
|
6378
6709
|
"",
|
|
6379
|
-
` ${
|
|
6380
|
-
` ${
|
|
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(
|
|
6717
|
+
lines.push(chalk2.italic.white(" " + l));
|
|
6387
6718
|
} else {
|
|
6388
|
-
lines.push(
|
|
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 = [
|
|
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(
|
|
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(` ${
|
|
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(` ${
|
|
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(` ${
|
|
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
|
-
|
|
6768
|
+
chalk2.dim("YOU'RE AI-NATIVE"),
|
|
6438
6769
|
"",
|
|
6439
|
-
` commits since 2024: ${
|
|
6440
|
-
` AI co-authored: ${
|
|
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)} ${
|
|
6773
|
+
` ${bar2(pct, 100, 22)} ${chalk2.white(pct + "%")}${bench ? ` ${bench}` : ""}`,
|
|
6443
6774
|
...streak >= 2 ? [
|
|
6444
|
-
` ${
|
|
6775
|
+
` ${chalk2.white(`${streak}-day streak`)}${chalk2.dim(", coding without missing a day.")}`
|
|
6445
6776
|
] : [],
|
|
6446
6777
|
"",
|
|
6447
|
-
|
|
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 ?
|
|
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 ` ${
|
|
6481
|
-
}).join("\n") :
|
|
6482
|
-
const deltaLine = t.multiplier_vs_prior !== null && t.multiplier_vs_prior >= 1.05 ?
|
|
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 ?
|
|
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 ?
|
|
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 ? ` ${
|
|
6489
|
-
const cursorNote = t.cursor_estimated ? t.cursor_token_share >= 0.9 ?
|
|
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
|
-
) :
|
|
6822
|
+
) : chalk2.dim(
|
|
6492
6823
|
" includes Cursor (tokens + cost estimated from active time)"
|
|
6493
6824
|
) : "";
|
|
6494
|
-
const cachedNote = t.cache_tokens > t.work_tokens ?
|
|
6495
|
-
const generatedLine = t.work_tokens > 0 ? ` ${
|
|
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
|
-
|
|
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 ? ` ${
|
|
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 =
|
|
6525
|
-
const label = t.dominant === r.from_tool ?
|
|
6526
|
-
return ` ${
|
|
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
|
-
|
|
6861
|
+
chalk2.dim("YOU SWITCHED TOOLS"),
|
|
6531
6862
|
"",
|
|
6532
6863
|
...rows,
|
|
6533
6864
|
"",
|
|
6534
|
-
|
|
6535
|
-
|
|
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
|
-
|
|
6875
|
+
chalk2.dim("YOU'RE A LOYALIST"),
|
|
6545
6876
|
"",
|
|
6546
6877
|
` ${bigNumber(r.tool.toUpperCase())}`,
|
|
6547
6878
|
"",
|
|
6548
|
-
|
|
6879
|
+
chalk2.dim(
|
|
6549
6880
|
` ${r.sessions_count} sessions across ${r.months_count} month${r.months_count === 1 ? "" : "s"}.`
|
|
6550
6881
|
),
|
|
6551
|
-
|
|
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 =
|
|
6561
|
-
return ` ${
|
|
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
|
-
|
|
6896
|
+
chalk2.dim("YOU'RE A POLYGLOT"),
|
|
6566
6897
|
"",
|
|
6567
6898
|
...rows,
|
|
6568
6899
|
"",
|
|
6569
|
-
|
|
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) =>
|
|
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") +
|
|
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)) :
|
|
6641
|
-
return [label,
|
|
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 = [
|
|
6980
|
+
const lines = [chalk2.dim(header), ""];
|
|
6650
6981
|
lines.push(
|
|
6651
|
-
" " + [blankCol,
|
|
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: ${
|
|
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(
|
|
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
|
-
|
|
7004
|
+
chalk2.dim("YOUR CODE FOOTPRINT"),
|
|
6674
7005
|
"",
|
|
6675
7006
|
` ${bigNumber(f.label.toUpperCase())}`,
|
|
6676
7007
|
"",
|
|
6677
|
-
` ${
|
|
6678
|
-
` ${
|
|
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
|
-
` ${
|
|
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
|
-
` ${
|
|
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(
|
|
7020
|
+
lines.push(chalk2.italic.white(" " + l));
|
|
6690
7021
|
} else {
|
|
6691
|
-
lines.push(
|
|
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
|
-
|
|
7033
|
+
chalk2.dim("HOW MANY AGENTS YOU JUGGLE"),
|
|
6703
7034
|
"",
|
|
6704
|
-
` ${bigNumber(`~${c.open_tab_avg}`)} ${
|
|
7035
|
+
` ${bigNumber(`~${c.open_tab_avg}`)} ${chalk2.white("agents open at once")}${bench ? ` ${bench}` : ""}`,
|
|
6705
7036
|
"",
|
|
6706
|
-
` ${
|
|
6707
|
-
` ${
|
|
6708
|
-
` ${
|
|
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(
|
|
7044
|
+
lines.push(chalk2.italic.white(" " + l));
|
|
6714
7045
|
} else {
|
|
6715
|
-
lines.push(
|
|
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 = [
|
|
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(
|
|
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(
|
|
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]) => ` ${
|
|
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
|
-
` ${
|
|
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
|
-
` ${
|
|
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
|
-
|
|
7104
|
+
chalk2.dim(" you are\u2026"),
|
|
6774
7105
|
"",
|
|
6775
|
-
|
|
7106
|
+
chalk2.hex("#ff8a00")(mascot),
|
|
6776
7107
|
"",
|
|
6777
7108
|
` ${archetype}`,
|
|
6778
7109
|
"",
|
|
6779
|
-
` ${
|
|
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 : ` ${
|
|
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 ? ` ${
|
|
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,
|
|
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 ? ` ${
|
|
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,
|
|
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,
|
|
7136
|
+
cohort ? centerText(cohort, chalk2.dim) : null,
|
|
6806
7137
|
"",
|
|
6807
|
-
centerText(view.archetype_label,
|
|
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
|
-
|
|
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
|
-
|
|
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 ?
|
|
7074
|
-
const title = active ?
|
|
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(
|
|
7407
|
+
for (const line of opt.lines) out.push(chalk3.dim(` ${line}`));
|
|
7077
7408
|
out.push("");
|
|
7078
7409
|
});
|
|
7079
|
-
out.push(
|
|
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 =
|
|
7183
|
-
const text =
|
|
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
|
-
|
|
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 =
|
|
7312
|
-
|
|
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.
|
|
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
|
}
|