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