polymath-society 0.2.28 → 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -55,13 +55,10 @@ function electronAppData() {
55
55
  function resolveDataRoot(env = process.env, cwd = process.cwd(), appData = electronAppData, home = os.homedir()) {
56
56
  const fromEnv = env.POLYMATH_DATA_DIR;
57
57
  if (fromEnv && fromEnv.trim()) return path.resolve(fromEnv);
58
- const local = path.join(cwd, ".data");
59
58
  const dflt = path.join(home, ".polymath-society", "data");
60
- if (existsSync(local) && hasCompletedAnalysis(local)) return local;
61
59
  const fromApp = appData();
62
60
  if (fromApp && hasCompletedAnalysis(fromApp)) return fromApp;
63
61
  if (hasCompletedAnalysis(dflt)) return dflt;
64
- if (existsSync(local)) return local;
65
62
  if (fromApp) return fromApp;
66
63
  return dflt;
67
64
  }
@@ -484,7 +481,8 @@ var init_cursor = __esm({
484
481
  });
485
482
 
486
483
  // ../coding-core/dist/raw.js
487
- import { promises as fs2 } from "fs";
484
+ import { promises as fs2, createReadStream } from "fs";
485
+ import readline from "readline";
488
486
  import path2 from "path";
489
487
  import os2 from "os";
490
488
  function resolveSourceRoot(envVar, ...defaultSegments) {
@@ -600,10 +598,18 @@ function blank(sessionId, source, file2) {
600
598
  klassReason: ""
601
599
  };
602
600
  }
603
- function parseClaudeCode(raw, sessionId, file2) {
601
+ function jsonlLines(file2) {
602
+ return readline.createInterface({ input: createReadStream(file2, { encoding: "utf-8" }), crlfDelay: Infinity });
603
+ }
604
+ function shedAgentBulk(s) {
605
+ if (s.klass === "agent" && s.events.length > 2)
606
+ s.events = [s.events[0], s.events[s.events.length - 1]];
607
+ return s;
608
+ }
609
+ async function parseClaudeCode(input, sessionId, file2) {
604
610
  const s = blank(sessionId, "claude-code", file2);
605
611
  const seen = /* @__PURE__ */ new Set();
606
- for (const lineRaw of raw.split("\n")) {
612
+ for await (const lineRaw of typeof input === "string" ? input.split("\n") : input) {
607
613
  const line = lineRaw.trim();
608
614
  if (!line)
609
615
  continue;
@@ -705,7 +711,7 @@ function parseClaudeCode(raw, sessionId, file2) {
705
711
  const c = classify(s);
706
712
  s.klass = c.klass;
707
713
  s.klassReason = c.reason;
708
- return s;
714
+ return shedAgentBulk(s);
709
715
  }
710
716
  function codexText(content) {
711
717
  if (typeof content === "string")
@@ -727,10 +733,10 @@ function codexText(content) {
727
733
  }
728
734
  return "";
729
735
  }
730
- function parseCodex(raw, file2) {
736
+ async function parseCodex(input, file2) {
731
737
  let sessionId = path2.basename(file2, ".jsonl");
732
738
  const s = blank(sessionId, "codex", file2);
733
- for (const lineRaw of raw.split("\n")) {
739
+ for await (const lineRaw of typeof input === "string" ? input.split("\n") : input) {
734
740
  const line = lineRaw.trim();
735
741
  if (!line)
736
742
  continue;
@@ -796,7 +802,7 @@ function parseCodex(raw, file2) {
796
802
  const c = classify(s);
797
803
  s.klass = c.klass;
798
804
  s.klassReason = c.reason;
799
- return s;
805
+ return shedAgentBulk(s);
800
806
  }
801
807
  async function readClaudeCodeAt(base2, machine) {
802
808
  const out = [];
@@ -820,13 +826,12 @@ async function readClaudeCodeAt(base2, machine) {
820
826
  }
821
827
  for (const f of files) {
822
828
  const file2 = path2.join(full, f);
823
- let raw;
829
+ let s = null;
824
830
  try {
825
- raw = await fs2.readFile(file2, "utf-8");
831
+ s = await parseClaudeCode(jsonlLines(file2), path2.basename(f, ".jsonl"), file2);
826
832
  } catch {
827
833
  continue;
828
834
  }
829
- const s = parseClaudeCode(raw, path2.basename(f, ".jsonl"), file2);
830
835
  if (s) {
831
836
  if (machine)
832
837
  s.machine = machine;
@@ -861,13 +866,12 @@ async function readCodexAt(base2, machine) {
861
866
  await walkJsonl(base2, files);
862
867
  const out = [];
863
868
  for (const file2 of files) {
864
- let raw;
869
+ let s = null;
865
870
  try {
866
- raw = await fs2.readFile(file2, "utf-8");
871
+ s = await parseCodex(jsonlLines(file2), file2);
867
872
  } catch {
868
873
  continue;
869
874
  }
870
- const s = parseCodex(raw, file2);
871
875
  if (s) {
872
876
  if (machine)
873
877
  s.machine = machine;
@@ -1073,7 +1077,7 @@ function rawSessionFromCursorComposer(c, file2, cwd) {
1073
1077
  const cl = classify(s);
1074
1078
  s.klass = cl.klass;
1075
1079
  s.klassReason = cl.reason;
1076
- return s;
1080
+ return shedAgentBulk(s);
1077
1081
  }
1078
1082
  function parseCursorTranscript(raw, composerId, file2, ws, fileMtime) {
1079
1083
  const s = blank(composerId, "cursor", file2);
@@ -1118,7 +1122,7 @@ function parseCursorTranscript(raw, composerId, file2, ws, fileMtime) {
1118
1122
  const c = classify(s);
1119
1123
  s.klass = c.klass;
1120
1124
  s.klassReason = c.reason;
1121
- return s;
1125
+ return shedAgentBulk(s);
1122
1126
  }
1123
1127
  function cursorWorkspaceSession(ws, idx) {
1124
1128
  if (ws.generations.length === 0)
@@ -1140,7 +1144,7 @@ function cursorWorkspaceSession(ws, idx) {
1140
1144
  const c = classify(s);
1141
1145
  s.klass = c.klass;
1142
1146
  s.klassReason = c.reason;
1143
- return s;
1147
+ return shedAgentBulk(s);
1144
1148
  }
1145
1149
  async function readCursorAt(roots, machine) {
1146
1150
  const workspaces = await readCursorWorkspaces(roots.workspace);
@@ -1254,6 +1258,8 @@ async function readAllSessions(opts2 = {}) {
1254
1258
  ]);
1255
1259
  const all = [...cc, ...cx, ...cr, ...mm];
1256
1260
  demoteTemplateFanouts(all);
1261
+ for (const s of all)
1262
+ shedAgentBulk(s);
1257
1263
  return all;
1258
1264
  }
1259
1265
  var SOURCE_ROOT_ENV_VARS, anyRootOverridden, claudeProjectsRoot, codexSessionsRoot, cursorWorkspaceRoot, cursorProjectsRoot, cursorGlobalRoot, MACHINE_CURSOR_FILE_RE, SKIP_DIR_RE, AGENT_CWD_RE, PERSONA_RE, OUTPUT_CONTRACT_RES, CAPS_HEADER_RE, PING_RE, ms, CURSOR_USER_QUERY_RE2, NODE_SQLITE2, FANOUT_PREFIX, FANOUT_MIN_SESSIONS;
@@ -18779,7 +18785,7 @@ var init_runStats = __esm({
18779
18785
  "../../lib/agents/shared/runStats.ts"() {
18780
18786
  "use strict";
18781
18787
  G = globalThis;
18782
- file = () => path11.join(process.cwd(), ".data", "run-stats.jsonl");
18788
+ file = () => path11.join(process.env.POLYMATH_DATA_DIR || path11.join(process.cwd(), ".data"), "run-stats.jsonl");
18783
18789
  }
18784
18790
  });
18785
18791
 
@@ -22884,11 +22890,11 @@ function stages(o) {
22884
22890
  // so they run WHILE grading runs (grading is the wall-clock giant; these
22885
22891
  // finish inside its shadow). Budget: grade 8 + digest 3 + walkthrough 3 +
22886
22892
  // two single-call stages ≈ 16 peak subprocesses (measured-safe envelope).
22887
- { script: "coding-grade.mts", label: "AI-grade every substantial session", llm: true, weight: 30, args: gradeArgs, batch: "heavy" },
22888
- { script: "coding-day-digest.mts", label: "Day-by-day digests", llm: true, weight: 17, args: ["--model", narrativeModel, "--conc", String(h.digest)], out: "coding/day-digest.json", batch: "heavy" },
22889
- // walkthrough weight 28 = was 55 at 2 workers, ~28 at the rebalanced 4
22890
- { script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, weight: 28, args: ["--model", narrativeModel, "--conc", String(h.walkthrough)], batch: "heavy" },
22891
- { script: "coding-frontier-detail.mts", label: "What you're doing at the frontier", llm: true, weight: 3, args: [], out: "coding/frontier-detail.json", batch: "heavy" },
22893
+ { script: "coding-grade.mts", label: "AI-grade every substantial session", llm: true, weight: 32, args: gradeArgs, batch: "heavy" },
22894
+ { script: "coding-day-digest.mts", label: "Day-by-day digests", llm: true, weight: 19, args: ["--model", narrativeModel, "--conc", String(h.digest)], out: "coding/day-digest.json", batch: "heavy" },
22895
+ // walkthrough weight 38 = MEASURED on the rebalanced 4 workers (2026-07-23 clean run; the 2→4 speedup was 1.5x, not 2x)
22896
+ { script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, weight: 38, args: ["--model", narrativeModel, "--conc", String(h.walkthrough)], batch: "heavy" },
22897
+ { script: "coding-frontier-detail.mts", label: "What you're doing at the frontier", llm: true, weight: 2, args: [], out: "coding/frontier-detail.json", batch: "heavy" },
22892
22898
  { script: "coding-delegation.mts", label: "Delegation patterns", llm: true, weight: 2, args: ["--model", model, "--conc", String(h.delegation)], out: "coding/delegation-rollup.json", batch: "heavy" },
22893
22899
  // The FULL focus stage: flow-rule judgment + the paste-into-CLAUDE.md block.
22894
22900
  // The deterministic --skip-llm run above keeps the numbers current every
@@ -22925,7 +22931,7 @@ function stages(o) {
22925
22931
  // calls — it deliberately ignores the AI recency window and covers the full
22926
22932
  // history); disjoint output, so it rides the synthesis batch. Like
22927
22933
  // agglomerate it defaults itself to Opus; an explicit model choice wins.
22928
- { script: "coding-growth.mts", label: "Growth arcs + learning rate", llm: true, weight: 5, args: o.model ? ["--model", o.model] : [], out: "coding/growth.json", batch: "synthesis" },
22934
+ { script: "coding-growth.mts", label: "Growth arcs + learning rate", llm: true, weight: 3, args: o.model ? ["--model", o.model] : [], out: "coding/growth.json", batch: "synthesis" },
22929
22935
  // nutshell stays LAST + alone: it merges INTO aggregate.json while the two
22930
22936
  // above read the compiled profile — racing them re-reads mid-write.
22931
22937
  { script: "coding-nutshell.mts", label: "Big-picture nutshell", llm: true, weight: 1, args: [], out: "coding/aggregate.json" }
@@ -23103,6 +23109,10 @@ async function runPipeline(o) {
23103
23109
  }
23104
23110
  const dataRoot = codingDataRoot(o.repoRoot);
23105
23111
  const env = { ...process.env, POLYMATH_RESILIENT: "1", POLYMATH_DATA_DIR: dataRoot };
23112
+ if (!/max-old-space-size/.test(env.NODE_OPTIONS ?? "")) {
23113
+ const headroomMb = Math.min(12288, Math.max(4096, Math.round(os8.totalmem() / 2 ** 20 * 0.6)));
23114
+ env.NODE_OPTIONS = `${env.NODE_OPTIONS ?? ""} --max-old-space-size=${headroomMb}`.trim();
23115
+ }
23106
23116
  if (!o.overnight) env.POLYMATH_RESILIENT_NO_WINDOW = "1";
23107
23117
  const stopKeepAwake = o.overnight ? startKeepAwake() : () => {
23108
23118
  };
@@ -23220,6 +23230,7 @@ __export(chatPipeline_exports, {
23220
23230
  runChatPipeline: () => runChatPipeline
23221
23231
  });
23222
23232
  import { spawn as spawn8 } from "child_process";
23233
+ import os9 from "os";
23223
23234
  import path39 from "path";
23224
23235
  import { existsSync as existsSync6, promises as fs33 } from "fs";
23225
23236
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -23323,6 +23334,10 @@ async function runChatPipeline(o) {
23323
23334
  AGENT_MODEL: o.model ?? process.env.AGENT_MODEL ?? "sonnet"
23324
23335
  };
23325
23336
  if (!o.overnight) env.POLYMATH_RESILIENT_NO_WINDOW = "1";
23337
+ if (!/max-old-space-size/.test(env.NODE_OPTIONS ?? "")) {
23338
+ const headroomMb = Math.min(12288, Math.max(4096, Math.round(os9.totalmem() / 2 ** 20 * 0.6)));
23339
+ env.NODE_OPTIONS = `${env.NODE_OPTIONS ?? ""} --max-old-space-size=${headroomMb}`.trim();
23340
+ }
23326
23341
  const failed = [];
23327
23342
  const ingested = [];
23328
23343
  const notIngestable = [];
@@ -23415,6 +23430,74 @@ var init_chatPipeline = __esm({
23415
23430
  }
23416
23431
  });
23417
23432
 
23433
+ // src/ttyGuard.ts
23434
+ import { spawnSync as spawnSync3 } from "node:child_process";
23435
+ function stty(...args) {
23436
+ try {
23437
+ const r = spawnSync3("stty", args, { stdio: ["inherit", "pipe", "ignore"] });
23438
+ return r.status === 0 ? String(r.stdout ?? "").trim() : null;
23439
+ } catch {
23440
+ return null;
23441
+ }
23442
+ }
23443
+ function guardTty() {
23444
+ if (guarded || process.platform === "win32" || !process.stdin.isTTY) return;
23445
+ guarded = true;
23446
+ stty("isig", "icrnl");
23447
+ const saved = stty("-g");
23448
+ if (saved) process.on("exit", () => void stty(saved));
23449
+ }
23450
+ function attachPromptAbort(rl) {
23451
+ rl.on("SIGINT", () => {
23452
+ process.stderr.write("^C\n");
23453
+ rl.close();
23454
+ process.exit(130);
23455
+ });
23456
+ }
23457
+ function watchCtrlC(onCtrlC) {
23458
+ const stdin = process.stdin;
23459
+ if (process.platform === "win32" || !stdin.isTTY || typeof stdin.setRawMode !== "function") {
23460
+ return () => {
23461
+ };
23462
+ }
23463
+ const onData = (b) => {
23464
+ for (const byte of b) {
23465
+ if (byte === 3 || byte === 28) {
23466
+ onCtrlC();
23467
+ return;
23468
+ }
23469
+ if (byte === 26) {
23470
+ stdin.setRawMode(false);
23471
+ process.once("SIGCONT", () => {
23472
+ try {
23473
+ stdin.setRawMode(true);
23474
+ } catch {
23475
+ }
23476
+ });
23477
+ process.kill(process.pid, "SIGTSTP");
23478
+ }
23479
+ }
23480
+ };
23481
+ stdin.setRawMode(true);
23482
+ stdin.resume();
23483
+ stdin.on("data", onData);
23484
+ return () => {
23485
+ stdin.off("data", onData);
23486
+ try {
23487
+ stdin.setRawMode(false);
23488
+ } catch {
23489
+ }
23490
+ stdin.pause();
23491
+ };
23492
+ }
23493
+ var guarded;
23494
+ var init_ttyGuard = __esm({
23495
+ "src/ttyGuard.ts"() {
23496
+ "use strict";
23497
+ guarded = false;
23498
+ }
23499
+ });
23500
+
23418
23501
  // src/notify.ts
23419
23502
  import { spawn as spawn9 } from "node:child_process";
23420
23503
  function systemNotify(title, body) {
@@ -24047,7 +24130,7 @@ __export(plan_exports, {
24047
24130
  tierFromStrings: () => tierFromStrings
24048
24131
  });
24049
24132
  import { promises as fs36 } from "fs";
24050
- import os9 from "os";
24133
+ import os10 from "os";
24051
24134
  import path42 from "path";
24052
24135
  function supportsFullAnalysis(tier) {
24053
24136
  if (process.env.POLYMATH_FORCE_FULL === "1") return true;
@@ -24072,7 +24155,7 @@ function tierFromChatgptPlan(planType) {
24072
24155
  if (s === "free") return "gpt_free";
24073
24156
  return "unknown";
24074
24157
  }
24075
- async function detectCodexPlan(home = os9.homedir()) {
24158
+ async function detectCodexPlan(home = os10.homedir()) {
24076
24159
  try {
24077
24160
  const raw = JSON.parse(await fs36.readFile(path42.join(home, ".codex", "auth.json"), "utf8"));
24078
24161
  for (const tok of [raw.tokens?.id_token, raw.tokens?.access_token]) {
@@ -24088,7 +24171,7 @@ async function detectCodexPlan(home = os9.homedir()) {
24088
24171
  }
24089
24172
  return "unknown";
24090
24173
  }
24091
- async function detectPlan(home = os9.homedir()) {
24174
+ async function detectPlan(home = os10.homedir()) {
24092
24175
  let tier = "unknown";
24093
24176
  try {
24094
24177
  const raw = JSON.parse(await fs36.readFile(path42.join(home, ".claude.json"), "utf8"));
@@ -24200,9 +24283,9 @@ __export(wizard_exports, {
24200
24283
  scanJsonlDir: () => scanJsonlDir
24201
24284
  });
24202
24285
  import { promises as fs37 } from "fs";
24203
- import os10 from "os";
24286
+ import os11 from "os";
24204
24287
  import path43 from "path";
24205
- import * as readline from "node:readline/promises";
24288
+ import * as readline2 from "node:readline/promises";
24206
24289
  async function scanJsonlDir(label, dir, maxDepth = 1) {
24207
24290
  let files = 0;
24208
24291
  let newest = 0;
@@ -24315,7 +24398,8 @@ function describeExport(f, i, included, hadPrevious) {
24315
24398
  ${dim(path43.basename(f.path))} ${dim(`(${f.note})`)}`;
24316
24399
  }
24317
24400
  async function runWizard() {
24318
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
24401
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
24402
+ attachPromptAbort(rl);
24319
24403
  const say = (s = "") => console.error(s);
24320
24404
  const lineQueue = [];
24321
24405
  let waiting = null;
@@ -24393,7 +24477,7 @@ async function runWizard() {
24393
24477
  for (let i = 0; i < agentScans.length; i++) {
24394
24478
  const s = agentScans[i];
24395
24479
  const idx = present.indexOf(agentIds[i]);
24396
- say(s.files > 0 ? ` ${idx + 1}. ${green("\u2713")} ${bold(s.label)} \u2014 ${s.files.toLocaleString()} log files \xB7 newest ${s.newest} ${dim(s.dir.replace(os10.homedir(), "~"))}` : ` ${dim("\xB7")} ${dim(`${s.label} \u2014 none found`)} ${dim(s.dir.replace(os10.homedir(), "~"))}`);
24480
+ say(s.files > 0 ? ` ${idx + 1}. ${green("\u2713")} ${bold(s.label)} \u2014 ${s.files.toLocaleString()} log files \xB7 newest ${s.newest} ${dim(s.dir.replace(os11.homedir(), "~"))}` : ` ${dim("\xB7")} ${dim(`${s.label} \u2014 none found`)} ${dim(s.dir.replace(os11.homedir(), "~"))}`);
24397
24481
  }
24398
24482
  say(dim(" (log files include forks and agent runs \u2014 the report separates your real sessions out)"));
24399
24483
  if (present.length === 0) {
@@ -24445,22 +24529,19 @@ async function runWizard() {
24445
24529
  {
24446
24530
  const { looksLikeEmail: looksLikeEmail2, recordContact: recordContact2 } = await Promise.resolve().then(() => (init_reminderEmail(), reminderEmail_exports));
24447
24531
  say();
24448
- if (notifyEmail) {
24449
- say(` Updates go to ${bold(notifyEmail)}`);
24450
- } else {
24451
- const e = (await ask(` Your email ${dim("(recommended \u2014 we'll send your report link and occasional Polymath updates, and can nudge you when an export arrives; Enter to skip)")}: `)).trim();
24452
- if (e && looksLikeEmail2(e)) {
24453
- notifyEmail = e;
24454
- try {
24455
- await fs37.mkdir(dataDir, { recursive: true });
24456
- await fs37.writeFile(path43.join(dataDir, "notify-email.json"), JSON.stringify({ email: e }));
24457
- } catch {
24458
- }
24459
- await recordContact2(e, "wizard", reminderState.token).catch(() => false);
24460
- say(` ${green("\u2713")} Got it, we'll keep you posted at ${bold(e)}.`);
24461
- } else if (e) {
24462
- say(dim(` "${e}" doesn't look like an email \u2014 skipping.`));
24532
+ const e = (await ask(notifyEmail ? ` Your email ${dim(`(currently ${notifyEmail} \u2014 Enter if this is correct, or type a new one)`)}: ` : ` Your email ${dim("(recommended \u2014 we'll send your report link and occasional Polymath updates, and can nudge you when an export arrives; Enter to skip)")}: `)).trim();
24533
+ if (e && looksLikeEmail2(e)) {
24534
+ const correcting = !!notifyEmail;
24535
+ notifyEmail = e;
24536
+ try {
24537
+ await fs37.mkdir(dataDir, { recursive: true });
24538
+ await fs37.writeFile(path43.join(dataDir, "notify-email.json"), JSON.stringify({ email: e }));
24539
+ } catch {
24463
24540
  }
24541
+ await recordContact2(e, correcting ? "wizard-correction" : "wizard", reminderState.token).catch(() => false);
24542
+ say(` ${green("\u2713")} Got it, we'll keep you posted at ${bold(e)}.`);
24543
+ } else if (e) {
24544
+ say(dim(notifyEmail ? ` "${e}" doesn't look like an email \u2014 keeping ${notifyEmail}.` : ` "${e}" doesn't look like an email \u2014 skipping.`));
24464
24545
  }
24465
24546
  }
24466
24547
  const tracked = { token: reminderState.token };
@@ -24476,9 +24557,9 @@ async function runWizard() {
24476
24557
  }
24477
24558
  };
24478
24559
  say(dim(" Scanning for known export formats (Spotlight + the usual folders, a few seconds)..."));
24479
- for (const c of await deterministicDiscover(os10.homedir())) add(await identifyPath(c));
24480
- for (const f of await scanForExports(path43.join(os10.homedir(), "Downloads"))) add(f);
24481
- for (const f of await scanForObsidianVaults(os10.homedir())) add(f);
24560
+ for (const c of await deterministicDiscover(os11.homedir())) add(await identifyPath(c));
24561
+ for (const f of await scanForExports(path43.join(os11.homedir(), "Downloads"))) add(f);
24562
+ for (const f of await scanForObsidianVaults(os11.homedir())) add(f);
24482
24563
  for (const f of [...previous?.exports.included ?? [], ...previous?.exports.excluded ?? []]) add(f);
24483
24564
  let linksShown = false;
24484
24565
  if (!found.length) {
@@ -24489,7 +24570,7 @@ async function runWizard() {
24489
24570
  try {
24490
24571
  const { invokeAgent: invokeAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
24491
24572
  say(dim(" Searching (your agent writes its own filesystem searches; nothing leaves this machine; 90s max)..."));
24492
- const candidates = await agentDiscoverCandidates((inv) => invokeAgent3(inv), os10.homedir(), []);
24573
+ const candidates = await agentDiscoverCandidates((inv) => invokeAgent3(inv), os11.homedir(), []);
24493
24574
  for (const c of candidates) add(await identifyPath(c));
24494
24575
  } catch (e) {
24495
24576
  say(dim(` Agent search unavailable (${e instanceof Error ? e.message.slice(0, 80) : "failed"}).`));
@@ -24497,7 +24578,7 @@ async function runWizard() {
24497
24578
  }
24498
24579
  if (!found.length) {
24499
24580
  const folder = await ask(` Got them in a specific folder? ${dim("(path, or Enter to skip)")}: `);
24500
- if (folder) for (const f of await scanForExports(folder.replace(/^~(?=$|\/)/, os10.homedir()))) add(f);
24581
+ if (folder) for (const f of await scanForExports(folder.replace(/^~(?=$|\/)/, os11.homedir()))) add(f);
24501
24582
  }
24502
24583
  }
24503
24584
  if (found.length) {
@@ -24522,7 +24603,7 @@ async function runWizard() {
24522
24603
  approvedExports = manifest.exports.included;
24523
24604
  await writeManifest(dataDir, manifest);
24524
24605
  const nIn = manifest.exports.included.length;
24525
- say(` ${green("\u2713")} Saved to ${path43.join(dataDir, "exports-manifest.json").replace(os10.homedir(), "~")} \u2014 ${nIn} source${nIn === 1 ? "" : "s"} approved, ${manifest.exports.excluded.length} excluded (re-run anytime to change).`);
24606
+ say(` ${green("\u2713")} Saved to ${path43.join(dataDir, "exports-manifest.json").replace(os11.homedir(), "~")} \u2014 ${nIn} source${nIn === 1 ? "" : "s"} approved, ${manifest.exports.excluded.length} excluded (re-run anytime to change).`);
24526
24607
  for (const f of manifest.exports.excluded) say(` ${dim(`\u2717 ${path43.basename(f.path)} \u2014 excluded, never read past identification.`)}`);
24527
24608
  if (manifest.exports.included.some((f) => f.kind !== "unknown")) {
24528
24609
  say(` ${dim("These are ingested and analyzed locally when you pick the full analysis below.")}`);
@@ -24573,7 +24654,8 @@ async function runWizard() {
24573
24654
  say(` ${bold("Proposed schedule")} \u2014 sized to your plan (${plan.label}):`);
24574
24655
  say();
24575
24656
  say(` ${bold("Your analysis")} runs now \u2014 everything measured from your logs: words/day, flow state, focus, projects, parallelism`);
24576
- say(` ${bold("AI sections")} support for your plan is coming soon! Only the AI-graded sections (judgement, agency, taste, growth) won't be visible yet`);
24657
+ const planPhrase = plan.tier === "pro" ? "the $20 Claude Pro plan" : plan.tier === "gpt_plus" ? "the $20 ChatGPT Plus plan" : "the free ChatGPT plan";
24658
+ say(` ${bold("AI sections")} not supported on ${planPhrase} just yet \u2014 coming soon! Only the AI-graded sections (judgement, agency, taste, growth) won't be visible`);
24577
24659
  say();
24578
24660
  await ask(` Press Enter to run your analysis`).catch(() => "");
24579
24661
  say(dim(" On it. The report link appears right away.\n"));
@@ -24636,7 +24718,8 @@ function newExportsSince(found, m) {
24636
24718
  }
24637
24719
  async function returningUserFlow(dataDir) {
24638
24720
  const say = (s = "") => console.error(s);
24639
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
24721
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
24722
+ attachPromptAbort(rl);
24640
24723
  try {
24641
24724
  say();
24642
24725
  say(bold(" What would you like to do?"));
@@ -24683,17 +24766,18 @@ async function askEmailAndQueueReminder(dataDir, say, ask) {
24683
24766
  }
24684
24767
  const clicked = await fetchClickedKinds2(state.token);
24685
24768
  if (!clicked.length) return;
24686
- if (!email) {
24687
- const e = (await ask(` Leave your email and we'll remind you when your exports should be ready ${dim("(Enter to skip)")}`)).trim();
24769
+ {
24770
+ const e = (await ask(email ? ` Your email ${dim(`(currently ${email} \u2014 Enter if this is correct, or type a new one)`)}: ` : ` Leave your email and we'll remind you when your exports should be ready ${dim("(Enter to skip)")}`)).trim();
24688
24771
  if (e && looksLikeEmail2(e)) {
24772
+ const correcting = !!email;
24689
24773
  email = e;
24690
24774
  await fs37.mkdir(dataDir, { recursive: true }).catch(() => {
24691
24775
  });
24692
24776
  await fs37.writeFile(path43.join(dataDir, "notify-email.json"), JSON.stringify({ email: e })).catch(() => {
24693
24777
  });
24694
- await recordContact2(e, "add-sources", state.token).catch(() => false);
24778
+ await recordContact2(e, correcting ? "wizard-correction" : "add-sources", state.token).catch(() => false);
24695
24779
  } else if (e) {
24696
- say(dim(` "${e}" doesn't look like an email \u2014 skipping the reminder.`));
24780
+ say(dim(email ? ` "${e}" doesn't look like an email \u2014 keeping ${email}.` : ` "${e}" doesn't look like an email \u2014 skipping the reminder.`));
24697
24781
  }
24698
24782
  }
24699
24783
  if (!email) return;
@@ -24731,9 +24815,9 @@ async function addSourcesFlow(dataDir, say, rl) {
24731
24815
  found.push(f);
24732
24816
  }
24733
24817
  };
24734
- for (const c of await deterministicDiscover(os10.homedir())) add(await identifyPath(c));
24735
- for (const f of await scanForExports(path43.join(os10.homedir(), "Downloads"))) add(f);
24736
- for (const f of await scanForObsidianVaults(os10.homedir())) add(f);
24818
+ for (const c of await deterministicDiscover(os11.homedir())) add(await identifyPath(c));
24819
+ for (const f of await scanForExports(path43.join(os11.homedir(), "Downloads"))) add(f);
24820
+ for (const f of await scanForObsidianVaults(os11.homedir())) add(f);
24737
24821
  for (const f of [...previous?.exports.included ?? [], ...previous?.exports.excluded ?? []]) {
24738
24822
  if (await fs37.stat(f.path).then(() => true, () => false)) add(f);
24739
24823
  }
@@ -24835,6 +24919,7 @@ var init_wizard = __esm({
24835
24919
  init_exportScan();
24836
24920
  init_manifest();
24837
24921
  init_dataHome();
24922
+ init_ttyGuard();
24838
24923
  init_raw2();
24839
24924
  color = process.stdout.isTTY && !process.env.NO_COLOR;
24840
24925
  dim = (s) => color ? `\x1B[2m${s}\x1B[0m` : s;
@@ -24882,6 +24967,7 @@ function makeMultiBar(out = process.stderr) {
24882
24967
  return ` ${name17.padEnd(6)} [${"\u2588".repeat(fill)}${"\u2591".repeat(width - fill)}] ${String(Math.round(l.shown * 100)).padStart(3)}%`;
24883
24968
  };
24884
24969
  const out2 = [];
24970
+ out2.push("");
24885
24971
  out2.push("This is a background process. We'll let you know when it's finished.");
24886
24972
  out2.push("It may take anywhere from 30 minutes to an hour.");
24887
24973
  out2.push("");
@@ -25059,8 +25145,8 @@ try {
25059
25145
  import { promises as fs38, existsSync as existsSync7 } from "fs";
25060
25146
  import path44 from "path";
25061
25147
  import { fileURLToPath as fileURLToPath5 } from "url";
25062
- import { spawnSync as spawnSync3 } from "child_process";
25063
- import readline2 from "readline";
25148
+ import { spawnSync as spawnSync4 } from "child_process";
25149
+ import readline3 from "readline";
25064
25150
 
25065
25151
  // src/openBrowser.ts
25066
25152
  import { spawn } from "child_process";
@@ -28096,6 +28182,7 @@ init_auth();
28096
28182
  init_pipeline2();
28097
28183
  init_chatPipeline();
28098
28184
  init_manifest();
28185
+ init_ttyGuard();
28099
28186
  init_profile();
28100
28187
  var __dirname2 = path44.dirname(fileURLToPath5(import.meta.url));
28101
28188
  var REPO_ROOT = path44.resolve(__dirname2, "..", "..", "..");
@@ -28247,8 +28334,9 @@ PROJECTS`);
28247
28334
  log(``);
28248
28335
  }
28249
28336
  async function askYesNo(q, def = true) {
28250
- const readline3 = await import("node:readline/promises");
28251
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
28337
+ const readline4 = await import("node:readline/promises");
28338
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
28339
+ attachPromptAbort(rl);
28252
28340
  try {
28253
28341
  for (; ; ) {
28254
28342
  const a = (await rl.question(`${q} `)).trim().toLowerCase();
@@ -28277,6 +28365,7 @@ async function runGrade(opts2, provider, profile) {
28277
28365
  await provider.criteriaMean(profile.sessions);
28278
28366
  }
28279
28367
  async function main() {
28368
+ guardTty();
28280
28369
  if (process.argv[2] === "remind-exports") {
28281
28370
  const { runReminder: runReminder2 } = await Promise.resolve().then(() => (init_remind(), remind_exports));
28282
28371
  const argv = process.argv.slice(3);
@@ -28304,8 +28393,13 @@ async function main() {
28304
28393
  await printBanner2();
28305
28394
  console.error("");
28306
28395
  await maybePrintUpdateNotice(opts2);
28307
- console.error(` data home: ${DATA_ROOT}`);
28308
- console.error(" Checking for existing analysis\u2026");
28396
+ {
28397
+ const { osc8: osc82 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
28398
+ const boldTty = process.stderr.isTTY && !process.env.NO_COLOR;
28399
+ const b = (t) => boldTty ? `\x1B[1m${t}\x1B[0m` : t;
28400
+ console.error(` ${b("Step 0 \xB7 Want to see some sample reports before you run?")} ${osc82("https://polymathsociety.us/sample", "https://polymathsociety.us/sample")}`);
28401
+ console.error("");
28402
+ }
28309
28403
  const prior = process.env.POLYMATH_WIZARD === "1" ? null : await readPriorAnalysis();
28310
28404
  if (prior) {
28311
28405
  console.error(` Found your finished report \u2014 ${prior.graded} graded sessions, last updated ${prior.when}.`);
@@ -28540,7 +28634,7 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
28540
28634
  codingLane.stageDone(key2);
28541
28635
  const f = codingFrac();
28542
28636
  codingLane.setFrac(f);
28543
- progress.coding = { ...progress.coding, i: Math.min(aiDone.coding + 1, aiCount.coding), total: aiCount.coding, done: false, frac: f, withinFrac: Math.round(sumFracs("coding") * 1e3) / 1e3 };
28637
+ progress.coding = { ...progress.coding, label: progress.coding?.label ?? label, i: Math.min(aiDone.coding + 1, aiCount.coding), total: aiCount.coding, done: false, frac: f, withinFrac: Math.round(sumFracs("coding") * 1e3) / 1e3 };
28544
28638
  earlyHandle.setProgress(progress);
28545
28639
  },
28546
28640
  // While pinned, the scrolling stage output stays quiet ("prints hella
@@ -28760,6 +28854,7 @@ No analysis here yet \u2014 computing the instant deterministic metrics now (fre
28760
28854
  };
28761
28855
  process.on("SIGINT", shutdown(130));
28762
28856
  process.on("SIGTERM", shutdown(143));
28857
+ watchCtrlC(() => void shutdown(130)());
28763
28858
  return;
28764
28859
  }
28765
28860
  const profile = await analyze({ codex: opts2.codex, idleGapMin: opts2.idleGapMin, tz: opts2.tz, spanHourMin: opts2.spanHourMin });
@@ -28796,7 +28891,8 @@ async function promptAutoUpdate(current, latest) {
28796
28891
  console.error(`
28797
28892
  New version available: polymath-society ${current} \u2192 ${latest}`);
28798
28893
  const answer = await new Promise((resolve2) => {
28799
- const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
28894
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
28895
+ attachPromptAbort(rl);
28800
28896
  const timer = setTimeout(() => {
28801
28897
  rl.close();
28802
28898
  resolve2("");
@@ -28814,7 +28910,7 @@ async function promptAutoUpdate(current, latest) {
28814
28910
  console.error(`
28815
28911
  Updating\u2026
28816
28912
  `);
28817
- const install = spawnSync3("npm", ["install", "-g", "polymath-society@latest"], {
28913
+ const install = spawnSync4("npm", ["install", "-g", "polymath-society@latest"], {
28818
28914
  stdio: "inherit",
28819
28915
  shell: process.platform === "win32"
28820
28916
  });
@@ -28828,7 +28924,7 @@ async function promptAutoUpdate(current, latest) {
28828
28924
  console.error(`
28829
28925
  Updated to ${latest}. Relaunching\u2026
28830
28926
  `);
28831
- const relaunch = spawnSync3("polymath-society", process.argv.slice(2), {
28927
+ const relaunch = spawnSync4("polymath-society", process.argv.slice(2), {
28832
28928
  stdio: "inherit",
28833
28929
  shell: process.platform === "win32"
28834
28930
  });
@@ -16505,7 +16505,7 @@ import path7 from "path";
16505
16505
  import { appendFileSync, mkdirSync } from "fs";
16506
16506
  import path6 from "path";
16507
16507
  var G = globalThis;
16508
- var file = () => path6.join(process.cwd(), ".data", "run-stats.jsonl");
16508
+ var file = () => path6.join(process.env.POLYMATH_DATA_DIR || path6.join(process.cwd(), ".data"), "run-stats.jsonl");
16509
16509
  function write(ev) {
16510
16510
  try {
16511
16511
  mkdirSync(path6.dirname(file()), { recursive: true });
@@ -15302,7 +15302,7 @@ import path8 from "path";
15302
15302
  import { appendFileSync, mkdirSync } from "fs";
15303
15303
  import path7 from "path";
15304
15304
  var G = globalThis;
15305
- var file = () => path7.join(process.cwd(), ".data", "run-stats.jsonl");
15305
+ var file = () => path7.join(process.env.POLYMATH_DATA_DIR || path7.join(process.cwd(), ".data"), "run-stats.jsonl");
15306
15306
  function write(ev) {
15307
15307
  try {
15308
15308
  mkdirSync(path7.dirname(file()), { recursive: true });
@@ -15302,7 +15302,7 @@ import path8 from "path";
15302
15302
  import { appendFileSync, mkdirSync } from "fs";
15303
15303
  import path7 from "path";
15304
15304
  var G = globalThis;
15305
- var file = () => path7.join(process.cwd(), ".data", "run-stats.jsonl");
15305
+ var file = () => path7.join(process.env.POLYMATH_DATA_DIR || path7.join(process.cwd(), ".data"), "run-stats.jsonl");
15306
15306
  function write(ev) {
15307
15307
  try {
15308
15308
  mkdirSync(path7.dirname(file()), { recursive: true });
@@ -15297,7 +15297,7 @@ import path7 from "path";
15297
15297
  import { appendFileSync, mkdirSync } from "fs";
15298
15298
  import path6 from "path";
15299
15299
  var G = globalThis;
15300
- var file = () => path6.join(process.cwd(), ".data", "run-stats.jsonl");
15300
+ var file = () => path6.join(process.env.POLYMATH_DATA_DIR || path6.join(process.cwd(), ".data"), "run-stats.jsonl");
15301
15301
  function write(ev) {
15302
15302
  try {
15303
15303
  mkdirSync(path6.dirname(file()), { recursive: true });
@@ -16293,7 +16293,7 @@ import path9 from "path";
16293
16293
  import { appendFileSync, mkdirSync } from "fs";
16294
16294
  import path8 from "path";
16295
16295
  var G = globalThis;
16296
- var file = () => path8.join(process.cwd(), ".data", "run-stats.jsonl");
16296
+ var file = () => path8.join(process.env.POLYMATH_DATA_DIR || path8.join(process.cwd(), ".data"), "run-stats.jsonl");
16297
16297
  function write(ev) {
16298
16298
  try {
16299
16299
  mkdirSync(path8.dirname(file()), { recursive: true });
@@ -16293,7 +16293,7 @@ import path9 from "path";
16293
16293
  import { appendFileSync, mkdirSync } from "fs";
16294
16294
  import path8 from "path";
16295
16295
  var G = globalThis;
16296
- var file = () => path8.join(process.cwd(), ".data", "run-stats.jsonl");
16296
+ var file = () => path8.join(process.env.POLYMATH_DATA_DIR || path8.join(process.cwd(), ".data"), "run-stats.jsonl");
16297
16297
  function write(ev) {
16298
16298
  try {
16299
16299
  mkdirSync(path8.dirname(file()), { recursive: true });
@@ -16293,7 +16293,7 @@ import path9 from "path";
16293
16293
  import { appendFileSync, mkdirSync } from "fs";
16294
16294
  import path8 from "path";
16295
16295
  var G = globalThis;
16296
- var file = () => path8.join(process.cwd(), ".data", "run-stats.jsonl");
16296
+ var file = () => path8.join(process.env.POLYMATH_DATA_DIR || path8.join(process.cwd(), ".data"), "run-stats.jsonl");
16297
16297
  function write(ev) {
16298
16298
  try {
16299
16299
  mkdirSync(path8.dirname(file()), { recursive: true });