polymath-society 0.2.4 → 0.2.5

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
@@ -32,6 +32,33 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  mod
33
33
  ));
34
34
 
35
+ // src/dataHome.ts
36
+ import { existsSync } from "fs";
37
+ import os from "os";
38
+ import path from "path";
39
+ function electronAppData() {
40
+ const candidates = [
41
+ "/Applications/Polymath.app/Contents/Resources/standalone/.data",
42
+ path.join(os.homedir(), "Applications", "Polymath.app", "Contents", "Resources", "standalone", ".data")
43
+ ];
44
+ for (const c of candidates) if (existsSync(path.join(c, "coding"))) return c;
45
+ return null;
46
+ }
47
+ function resolveDataRoot(env = process.env, cwd = process.cwd(), appData = electronAppData) {
48
+ const fromEnv = env.POLYMATH_DATA_DIR;
49
+ if (fromEnv && fromEnv.trim()) return path.resolve(fromEnv);
50
+ const local = path.join(cwd, ".data");
51
+ if (existsSync(local)) return local;
52
+ const fromApp = appData();
53
+ if (fromApp) return fromApp;
54
+ return path.join(os.homedir(), ".polymath-society", "data");
55
+ }
56
+ var init_dataHome = __esm({
57
+ "src/dataHome.ts"() {
58
+ "use strict";
59
+ }
60
+ });
61
+
35
62
  // ../coding-core/dist/injected.js
36
63
  function stripInjected(t) {
37
64
  for (const re2 of INJECTED_RES)
@@ -73,15 +100,15 @@ var init_injected = __esm({
73
100
 
74
101
  // ../coding-core/dist/raw.js
75
102
  import { promises as fs } from "fs";
76
- import path from "path";
77
- import os from "os";
103
+ import path2 from "path";
104
+ import os2 from "os";
78
105
  function resolveSourceRoot(envVar, ...defaultSegments) {
79
106
  const explicit = process.env[envVar];
80
107
  if (explicit)
81
108
  return explicit;
82
109
  if (anyRootOverridden())
83
110
  return null;
84
- return path.join(os.homedir(), ...defaultSegments);
111
+ return path2.join(os2.homedir(), ...defaultSegments);
85
112
  }
86
113
  function sourceRoots() {
87
114
  return {
@@ -305,7 +332,7 @@ function codexText(content) {
305
332
  return "";
306
333
  }
307
334
  function parseCodex(raw, file2) {
308
- let sessionId = path.basename(file2, ".jsonl");
335
+ let sessionId = path2.basename(file2, ".jsonl");
309
336
  const s = blank(sessionId, "codex", file2);
310
337
  for (const lineRaw of raw.split("\n")) {
311
338
  const line = lineRaw.trim();
@@ -389,7 +416,7 @@ async function readClaudeCode() {
389
416
  for (const dir of dirs) {
390
417
  if (SKIP_DIR_RE.test(dir))
391
418
  continue;
392
- const full = path.join(base2, dir);
419
+ const full = path2.join(base2, dir);
393
420
  let files = [];
394
421
  try {
395
422
  files = (await fs.readdir(full)).filter((f) => f.endsWith(".jsonl"));
@@ -397,14 +424,14 @@ async function readClaudeCode() {
397
424
  continue;
398
425
  }
399
426
  for (const f of files) {
400
- const file2 = path.join(full, f);
427
+ const file2 = path2.join(full, f);
401
428
  let raw;
402
429
  try {
403
430
  raw = await fs.readFile(file2, "utf-8");
404
431
  } catch {
405
432
  continue;
406
433
  }
407
- const s = parseClaudeCode(raw, path.basename(f, ".jsonl"), file2);
434
+ const s = parseClaudeCode(raw, path2.basename(f, ".jsonl"), file2);
408
435
  if (s)
409
436
  out.push(s);
410
437
  }
@@ -419,7 +446,7 @@ async function walkJsonl(dir, acc) {
419
446
  return;
420
447
  }
421
448
  for (const ent of entries) {
422
- const full = path.join(dir, ent.name);
449
+ const full = path2.join(dir, ent.name);
423
450
  if (ent.isDirectory())
424
451
  await walkJsonl(full, acc);
425
452
  else if (ent.name.endsWith(".jsonl"))
@@ -514,15 +541,15 @@ async function readCursorWorkspaces() {
514
541
  for (const d of dirs) {
515
542
  if (!d.isDirectory())
516
543
  continue;
517
- const wsDir = path.join(base2, d.name);
544
+ const wsDir = path2.join(base2, d.name);
518
545
  let folder = null;
519
546
  try {
520
- folder = fileUriToPath(JSON.parse(await fs.readFile(path.join(wsDir, "workspace.json"), "utf-8")).folder);
547
+ folder = fileUriToPath(JSON.parse(await fs.readFile(path2.join(wsDir, "workspace.json"), "utf-8")).folder);
521
548
  } catch {
522
549
  }
523
550
  if (!folder)
524
551
  continue;
525
- const dbFile = path.join(wsDir, "state.vscdb");
552
+ const dbFile = path2.join(wsDir, "state.vscdb");
526
553
  try {
527
554
  await fs.access(dbFile);
528
555
  } catch {
@@ -664,7 +691,7 @@ async function readCursor() {
664
691
  const slug = d.name;
665
692
  if (SKIP_DIR_RE.test(slug))
666
693
  continue;
667
- const transcriptsDir = path.join(projects, slug, "agent-transcripts");
694
+ const transcriptsDir = path2.join(projects, slug, "agent-transcripts");
668
695
  let composers = [];
669
696
  try {
670
697
  composers = await fs.readdir(transcriptsDir, { withFileTypes: true });
@@ -679,7 +706,7 @@ async function readCursor() {
679
706
  if (!c.isDirectory())
680
707
  continue;
681
708
  const composerId = c.name;
682
- const jf = path.join(transcriptsDir, composerId, `${composerId}.jsonl`);
709
+ const jf = path2.join(transcriptsDir, composerId, `${composerId}.jsonl`);
683
710
  let raw;
684
711
  let mtime = Date.now();
685
712
  try {
@@ -812,9 +839,9 @@ var init_adapter = __esm({
812
839
 
813
840
  // src/grade/backends.ts
814
841
  import { spawnSync } from "child_process";
815
- import { existsSync } from "fs";
816
- import os2 from "os";
817
- import path2 from "path";
842
+ import { existsSync as existsSync2 } from "fs";
843
+ import os3 from "os";
844
+ import path3 from "path";
818
845
  function run(bin, args) {
819
846
  try {
820
847
  const r = spawnSync(bin, args, { encoding: "utf-8", timeout: PROBE_TIMEOUT_MS });
@@ -827,14 +854,14 @@ function resolveBin(envOverride, name17, candidates) {
827
854
  if (envOverride) return envOverride;
828
855
  const found = run("/bin/sh", ["-c", `command -v ${name17}`]);
829
856
  if (found.code === 0 && found.text) return found.text.split("\n")[0];
830
- for (const c of candidates) if (existsSync(c)) return c;
857
+ for (const c of candidates) if (existsSync2(c)) return c;
831
858
  return null;
832
859
  }
833
860
  function claudeBin() {
834
861
  return resolveBin(process.env.CLAUDE_BIN, "claude", [
835
862
  "/opt/homebrew/bin/claude",
836
863
  "/usr/local/bin/claude",
837
- path2.join(HOME, ".local/bin/claude")
864
+ path3.join(HOME, ".local/bin/claude")
838
865
  ]);
839
866
  }
840
867
  function codexBin() {
@@ -855,7 +882,7 @@ function probeCli() {
855
882
  const kc = run("security", ["find-generic-password", "-s", "Claude Code-credentials"]);
856
883
  if (kc.code === 0) return { name: "cli", bin, authed: true, detail: "Keychain OAuth credentials", loginCmd };
857
884
  }
858
- if (existsSync(path2.join(HOME, ".claude/.credentials.json")))
885
+ if (existsSync2(path3.join(HOME, ".claude/.credentials.json")))
859
886
  return { name: "cli", bin, authed: true, detail: "~/.claude/.credentials.json", loginCmd };
860
887
  return { name: "cli", bin, authed: false, detail: "not logged in", loginCmd };
861
888
  }
@@ -902,7 +929,7 @@ var HOME, PROBE_TIMEOUT_MS, cached;
902
929
  var init_backends = __esm({
903
930
  "src/grade/backends.ts"() {
904
931
  "use strict";
905
- HOME = os2.homedir();
932
+ HOME = os3.homedir();
906
933
  PROBE_TIMEOUT_MS = 15e3;
907
934
  cached = null;
908
935
  }
@@ -1292,9 +1319,9 @@ var require_secure_json_parse = __commonJS({
1292
1319
 
1293
1320
  // src/manifest.ts
1294
1321
  import { promises as fs24 } from "fs";
1295
- import path29 from "path";
1322
+ import path30 from "path";
1296
1323
  function manifestPath(dataDir) {
1297
- return path29.join(dataDir, MANIFEST_BASENAME);
1324
+ return path30.join(dataDir, MANIFEST_BASENAME);
1298
1325
  }
1299
1326
  async function readManifest(dataDir) {
1300
1327
  const p = manifestPath(dataDir);
@@ -1333,15 +1360,15 @@ async function writeManifest(dataDir, m) {
1333
1360
  function parsePicker(input, defaults) {
1334
1361
  const t = input.trim().toLowerCase();
1335
1362
  if (t === "") return [...defaults];
1336
- if (t === "all" || t === "a") return defaults.map(() => true);
1337
- if (t === "none" || t === "n") return defaults.map(() => false);
1363
+ if (t === "none" || t === "n") return defaults.map(() => true);
1364
+ if (t === "all" || t === "a") return defaults.map(() => false);
1338
1365
  const tokens = t.split(/[\s,]+/).filter(Boolean);
1339
- const out = [...defaults];
1366
+ const out = defaults.map(() => true);
1340
1367
  for (const tok of tokens) {
1341
1368
  if (!/^\d+$/.test(tok)) return null;
1342
1369
  const i = Number(tok) - 1;
1343
1370
  if (i < 0 || i >= defaults.length) return null;
1344
- out[i] = !out[i];
1371
+ out[i] = false;
1345
1372
  }
1346
1373
  return out;
1347
1374
  }
@@ -1460,7 +1487,7 @@ var init_remind = __esm({
1460
1487
  import { promises as fs28 } from "fs";
1461
1488
  import { execFile as execFile2 } from "child_process";
1462
1489
  import { promisify as promisify2 } from "util";
1463
- import path33 from "path";
1490
+ import path34 from "path";
1464
1491
  async function zipEntries(zip) {
1465
1492
  try {
1466
1493
  const { stdout } = await run3("unzip", ["-Z1", zip], { maxBuffer: 8 * 1024 * 1024 });
@@ -1514,7 +1541,7 @@ async function identifyZip(zip, stat) {
1514
1541
  note: ""
1515
1542
  };
1516
1543
  if (!entries) {
1517
- const n = path33.basename(zip).toLowerCase();
1544
+ const n = path34.basename(zip).toLowerCase();
1518
1545
  if (n.includes("chatgpt") || n.includes("openai")) {
1519
1546
  found.kind = "chatgpt";
1520
1547
  found.note = "matched by filename only (no unzip binary to verify)";
@@ -1525,15 +1552,30 @@ async function identifyZip(zip, stat) {
1525
1552
  found.note = "matched by filename only (no unzip binary to verify)";
1526
1553
  return found;
1527
1554
  }
1528
- if (n.includes("notion") || n.startsWith("export-")) {
1555
+ if (n.includes("notion") || /(^|[-_ ])export-/.test(n)) {
1529
1556
  found.kind = "notion";
1530
1557
  found.note = "matched by filename only (no unzip binary to verify)";
1531
1558
  return found;
1532
1559
  }
1533
1560
  return null;
1534
1561
  }
1535
- const { kind, note } = classify2(entries);
1536
- if (kind === "unknown") return null;
1562
+ let { kind, note } = classify2(entries);
1563
+ if (kind === "unknown") {
1564
+ const nb = path34.basename(zip).toLowerCase();
1565
+ const nameHint = nb.includes("notion") || /(^|[-_ ])export-/.test(nb);
1566
+ const inner = entries.filter((e) => e.toLowerCase().endsWith(".zip"));
1567
+ const partZips = inner.some((e) => /export[^/]*part[-_ ]?\d+\.zip$/i.test(e.toLowerCase()));
1568
+ const pages = entries.filter((e) => /\.(md|html)$/.test(e)).length;
1569
+ if (partZips || inner.length > 0 && nameHint) {
1570
+ kind = "notion";
1571
+ note = `nested export zip${inner.length === 1 ? "" : "s"} (${inner.length}) \u2014 Notion multi-part layout`;
1572
+ } else if (pages >= 1 && nameHint) {
1573
+ kind = "notion";
1574
+ note = `${pages} .md/.html page${pages === 1 ? "" : "s"} + export filename (Notion layout)`;
1575
+ } else {
1576
+ return null;
1577
+ }
1578
+ }
1537
1579
  found.kind = kind;
1538
1580
  found.note = note;
1539
1581
  if (kind === "chatgpt") {
@@ -1558,7 +1600,7 @@ async function identifyDir(dir) {
1558
1600
  const found = { kind, path: dir, account: null, sizeMB: 0, modified: st.mtime.toISOString().slice(0, 10), note };
1559
1601
  const read = async (n) => {
1560
1602
  try {
1561
- return await fs28.readFile(path33.join(dir, n), "utf8");
1603
+ return await fs28.readFile(path34.join(dir, n), "utf8");
1562
1604
  } catch {
1563
1605
  return null;
1564
1606
  }
@@ -1579,8 +1621,33 @@ async function identifyPath(p) {
1579
1621
  if (st.isDirectory()) return identifyDir(p);
1580
1622
  return null;
1581
1623
  }
1582
- async function agentDiscoverCandidates(invoke, home) {
1583
- const prompt = `Search this machine for AI-chat export artifacts the user downloaded. You are running locally on the user's own computer with their consent; nothing you find leaves the machine.
1624
+ async function deterministicDiscover(home) {
1625
+ const out = [];
1626
+ if (process.platform === "darwin") {
1627
+ const { execFile: execFile3 } = await import("node:child_process");
1628
+ const run4 = (q) => new Promise((resolve2) => {
1629
+ execFile3("mdfind", ["-onlyin", home, q], { timeout: 1e4, maxBuffer: 4 << 20 }, (err, stdout) => {
1630
+ resolve2(err ? [] : stdout.split("\n").filter(Boolean));
1631
+ });
1632
+ });
1633
+ for (const q of MDFIND_QUERIES) {
1634
+ for (const hit of (await run4(q)).slice(0, 40)) {
1635
+ out.push(hit.endsWith("conversations.json") ? hit.slice(0, hit.lastIndexOf("/")) : hit);
1636
+ }
1637
+ }
1638
+ }
1639
+ const path38 = await import("node:path");
1640
+ for (const dir of ["Downloads", "Desktop", "Documents"]) {
1641
+ for (const f of await scanForExports(path38.join(home, dir)).catch(() => [])) out.push(f.path);
1642
+ }
1643
+ return [...new Set(out)].slice(0, 80);
1644
+ }
1645
+ async function agentDiscoverCandidates(invoke, home, alreadyFound = []) {
1646
+ const seeded = alreadyFound.length ? `
1647
+
1648
+ Already located by a deterministic scan (do NOT re-report these; look for anything ELSE):
1649
+ ${alreadyFound.slice(0, 30).map((p) => `- ${p}`).join("\n")}` : "";
1650
+ const prompt = `Search this machine for AI-chat export artifacts the user downloaded. You are running locally on the user's own computer with their consent; nothing you find leaves the machine.${seeded}
1584
1651
 
1585
1652
  Look for:
1586
1653
  - ChatGPT data exports: zips or unzipped folders containing conversations.json (or conversations-000.json shards) plus user.json / chat.html
@@ -1615,7 +1682,7 @@ async function countMdNotes(root, cap = 2e3) {
1615
1682
  for (const e of entries) {
1616
1683
  if (n >= cap) return;
1617
1684
  if (e.name.startsWith(".")) continue;
1618
- if (e.isDirectory()) await walk(path33.join(d, e.name), depth + 1);
1685
+ if (e.isDirectory()) await walk(path34.join(d, e.name), depth + 1);
1619
1686
  else if (e.name.endsWith(".md")) n++;
1620
1687
  }
1621
1688
  }
@@ -1624,7 +1691,7 @@ async function countMdNotes(root, cap = 2e3) {
1624
1691
  }
1625
1692
  async function identifyObsidianVault(dir) {
1626
1693
  try {
1627
- const st = await fs28.stat(path33.join(dir, ".obsidian"));
1694
+ const st = await fs28.stat(path34.join(dir, ".obsidian"));
1628
1695
  if (!st.isDirectory()) return null;
1629
1696
  } catch {
1630
1697
  return null;
@@ -1651,9 +1718,9 @@ async function scanForObsidianVaults(home) {
1651
1718
  }
1652
1719
  };
1653
1720
  const roots = [
1654
- path33.join(home, "Library", "Mobile Documents", "iCloud~md~obsidian", "Documents"),
1655
- path33.join(home, "Documents"),
1656
- path33.join(home, "Desktop"),
1721
+ path34.join(home, "Library", "Mobile Documents", "iCloud~md~obsidian", "Documents"),
1722
+ path34.join(home, "Documents"),
1723
+ path34.join(home, "Desktop"),
1657
1724
  home
1658
1725
  ];
1659
1726
  for (const root of roots) {
@@ -1665,7 +1732,7 @@ async function scanForObsidianVaults(home) {
1665
1732
  }
1666
1733
  for (const name17 of names) {
1667
1734
  if (name17.startsWith(".")) continue;
1668
- add(await identifyObsidianVault(path33.join(root, name17)));
1735
+ add(await identifyObsidianVault(path34.join(root, name17)));
1669
1736
  }
1670
1737
  }
1671
1738
  return out;
@@ -1680,7 +1747,7 @@ async function scanForExports(root) {
1680
1747
  }
1681
1748
  for (const name17 of names) {
1682
1749
  if (name17.startsWith(".")) continue;
1683
- const p = path33.join(root, name17);
1750
+ const p = path34.join(root, name17);
1684
1751
  let st;
1685
1752
  try {
1686
1753
  st = await fs28.stat(p);
@@ -1697,11 +1764,86 @@ async function scanForExports(root) {
1697
1764
  }
1698
1765
  return out.sort((a, b) => a.modified < b.modified ? 1 : -1);
1699
1766
  }
1700
- var run3;
1767
+ var run3, MDFIND_QUERIES;
1701
1768
  var init_exportScan = __esm({
1702
1769
  "src/exportScan.ts"() {
1703
1770
  "use strict";
1704
1771
  run3 = promisify2(execFile2);
1772
+ MDFIND_QUERIES = [
1773
+ // ChatGPT exports: the conversations.json inside the zip/folder
1774
+ `kMDItemFSName == "conversations.json"`,
1775
+ // claude.ai export zips: data-<uuid>-...-batch-0000.zip
1776
+ `kMDItemFSName == "data-*batch-*.zip"`,
1777
+ // Notion workspace exports
1778
+ `kMDItemFSName == "*Export-*.zip"`
1779
+ ];
1780
+ }
1781
+ });
1782
+
1783
+ // src/banner.ts
1784
+ var banner_exports = {};
1785
+ __export(banner_exports, {
1786
+ banner: () => banner,
1787
+ printBanner: () => printBanner
1788
+ });
1789
+ function banner(version, out = process.stderr) {
1790
+ const tty = !!out.isTTY && !process.env.NO_COLOR;
1791
+ const cols = out.columns || 80;
1792
+ if (!tty) return `
1793
+ Polymath Society v${version}
1794
+ ${TAGLINE}
1795
+ ${PRIVACY}
1796
+ `;
1797
+ if (cols >= 74) {
1798
+ const lines = KEYBLOCK.map((l) => ` ${GOLD}${l}${RESET}`);
1799
+ lines.push(` ${DIM} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 S O C I E T Y \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${RESET}`);
1800
+ lines.push("");
1801
+ lines.push(` ${TAGLINE} ${DIM}v${version}${RESET}`);
1802
+ lines.push(` ${DIM}${PRIVACY}${RESET}`);
1803
+ return `
1804
+ ${lines.join("\n")}
1805
+ `;
1806
+ }
1807
+ const right = ["", "POLYMATH", "SOCIETY", "", `${DIM}v${version}${RESET}`, ""];
1808
+ const mark = MONOGRAM.map((l, i) => ` ${GOLD}${l}${RESET} ${right[i]}`.trimEnd());
1809
+ return `
1810
+ ${mark.join("\n")}
1811
+
1812
+ ${TAGLINE}
1813
+ ${DIM}${PRIVACY}${RESET}
1814
+ `;
1815
+ }
1816
+ async function printBanner(out = process.stderr) {
1817
+ let version = "0.0.0";
1818
+ try {
1819
+ const { promises: fs32 } = await import("fs");
1820
+ const path38 = await import("path");
1821
+ const { fileURLToPath: fileURLToPath6 } = await import("url");
1822
+ const here = path38.dirname(fileURLToPath6(import.meta.url));
1823
+ const pkg = JSON.parse(await fs32.readFile(path38.join(here, "..", "package.json"), "utf-8"));
1824
+ version = String(pkg.version || version);
1825
+ } catch {
1826
+ }
1827
+ out.write(banner(version, out));
1828
+ }
1829
+ var GOLD, DIM, RESET, KEYBLOCK, MONOGRAM, TAGLINE, PRIVACY;
1830
+ var init_banner = __esm({
1831
+ "src/banner.ts"() {
1832
+ "use strict";
1833
+ GOLD = "\x1B[38;5;179m";
1834
+ DIM = "\x1B[2m";
1835
+ RESET = "\x1B[0m";
1836
+ KEYBLOCK = [
1837
+ "\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
1838
+ "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551",
1839
+ "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551",
1840
+ "\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551",
1841
+ "\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551",
1842
+ "\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D"
1843
+ ];
1844
+ MONOGRAM = ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2550\u255D ", "\u2588\u2588\u2551 ", "\u255A\u2550\u255D "];
1845
+ TAGLINE = "see how you actually work, from your own agent logs";
1846
+ PRIVACY = "private by construction \xB7 everything runs on this machine";
1705
1847
  }
1706
1848
  });
1707
1849
 
@@ -1742,14 +1884,150 @@ var init_reminderEmail = __esm({
1742
1884
  }
1743
1885
  });
1744
1886
 
1887
+ // src/plan.ts
1888
+ var plan_exports = {};
1889
+ __export(plan_exports, {
1890
+ detectCodexPlan: () => detectCodexPlan,
1891
+ detectPlan: () => detectPlan,
1892
+ estimateLaneCosts: () => estimateLaneCosts,
1893
+ remainingFractions: () => remainingFractions,
1894
+ scheduleLanes: () => scheduleLanes,
1895
+ tierFromChatgptPlan: () => tierFromChatgptPlan,
1896
+ tierFromStrings: () => tierFromStrings
1897
+ });
1898
+ import { promises as fs29 } from "fs";
1899
+ import os7 from "os";
1900
+ import path35 from "path";
1901
+ function tierFromStrings(...hints) {
1902
+ for (const h of hints) {
1903
+ if (!h) continue;
1904
+ const s = h.toLowerCase();
1905
+ if (s.includes("max_20x") || s.includes("max20x")) return "max_20x";
1906
+ if (s.includes("max_5x") || s.includes("max5x")) return "max_5x";
1907
+ if (s.includes("pro")) return "pro";
1908
+ if (s.includes("claude_max")) return "max_5x";
1909
+ }
1910
+ return "unknown";
1911
+ }
1912
+ function tierFromChatgptPlan(planType) {
1913
+ const s = (planType ?? "").toLowerCase();
1914
+ if (s === "pro") return "gpt_pro";
1915
+ if (s === "plus") return "gpt_plus";
1916
+ if (s === "team" || s === "enterprise" || s === "business") return "gpt_team";
1917
+ if (s === "free") return "gpt_free";
1918
+ return "unknown";
1919
+ }
1920
+ async function detectCodexPlan(home = os7.homedir()) {
1921
+ try {
1922
+ const raw = JSON.parse(await fs29.readFile(path35.join(home, ".codex", "auth.json"), "utf8"));
1923
+ for (const tok of [raw.tokens?.id_token, raw.tokens?.access_token]) {
1924
+ if (!tok) continue;
1925
+ try {
1926
+ const payload = JSON.parse(Buffer.from(tok.split(".")[1], "base64url").toString("utf8"));
1927
+ const t = tierFromChatgptPlan(payload["https://api.openai.com/auth"]?.chatgpt_plan_type);
1928
+ if (t !== "unknown") return t;
1929
+ } catch {
1930
+ }
1931
+ }
1932
+ } catch {
1933
+ }
1934
+ return "unknown";
1935
+ }
1936
+ async function detectPlan(home = os7.homedir()) {
1937
+ let tier = "unknown";
1938
+ try {
1939
+ const raw = JSON.parse(await fs29.readFile(path35.join(home, ".claude.json"), "utf8"));
1940
+ const oa = raw.oauthAccount ?? {};
1941
+ tier = tierFromStrings(oa.userRateLimitTier, oa.organizationRateLimitTier, oa.organizationType);
1942
+ } catch {
1943
+ }
1944
+ if (tier === "unknown") tier = await detectCodexPlan(home);
1945
+ return { tier, label: LABEL[tier], windowUsd: WINDOW_USD[tier] };
1946
+ }
1947
+ function estimateLaneCosts(input) {
1948
+ const codingUsd = Math.max(5, Math.round(input.agentLogFiles * 13e-4));
1949
+ const chatUsd = input.chatIncluded ? Math.round(40 + input.exportTotalMB * 0.8) : 0;
1950
+ return { codingUsd, chatUsd };
1951
+ }
1952
+ async function remainingFractions(dataRoot, agentLogFiles) {
1953
+ const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
1954
+ let coding = 1;
1955
+ try {
1956
+ const graded = (await fs29.readdir(path35.join(dataRoot, "coding", "grades"))).filter((f) => f.endsWith(".json")).length;
1957
+ const expected = Math.max(1, agentLogFiles * 56e-4);
1958
+ coding = clamp(1 - graded / expected, 0.05, 1);
1959
+ } catch {
1960
+ }
1961
+ let chatDone = 0;
1962
+ try {
1963
+ const st = await fs29.stat(path35.join(dataRoot, "signals", "index.jsonl"));
1964
+ if (st.size > 0) chatDone += 0.3;
1965
+ } catch {
1966
+ }
1967
+ try {
1968
+ const lenses = await fs29.readdir(path35.join(dataRoot, "engine-pilot", "grades"));
1969
+ let withPerson = 0;
1970
+ for (const l of lenses) {
1971
+ try {
1972
+ await fs29.stat(path35.join(dataRoot, "engine-pilot", "grades", l, "_person.json"));
1973
+ withPerson++;
1974
+ } catch {
1975
+ }
1976
+ }
1977
+ chatDone += 0.6 * Math.min(1, withPerson / 5);
1978
+ } catch {
1979
+ }
1980
+ return { coding, chat: clamp(1 - chatDone, 0.1, 1) };
1981
+ }
1982
+ function scheduleLanes(plan, est) {
1983
+ const fits = (usd) => usd <= plan.windowUsd * 0.5;
1984
+ const coding = fits(est.codingUsd) ? "now" : "overnight";
1985
+ const chat = est.chatUsd === 0 || fits(est.chatUsd) ? "now" : "overnight";
1986
+ return {
1987
+ coding,
1988
+ chat,
1989
+ codingReason: coding === "now" ? `runs now (fits comfortably inside one 5-hour window on ${plan.label})` : `starts at 2:00 am tonight (a full grade would eat most of your daytime window on ${plan.label})`,
1990
+ chatReason: est.chatUsd === 0 ? "no chat/notes sources included" : chat === "now" ? `runs now alongside coding (your chat corpus is small enough for ${plan.label})` : `starts at 2:00 am tonight (your chat corpus is large; this protects your daytime quota on ${plan.label})`
1991
+ };
1992
+ }
1993
+ var WINDOW_USD, LABEL;
1994
+ var init_plan = __esm({
1995
+ "src/plan.ts"() {
1996
+ "use strict";
1997
+ WINDOW_USD = {
1998
+ max_20x: 300,
1999
+ max_5x: 75,
2000
+ pro: 15,
2001
+ // ChatGPT tiers, mapped to rough Claude-window parity by monthly price
2002
+ // ($200 Pro ≈ Max 20x; $20 Plus ≈ Claude Pro; Team between; free ≈ nothing).
2003
+ gpt_pro: 300,
2004
+ gpt_plus: 20,
2005
+ gpt_team: 50,
2006
+ gpt_free: 5,
2007
+ unknown: 15
2008
+ // conservative: schedule like Pro when we can't tell
2009
+ };
2010
+ LABEL = {
2011
+ max_20x: "Claude Max 20x",
2012
+ max_5x: "Claude Max 5x",
2013
+ pro: "Claude Pro",
2014
+ gpt_pro: "ChatGPT Pro (Codex)",
2015
+ gpt_plus: "ChatGPT Plus (Codex)",
2016
+ gpt_team: "ChatGPT Team (Codex)",
2017
+ gpt_free: "ChatGPT Free (Codex)",
2018
+ unknown: "an undetected plan (scheduling conservatively)"
2019
+ };
2020
+ }
2021
+ });
2022
+
1745
2023
  // src/wizard.ts
1746
2024
  var wizard_exports = {};
1747
2025
  __export(wizard_exports, {
1748
2026
  runWizard: () => runWizard
1749
2027
  });
1750
- import { promises as fs29 } from "fs";
1751
- import os6 from "os";
1752
- import path34 from "path";
2028
+ import { promises as fs30 } from "fs";
2029
+ import os8 from "os";
2030
+ import path36 from "path";
1753
2031
  import * as readline from "node:readline/promises";
1754
2032
  async function scanJsonlDir(label, dir) {
1755
2033
  let files = 0;
@@ -1758,15 +2036,15 @@ async function scanJsonlDir(label, dir) {
1758
2036
  if (depth > 1) return;
1759
2037
  let names = [];
1760
2038
  try {
1761
- names = await fs29.readdir(d);
2039
+ names = await fs30.readdir(d);
1762
2040
  } catch {
1763
2041
  return;
1764
2042
  }
1765
2043
  for (const n of names) {
1766
- const p = path34.join(d, n);
2044
+ const p = path36.join(d, n);
1767
2045
  let st;
1768
2046
  try {
1769
- st = await fs29.stat(p);
2047
+ st = await fs30.stat(p);
1770
2048
  } catch {
1771
2049
  continue;
1772
2050
  }
@@ -1792,7 +2070,7 @@ function describeExport(f, i, included, hadPrevious) {
1792
2070
  const size = f.sizeMB ? ` \xB7 ${f.sizeMB} MB` : "";
1793
2071
  const state = hadPrevious ? included ? green(" [use]") : dim(" [skip \u2014 previously excluded]") : "";
1794
2072
  return ` ${i + 1}. ${bold(kind)}${who}${size} \xB7 ${f.modified}${state}
1795
- ${dim(path34.basename(f.path))} ${dim(`(${f.note})`)}`;
2073
+ ${dim(path36.basename(f.path))} ${dim(`(${f.note})`)}`;
1796
2074
  }
1797
2075
  async function runWizard() {
1798
2076
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
@@ -1835,20 +2113,19 @@ async function runWizard() {
1835
2113
  return a === "" ? def : a.startsWith("y");
1836
2114
  };
1837
2115
  const pick2 = async (defaults, hadPrevious) => {
1838
- const hint = hadPrevious ? "(Enter = keep previous \xB7 numbers to toggle, e.g. '2 4' \xB7 'all' \xB7 'none')" : "(Enter = all \xB7 numbers to exclude, e.g. '2 4' \xB7 'none')";
2116
+ const hint = "(Enter = take everything into account \xB7 numbers to exclude, e.g. '2 4' \xB7 'all' = exclude everything)";
1839
2117
  for (; ; ) {
1840
- const a = await ask(` Use which? ${dim(hint)} `);
2118
+ const a = await ask(` Exclude any? ${dim(hint)} `);
1841
2119
  const r = parsePicker(a, defaults);
1842
2120
  if (r) return r;
1843
2121
  say(dim(` Didn't understand "${a}" \u2014 numbers between 1 and ${defaults.length}, or all/none.`));
1844
2122
  }
1845
2123
  };
1846
2124
  try {
2125
+ const { printBanner: printBanner2 } = await Promise.resolve().then(() => (init_banner(), banner_exports));
2126
+ await printBanner2();
1847
2127
  say();
1848
- say(` ${accent("\u25C6")} ${bold("Polymath")} \u2014 see how you actually work, from your own agent logs.`);
1849
- say(` ${dim("Private by construction: everything runs on this machine. Nothing is uploaded.")}`);
1850
- say();
1851
- const dataDir = path34.join(process.cwd(), ".data");
2128
+ const dataDir = resolveDataRoot();
1852
2129
  const previous = await readManifest(dataDir).catch((e) => {
1853
2130
  say(dim(` (couldn't read previous choices: ${e.message})`));
1854
2131
  return null;
@@ -1863,7 +2140,7 @@ async function runWizard() {
1863
2140
  for (let i = 0; i < agentScans.length; i++) {
1864
2141
  const s = agentScans[i];
1865
2142
  const idx = present.indexOf(agentIds[i]);
1866
- 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(os6.homedir(), "~"))}` : ` ${dim("\xB7")} ${dim(`${s.label} \u2014 none found`)} ${dim(s.dir.replace(os6.homedir(), "~"))}`);
2143
+ 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(os8.homedir(), "~"))}` : ` ${dim("\xB7")} ${dim(`${s.label} \u2014 none found`)} ${dim(s.dir.replace(os8.homedir(), "~"))}`);
1867
2144
  }
1868
2145
  say(dim(" (log files include forks and agent runs \u2014 the report separates your real sessions out)"));
1869
2146
  if (present.length === 0) {
@@ -1889,6 +2166,32 @@ async function runWizard() {
1889
2166
  excluded: present.filter((_, i) => !agentInclude[i])
1890
2167
  };
1891
2168
  for (const id of agents.excluded) say(` ${dim(`\u2717 ${id} excluded \u2014 its logs will not be read.`)}`);
2169
+ let notifyEmail = null;
2170
+ try {
2171
+ notifyEmail = JSON.parse(await fs30.readFile(path36.join(dataDir, "notify-email.json"), "utf8")).email ?? null;
2172
+ } catch {
2173
+ }
2174
+ {
2175
+ const { looksLikeEmail: looksLikeEmail2 } = await Promise.resolve().then(() => (init_reminderEmail(), reminderEmail_exports));
2176
+ say();
2177
+ if (notifyEmail) {
2178
+ say(` Updates go to ${bold(notifyEmail)} ${dim("(stored only on this machine)")}`);
2179
+ } else {
2180
+ const e = (await ask(` Your email ${dim("(recommended \u2014 a heads-up when your report is ready or your exports arrive; Enter to skip)")}: `)).trim();
2181
+ if (e && looksLikeEmail2(e)) {
2182
+ notifyEmail = e;
2183
+ try {
2184
+ await fs30.mkdir(dataDir, { recursive: true });
2185
+ await fs30.writeFile(path36.join(dataDir, "notify-email.json"), JSON.stringify({ email: e }));
2186
+ } catch {
2187
+ }
2188
+ say(` ${green("\u2713")} Saved on this machine only.`);
2189
+ } else if (e) {
2190
+ say(dim(` "${e}" doesn't look like an email \u2014 skipping.`));
2191
+ }
2192
+ }
2193
+ }
2194
+ let approvedExports = [];
1892
2195
  say();
1893
2196
  say(bold(" Step 2 \xB7 Chat + notes sources") + dim(" (optional \u2014 ChatGPT / claude.ai / Notion exports, Obsidian vaults)"));
1894
2197
  let found = [];
@@ -1899,32 +2202,32 @@ async function runWizard() {
1899
2202
  found.push(f);
1900
2203
  }
1901
2204
  };
1902
- let searched = false;
1903
- if (await yes(` Search this machine for export files using your own local agent? ${dim("(runs locally, ~a minute)")}`)) {
1904
- searched = true;
1905
- try {
1906
- const { invokeAgent: invokeAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
1907
- say(dim(" Searching (your agent is writing its own filesystem searches; nothing leaves this machine)..."));
1908
- const candidates = await agentDiscoverCandidates((inv) => invokeAgent3(inv), os6.homedir());
1909
- for (const c of candidates) add(await identifyPath(c));
1910
- } catch (e) {
1911
- say(dim(` Agent search unavailable (${e instanceof Error ? e.message.slice(0, 80) : "failed"}) \u2014 checking ~/Downloads instead.`));
1912
- }
1913
- }
1914
- for (const f of await scanForExports(path34.join(os6.homedir(), "Downloads"))) add(f);
1915
- for (const f of await scanForObsidianVaults(os6.homedir())) add(f);
2205
+ say(dim(" Scanning for known export formats (Spotlight + the usual folders, a few seconds)..."));
2206
+ for (const c of await deterministicDiscover(os8.homedir())) add(await identifyPath(c));
2207
+ for (const f of await scanForExports(path36.join(os8.homedir(), "Downloads"))) add(f);
2208
+ for (const f of await scanForObsidianVaults(os8.homedir())) add(f);
1916
2209
  for (const f of [...previous?.exports.included ?? [], ...previous?.exports.excluded ?? []]) add(f);
1917
2210
  if (!found.length) {
1918
- say(dim(` ${searched ? "Nothing recognized on this machine." : "Checked ~/Downloads and the Obsidian locations \u2014 nothing found."}`));
1919
- const folder = await ask(` Got them in a specific folder? ${dim("(path, or Enter to skip)")}: `);
1920
- if (folder) for (const f of await scanForExports(folder.replace(/^~(?=$|\/)/, os6.homedir()))) add(f);
2211
+ say(dim(" Nothing found in the usual places."));
2212
+ if (await yes(` Search deeper with your own local agent? ${dim("(capped at ~90s)")}`, false)) {
2213
+ try {
2214
+ const { invokeAgent: invokeAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
2215
+ say(dim(" Searching (your agent writes its own filesystem searches; nothing leaves this machine; 90s max)..."));
2216
+ const candidates = await agentDiscoverCandidates((inv) => invokeAgent3(inv), os8.homedir(), []);
2217
+ for (const c of candidates) add(await identifyPath(c));
2218
+ } catch (e) {
2219
+ say(dim(` Agent search unavailable (${e instanceof Error ? e.message.slice(0, 80) : "failed"}).`));
2220
+ }
2221
+ }
2222
+ if (!found.length) {
2223
+ const folder = await ask(` Got them in a specific folder? ${dim("(path, or Enter to skip)")}: `);
2224
+ if (folder) for (const f of await scanForExports(folder.replace(/^~(?=$|\/)/, os8.homedir()))) add(f);
2225
+ }
1921
2226
  }
1922
2227
  if (found.length) {
1923
2228
  found.sort((a, b) => a.modified < b.modified ? 1 : -1);
1924
- const prevExcludedPaths = new Set((previous?.exports.excluded ?? []).map((f) => f.path));
1925
- const prevKnownPaths = new Set([...previous?.exports.included ?? [], ...previous?.exports.excluded ?? []].map((f) => f.path));
1926
- const hadPrevious = found.some((f) => prevKnownPaths.has(f.path));
1927
- const defaults = found.map((f) => !prevExcludedPaths.has(f.path));
2229
+ const hadPrevious = false;
2230
+ const defaults = found.map(() => true);
1928
2231
  say(` Found ${dim("(identified locally from the files themselves)")}:`);
1929
2232
  found.forEach((f, i) => say(describeExport(f, i, defaults[i], hadPrevious)));
1930
2233
  {
@@ -1950,10 +2253,11 @@ async function runWizard() {
1950
2253
  excluded: found.filter((_, i) => !include[i])
1951
2254
  }
1952
2255
  };
2256
+ approvedExports = manifest.exports.included;
1953
2257
  await writeManifest(dataDir, manifest);
1954
2258
  const nIn = manifest.exports.included.length;
1955
- say(` ${green("\u2713")} Saved to .data/exports-manifest.json \u2014 ${nIn} source${nIn === 1 ? "" : "s"} approved, ${manifest.exports.excluded.length} excluded (re-run anytime to change).`);
1956
- for (const f of manifest.exports.excluded) say(` ${dim(`\u2717 ${path34.basename(f.path)} \u2014 excluded, never read past identification.`)}`);
2259
+ say(` ${green("\u2713")} Saved to ${path36.join(dataDir, "exports-manifest.json").replace(os8.homedir(), "~")} \u2014 ${nIn} source${nIn === 1 ? "" : "s"} approved, ${manifest.exports.excluded.length} excluded (re-run anytime to change).`);
2260
+ for (const f of manifest.exports.excluded) say(` ${dim(`\u2717 ${path36.basename(f.path)} \u2014 excluded, never read past identification.`)}`);
1957
2261
  if (manifest.exports.included.some((f) => f.kind !== "unknown")) {
1958
2262
  say(` ${dim("These are ingested and analyzed locally when you pick the full analysis below.")}`);
1959
2263
  }
@@ -1974,64 +2278,231 @@ async function runWizard() {
1974
2278
  const { EXPORT_LINKS: EXPORT_LINKS2 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
1975
2279
  const haveKinds = new Set(found.map((f) => f.kind));
1976
2280
  const missing = EXPORT_LINKS2.filter((l) => l.requestUrl && !haveKinds.has(l.kind));
1977
- if (missing.length) {
1978
- say();
1979
- say(bold(" Step 2b \xB7 Want a reminder?"));
1980
- const { requestEmailReminder: requestEmailReminder2, looksLikeEmail: looksLikeEmail2 } = await Promise.resolve().then(() => (init_reminderEmail(), reminderEmail_exports));
2281
+ if (missing.length && notifyEmail) {
2282
+ const { requestEmailReminder: requestEmailReminder2 } = await Promise.resolve().then(() => (init_reminderEmail(), reminderEmail_exports));
1981
2283
  const kinds = missing.map((l) => l.kind);
1982
- const prompt = ` Enter email here if you want to be reminded to run this in a few days, once your exports are ready ${dim("(Enter to skip)")}: `;
1983
- let email = await ask(prompt);
1984
- if (email && !looksLikeEmail2(email)) {
1985
- say(` "${email}" doesn't look like an email address.`);
1986
- email = await ask(prompt);
1987
- }
1988
- if (email && looksLikeEmail2(email)) {
1989
- say(dim(" (your address is stored only until that one reminder email sends, then deleted)"));
1990
- const r = await requestEmailReminder2(email, kinds, 48);
1991
- if (r.ok) {
1992
- say(` ${green("\u2713")} ${r.alreadySet ? "A reminder is already set for this email \u2014 you're covered." : "Reminder set \u2014 check your inbox in ~2 days."}`);
1993
- } else {
1994
- say(` Couldn't reach the server (${r.error}) \u2014 set a local notification instead; it won't survive a reboot.`);
1995
- const { spawn: spawn10 } = await import("node:child_process");
1996
- spawn10(process.execPath, [process.argv[1], "remind-exports", "--hours", "48", "--kinds", kinds.join(",")], { detached: true, stdio: "ignore" }).unref();
1997
- }
1998
- } else if (email) {
1999
- say(` Still doesn't look like an email \u2014 skipping the reminder.`);
2284
+ const r = await requestEmailReminder2(notifyEmail, kinds, 48);
2285
+ if (r.ok) say(` ${green("\u2713")} We'll email ${notifyEmail} in ~2 days, when the ${missing.map((l) => l.label).join(" / ")} export${missing.length === 1 ? "" : "s"} should be ready.`);
2286
+ else {
2287
+ const { spawn: spawn10 } = await import("node:child_process");
2288
+ spawn10(process.execPath, [process.argv[1], "remind-exports", "--hours", "48", "--kinds", kinds.join(",")], { detached: true, stdio: "ignore" }).unref();
2289
+ say(dim(` (couldn't reach the server \u2014 set a local notification instead)`));
2000
2290
  }
2001
2291
  }
2002
2292
  }
2293
+ const { detectPlan: detectPlan2, estimateLaneCosts: estimateLaneCosts2, scheduleLanes: scheduleLanes2, remainingFractions: remainingFractions2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
2294
+ const plan = await detectPlan2();
2295
+ const agentLogFiles = agentScans.reduce((a, s2) => a + s2.files, 0);
2296
+ const full = estimateLaneCosts2({
2297
+ agentLogFiles,
2298
+ exportTotalMB: approvedExports.reduce((a, f) => a + (f.sizeMB || 0), 0),
2299
+ chatIncluded: approvedExports.length > 0
2300
+ });
2301
+ const remain = await remainingFractions2(dataDir, agentLogFiles);
2302
+ const est = { codingUsd: Math.round(full.codingUsd * remain.coding), chatUsd: Math.round(full.chatUsd * remain.chat) };
2303
+ const sched = scheduleLanes2(plan, est);
2304
+ const resumed = full.codingUsd > 0 && remain.coding < 0.5 || full.chatUsd > 0 && remain.chat < 0.5;
2305
+ const anyOvernight = sched.coding === "overnight" || est.chatUsd > 0 && sched.chat === "overnight";
2003
2306
  say();
2004
2307
  say(bold(" Step 3 \xB7 How deep?"));
2005
- say(` ${bold("1)")} Full analysis \u2014 AI grading on your own Claude/Codex login ${dim("(recommended)")}`);
2006
- say(` ${dim("no API key; spends your subscription; a big corpus can take ~an hour")}`);
2007
- say(` ${bold("2)")} Full analysis overnight \u2014 grades in the 02:00 window, protects your daytime quota`);
2308
+ say(` You're on ${bold(plan.label)}. The schedule below is sized to your plan + corpus:`);
2309
+ if (resumed) say(dim(" (an earlier run already processed most of this \u2014 the estimate covers only what's new; nothing done is redone)"));
2310
+ say(` \u2022 coding: ${sched.codingReason}`);
2311
+ if (est.chatUsd > 0) say(` \u2022 chats/notes: ${sched.chatReason}`);
2312
+ say(` ${bold("1)")} Full analysis on the schedule above ${dim("(recommended)")}`);
2313
+ say(` ${bold("2)")} Full analysis, everything overnight \u2014 all grading waits for the 02:00 window`);
2314
+ say(` ${bold("VERY IMPORTANT")} whenever anything runs overnight:`);
2315
+ say(` \u2022 keep your laptop plugged in`);
2316
+ say(` \u2022 keep the lid open \u2014 closed means sleep, and the run stalls until morning`);
2008
2317
  say(` ${bold("3)")} Just the numbers \u2014 instant deterministic metrics (words/day, flow, parallelism), free, ~a minute`);
2009
2318
  const depth = await ask(` Choose ${dim("[1/2/3, default 1]")}: `) || "1";
2319
+ const chosenOvernight = depth === "2" || depth === "1" && anyOvernight;
2320
+ if (depth !== "3" && chosenOvernight) {
2321
+ say();
2322
+ say(` ${bold("Reminder for the overnight part:")} laptop plugged in, lid open. The report page shows this too.`);
2323
+ }
2010
2324
  say();
2011
- say(dim(" On it. The report link appears when it's ready \u2014 Ctrl-C anytime.\n"));
2012
- return { cmd: "serve", grade: depth !== "3", overnight: depth === "2", open: true };
2325
+ say(dim(" On it. The report link appears right away and fills in as stages finish \u2014 Ctrl-C anytime.\n"));
2326
+ return {
2327
+ cmd: "serve",
2328
+ grade: depth !== "3",
2329
+ overnight: depth === "2" ? true : sched.coding === "overnight",
2330
+ chatOvernight: depth === "2" ? true : sched.chat === "overnight",
2331
+ open: true
2332
+ };
2013
2333
  } finally {
2014
2334
  rl.close();
2015
2335
  }
2016
2336
  }
2017
- var color, dim, bold, accent, green;
2337
+ var color, dim, bold, green;
2018
2338
  var init_wizard = __esm({
2019
2339
  "src/wizard.ts"() {
2020
2340
  "use strict";
2021
2341
  init_exportScan();
2022
2342
  init_manifest();
2343
+ init_dataHome();
2023
2344
  init_raw2();
2024
2345
  color = process.stdout.isTTY && !process.env.NO_COLOR;
2025
2346
  dim = (s) => color ? `\x1B[2m${s}\x1B[0m` : s;
2026
2347
  bold = (s) => color ? `\x1B[1m${s}\x1B[0m` : s;
2027
- accent = (s) => color ? `\x1B[38;5;173m${s}\x1B[0m` : s;
2028
2348
  green = (s) => color ? `\x1B[32m${s}\x1B[0m` : s;
2029
2349
  }
2030
2350
  });
2031
2351
 
2352
+ // src/progressBar.ts
2353
+ var progressBar_exports = {};
2354
+ __export(progressBar_exports, {
2355
+ makeMultiBar: () => makeMultiBar,
2356
+ makeProgressBar: () => makeProgressBar
2357
+ });
2358
+ function makeMultiBar(out = process.stderr) {
2359
+ const tty = !!out.isTTY;
2360
+ const t0 = Date.now();
2361
+ const lanes = /* @__PURE__ */ new Map();
2362
+ let link = "";
2363
+ let paintedRows = 0;
2364
+ const laneText = (name17, l) => {
2365
+ const w = l.within ? ` ${l.within.done}/${l.within.total}` : "";
2366
+ return `${name17} ${l.i}/${l.total} ${l.label.slice(0, 26)}${w}`;
2367
+ };
2368
+ const rows = () => {
2369
+ const frac = (l) => l.total ? (l.i - 1 + (l.within && l.within.total ? l.within.done / l.within.total : 0)) / l.total : 0;
2370
+ const parts = [...lanes.entries()].map(([n, l]) => laneText(n, l));
2371
+ const avg2 = lanes.size ? [...lanes.values()].reduce((a, l) => a + frac(l), 0) / lanes.size : 0;
2372
+ const width = 14;
2373
+ const fill = Math.max(0, Math.min(width, Math.round(avg2 * width)));
2374
+ const elapsedMin = Math.round((Date.now() - t0) / 6e4);
2375
+ const bar = `[${"\u2588".repeat(fill)}${"\u2591".repeat(width - fill)}] ${parts.join(" \xB7 ")} \xB7 ${elapsedMin}m`;
2376
+ return link ? [bar, `\u21B3 report (live, fills in as stages finish) \u2192 ${link}`] : [bar];
2377
+ };
2378
+ const clearPinned = () => {
2379
+ if (!paintedRows) return;
2380
+ out.write(CLEAR);
2381
+ for (let r = 1; r < paintedRows; r++) out.write(UP + CLEAR);
2382
+ paintedRows = 0;
2383
+ };
2384
+ const paint = () => {
2385
+ if (!tty) return;
2386
+ const r = rows();
2387
+ out.write(r.join("\n"));
2388
+ paintedRows = r.length;
2389
+ };
2390
+ const repaintAround = (line) => {
2391
+ if (!tty) {
2392
+ if (line != null) out.write(line + "\n");
2393
+ return;
2394
+ }
2395
+ clearPinned();
2396
+ if (line != null) out.write(line + "\n");
2397
+ paint();
2398
+ };
2399
+ return {
2400
+ lane(name17) {
2401
+ return {
2402
+ stage(i, total, label) {
2403
+ const l = lanes.get(name17) ?? { i: 0, total: 0, label: "", within: null };
2404
+ l.i = i;
2405
+ l.total = total;
2406
+ l.label = label;
2407
+ l.within = null;
2408
+ lanes.set(name17, l);
2409
+ repaintAround(`\u25B8 ${name17} [${i}/${total}] ${label}`);
2410
+ },
2411
+ log(line) {
2412
+ const l = lanes.get(name17);
2413
+ if (l) {
2414
+ const m = /Σ\s+(\d+)\/(\d+)/.exec(line);
2415
+ if (m) l.within = { done: Number(m[1]), total: Number(m[2]) };
2416
+ }
2417
+ repaintAround(tty ? line : `[${name17}] ${line.trimStart()}`);
2418
+ }
2419
+ };
2420
+ },
2421
+ setLink(url) {
2422
+ link = url;
2423
+ if (tty) repaintAround();
2424
+ else out.write(`report \u2192 ${url}
2425
+ `);
2426
+ },
2427
+ done() {
2428
+ clearPinned();
2429
+ }
2430
+ };
2431
+ }
2432
+ function makeProgressBar(out = process.stderr) {
2433
+ const tty = !!out.isTTY;
2434
+ const t0 = Date.now();
2435
+ let stageI = 0, stageN = 0, label = "";
2436
+ let within = null;
2437
+ let etaH = null;
2438
+ let painted = false;
2439
+ const bar = () => {
2440
+ const frac = stageN ? (stageI - 1 + (within && within.total ? within.done / within.total : 0)) / stageN : 0;
2441
+ const width = 18;
2442
+ const fill = Math.max(0, Math.min(width, Math.round(frac * width)));
2443
+ const elapsedMin = Math.round((Date.now() - t0) / 6e4);
2444
+ const eta = etaH != null ? ` \xB7 ~${etaH.toFixed(1)}h total` : "";
2445
+ const w = within ? ` \xB7 ${within.done}/${within.total}` : "";
2446
+ return `[${"\u2588".repeat(fill)}${"\u2591".repeat(width - fill)}] stage ${stageI}/${stageN} ${label.slice(0, 38)}${w} \xB7 ${elapsedMin}m${eta}`;
2447
+ };
2448
+ const paint = () => {
2449
+ if (!tty) return;
2450
+ out.write(CLEAR + bar());
2451
+ painted = true;
2452
+ };
2453
+ return {
2454
+ stage(i, total, lbl) {
2455
+ stageI = i;
2456
+ stageN = total;
2457
+ label = lbl;
2458
+ within = null;
2459
+ if (tty) {
2460
+ if (painted) out.write(CLEAR);
2461
+ out.write(`\u25B8 [${i}/${total}] ${lbl}
2462
+ `);
2463
+ paint();
2464
+ } else out.write(`\u25B8 [${i}/${total}] ${lbl}
2465
+ `);
2466
+ },
2467
+ log(line) {
2468
+ const m = /Σ\s+(\d+)\/(\d+)/.exec(line);
2469
+ if (m) within = { done: Number(m[1]), total: Number(m[2]) };
2470
+ const e = /~([\d.]+)h total/.exec(line);
2471
+ if (e) etaH = Number(e[1]);
2472
+ if (tty) {
2473
+ if (painted) out.write(CLEAR);
2474
+ out.write(line + "\n");
2475
+ paint();
2476
+ } else out.write(line + "\n");
2477
+ },
2478
+ done() {
2479
+ if (tty && painted) out.write(CLEAR);
2480
+ painted = false;
2481
+ }
2482
+ };
2483
+ }
2484
+ var CLEAR, UP;
2485
+ var init_progressBar = __esm({
2486
+ "src/progressBar.ts"() {
2487
+ "use strict";
2488
+ CLEAR = "\r\x1B[2K";
2489
+ UP = "\x1B[1A";
2490
+ }
2491
+ });
2492
+
2493
+ // src/dataHomeBoot.ts
2494
+ init_dataHome();
2495
+ import { mkdirSync } from "fs";
2496
+ var DATA_ROOT = resolveDataRoot();
2497
+ process.env.POLYMATH_DATA_DIR = DATA_ROOT;
2498
+ try {
2499
+ mkdirSync(DATA_ROOT, { recursive: true });
2500
+ } catch {
2501
+ }
2502
+
2032
2503
  // src/cli.ts
2033
- import { promises as fs30, existsSync as existsSync6 } from "fs";
2034
- import path35 from "path";
2504
+ import { promises as fs31, existsSync as existsSync7 } from "fs";
2505
+ import path37 from "path";
2035
2506
  import { fileURLToPath as fileURLToPath5 } from "url";
2036
2507
  import { spawn as spawn9 } from "child_process";
2037
2508
 
@@ -2947,8 +3418,8 @@ async function analyze(opts = {}) {
2947
3418
  // src/grade/grade.ts
2948
3419
  init_agent();
2949
3420
  import { promises as fs4 } from "fs";
2950
- import os3 from "os";
2951
- import path3 from "path";
3421
+ import os4 from "os";
3422
+ import path4 from "path";
2952
3423
 
2953
3424
  // ../../lib/calibration/fingerprint.ts
2954
3425
  import { createHash } from "node:crypto";
@@ -3321,7 +3792,7 @@ function validate(raw) {
3321
3792
  async function gradeSession(rec, opts = {}) {
3322
3793
  const mat = await materializeSession(rec.file);
3323
3794
  if (!mat || mat.humanChars < 200) return null;
3324
- const sandbox = await fs4.mkdtemp(path3.join(os3.tmpdir(), "ca-grade-"));
3795
+ const sandbox = await fs4.mkdtemp(path4.join(os4.tmpdir(), "ca-grade-"));
3325
3796
  try {
3326
3797
  const run4 = await invokeAgent(
3327
3798
  {
@@ -3349,7 +3820,7 @@ async function gradeSession(rec, opts = {}) {
3349
3820
  };
3350
3821
  if (opts.outDir) {
3351
3822
  await fs4.mkdir(opts.outDir, { recursive: true });
3352
- await fs4.writeFile(path3.join(opts.outDir, `${rec.sessionId}.json`), JSON.stringify(grade, null, 2));
3823
+ await fs4.writeFile(path4.join(opts.outDir, `${rec.sessionId}.json`), JSON.stringify(grade, null, 2));
3353
3824
  }
3354
3825
  return grade;
3355
3826
  } finally {
@@ -3481,13 +3952,13 @@ function buildShareSections(p) {
3481
3952
  // src/server.ts
3482
3953
  import http from "http";
3483
3954
  import { promises as fs21 } from "fs";
3484
- import path25 from "path";
3955
+ import path26 from "path";
3485
3956
  import { fileURLToPath as fileURLToPath2 } from "url";
3486
3957
 
3487
3958
  // src/auth.ts
3488
3959
  import { createHash as createHash2, randomBytes } from "crypto";
3489
3960
  import { promises as fs5 } from "fs";
3490
- import path4 from "path";
3961
+ import path5 from "path";
3491
3962
 
3492
3963
  // src/central.ts
3493
3964
  var DEFAULT_SUPABASE_URL = "https://zcbonfjgiorrxczyekxl.supabase.co";
@@ -3499,7 +3970,7 @@ function centralConfig() {
3499
3970
  }
3500
3971
 
3501
3972
  // src/auth.ts
3502
- var identityFile = (dataDir) => path4.join(dataDir, "identity.json");
3973
+ var identityFile = (dataDir) => path5.join(dataDir, "identity.json");
3503
3974
  var pending = /* @__PURE__ */ new Map();
3504
3975
  var b64url = (b) => b.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
3505
3976
  function beginLogin(serverUrl) {
@@ -3793,16 +4264,16 @@ async function getCalibration() {
3793
4264
 
3794
4265
  // src/feedback.ts
3795
4266
  import { promises as fs14 } from "fs";
3796
- import path18 from "path";
4267
+ import path19 from "path";
3797
4268
 
3798
4269
  // ../../lib/agents/shared/agent.ts
3799
4270
  import { readFileSync } from "fs";
3800
- import path12 from "path";
4271
+ import path13 from "path";
3801
4272
 
3802
4273
  // ../../lib/agents/shared/cliAdapter.ts
3803
4274
  import { spawn as spawn3 } from "child_process";
3804
4275
  import { promises as fs7 } from "fs";
3805
- import path6 from "path";
4276
+ import path7 from "path";
3806
4277
 
3807
4278
  // ../../lib/agents/shared/adapter.ts
3808
4279
  function extractJson2(text2) {
@@ -3827,7 +4298,7 @@ function extractJson2(text2) {
3827
4298
 
3828
4299
  // ../../lib/agents/shared/toolLog.ts
3829
4300
  import { promises as fs6 } from "fs";
3830
- import path5 from "path";
4301
+ import path6 from "path";
3831
4302
  async function writeToolLog(logPath, d) {
3832
4303
  const counts = {};
3833
4304
  for (const c of d.toolCalls) counts[c.tool] = (counts[c.tool] ?? 0) + 1;
@@ -3843,7 +4314,7 @@ async function writeToolLog(logPath, d) {
3843
4314
  L.push(``);
3844
4315
  L.push(`## Full call sequence (${d.toolCalls.length})`);
3845
4316
  d.toolCalls.forEach((c, i) => L.push(` ${String(i + 1).padStart(3, "0")} ${c.tool.padEnd(5)} ${c.target}`));
3846
- await fs6.mkdir(path5.dirname(logPath), { recursive: true });
4317
+ await fs6.mkdir(path6.dirname(logPath), { recursive: true });
3847
4318
  await fs6.writeFile(logPath, L.join("\n"), "utf-8");
3848
4319
  const json = {
3849
4320
  roots: d.roots,
@@ -3859,11 +4330,11 @@ async function writeToolLog(logPath, d) {
3859
4330
 
3860
4331
  // ../../lib/agents/shared/cliAdapter.ts
3861
4332
  var CLAUDE_BIN = process.env.CLAUDE_BIN || "claude";
3862
- var USAGE_LEDGER = () => path6.join(process.env.POLYMATH_DATA_DIR ? path6.resolve(process.env.POLYMATH_DATA_DIR) : path6.join(process.cwd(), ".data"), "usage.jsonl");
4333
+ var USAGE_LEDGER = () => path7.join(process.env.POLYMATH_DATA_DIR ? path7.resolve(process.env.POLYMATH_DATA_DIR) : path7.join(process.cwd(), ".data"), "usage.jsonl");
3863
4334
  async function appendUsageLedger(line) {
3864
4335
  try {
3865
4336
  const f = USAGE_LEDGER();
3866
- await fs7.mkdir(path6.dirname(f), { recursive: true });
4337
+ await fs7.mkdir(path7.dirname(f), { recursive: true });
3867
4338
  await fs7.appendFile(f, JSON.stringify(line) + "\n", "utf8");
3868
4339
  } catch {
3869
4340
  }
@@ -4007,8 +4478,8 @@ var cliAdapter2 = {
4007
4478
  }
4008
4479
  await appendUsageLedger({
4009
4480
  at: new Date(started).toISOString(),
4010
- script: path6.basename(process.argv[1] ?? "unknown").replace(/\.(mts|ts|mjs|js)$/, ""),
4011
- label: inv.logPath ? path6.basename(inv.logPath).replace(/\.log$/, "") : void 0,
4481
+ script: path7.basename(process.argv[1] ?? "unknown").replace(/\.(mts|ts|mjs|js)$/, ""),
4482
+ label: inv.logPath ? path7.basename(inv.logPath).replace(/\.log$/, "") : void 0,
4012
4483
  // Never blank: an unstamped model made 3 calls ($19.51) unattributable in
4013
4484
  // the 2026-07-06 usage audit. "cli-default" = no --model flag was passed.
4014
4485
  model: inv.model ?? "cli-default",
@@ -4035,15 +4506,15 @@ var cliAdapter2 = {
4035
4506
 
4036
4507
  // ../../lib/agents/shared/codexAdapter.ts
4037
4508
  import { spawn as spawn4 } from "child_process";
4038
- import { existsSync as existsSync3, statSync } from "fs";
4039
- import path8 from "path";
4509
+ import { existsSync as existsSync4, statSync } from "fs";
4510
+ import path9 from "path";
4040
4511
 
4041
4512
  // ../../lib/agents/shared/backends.ts
4042
4513
  import { spawnSync as spawnSync2 } from "child_process";
4043
- import { existsSync as existsSync2 } from "fs";
4044
- import os4 from "os";
4045
- import path7 from "path";
4046
- var HOME2 = os4.homedir();
4514
+ import { existsSync as existsSync3 } from "fs";
4515
+ import os5 from "os";
4516
+ import path8 from "path";
4517
+ var HOME2 = os5.homedir();
4047
4518
  var PROBE_TIMEOUT_MS2 = 15e3;
4048
4519
  function run2(bin, args) {
4049
4520
  try {
@@ -4057,7 +4528,7 @@ function resolveBin2(envOverride, name17, candidates) {
4057
4528
  if (envOverride) return envOverride;
4058
4529
  const found = run2("/bin/sh", ["-c", `command -v ${name17}`]);
4059
4530
  if (found.code === 0 && found.text) return found.text.split("\n")[0];
4060
- for (const c of candidates) if (existsSync2(c)) return c;
4531
+ for (const c of candidates) if (existsSync3(c)) return c;
4061
4532
  return null;
4062
4533
  }
4063
4534
  function codexBin2() {
@@ -4068,14 +4539,14 @@ function codexBin2() {
4068
4539
  }
4069
4540
  function cursorAgentBin() {
4070
4541
  return resolveBin2(process.env.CURSOR_AGENT_BIN, "cursor-agent", [
4071
- path7.join(HOME2, ".local/bin/cursor-agent")
4542
+ path8.join(HOME2, ".local/bin/cursor-agent")
4072
4543
  ]);
4073
4544
  }
4074
4545
  function claudeBin2() {
4075
4546
  return resolveBin2(process.env.CLAUDE_BIN, "claude", [
4076
4547
  "/opt/homebrew/bin/claude",
4077
4548
  "/usr/local/bin/claude",
4078
- path7.join(HOME2, ".local/bin/claude")
4549
+ path8.join(HOME2, ".local/bin/claude")
4079
4550
  ]);
4080
4551
  }
4081
4552
  function probeCli2() {
@@ -4089,7 +4560,7 @@ function probeCli2() {
4089
4560
  const kc = run2("security", ["find-generic-password", "-s", "Claude Code-credentials"]);
4090
4561
  if (kc.code === 0) return { name: "cli", bin, authed: true, detail: "Keychain OAuth credentials" };
4091
4562
  }
4092
- if (existsSync2(path7.join(HOME2, ".claude/.credentials.json")))
4563
+ if (existsSync3(path8.join(HOME2, ".claude/.credentials.json")))
4093
4564
  return { name: "cli", bin, authed: true, detail: "~/.claude/.credentials.json" };
4094
4565
  return { name: "cli", bin, authed: false, detail: "no Claude credentials (run `claude` once to log in)" };
4095
4566
  }
@@ -4154,10 +4625,10 @@ function filesUnderRoots(command, roots, cwd) {
4154
4625
  const hits = [];
4155
4626
  for (const tok of command.split(/[\s'"`;|&()<>]+/)) {
4156
4627
  if (!tok || tok.startsWith("-") || !tok.includes("/") && !tok.includes(".")) continue;
4157
- const abs = path8.isAbsolute(tok) ? tok : path8.resolve(cwd, tok);
4158
- if (!roots.some((r) => abs === r || abs.startsWith(r + path8.sep))) continue;
4628
+ const abs = path9.isAbsolute(tok) ? tok : path9.resolve(cwd, tok);
4629
+ if (!roots.some((r) => abs === r || abs.startsWith(r + path9.sep))) continue;
4159
4630
  try {
4160
- if (existsSync3(abs) && statSync(abs).isFile()) hits.push(abs);
4631
+ if (existsSync4(abs) && statSync(abs).isFile()) hits.push(abs);
4161
4632
  } catch {
4162
4633
  }
4163
4634
  }
@@ -5808,8 +6279,8 @@ function getErrorMap() {
5808
6279
 
5809
6280
  // ../../node_modules/zod/v3/helpers/parseUtil.js
5810
6281
  var makeIssue = (params) => {
5811
- const { data, path: path36, errorMaps, issueData } = params;
5812
- const fullPath = [...path36, ...issueData.path || []];
6282
+ const { data, path: path38, errorMaps, issueData } = params;
6283
+ const fullPath = [...path38, ...issueData.path || []];
5813
6284
  const fullIssue = {
5814
6285
  ...issueData,
5815
6286
  path: fullPath
@@ -5925,11 +6396,11 @@ var errorUtil;
5925
6396
 
5926
6397
  // ../../node_modules/zod/v3/types.js
5927
6398
  var ParseInputLazyPath = class {
5928
- constructor(parent, value, path36, key) {
6399
+ constructor(parent, value, path38, key) {
5929
6400
  this._cachedPath = [];
5930
6401
  this.parent = parent;
5931
6402
  this.data = value;
5932
- this._path = path36;
6403
+ this._path = path38;
5933
6404
  this._key = key;
5934
6405
  }
5935
6406
  get path() {
@@ -18716,39 +19187,39 @@ function createOpenAI(options = {}) {
18716
19187
  });
18717
19188
  const createChatModel = (modelId, settings = {}) => new OpenAIChatLanguageModel(modelId, settings, {
18718
19189
  provider: `${providerName}.chat`,
18719
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19190
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18720
19191
  headers: getHeaders,
18721
19192
  compatibility,
18722
19193
  fetch: options.fetch
18723
19194
  });
18724
19195
  const createCompletionModel = (modelId, settings = {}) => new OpenAICompletionLanguageModel(modelId, settings, {
18725
19196
  provider: `${providerName}.completion`,
18726
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19197
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18727
19198
  headers: getHeaders,
18728
19199
  compatibility,
18729
19200
  fetch: options.fetch
18730
19201
  });
18731
19202
  const createEmbeddingModel = (modelId, settings = {}) => new OpenAIEmbeddingModel(modelId, settings, {
18732
19203
  provider: `${providerName}.embedding`,
18733
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19204
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18734
19205
  headers: getHeaders,
18735
19206
  fetch: options.fetch
18736
19207
  });
18737
19208
  const createImageModel = (modelId, settings = {}) => new OpenAIImageModel(modelId, settings, {
18738
19209
  provider: `${providerName}.image`,
18739
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19210
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18740
19211
  headers: getHeaders,
18741
19212
  fetch: options.fetch
18742
19213
  });
18743
19214
  const createTranscriptionModel = (modelId) => new OpenAITranscriptionModel(modelId, {
18744
19215
  provider: `${providerName}.transcription`,
18745
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19216
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18746
19217
  headers: getHeaders,
18747
19218
  fetch: options.fetch
18748
19219
  });
18749
19220
  const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
18750
19221
  provider: `${providerName}.speech`,
18751
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19222
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18752
19223
  headers: getHeaders,
18753
19224
  fetch: options.fetch
18754
19225
  });
@@ -18769,7 +19240,7 @@ function createOpenAI(options = {}) {
18769
19240
  const createResponsesModel = (modelId) => {
18770
19241
  return new OpenAIResponsesLanguageModel(modelId, {
18771
19242
  provider: `${providerName}.responses`,
18772
- url: ({ path: path36 }) => `${baseURL}${path36}`,
19243
+ url: ({ path: path38 }) => `${baseURL}${path38}`,
18773
19244
  headers: getHeaders,
18774
19245
  fetch: options.fetch
18775
19246
  });
@@ -18802,12 +19273,12 @@ var openai = createOpenAI({
18802
19273
  import { promises as fs8 } from "fs";
18803
19274
  import { execFile } from "child_process";
18804
19275
  import { promisify } from "util";
18805
- import path9 from "path";
19276
+ import path10 from "path";
18806
19277
  var exec = promisify(execFile);
18807
19278
  function safeResolve(root, p) {
18808
- const abs = path9.resolve(root, p);
18809
- const rel = path9.relative(root, abs);
18810
- if (rel.startsWith("..") || path9.isAbsolute(rel)) {
19279
+ const abs = path10.resolve(root, p);
19280
+ const rel = path10.relative(root, abs);
19281
+ if (rel.startsWith("..") || path10.isAbsolute(rel)) {
18811
19282
  throw new Error(`path escapes sandbox: ${p}`);
18812
19283
  }
18813
19284
  return abs;
@@ -18919,16 +19390,16 @@ var apiAdapter = {
18919
19390
  // ../../lib/agents/shared/limitGate.ts
18920
19391
  import { AsyncLocalStorage } from "async_hooks";
18921
19392
  import { appendFile, mkdir } from "fs/promises";
18922
- import path11 from "path";
19393
+ import path12 from "path";
18923
19394
 
18924
19395
  // ../../lib/agents/shared/runStats.ts
18925
- import { appendFileSync, mkdirSync } from "fs";
18926
- import path10 from "path";
19396
+ import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
19397
+ import path11 from "path";
18927
19398
  var G = globalThis;
18928
- var file = () => path10.join(process.cwd(), ".data", "run-stats.jsonl");
19399
+ var file = () => path11.join(process.cwd(), ".data", "run-stats.jsonl");
18929
19400
  function write(ev) {
18930
19401
  try {
18931
- mkdirSync(path10.dirname(file()), { recursive: true });
19402
+ mkdirSync2(path11.dirname(file()), { recursive: true });
18932
19403
  appendFileSync(file(), JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...ev }) + "\n");
18933
19404
  } catch {
18934
19405
  }
@@ -19216,7 +19687,7 @@ function logGate(msg) {
19216
19687
  }
19217
19688
  async function recordRawForm(label, info, error, result) {
19218
19689
  try {
19219
- const dir = process.env.POLYMATH_DATA_DIR ? path11.resolve(process.env.POLYMATH_DATA_DIR) : path11.join(process.cwd(), ".data");
19690
+ const dir = process.env.POLYMATH_DATA_DIR ? path12.resolve(process.env.POLYMATH_DATA_DIR) : path12.join(process.cwd(), ".data");
19220
19691
  await mkdir(dir, { recursive: true });
19221
19692
  const raw = error instanceof Error ? error.message : result && typeof result === "object" && "raw" in result ? String(result.raw) : String(error ?? "");
19222
19693
  const line = JSON.stringify({
@@ -19227,7 +19698,7 @@ async function recordRawForm(label, info, error, result) {
19227
19698
  resetAt: info.resetAt ? new Date(info.resetAt).toISOString() : null,
19228
19699
  sample: raw.slice(0, 600)
19229
19700
  }) + "\n";
19230
- await appendFile(path11.join(dir, "limit-gate.log"), line);
19701
+ await appendFile(path12.join(dir, "limit-gate.log"), line);
19231
19702
  } catch {
19232
19703
  }
19233
19704
  }
@@ -19241,7 +19712,7 @@ var ADAPTERS2 = {
19241
19712
  };
19242
19713
  function preferredEngine() {
19243
19714
  try {
19244
- const p = JSON.parse(readFileSync(path12.join(process.cwd(), ".data", "profile.json"), "utf-8"));
19715
+ const p = JSON.parse(readFileSync(path13.join(process.cwd(), ".data", "profile.json"), "utf-8"));
19245
19716
  const e = p?.preferredEngine;
19246
19717
  return e === "cli" || e === "codex" || e === "cursor" || e === "api" ? e : null;
19247
19718
  } catch {
@@ -19261,7 +19732,7 @@ function getAdapter2(name17) {
19261
19732
  function invokeAgent2(inv, opts = {}) {
19262
19733
  const once = () => getAdapter2(opts.adapter).run(inv);
19263
19734
  if (!resilientActive()) return once();
19264
- const label = inv.logPath ? path12.basename(inv.logPath) : "agent";
19735
+ const label = inv.logPath ? path13.basename(inv.logPath) : "agent";
19265
19736
  return withLimitRetry(once, { label });
19266
19737
  }
19267
19738
 
@@ -19461,22 +19932,22 @@ Return the updated redacted feedback + context + rich evidence package (keep any
19461
19932
 
19462
19933
  // ../../lib/agents/feedback/jobs.ts
19463
19934
  import { promises as fs13 } from "fs";
19464
- import path17 from "path";
19935
+ import path18 from "path";
19465
19936
 
19466
19937
  // ../../lib/agents/feedback/diagnostic.ts
19467
19938
  import { promises as fs10 } from "node:fs";
19468
- import path15 from "node:path";
19939
+ import path16 from "node:path";
19469
19940
 
19470
19941
  // ../../lib/agents/engine/data-dir.ts
19471
- import path13 from "path";
19472
- var ENGINE_DATA = process.env.POLYMATH_DATA_DIR ? path13.resolve(process.env.POLYMATH_DATA_DIR) : path13.join(process.cwd(), ".data");
19942
+ import path14 from "path";
19943
+ var ENGINE_DATA = process.env.POLYMATH_DATA_DIR ? path14.resolve(process.env.POLYMATH_DATA_DIR) : path14.join(process.cwd(), ".data");
19473
19944
 
19474
19945
  // ../../lib/agents/engine/select.ts
19475
19946
  import { promises as fs9 } from "fs";
19476
- import path14 from "path";
19947
+ import path15 from "path";
19477
19948
  var CODING_SOURCES = /* @__PURE__ */ new Set(["claude-code", "codex"]);
19478
19949
  async function loadSignalIndex(signalsDir) {
19479
- const file2 = path14.join(signalsDir, "index.jsonl");
19950
+ const file2 = path15.join(signalsDir, "index.jsonl");
19480
19951
  const recs = [];
19481
19952
  try {
19482
19953
  for (const line of (await fs9.readFile(file2, "utf-8")).split("\n")) {
@@ -20323,8 +20794,8 @@ var growthLens = {
20323
20794
  };
20324
20795
 
20325
20796
  // ../../lib/agents/feedback/diagnostic.ts
20326
- var ENGINE_DIR = path15.join(ENGINE_DATA, "engine-pilot");
20327
- var SIGNALS_DIR = path15.join(ENGINE_DATA, "signals");
20797
+ var ENGINE_DIR = path16.join(ENGINE_DATA, "engine-pilot");
20798
+ var SIGNALS_DIR = path16.join(ENGINE_DATA, "signals");
20328
20799
  var LENS_SPECS = { reasoning: reasoningLens, agency: agencyLens, interpersonal: interpersonalLens, ocean: oceanLens, growth: growthLens };
20329
20800
  var GATE_SYSTEM = `You triage feedback on a personality/reasoning/agency analysis. Decide whether the feedback alleges the analysis is WRONG, MISSED something, or is drawing on the WRONG EVIDENCE \u2014 vs. mere agreement/thanks with no claim. A REDIRECT counts as a miss: "stop leaning on my love life, read this from how I treat other people" alleges the current read is mis-sourced \u2192 investigate: true.
20330
20801
 
@@ -20377,10 +20848,10 @@ async function facetScoreFor(lensDir, docId2, facetKey) {
20377
20848
  const s = typeof f.score === "number" ? f.score : typeof f.bestEstimate === "number" ? f.bestEstimate : null;
20378
20849
  return s == null ? null : { analyzed: true, score: s };
20379
20850
  };
20380
- const af = await fs10.readFile(path15.join(lensDir, `${docId2}.allfacets.json`), "utf8").then(JSON.parse).catch(() => null);
20851
+ const af = await fs10.readFile(path16.join(lensDir, `${docId2}.allfacets.json`), "utf8").then(JSON.parse).catch(() => null);
20381
20852
  const a = tryFacets(af?.result?.facets);
20382
20853
  if (a) return a;
20383
- const pc = await fs10.readFile(path15.join(lensDir, `${docId2}.json`), "utf8").then(JSON.parse).catch(() => null);
20854
+ const pc = await fs10.readFile(path16.join(lensDir, `${docId2}.json`), "utf8").then(JSON.parse).catch(() => null);
20384
20855
  const b = tryFacets(pc?.result?.facets ?? pc?.facets);
20385
20856
  if (b) return b;
20386
20857
  return { analyzed: false, score: null };
@@ -20389,7 +20860,7 @@ async function runFeedbackDiagnostic(input) {
20389
20860
  const rawFeedback = (input.rawFeedback || "").trim().slice(0, 4e3);
20390
20861
  const lens = (input.locus?.lens || "").trim();
20391
20862
  const facetKey = (input.locus?.facetKey || "").trim();
20392
- const lensDir = path15.join(ENGINE_DIR, "grades", lens);
20863
+ const lensDir = path16.join(ENGINE_DIR, "grades", lens);
20393
20864
  const facetSpec = LENS_SPECS[lens]?.facets.find((f) => f.key === facetKey);
20394
20865
  const facetDef = facetSpec ? `${facetSpec.name}: ${String(facetSpec.definition || "").slice(0, 1600)}` : "";
20395
20866
  let usedEvidence = "";
@@ -20430,7 +20901,7 @@ Does this allege the analysis missed / got-wrong / mis-sourced a pattern (a redi
20430
20901
  used += line.length + 1;
20431
20902
  shown++;
20432
20903
  }
20433
- const lensJson = await fs10.readFile(path15.join(ENGINE_DIR, `${lens}-latest.json`), "utf8").then((s) => s.slice(0, 3e3)).catch(() => "");
20904
+ const lensJson = await fs10.readFile(path16.join(ENGINE_DIR, `${lens}-latest.json`), "utf8").then((s) => s.slice(0, 3e3)).catch(() => "");
20434
20905
  let active = 0;
20435
20906
  const queue = [];
20436
20907
  const limit = async (fn) => {
@@ -20504,7 +20975,7 @@ Pick up to ${perChunkPick} docIds whose summaries plausibly show the TRAIT per t
20504
20975
  cand.splice(14);
20505
20976
  const rowByld = new Map(rows.map((r) => [r.docId, r]));
20506
20977
  const verdicts = await Promise.all(cand.map((c) => limit(async () => {
20507
- const full = await fs10.readFile(path15.join(ENGINE_DIR, "docs", `${c.docId}.txt`), "utf8").catch(() => "");
20978
+ const full = await fs10.readFile(path16.join(ENGINE_DIR, "docs", `${c.docId}.txt`), "utf8").catch(() => "");
20508
20979
  if (!full) return null;
20509
20980
  const body = full.length > 45e3 ? `${full.slice(0, 32e3)}
20510
20981
 
@@ -20589,7 +21060,7 @@ Output ONE fenced json block:
20589
21060
 
20590
21061
  // ../../lib/agents/feedback/coding-diagnostic.ts
20591
21062
  import { promises as fs12 } from "node:fs";
20592
- import path16 from "node:path";
21063
+ import path17 from "node:path";
20593
21064
 
20594
21065
  // ../../lib/agents/coding/materialize.ts
20595
21066
  init_injected();
@@ -20729,8 +21200,8 @@ async function materializeSession2(file2) {
20729
21200
  }
20730
21201
 
20731
21202
  // ../../lib/agents/feedback/coding-diagnostic.ts
20732
- var CODING_DIR = path16.join(ENGINE_DATA, "coding");
20733
- var GRADES_DIR = path16.join(CODING_DIR, "grades");
21203
+ var CODING_DIR = path17.join(ENGINE_DATA, "coding");
21204
+ var GRADES_DIR = path17.join(CODING_DIR, "grades");
20734
21205
  var GRADED_KEYS = new Set(GRADED_CRITERIA.map((c) => c.key));
20735
21206
  var scoreOf = (c) => typeof c.score === "number" ? c.score : typeof c.bestEstimate === "number" ? c.bestEstimate : null;
20736
21207
  function criteriaRubric(keys) {
@@ -20743,13 +21214,13 @@ async function loadGrades() {
20743
21214
  const out = [];
20744
21215
  for (const f of files) {
20745
21216
  if (!f.endsWith(".json")) continue;
20746
- const g = await fs12.readFile(path16.join(GRADES_DIR, f), "utf8").then(JSON.parse).catch(() => null);
21217
+ const g = await fs12.readFile(path17.join(GRADES_DIR, f), "utf8").then(JSON.parse).catch(() => null);
20747
21218
  if (g && g.sessionId && Array.isArray(g.criteria)) out.push(g);
20748
21219
  }
20749
21220
  return out;
20750
21221
  }
20751
21222
  async function loadSessMeta() {
20752
- const arr = await fs12.readFile(path16.join(CODING_DIR, "sessions.json"), "utf8").then(JSON.parse).catch(() => []);
21223
+ const arr = await fs12.readFile(path17.join(CODING_DIR, "sessions.json"), "utf8").then(JSON.parse).catch(() => []);
20753
21224
  const m = /* @__PURE__ */ new Map();
20754
21225
  if (Array.isArray(arr)) {
20755
21226
  for (const s of arr) {
@@ -21046,7 +21517,7 @@ function diagnosticJobStatus(key) {
21046
21517
  return REG.get(key) ?? null;
21047
21518
  }
21048
21519
  async function mergeIntoStateFile(key, diagnostic) {
21049
- const FILE = path17.join(process.env.POLYMATH_DATA_DIR || path17.join(process.cwd(), ".data"), "engine-pilot", "feedback-jobs.json");
21520
+ const FILE = path18.join(process.env.POLYMATH_DATA_DIR || path18.join(process.cwd(), ".data"), "engine-pilot", "feedback-jobs.json");
21050
21521
  const s = await fs13.readFile(FILE, "utf8").then(JSON.parse).catch(() => ({ version: 1, jobs: {} }));
21051
21522
  const jobs = s.jobs || {};
21052
21523
  const j = jobs[key];
@@ -21056,12 +21527,12 @@ async function mergeIntoStateFile(key, diagnostic) {
21056
21527
  j.diagRunning = false;
21057
21528
  j.status = "ready";
21058
21529
  j.evidenceFailed = false;
21059
- await fs13.mkdir(path17.dirname(FILE), { recursive: true });
21530
+ await fs13.mkdir(path18.dirname(FILE), { recursive: true });
21060
21531
  await fs13.writeFile(FILE, JSON.stringify({ version: 1, jobs }, null, 2), "utf8");
21061
21532
  }
21062
21533
 
21063
21534
  // src/feedback.ts
21064
- var jobsFile = (dataDir) => path18.join(dataDir, "feedback-jobs.json");
21535
+ var jobsFile = (dataDir) => path19.join(dataDir, "feedback-jobs.json");
21065
21536
  var PAYLOAD_CAP = 6e4;
21066
21537
  async function handlePreview(body) {
21067
21538
  if (!String(body.rawFeedback || "").trim()) return { status: 400, json: { error: "no feedback" } };
@@ -21146,7 +21617,7 @@ async function handleSubmit(dataDir, body) {
21146
21617
  }
21147
21618
  try {
21148
21619
  await fs14.mkdir(dataDir, { recursive: true });
21149
- await fs14.appendFile(path18.join(dataDir, "feedback-submits.log"), JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...row }) + "\n");
21620
+ await fs14.appendFile(path19.join(dataDir, "feedback-submits.log"), JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...row }) + "\n");
21150
21621
  } catch {
21151
21622
  }
21152
21623
  console.error(` [feedback/submit] stored ${String(row.id)} (${sectionKey}) for ${auth.identity.email}`);
@@ -21294,16 +21765,16 @@ async function handleCodingChat(profile, dataDir, body, res) {
21294
21765
  // src/personApi.ts
21295
21766
  init_agent();
21296
21767
  import { promises as fs18 } from "fs";
21297
- import path22 from "path";
21768
+ import path23 from "path";
21298
21769
  import { fileURLToPath } from "url";
21299
21770
 
21300
21771
  // ../../lib/agents/engine/person-profile.ts
21301
21772
  import { promises as fs16 } from "fs";
21302
- import path20 from "path";
21773
+ import path21 from "path";
21303
21774
 
21304
21775
  // ../../lib/agents/engine/corpus.ts
21305
21776
  import { promises as fs15 } from "fs";
21306
- import path19 from "path";
21777
+ import path20 from "path";
21307
21778
  import crypto2 from "crypto";
21308
21779
  var CHAT_SOURCES = /* @__PURE__ */ new Set(["claude", "chatgpt"]);
21309
21780
  var NOTE_FILES = ["obsidian.json", "apple-notes.json", "notion.json"];
@@ -21321,7 +21792,7 @@ var NAME_REDACT = /akshay\s*g?\.?\s*iyer|akshaygiyer|akshay|iyer|flomi|polymath\
21321
21792
  var redactName = (text2) => text2.replace(NAME_REDACT, "[X]");
21322
21793
  async function loadJson(importedDir, file2) {
21323
21794
  try {
21324
- return JSON.parse(await fs15.readFile(path19.join(importedDir, file2), "utf-8"));
21795
+ return JSON.parse(await fs15.readFile(path20.join(importedDir, file2), "utf-8"));
21325
21796
  } catch {
21326
21797
  return [];
21327
21798
  }
@@ -21387,11 +21858,11 @@ ${body}`;
21387
21858
 
21388
21859
  // ../../lib/agents/engine/person-profile.ts
21389
21860
  var ROOT = process.cwd();
21390
- var OUT = path20.join(ENGINE_DATA, "engine-pilot");
21391
- var DOCS = path20.join(OUT, "docs");
21392
- var IMPORTED = path20.join(ENGINE_DATA, "imported");
21393
- var SIGNALS = path20.join(ENGINE_DATA, "signals");
21394
- var GRADES = path20.join(OUT, "grades");
21861
+ var OUT = path21.join(ENGINE_DATA, "engine-pilot");
21862
+ var DOCS = path21.join(OUT, "docs");
21863
+ var IMPORTED = path21.join(ENGINE_DATA, "imported");
21864
+ var SIGNALS = path21.join(ENGINE_DATA, "signals");
21865
+ var GRADES = path21.join(OUT, "grades");
21395
21866
  var DIMF = {
21396
21867
  reasoning: reasoningLens.facets.map((f) => [f.key, f.name]),
21397
21868
  agency: agencyLens.facets.map((f) => [f.key, f.name]),
@@ -21414,7 +21885,7 @@ var PERSON_LENSES = [
21414
21885
  var num2 = (v) => v == null || v === "" ? null : Number(v);
21415
21886
  async function loadIndex() {
21416
21887
  const m = {};
21417
- for (const line of (await fs16.readFile(path20.join(SIGNALS, "index.jsonl"), "utf8").catch(() => "")).split("\n")) {
21888
+ for (const line of (await fs16.readFile(path21.join(SIGNALS, "index.jsonl"), "utf8").catch(() => "")).split("\n")) {
21418
21889
  if (!line.trim()) continue;
21419
21890
  try {
21420
21891
  const r = JSON.parse(line);
@@ -21425,12 +21896,12 @@ async function loadIndex() {
21425
21896
  return m;
21426
21897
  }
21427
21898
  async function loadFacetTakes(lensKey, idx) {
21428
- const dir = path20.join(GRADES, lensKey);
21899
+ const dir = path21.join(GRADES, lensKey);
21429
21900
  const files = (await fs16.readdir(dir).catch(() => [])).filter((f) => f.endsWith(".allfacets.json") && !f.startsWith("_"));
21430
21901
  const byFacet = /* @__PURE__ */ new Map();
21431
21902
  for (const file2 of files) {
21432
21903
  const id = file2.replace(".allfacets.json", "");
21433
- const d = await fs16.readFile(path20.join(dir, file2), "utf8").then(JSON.parse).catch(() => null);
21904
+ const d = await fs16.readFile(path21.join(dir, file2), "utf8").then(JSON.parse).catch(() => null);
21434
21905
  const facets2 = d?.result?.facets;
21435
21906
  if (!Array.isArray(facets2)) continue;
21436
21907
  const meta = idx[id] || { title: "", date: "", source: "" };
@@ -21461,7 +21932,7 @@ function distOf2(takes) {
21461
21932
  return { n, observed, mean: Math.round(mean2 * 100) / 100, median: median2, hist };
21462
21933
  }
21463
21934
  async function readGrowthAgent() {
21464
- const j = await fs16.readFile(path20.join(OUT, "growth-latest.json"), "utf8").then(JSON.parse).catch(() => null);
21935
+ const j = await fs16.readFile(path21.join(OUT, "growth-latest.json"), "utf8").then(JSON.parse).catch(() => null);
21465
21936
  const raw = j?.raw;
21466
21937
  if (!raw || !raw.facets) return null;
21467
21938
  const arr = (v) => Array.isArray(v) ? v : [];
@@ -21506,7 +21977,7 @@ async function compilePerson() {
21506
21977
  const m = (id) => ({ id, title: idx[id]?.title || id, date: idx[id]?.date || "", source: idx[id]?.source || void 0 });
21507
21978
  const out = [];
21508
21979
  for (const [key, lens] of PERSON_LENSES) {
21509
- const person = await fs16.readFile(path20.join(GRADES, key, "_person.json"), "utf8").then(JSON.parse).catch(() => null);
21980
+ const person = await fs16.readFile(path21.join(GRADES, key, "_person.json"), "utf8").then(JSON.parse).catch(() => null);
21510
21981
  const growth = key === "growth" ? await readGrowthAgent() : null;
21511
21982
  const byFacet = await loadFacetTakes(key, idx);
21512
21983
  const pf = (k) => (person?.facets || []).find((x) => String(x.facetKey) === k) || null;
@@ -21553,7 +22024,7 @@ async function compilePerson() {
21553
22024
  async function gradeDetail(docId2, lensKey) {
21554
22025
  const idx = await loadIndex();
21555
22026
  const meta = idx[docId2] || { title: docId2, date: "", source: "" };
21556
- const af = await fs16.readFile(path20.join(GRADES, lensKey, `${docId2}.allfacets.json`), "utf8").then(JSON.parse).catch(() => null);
22027
+ const af = await fs16.readFile(path21.join(GRADES, lensKey, `${docId2}.allfacets.json`), "utf8").then(JSON.parse).catch(() => null);
21557
22028
  const facets2 = af?.result?.facets || [];
21558
22029
  const lensLabel = PERSON_LENSES.find(([k]) => k === lensKey)?.[1].label || lensKey;
21559
22030
  return {
@@ -21595,14 +22066,14 @@ function stripCitations(s) {
21595
22066
  return s.replace(/[\s\S]*?/g, "").replace(/[-]/g, "").replace(/ +\n/g, "\n");
21596
22067
  }
21597
22068
  async function readTranscript(id) {
21598
- const txt = (await fullDocs()).get(id) ?? await fs16.readFile(path20.join(DOCS, `${id}.txt`), "utf8").catch(() => null);
22069
+ const txt = (await fullDocs()).get(id) ?? await fs16.readFile(path21.join(DOCS, `${id}.txt`), "utf8").catch(() => null);
21599
22070
  return txt == null ? null : stripCitations(txt);
21600
22071
  }
21601
22072
 
21602
22073
  // ../../lib/agents/engine/self-image-ref.ts
21603
22074
  import { promises as fs17 } from "fs";
21604
- import path21 from "path";
21605
- var SELF_IMAGE_PATH = path21.join(ENGINE_DATA, "engine-pilot", "self-image.json");
22075
+ import path22 from "path";
22076
+ var SELF_IMAGE_PATH = path22.join(ENGINE_DATA, "engine-pilot", "self-image.json");
21606
22077
  async function loadSelfImage() {
21607
22078
  try {
21608
22079
  return JSON.parse(await fs17.readFile(SELF_IMAGE_PATH, "utf8"));
@@ -21641,8 +22112,8 @@ ${parts.join("\n\n")}`;
21641
22112
  }
21642
22113
 
21643
22114
  // src/personApi.ts
21644
- var MODULE_DIR = path22.dirname(fileURLToPath(import.meta.url));
21645
- var PILOT = (f) => path22.join(ENGINE_DATA, "engine-pilot", f);
22115
+ var MODULE_DIR = path23.dirname(fileURLToPath(import.meta.url));
22116
+ var PILOT = (f) => path23.join(ENGINE_DATA, "engine-pilot", f);
21646
22117
  var memo2 = null;
21647
22118
  var TTL2 = 6e4;
21648
22119
  function deSlop(v) {
@@ -21727,12 +22198,12 @@ async function handlePersonFeedback(method, body) {
21727
22198
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
21728
22199
  if (text2) store.entries[key] = { text: text2, updatedAt };
21729
22200
  else delete store.entries[key];
21730
- await fs18.mkdir(path22.dirname(FEEDBACK_FILE()), { recursive: true });
22201
+ await fs18.mkdir(path23.dirname(FEEDBACK_FILE()), { recursive: true });
21731
22202
  await fs18.writeFile(FEEDBACK_FILE(), JSON.stringify(store, null, 2));
21732
22203
  return { status: 200, json: { ok: true, updatedAt } };
21733
22204
  }
21734
22205
  async function handleLoops() {
21735
- const loops = await fs18.readFile(path22.join(ENGINE_DATA, "loops.json"), "utf8").then((s) => JSON.parse(s)).catch(() => null);
22206
+ const loops = await fs18.readFile(path23.join(ENGINE_DATA, "loops.json"), "utf8").then((s) => JSON.parse(s)).catch(() => null);
21736
22207
  return { status: 200, json: { loops } };
21737
22208
  }
21738
22209
  async function handleGrade(params) {
@@ -21780,9 +22251,9 @@ USE THEIR SELF-IMAGE SILENTLY (a separate read of how THEY see themselves, given
21780
22251
  You can OPEN their actual conversations: each cited one is a file named <id>.txt in the directory you have Read access to. When grounding or quoting would help (or they ask "where did you see that?"), Read the file and quote what they actually said. Output ONLY your reply to them \u2014 no preamble, no meta.`;
21781
22252
  async function readDataMap() {
21782
22253
  const candidates = [
21783
- path22.join(MODULE_DIR, "DATA-MAP.md"),
21784
- path22.resolve(MODULE_DIR, "..", "..", "..", "lib", "agents", "DATA-MAP.md"),
21785
- path22.join(process.cwd(), "lib", "agents", "DATA-MAP.md")
22254
+ path23.join(MODULE_DIR, "DATA-MAP.md"),
22255
+ path23.resolve(MODULE_DIR, "..", "..", "..", "lib", "agents", "DATA-MAP.md"),
22256
+ path23.join(process.cwd(), "lib", "agents", "DATA-MAP.md")
21786
22257
  ];
21787
22258
  for (const p of candidates) {
21788
22259
  try {
@@ -21858,16 +22329,16 @@ init_agent();
21858
22329
 
21859
22330
  // ../../lib/agents/engine/public-report.ts
21860
22331
  import { promises as fs19 } from "fs";
21861
- import path23 from "path";
21862
- var ENGINE = (f) => path23.join(ENGINE_DATA, "engine-pilot", f);
21863
- var CODING = (f) => path23.join(ENGINE_DATA, "coding", f);
22332
+ import path24 from "path";
22333
+ var ENGINE = (f) => path24.join(ENGINE_DATA, "engine-pilot", f);
22334
+ var CODING = (f) => path24.join(ENGINE_DATA, "coding", f);
21864
22335
  var PUBLIC_REPORT_PATH = ENGINE("public-report.json");
21865
22336
  var readJson = async (f, d) => fs19.readFile(f, "utf8").then((t) => JSON.parse(t)).catch(() => d);
21866
22337
  async function loadPublicReport() {
21867
22338
  return readJson(PUBLIC_REPORT_PATH, null);
21868
22339
  }
21869
22340
  async function savePublicReport(r) {
21870
- await fs19.mkdir(path23.dirname(PUBLIC_REPORT_PATH), { recursive: true });
22341
+ await fs19.mkdir(path24.dirname(PUBLIC_REPORT_PATH), { recursive: true });
21871
22342
  await fs19.writeFile(PUBLIC_REPORT_PATH, JSON.stringify(r, null, 2), "utf8");
21872
22343
  }
21873
22344
  var facetScore = (f) => {
@@ -22211,9 +22682,9 @@ async function buildGroundingDigest() {
22211
22682
 
22212
22683
  // ../../lib/agents/engine/external-ref.ts
22213
22684
  import { promises as fs20 } from "fs";
22214
- import path24 from "path";
22215
- var EXTERNAL_PATH = path24.join(ENGINE_DATA, "engine-pilot", "external.json");
22216
- var RESUME_PDF_PATH = path24.join(ENGINE_DATA, "engine-pilot", "resume.pdf");
22685
+ import path25 from "path";
22686
+ var EXTERNAL_PATH = path25.join(ENGINE_DATA, "engine-pilot", "external.json");
22687
+ var RESUME_PDF_PATH = path25.join(ENGINE_DATA, "engine-pilot", "resume.pdf");
22217
22688
  async function loadExternalFacts() {
22218
22689
  try {
22219
22690
  return JSON.parse(await fs20.readFile(EXTERNAL_PATH, "utf8"));
@@ -22344,8 +22815,9 @@ async function handleVisibility(dataDir, method, body) {
22344
22815
  }
22345
22816
 
22346
22817
  // src/server.ts
22347
- var __dirname = path25.dirname(fileURLToPath2(import.meta.url));
22348
- var UI_DIR = path25.join(__dirname, "web");
22818
+ init_dataHome();
22819
+ var __dirname = path26.dirname(fileURLToPath2(import.meta.url));
22820
+ var UI_DIR = path26.join(__dirname, "web");
22349
22821
  var DEFAULT_SHARE_URL = "https://polymathsociety.us/api/coding-share";
22350
22822
  var MIME = {
22351
22823
  ".html": "text/html; charset=utf-8",
@@ -22376,19 +22848,19 @@ async function readBody(req, maxBytes = 8e6) {
22376
22848
  async function serveStatic(res, urlPath) {
22377
22849
  const clean = urlPath.split("?")[0];
22378
22850
  const rel = clean === "/" ? "index.html" : clean.replace(/^\/+/, "");
22379
- const full = path25.normalize(path25.join(UI_DIR, rel));
22851
+ const full = path26.normalize(path26.join(UI_DIR, rel));
22380
22852
  if (!full.startsWith(UI_DIR)) {
22381
22853
  res.writeHead(403).end("forbidden");
22382
22854
  return;
22383
22855
  }
22384
22856
  try {
22385
22857
  const data = await fs21.readFile(full);
22386
- res.writeHead(200, { "content-type": MIME[path25.extname(full)] ?? "application/octet-stream" });
22858
+ res.writeHead(200, { "content-type": MIME[path26.extname(full)] ?? "application/octet-stream" });
22387
22859
  res.end(data);
22388
22860
  } catch {
22389
- if (!path25.extname(full)) {
22861
+ if (!path26.extname(full)) {
22390
22862
  try {
22391
- const idx = await fs21.readFile(path25.join(UI_DIR, "index.html"));
22863
+ const idx = await fs21.readFile(path26.join(UI_DIR, "index.html"));
22392
22864
  res.writeHead(200, { "content-type": MIME[".html"] }).end(idx);
22393
22865
  return;
22394
22866
  } catch {
@@ -22413,7 +22885,7 @@ async function handleShare(res, body, opts, dataDir) {
22413
22885
  res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ ok: false, error: "bad JSON" }));
22414
22886
  return;
22415
22887
  }
22416
- const all = opts.profile?.sections ?? {};
22888
+ const all = opts.shareSections ?? opts.profile?.sections ?? {};
22417
22889
  const wanted = parsed.all ? Object.keys(all) : Array.isArray(parsed.sections) ? parsed.sections : [];
22418
22890
  const sections = {};
22419
22891
  for (const key of wanted) {
@@ -22453,8 +22925,10 @@ async function handleShare(res, body, opts, dataDir) {
22453
22925
  }
22454
22926
  }
22455
22927
  function startServer(opts) {
22928
+ let liveProfile = opts.profile;
22929
+ let liveProgress = {};
22456
22930
  const host = opts.host ?? "127.0.0.1";
22457
- const dataDir = opts.dataDir ?? path25.join(process.cwd(), ".data");
22931
+ const dataDir = opts.dataDir ?? resolveDataRoot();
22458
22932
  let serverUrl = "";
22459
22933
  const json = (res, status, body) => res.writeHead(status, { "content-type": MIME[".json"] }).end(JSON.stringify(body));
22460
22934
  const server = http.createServer(async (req, res) => {
@@ -22462,7 +22936,11 @@ function startServer(opts) {
22462
22936
  const url = req.url ?? "/";
22463
22937
  const route = url.split("?")[0];
22464
22938
  if (req.method === "GET" && (url === "/api/coding" || url === "/profile.json" || url.startsWith("/profile.json?"))) {
22465
- res.writeHead(200, { "content-type": MIME[".json"] }).end(JSON.stringify(opts.profile));
22939
+ res.writeHead(200, { "content-type": MIME[".json"] }).end(JSON.stringify(liveProfile));
22940
+ return;
22941
+ }
22942
+ if (req.method === "GET" && url === "/api/progress") {
22943
+ res.writeHead(200, { "content-type": MIME[".json"] }).end(JSON.stringify(liveProgress));
22466
22944
  return;
22467
22945
  }
22468
22946
  if (req.method === "GET" && route === "/meta.json") {
@@ -22602,7 +23080,7 @@ function startServer(opts) {
22602
23080
  }
22603
23081
  if (route === "/api/coding/chat" && req.method === "POST") {
22604
23082
  const body = JSON.parse(await readBody(req) || "{}");
22605
- await handleCodingChat(opts.profile, dataDir, body, res);
23083
+ await handleCodingChat(liveProfile, dataDir, body, res);
22606
23084
  return;
22607
23085
  }
22608
23086
  if (req.method === "POST" && url === "/share") {
@@ -22638,7 +23116,13 @@ function startServer(opts) {
22638
23116
  resolve2({
22639
23117
  url: serverUrl,
22640
23118
  port,
22641
- close: () => new Promise((r) => server.close(() => r()))
23119
+ close: () => new Promise((r) => server.close(() => r())),
23120
+ setProfile: (p) => {
23121
+ liveProfile = p;
23122
+ },
23123
+ setProgress: (p) => {
23124
+ liveProgress = p;
23125
+ }
22642
23126
  });
22643
23127
  });
22644
23128
  });
@@ -22646,28 +23130,29 @@ function startServer(opts) {
22646
23130
 
22647
23131
  // src/pipeline.ts
22648
23132
  import { spawn as spawn6 } from "child_process";
22649
- import path28 from "path";
22650
- import { existsSync as existsSync4, promises as fs23 } from "fs";
23133
+ import path29 from "path";
23134
+ import { existsSync as existsSync5, promises as fs23 } from "fs";
22651
23135
  import { fileURLToPath as fileURLToPath3 } from "url";
23136
+ init_dataHome();
22652
23137
 
22653
23138
  // ../../lib/agents/shared/runLog.ts
22654
23139
  import { createHash as createHash3 } from "crypto";
22655
23140
  import { promises as fs22 } from "fs";
22656
- import path27 from "path";
23141
+ import path28 from "path";
22657
23142
 
22658
23143
  // ../../lib/agents/shared/paths.ts
22659
- import os5 from "os";
22660
- import path26 from "path";
23144
+ import os6 from "os";
23145
+ import path27 from "path";
22661
23146
  function polymathHome() {
22662
- return process.env.POLYMATH_HOME || path26.join(os5.homedir(), ".polymath-society");
23147
+ return process.env.POLYMATH_HOME || path27.join(os6.homedir(), ".polymath-society");
22663
23148
  }
22664
23149
  function agentDir(agent) {
22665
- return path26.join(polymathHome(), agent);
23150
+ return path27.join(polymathHome(), agent);
22666
23151
  }
22667
23152
 
22668
23153
  // ../../lib/agents/shared/runLog.ts
22669
23154
  function runRefPath() {
22670
- return path27.join(agentDir("report"), "latest.run.json");
23155
+ return path28.join(agentDir("report"), "latest.run.json");
22671
23156
  }
22672
23157
  function sha256Hex(s) {
22673
23158
  return createHash3("sha256").update(s, "utf8").digest("hex");
@@ -22699,7 +23184,7 @@ async function rpc(url, key, fn, args) {
22699
23184
  }
22700
23185
  async function writeRef(refPath, ref) {
22701
23186
  try {
22702
- await fs22.mkdir(path27.dirname(refPath), { recursive: true });
23187
+ await fs22.mkdir(path28.dirname(refPath), { recursive: true });
22703
23188
  await fs22.writeFile(refPath, JSON.stringify(ref, null, 2), "utf-8");
22704
23189
  } catch {
22705
23190
  }
@@ -22750,7 +23235,7 @@ var LiveRunLog = class {
22750
23235
  if (this.finished) return;
22751
23236
  this.finished = true;
22752
23237
  const legacy = this.opts.artifacts === void 0;
22753
- const list = legacy ? [path27.join(agentDir("report"), "latest.json")] : typeof this.opts.artifacts === "function" ? await this.opts.artifacts().catch(() => []) : this.opts.artifacts;
23238
+ const list = legacy ? [path28.join(agentDir("report"), "latest.json")] : typeof this.opts.artifacts === "function" ? await this.opts.artifacts().catch(() => []) : this.opts.artifacts;
22754
23239
  const refPath = this.opts.refPath ?? runRefPath();
22755
23240
  const shas = {};
22756
23241
  const ordered = [];
@@ -22764,7 +23249,7 @@ var LiveRunLog = class {
22764
23249
  }
22765
23250
  if (firstRaw === null) firstRaw = raw;
22766
23251
  const sha = sha256Hex(raw);
22767
- shas[path27.relative(path27.dirname(refPath), p).split(path27.sep).join("/")] = sha;
23252
+ shas[path28.relative(path28.dirname(refPath), p).split(path28.sep).join("/")] = sha;
22768
23253
  ordered.push(sha);
22769
23254
  }
22770
23255
  this.post(async () => {
@@ -22817,10 +23302,10 @@ async function startRunLog(clientInfo = {}, opts = {}) {
22817
23302
  }
22818
23303
 
22819
23304
  // src/pipeline.ts
22820
- var MODULE_DIR2 = path28.dirname(fileURLToPath3(import.meta.url));
23305
+ var MODULE_DIR2 = path29.dirname(fileURLToPath3(import.meta.url));
22821
23306
  function stages(o) {
22822
23307
  const model = o.model ?? "sonnet";
22823
- const conc = String(o.conc ?? 4);
23308
+ const conc = String(o.conc ?? 8);
22824
23309
  const buildArgs = [];
22825
23310
  if (o.codex === false) buildArgs.push("--no-codex");
22826
23311
  if (o.idleGapMin != null) buildArgs.push("--idle", String(o.idleGapMin));
@@ -22829,42 +23314,50 @@ function stages(o) {
22829
23314
  return [
22830
23315
  // ---- deterministic foundation (fast, free) ----
22831
23316
  { script: "coding-build.mts", label: "Scrape + classify your sessions", llm: false, args: buildArgs, out: "coding/sessions.json" },
22832
- { script: "coding-flow.mts", label: "Flow-state slots", llm: false, args: [], out: "coding/flow.json" },
22833
- { script: "coding-gap-dist.mts", label: "Inter-prompt gap distribution", llm: false, args: [], out: "coding/gap-dist.json" },
22834
- { script: "coding-frontier.mts", label: "Frontier tech arsenal", llm: false, args: [], out: "coding/frontier.json" },
22835
- { script: "coding-projects.mts", label: "Projects + contributions", llm: false, args: [], out: "coding/projects.json" },
22836
- // ---- AI grading (the heavy stage resilient + immediate) ----
22837
- { script: "coding-grade.mts", label: "AI-grade every substantial session", llm: true, args: gradeArgs },
23317
+ // ---- deterministic metrics: all read ONLY sessions.json, disjoint outputs ----
23318
+ { script: "coding-flow.mts", label: "Flow-state slots", llm: false, args: [], out: "coding/flow.json", batch: "metrics" },
23319
+ { script: "coding-focus.mts", label: "Focus map + breaks (deterministic)", llm: false, args: ["--skip-llm"], out: "coding/focus.json", batch: "metrics" },
23320
+ { script: "coding-gap-dist.mts", label: "Inter-prompt gap distribution", llm: false, args: [], out: "coding/gap-dist.json", batch: "metrics" },
23321
+ { script: "coding-frontier.mts", label: "Frontier tech arsenal", llm: false, args: [], out: "coding/frontier.json", batch: "metrics" },
23322
+ { script: "coding-projects.mts", label: "Projects + contributions", llm: false, args: [], out: "coding/projects.json", batch: "metrics" },
23323
+ // ---- THE HEAVY BATCH — grading + everything that needs ONLY sessions.json.
23324
+ // Digests, walkthroughs, frontier detail, and delegation never read grades,
23325
+ // so they run WHILE grading runs (grading is the wall-clock giant; these
23326
+ // finish inside its shadow). Budget: grade 8 + digest 3 + walkthrough 3 +
23327
+ // two single-call stages ≈ 16 peak subprocesses (measured-safe envelope).
23328
+ { script: "coding-grade.mts", label: "AI-grade every substantial session", llm: true, args: gradeArgs, batch: "heavy" },
23329
+ { script: "coding-day-digest.mts", label: "Day-by-day digests", llm: true, args: ["--model", model, "--conc", "3"], out: "coding/day-digest.json", batch: "heavy" },
23330
+ { script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, args: ["--model", model, "--conc", "3"], batch: "heavy" },
23331
+ { script: "coding-frontier-detail.mts", label: "What you're doing at the frontier", llm: true, args: [], out: "coding/frontier-detail.json", batch: "heavy" },
23332
+ { script: "coding-delegation.mts", label: "Delegation patterns", llm: true, args: ["--model", model], out: "coding/delegation-rollup.json", batch: "heavy" },
22838
23333
  // ---- corpus aggregate (reads grades; deterministic rollup) ----
22839
23334
  { script: "coding-aggregate.mts", label: "Corpus aggregate \u2014 ability ceiling + flow", llm: false, args: [], out: "coding/aggregate.json" },
22840
- // ---- LLM aggregators (one process each, resume-aware) ----
22841
- { script: "coding-agglomerate.mts", label: "Agglomerated criteria + signature moves", llm: true, args: ["--model", model], out: "coding/agglomerate.json" },
22842
- { script: "coding-expertise.mts", label: "Domain-expertise map", llm: true, args: [], out: "coding/expertise.json" },
22843
- { script: "coding-frontier-detail.mts", label: "What you're doing at the frontier", llm: true, args: [], out: "coding/frontier-detail.json" },
22844
- { script: "coding-delegation.mts", label: "Delegation patterns", llm: true, args: ["--model", model], out: "coding/delegation-rollup.json" },
22845
- { script: "coding-day-digest.mts", label: "Day-by-day digests", llm: true, args: ["--model", model], out: "coding/day-digest.json" },
22846
- { script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, args: ["--model", model, "--conc", conc] },
22847
- // ---- final synthesis (reads the finished aggregates) ----
22848
- { script: "coding-coaching.mts", label: "Coaching + feel-seen copy", llm: true, args: [], out: "coding/coaching.json" },
22849
- { script: "coding-gap.mts", label: "Gap-to-close vs best practice", llm: true, args: [], out: "coding/gap.json" },
23335
+ // ---- grade readers (grades/* is complete once aggregate ran) ----
23336
+ { script: "coding-agglomerate.mts", label: "Agglomerated criteria + signature moves", llm: true, args: ["--model", model], out: "coding/agglomerate.json", batch: "grade-readers" },
23337
+ { script: "coding-expertise.mts", label: "Domain-expertise map", llm: true, args: [], out: "coding/expertise.json", batch: "grade-readers" },
23338
+ // ---- final synthesis (reads the finished aggregates + batch outputs) ----
23339
+ { script: "coding-coaching.mts", label: "Coaching + feel-seen copy", llm: true, args: [], out: "coding/coaching.json", batch: "synthesis" },
23340
+ { script: "coding-gap.mts", label: "Gap-to-close vs best practice", llm: true, args: [], out: "coding/gap.json", batch: "synthesis" },
23341
+ // nutshell stays LAST + alone: it merges INTO aggregate.json while the two
23342
+ // above read the compiled profile racing them re-reads mid-write.
22850
23343
  { script: "coding-nutshell.mts", label: "Big-picture nutshell", llm: true, args: [], out: "coding/aggregate.json" }
22851
23344
  // merges bigPicture INTO aggregate.json
22852
23345
  ];
22853
23346
  }
22854
23347
  function bundledPipelineDir() {
22855
- const d = path28.join(MODULE_DIR2, "pipeline");
22856
- return existsSync4(path28.join(d, "coding-build.js")) ? d : null;
23348
+ const d = path29.join(MODULE_DIR2, "pipeline");
23349
+ return existsSync5(path29.join(d, "coding-build.js")) ? d : null;
22857
23350
  }
22858
23351
  function stageInvocation(repoRoot, scriptRel, args) {
22859
- const scriptAbs = path28.join(repoRoot, "scripts", scriptRel);
22860
- if (existsSync4(scriptAbs)) {
22861
- const local = path28.join(repoRoot, "node_modules", ".bin", "tsx");
22862
- if (existsSync4(local)) return { cmd: local, argv: [scriptAbs, ...args], cwd: repoRoot };
23352
+ const scriptAbs = path29.join(repoRoot, "scripts", scriptRel);
23353
+ if (existsSync5(scriptAbs)) {
23354
+ const local = path29.join(repoRoot, "node_modules", ".bin", "tsx");
23355
+ if (existsSync5(local)) return { cmd: local, argv: [scriptAbs, ...args], cwd: repoRoot };
22863
23356
  return { cmd: "npx", argv: ["tsx", scriptAbs, ...args], cwd: repoRoot };
22864
23357
  }
22865
23358
  const bundled = bundledPipelineDir();
22866
23359
  if (bundled) {
22867
- const js = path28.join(bundled, scriptRel.replace(/\.mts$/, ".js"));
23360
+ const js = path29.join(bundled, scriptRel.replace(/\.mts$/, ".js"));
22868
23361
  return { cmd: process.execPath, argv: [js, ...args], cwd: process.cwd() };
22869
23362
  }
22870
23363
  throw new Error(`pipeline stage ${scriptRel}: neither repo scripts/ nor bundled dist/pipeline/ found`);
@@ -22884,14 +23377,17 @@ function runStage(repoRoot, st, env, onLine) {
22884
23377
  });
22885
23378
  }
22886
23379
  function hasPipeline(repoRoot) {
22887
- return existsSync4(path28.join(repoRoot, "scripts", "coding-build.mts")) || bundledPipelineDir() != null;
23380
+ return existsSync5(path29.join(repoRoot, "scripts", "coding-build.mts")) || bundledPipelineDir() != null;
22888
23381
  }
22889
23382
  function codingDataRoot(repoRoot) {
22890
- return existsSync4(path28.join(repoRoot, "scripts", "coding-build.mts")) ? path28.join(repoRoot, ".data") : path28.join(process.cwd(), ".data");
23383
+ if (!process.env.POLYMATH_DATA_DIR && existsSync5(path29.join(repoRoot, "scripts", "coding-build.mts"))) {
23384
+ return path29.join(repoRoot, ".data");
23385
+ }
23386
+ return resolveDataRoot();
22891
23387
  }
22892
23388
  async function finalCodingArtifacts(codingDir) {
22893
23389
  const files = await fs23.readdir(codingDir).catch(() => []);
22894
- return files.filter((f) => f.endsWith(".json") && f !== "report-run.json" && !f.startsWith(".")).sort().map((f) => path28.join(codingDir, f));
23390
+ return files.filter((f) => f.endsWith(".json") && f !== "report-run.json" && !f.startsWith(".")).sort().map((f) => path29.join(codingDir, f));
22895
23391
  }
22896
23392
  async function runPipeline(o) {
22897
23393
  if (!hasPipeline(o.repoRoot)) {
@@ -22899,11 +23395,18 @@ async function runPipeline(o) {
22899
23395
  "full-pipeline refresh: neither the polymath repo (scripts/coding-*.mts at " + o.repoRoot + ") nor the bundled pipeline (dist/pipeline/) was found \u2014 the package build is broken."
22900
23396
  );
22901
23397
  }
22902
- const env = { ...process.env, POLYMATH_RESILIENT: "1" };
22903
- if (!o.overnight) env.POLYMATH_RESILIENT_NO_WINDOW = "1";
22904
- const list = stages(o);
22905
23398
  const dataRoot = codingDataRoot(o.repoRoot);
22906
- const codingDir = path28.join(dataRoot, "coding");
23399
+ const env = { ...process.env, POLYMATH_RESILIENT: "1", POLYMATH_DATA_DIR: dataRoot };
23400
+ if (!o.overnight) env.POLYMATH_RESILIENT_NO_WINDOW = "1";
23401
+ if (o.overnight && process.platform === "darwin") {
23402
+ try {
23403
+ const { spawn: spawn10 } = await import("node:child_process");
23404
+ spawn10("caffeinate", ["-ims", "-w", String(process.pid)], { detached: true, stdio: "ignore" }).unref();
23405
+ } catch {
23406
+ }
23407
+ }
23408
+ const list = stages(o).filter((st) => !o.deterministicOnly || !st.llm);
23409
+ const codingDir = path29.join(dataRoot, "coding");
22907
23410
  const central = centralConfig();
22908
23411
  const t0 = Date.now();
22909
23412
  const log = await startRunLog(
@@ -22912,26 +23415,35 @@ async function runPipeline(o) {
22912
23415
  supabaseUrl: central.supabaseUrl,
22913
23416
  supabaseAnonKey: central.supabaseAnonKey,
22914
23417
  artifacts: () => finalCodingArtifacts(codingDir),
22915
- refPath: path28.join(codingDir, "report-run.json")
23418
+ refPath: path29.join(codingDir, "report-run.json")
22916
23419
  // next to the served artifacts
22917
23420
  }
22918
23421
  );
22919
23422
  const failed = [];
22920
- for (let i = 0; i < list.length; i++) {
22921
- const st = list[i];
22922
- o.onStage?.(i + 1, list.length, st.label, st.llm);
22923
- const stStart = Date.now();
22924
- let ok = true;
22925
- try {
22926
- await runStage(o.repoRoot, st, env, o.onLine);
22927
- } catch (e) {
22928
- ok = false;
22929
- const error = e.message.slice(0, 200);
22930
- failed.push({ label: st.label, script: st.script, error });
22931
- o.onLine?.(`\u26A0\uFE0F stage FAILED (continuing): ${st.label} \u2014 ${error}`);
22932
- }
22933
- const sha = st.out ? await fileSha(path28.join(dataRoot, st.out)) : null;
22934
- log.event(st.script.replace(/\.mts$/, ""), { ok, ms: Date.now() - stStart, ...sha ? { sha } : {} });
23423
+ const groups = [];
23424
+ for (const st of list) {
23425
+ const g = groups[groups.length - 1];
23426
+ if (g && st.batch && g[0].batch === st.batch) g.push(st);
23427
+ else groups.push([st]);
23428
+ }
23429
+ let stageNo = 0;
23430
+ for (const group of groups) {
23431
+ await Promise.all(group.map(async (st) => {
23432
+ o.onStage?.(++stageNo, list.length, group.length > 1 ? `${st.label} (parallel \xD7${group.length})` : st.label, st.llm);
23433
+ const stStart = Date.now();
23434
+ let ok = true;
23435
+ const tag = group.length > 1 ? `[${st.script.replace(/^coding-|\.mts$/g, "")}] ` : "";
23436
+ try {
23437
+ await runStage(o.repoRoot, st, env, o.onLine && ((line) => o.onLine(`${tag}${line}`)));
23438
+ } catch (e) {
23439
+ ok = false;
23440
+ const error = e.message.slice(0, 200);
23441
+ failed.push({ label: st.label, script: st.script, error });
23442
+ o.onLine?.(`\u26A0\uFE0F stage FAILED (continuing): ${st.label} \u2014 ${error}`);
23443
+ }
23444
+ const sha = st.out ? await fileSha(path29.join(dataRoot, st.out)) : null;
23445
+ log.event(st.script.replace(/\.mts$/, ""), { ok, ms: Date.now() - stStart, ...sha ? { sha } : {} });
23446
+ }));
22935
23447
  }
22936
23448
  log.event("pipeline_completed", { ok: failed.length === 0, failedStages: failed.length, ms: Date.now() - t0 });
22937
23449
  await log.finish();
@@ -22941,10 +23453,11 @@ async function runPipeline(o) {
22941
23453
  // src/chatPipeline.ts
22942
23454
  init_manifest();
22943
23455
  import { spawn as spawn7 } from "child_process";
22944
- import path30 from "path";
22945
- import { existsSync as existsSync5, promises as fs25 } from "fs";
23456
+ import path31 from "path";
23457
+ import { existsSync as existsSync6, promises as fs25 } from "fs";
22946
23458
  import { fileURLToPath as fileURLToPath4 } from "url";
22947
- var MODULE_DIR3 = path30.dirname(fileURLToPath4(import.meta.url));
23459
+ init_dataHome();
23460
+ var MODULE_DIR3 = path31.dirname(fileURLToPath4(import.meta.url));
22948
23461
  var INGESTABLE_KINDS = ["chatgpt", "claude", "notion", "obsidian"];
22949
23462
  function chatEngineStages() {
22950
23463
  return [
@@ -22963,37 +23476,40 @@ function chatEngineStages() {
22963
23476
  ];
22964
23477
  }
22965
23478
  function bundledEngineDir() {
22966
- const d = path30.join(MODULE_DIR3, "engine");
22967
- return existsSync5(path30.join(d, "run-tagger.js")) ? d : null;
23479
+ const d = path31.join(MODULE_DIR3, "engine");
23480
+ return existsSync6(path31.join(d, "run-tagger.js")) ? d : null;
22968
23481
  }
22969
23482
  function stageInvocation2(repoRoot, scriptRel, args) {
22970
- const scriptAbs = path30.join(repoRoot, "scripts", scriptRel);
22971
- if (existsSync5(scriptAbs)) {
22972
- const local = path30.join(repoRoot, "node_modules", ".bin", "tsx");
22973
- if (existsSync5(local)) return { cmd: local, argv: [scriptAbs, ...args], cwd: repoRoot };
23483
+ const scriptAbs = path31.join(repoRoot, "scripts", scriptRel);
23484
+ if (existsSync6(scriptAbs)) {
23485
+ const local = path31.join(repoRoot, "node_modules", ".bin", "tsx");
23486
+ if (existsSync6(local)) return { cmd: local, argv: [scriptAbs, ...args], cwd: repoRoot };
22974
23487
  return { cmd: "npx", argv: ["tsx", scriptAbs, ...args], cwd: repoRoot };
22975
23488
  }
22976
23489
  const bundled = bundledEngineDir();
22977
23490
  if (bundled) {
22978
- const js = path30.join(bundled, scriptRel.replace(/\.mts$/, ".js"));
23491
+ const js = path31.join(bundled, scriptRel.replace(/\.mts$/, ".js"));
22979
23492
  return { cmd: process.execPath, argv: [js, ...args], cwd: process.cwd() };
22980
23493
  }
22981
23494
  throw new Error(`chat-engine stage ${scriptRel}: neither repo scripts/ nor bundled dist/engine/ found`);
22982
23495
  }
22983
23496
  function hasChatPipeline(repoRoot) {
22984
- return existsSync5(path30.join(repoRoot, "scripts", "run-tagger.mts")) || bundledEngineDir() != null;
23497
+ return existsSync6(path31.join(repoRoot, "scripts", "run-tagger.mts")) || bundledEngineDir() != null;
22985
23498
  }
22986
23499
  function chatDataRoot(repoRoot) {
22987
- return existsSync5(path30.join(repoRoot, "scripts", "run-tagger.mts")) ? path30.join(repoRoot, ".data") : path30.join(process.cwd(), ".data");
23500
+ if (!process.env.POLYMATH_DATA_DIR && existsSync6(path31.join(repoRoot, "scripts", "run-tagger.mts"))) {
23501
+ return path31.join(repoRoot, ".data");
23502
+ }
23503
+ return resolveDataRoot();
22988
23504
  }
22989
23505
  async function finalChatArtifacts(dataRoot) {
22990
- const gradesDir = path30.join(dataRoot, "engine-pilot", "grades");
23506
+ const gradesDir = path31.join(dataRoot, "engine-pilot", "grades");
22991
23507
  const out = [];
22992
23508
  for (const lens of (await fs25.readdir(gradesDir).catch(() => [])).sort()) {
22993
- const p = path30.join(gradesDir, lens, "_person.json");
22994
- if (existsSync5(p)) out.push(p);
23509
+ const p = path31.join(gradesDir, lens, "_person.json");
23510
+ if (existsSync6(p)) out.push(p);
22995
23511
  }
22996
- out.push(path30.join(dataRoot, "engine-pilot", "public-report.json"));
23512
+ out.push(path31.join(dataRoot, "engine-pilot", "public-report.json"));
22997
23513
  return out;
22998
23514
  }
22999
23515
  function runStage2(repoRoot, st, env, onLine) {
@@ -23051,12 +23567,12 @@ async function runChatPipeline(o) {
23051
23567
  supabaseUrl: central.supabaseUrl,
23052
23568
  supabaseAnonKey: central.supabaseAnonKey,
23053
23569
  artifacts: () => finalChatArtifacts(dataRoot),
23054
- refPath: path30.join(dataRoot, "report-run.json")
23570
+ refPath: path31.join(dataRoot, "report-run.json")
23055
23571
  }
23056
23572
  );
23057
23573
  let idx = 0;
23058
23574
  for (const f of ingestable) {
23059
- const st = { script: "ingest-export.mts", label: `Ingest ${f.kind} \u2014 ${path30.basename(f.path)}`, llm: false, args: [f.kind, f.path] };
23575
+ const st = { script: "ingest-export.mts", label: `Ingest ${f.kind} \u2014 ${path31.basename(f.path)}`, llm: false, args: [f.kind, f.path] };
23060
23576
  o.onStage?.(++idx, total, st.label, false);
23061
23577
  const stStart = Date.now();
23062
23578
  let ok = true;
@@ -23083,7 +23599,7 @@ async function runChatPipeline(o) {
23083
23599
  failed.push({ label: st.label, script: st.script, error });
23084
23600
  o.onLine?.(`\u26A0\uFE0F stage FAILED (continuing): ${st.label} \u2014 ${error}`);
23085
23601
  }
23086
- const sha = st.out ? await fileSha(path30.join(dataRoot, st.out)) : null;
23602
+ const sha = st.out ? await fileSha(path31.join(dataRoot, st.out)) : null;
23087
23603
  log.event(st.script.replace(/\.mts$/, ""), { ok, ms: Date.now() - stStart, ...sha ? { sha } : {} });
23088
23604
  }
23089
23605
  log.event("pipeline_completed", { ok: failed.length === 0, failedStages: failed.length, ms: Date.now() - t0 });
@@ -23096,14 +23612,14 @@ init_manifest();
23096
23612
 
23097
23613
  // ../../lib/agents/coding/profile.ts
23098
23614
  import { promises as fs27 } from "fs";
23099
- import path32 from "path";
23615
+ import path33 from "path";
23100
23616
 
23101
23617
  // ../../lib/agents/coding/walkthrough.ts
23102
23618
  import { promises as fs26 } from "fs";
23103
- import path31 from "path";
23619
+ import path32 from "path";
23104
23620
  var IDLE_CAP_MS = 12 * 6e4;
23105
23621
  async function readWalkthrough(outDir, sessionId) {
23106
- return fs26.readFile(path31.join(outDir, `${sessionId}.json`), "utf8").then(JSON.parse).catch(() => null);
23622
+ return fs26.readFile(path32.join(outDir, `${sessionId}.json`), "utf8").then(JSON.parse).catch(() => null);
23107
23623
  }
23108
23624
 
23109
23625
  // ../../lib/agents/coding/workstyle.ts
@@ -23435,7 +23951,7 @@ var BEST = DEFAULT_CALIBRATION.codingBenchmarks;
23435
23951
 
23436
23952
  // ../../lib/agents/coding/profile.ts
23437
23953
  var ROOT2 = process.cwd();
23438
- var CODING_DIR2 = process.env.POLYMATH_DATA_DIR ? path32.join(process.env.POLYMATH_DATA_DIR, "coding") : path32.join(ROOT2, ".data", "coding");
23954
+ var CODING_DIR2 = process.env.POLYMATH_DATA_DIR ? path33.join(process.env.POLYMATH_DATA_DIR, "coding") : path33.join(ROOT2, ".data", "coding");
23439
23955
  var UMETA = /* @__PURE__ */ new Map();
23440
23956
  async function userMeta(sessionId, file2) {
23441
23957
  const hit = UMETA.get(sessionId);
@@ -23445,7 +23961,7 @@ async function userMeta(sessionId, file2) {
23445
23961
  UMETA.set(sessionId, meta);
23446
23962
  return meta;
23447
23963
  }
23448
- var GRADES2 = path32.join(CODING_DIR2, "grades");
23964
+ var GRADES2 = path33.join(CODING_DIR2, "grades");
23449
23965
  async function readJson2(p, fallback) {
23450
23966
  return fs27.readFile(p, "utf8").then((s) => JSON.parse(s)).catch(() => fallback);
23451
23967
  }
@@ -23476,11 +23992,11 @@ function distOf3(takes) {
23476
23992
  return { n, observed, mean: mean2, median: median2, hist };
23477
23993
  }
23478
23994
  async function compileCodingProfile() {
23479
- const allRecords = await readJson2(path32.join(CODING_DIR2, "sessions.json"), []);
23995
+ const allRecords = await readJson2(path33.join(CODING_DIR2, "sessions.json"), []);
23480
23996
  const dupIds = new Set(allRecords.filter((s) => s.duplicateOf).map((s) => s.sessionId));
23481
23997
  const sessions = allRecords.filter((s) => !s.duplicateOf);
23482
- const conc = await readJson2(path32.join(CODING_DIR2, "concurrency.json"), null);
23483
- const renames = await readJson2(path32.join(CODING_DIR2, "renames.json"), []);
23998
+ const conc = await readJson2(path33.join(CODING_DIR2, "concurrency.json"), null);
23999
+ const renames = await readJson2(path33.join(CODING_DIR2, "renames.json"), []);
23484
24000
  const byId = new Map(sessions.map((s) => [s.sessionId, s]));
23485
24001
  let gradeFiles = [];
23486
24002
  try {
@@ -23489,7 +24005,7 @@ async function compileCodingProfile() {
23489
24005
  }
23490
24006
  const grades = [];
23491
24007
  for (const f of gradeFiles) {
23492
- const g = await readJson2(path32.join(GRADES2, f), null);
24008
+ const g = await readJson2(path33.join(GRADES2, f), null);
23493
24009
  if (g && byId.has(g.sessionId)) grades.push(g);
23494
24010
  }
23495
24011
  const takesByCriterion = /* @__PURE__ */ new Map();
@@ -23598,13 +24114,13 @@ async function compileCodingProfile() {
23598
24114
  models: Object.fromEntries([...models.entries()].sort((a, b) => b[1] - a[1])),
23599
24115
  lateNightSessions: lateNight
23600
24116
  };
23601
- const agglomeration = await readJson2(path32.join(CODING_DIR2, "agglomerate.json"), null);
23602
- const dayDigest = await readJson2(path32.join(CODING_DIR2, "day-digest.json"), null);
24117
+ const agglomeration = await readJson2(path33.join(CODING_DIR2, "agglomerate.json"), null);
24118
+ const dayDigest = await readJson2(path33.join(CODING_DIR2, "day-digest.json"), null);
23603
24119
  if (dayDigest?.days) dayDigest.days = dayDigest.days.map((day) => {
23604
24120
  const convs = day.conversations.filter((c) => byId.has(c.sessionId));
23605
24121
  return { ...day, conversations: convs, conversationsTouched: convs.length, totalMessages: convs.reduce((a, c) => a + c.messageCount, 0) };
23606
24122
  }).filter((day) => day.conversations.length > 0);
23607
- const dayChart = await readJson2(path32.join(CODING_DIR2, "day-chart.json"), []);
24123
+ const dayChart = await readJson2(path33.join(CODING_DIR2, "day-chart.json"), []);
23608
24124
  const intervalsBy = /* @__PURE__ */ new Map();
23609
24125
  const titleBy = /* @__PURE__ */ new Map();
23610
24126
  const fileById = new Map(sessions.map((s) => [s.sessionId, s.file]));
@@ -23623,7 +24139,7 @@ async function compileCodingProfile() {
23623
24139
  return ms2;
23624
24140
  };
23625
24141
  const walkthroughs = {};
23626
- const wdir = path32.join(CODING_DIR2, "walkthroughs");
24142
+ const wdir = path33.join(CODING_DIR2, "walkthroughs");
23627
24143
  for (const f of await fs27.readdir(wdir).catch(() => [])) {
23628
24144
  if (!f.endsWith(".json")) continue;
23629
24145
  const w = await readWalkthrough(wdir, f.replace(".json", ""));
@@ -23671,17 +24187,17 @@ async function compileCodingProfile() {
23671
24187
  const tCeiling = throughputCeiling(Object.values(wordsByDay));
23672
24188
  const criteriaMean = {};
23673
24189
  for (const c of criteria) criteriaMean[c.key] = { mean: c.dist.mean, n: c.dist.n };
23674
- const flow = await readJson2(path32.join(CODING_DIR2, "flow.json"), null);
23675
- const focus = await readJson2(path32.join(CODING_DIR2, "focus.json"), null);
23676
- const expertiseMap = await readJson2(path32.join(CODING_DIR2, "expertise.json"), null);
23677
- const frontierArsenal = await readJson2(path32.join(CODING_DIR2, "frontier.json"), null);
23678
- const frontierDetail = await readJson2(path32.join(CODING_DIR2, "frontier-detail.json"), null);
23679
- const coaching = await readJson2(path32.join(CODING_DIR2, "coaching.json"), null);
23680
- const gap = await readJson2(path32.join(CODING_DIR2, "gap.json"), null);
23681
- const aggregate = await readJson2(path32.join(CODING_DIR2, "aggregate.json"), null);
23682
- const delegationRollup = await readJson2(path32.join(CODING_DIR2, "delegation-rollup.json"), null);
23683
- const gapDist = await readJson2(path32.join(CODING_DIR2, "gap-dist.json"), null);
23684
- const projects = await readJson2(path32.join(CODING_DIR2, "projects.json"), null);
24190
+ const flow = await readJson2(path33.join(CODING_DIR2, "flow.json"), null);
24191
+ const focus = await readJson2(path33.join(CODING_DIR2, "focus.json"), null);
24192
+ const expertiseMap = await readJson2(path33.join(CODING_DIR2, "expertise.json"), null);
24193
+ const frontierArsenal = await readJson2(path33.join(CODING_DIR2, "frontier.json"), null);
24194
+ const frontierDetail = await readJson2(path33.join(CODING_DIR2, "frontier-detail.json"), null);
24195
+ const coaching = await readJson2(path33.join(CODING_DIR2, "coaching.json"), null);
24196
+ const gap = await readJson2(path33.join(CODING_DIR2, "gap.json"), null);
24197
+ const aggregate = await readJson2(path33.join(CODING_DIR2, "aggregate.json"), null);
24198
+ const delegationRollup = await readJson2(path33.join(CODING_DIR2, "delegation-rollup.json"), null);
24199
+ const gapDist = await readJson2(path33.join(CODING_DIR2, "gap-dist.json"), null);
24200
+ const projects = await readJson2(path33.join(CODING_DIR2, "projects.json"), null);
23685
24201
  const workstyle = computeWorkstyle({
23686
24202
  dayChart,
23687
24203
  tz,
@@ -23801,8 +24317,8 @@ async function compileCodingProfile() {
23801
24317
  }
23802
24318
 
23803
24319
  // src/cli.ts
23804
- var __dirname2 = path35.dirname(fileURLToPath5(import.meta.url));
23805
- var REPO_ROOT = path35.resolve(__dirname2, "..", "..", "..");
24320
+ var __dirname2 = path37.dirname(fileURLToPath5(import.meta.url));
24321
+ var REPO_ROOT = path37.resolve(__dirname2, "..", "..", "..");
23806
24322
  function parseArgs(argv) {
23807
24323
  const p = {
23808
24324
  cmd: "profile",
@@ -23814,6 +24330,7 @@ function parseArgs(argv) {
23814
24330
  spanHourMin: void 0,
23815
24331
  grade: false,
23816
24332
  overnight: false,
24333
+ chatOvernight: void 0,
23817
24334
  regrade: false,
23818
24335
  conc: void 0,
23819
24336
  adapter: void 0,
@@ -23835,6 +24352,7 @@ function parseArgs(argv) {
23835
24352
  else if (a === "--span") p.spanHourMin = Number(argv[++i]);
23836
24353
  else if (a === "--grade") p.grade = true;
23837
24354
  else if (a === "--overnight") p.overnight = true;
24355
+ else if (a === "--chat-overnight") p.chatOvernight = true;
23838
24356
  else if (a === "--regrade") p.regrade = true;
23839
24357
  else if (a === "--conc") p.conc = Number(argv[++i]);
23840
24358
  else if (a === "--adapter") {
@@ -23897,8 +24415,9 @@ CLAUDE_PROJECTS_DIR / CODEX_SESSIONS_DIR / CURSOR_WORKSPACE_DIR /
23897
24415
  CURSOR_PROJECTS_DIR. Setting ANY of these isolates the run: every source you
23898
24416
  did not explicitly point somewhere is skipped, never read from this machine.
23899
24417
  Grading spends YOUR Claude/ChatGPT subscription, never an API key.
23900
- serve --grade writes its analysis to ./.data/coding under the CURRENT directory \u2014
23901
- re-run from the same directory and it resumes instead of re-grading.`;
24418
+ ALL analysis artifacts land under ONE data home, printed at startup:
24419
+ POLYMATH_DATA_DIR if set, else ./.data when it already exists, else
24420
+ ~/.polymath-society/data. Re-run anywhere \u2014 it resumes from that same folder.`;
23902
24421
  var round13 = (x) => Math.round(x * 10) / 10;
23903
24422
  function printProfileSummary(p) {
23904
24423
  const t = p.throughput;
@@ -23966,6 +24485,7 @@ async function main() {
23966
24485
  console.log(USAGE);
23967
24486
  return;
23968
24487
  }
24488
+ console.error(` data home: ${DATA_ROOT}`);
23969
24489
  const wantWizard = process.env.POLYMATH_WIZARD === "1" || process.env.POLYMATH_WIZARD !== "0" && process.argv.length <= 2 && !!process.stdin.isTTY && !!process.stdout.isTTY;
23970
24490
  if (wantWizard) {
23971
24491
  const { runWizard: runWizard2 } = await Promise.resolve().then(() => (init_wizard(), wizard_exports));
@@ -23974,13 +24494,14 @@ async function main() {
23974
24494
  opts.cmd = w.cmd;
23975
24495
  opts.grade = w.grade;
23976
24496
  opts.overnight = w.overnight;
24497
+ opts.chatOvernight = w.chatOvernight;
23977
24498
  opts.open = w.open;
23978
24499
  }
23979
24500
  if (opts.cmd === "grade") {
23980
24501
  const backend = ensureBackend(opts.adapter);
23981
24502
  if (!backend) process.exit(1);
23982
24503
  console.error(`Using ${backend.name === "cli" ? "Claude Code" : "Codex"} (${backend.detail}).`);
23983
- const gradeOut = path35.resolve(opts.out || ".coding-analyzer-grades");
24504
+ const gradeOut = path37.resolve(opts.out || ".coding-analyzer-grades");
23984
24505
  const provider = new LocalGradeProvider({
23985
24506
  adapter: opts.adapter,
23986
24507
  model: opts.model,
@@ -23989,8 +24510,8 @@ async function main() {
23989
24510
  });
23990
24511
  const profile2 = await analyze({ codex: opts.codex, idleGapMin: opts.idleGapMin, tz: opts.tz, spanHourMin: opts.spanHourMin, gradeProvider: provider });
23991
24512
  const detail = provider.detail();
23992
- await fs30.mkdir(gradeOut, { recursive: true });
23993
- await fs30.writeFile(path35.join(gradeOut, "graded.json"), JSON.stringify(detail, null, 2));
24513
+ await fs31.mkdir(gradeOut, { recursive: true });
24514
+ await fs31.writeFile(path37.join(gradeOut, "graded.json"), JSON.stringify(detail, null, 2));
23994
24515
  console.error(`
23995
24516
  Graded ${detail.gradedSessions} sessions \u2192 ${gradeOut}/graded.json`);
23996
24517
  for (const c of detail.criteria) console.error(` ${c.key.padEnd(12)} ${c.mean != null ? `mean ${c.mean} \xB7 ${c.n} sessions` : "no signal"}`);
@@ -23998,6 +24519,20 @@ Graded ${detail.gradedSessions} sessions \u2192 ${gradeOut}/graded.json`);
23998
24519
  return;
23999
24520
  }
24000
24521
  if (opts.cmd === "serve") {
24522
+ let earlyHandle = null;
24523
+ const runMarker = path37.join(DATA_ROOT, "coding", ".grade-run.json");
24524
+ if (!opts.grade) {
24525
+ try {
24526
+ const mk = JSON.parse(await fs31.readFile(runMarker, "utf8"));
24527
+ if (mk && mk.done === false) {
24528
+ console.error(`
24529
+ \u26A0\uFE0F An analysis started ${mk.startedAt ?? "earlier"} never finished (the process died mid-run).`);
24530
+ console.error(" Resuming it now \u2014 already-completed work is never redone.\n");
24531
+ opts.grade = true;
24532
+ }
24533
+ } catch {
24534
+ }
24535
+ }
24001
24536
  if (opts.grade) {
24002
24537
  if (!hasPipeline(REPO_ROOT)) {
24003
24538
  console.error("Can't run the full analysis \u2014 the bundled pipeline (dist/pipeline/) is missing from this install.");
@@ -24009,12 +24544,51 @@ Graded ${detail.gradedSessions} sessions \u2192 ${gradeOut}/graded.json`);
24009
24544
  console.error("Log in to Claude Code (or Codex) first \u2014 grading runs on YOUR subscription, no API key.\n");
24010
24545
  process.exit(1);
24011
24546
  }
24012
- const mode = opts.overnight ? "overnight (defers heavy grading to the 02:00 window)" : "now (resilient: pauses + resumes if you hit the usage wall)";
24547
+ const chatOvernight = opts.chatOvernight ?? opts.overnight;
24548
+ const when = (o) => o ? "at the 02:00 window tonight" : "now";
24013
24549
  console.error(`
24014
- Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"} \u2014 ${mode}.`);
24015
- console.error("This spends your subscription tokens and can take a while. The link appears when it's done.\n");
24550
+ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"} \u2014 coding ${when(opts.overnight)}, chats ${when(chatOvernight)}.`);
24551
+ console.error("This spends your subscription tokens and can take a while. The report link appears right away and fills in as stages finish.\n");
24016
24552
  const t0 = Date.now();
24017
- const { failed } = await runPipeline({
24553
+ try {
24554
+ await fs31.mkdir(path37.dirname(runMarker), { recursive: true });
24555
+ await fs31.writeFile(runMarker, JSON.stringify({ startedAt: (/* @__PURE__ */ new Date()).toISOString(), done: false }));
24556
+ } catch {
24557
+ }
24558
+ earlyHandle = await startServer({
24559
+ profile: await compileCodingProfile(),
24560
+ shareSections: {},
24561
+ version: await pkgVersion(),
24562
+ shareUrl: opts.shareUrl || process.env.PS_SHARE_URL || DEFAULT_SHARE_URL,
24563
+ port: opts.port,
24564
+ dataDir: DATA_ROOT
24565
+ });
24566
+ const progress = {
24567
+ coding: { i: 0, total: 0, label: "starting", done: false, overnight: !!opts.overnight },
24568
+ chat: { i: 0, total: 0, label: "starting", done: false, overnight: chatOvernight }
24569
+ };
24570
+ earlyHandle.setProgress(progress);
24571
+ const refreshProfile = async () => {
24572
+ try {
24573
+ earlyHandle.setProfile(await compileCodingProfile());
24574
+ } catch {
24575
+ }
24576
+ };
24577
+ const liveRefresh = setInterval(() => {
24578
+ void refreshProfile();
24579
+ }, Number(process.env.POLYMATH_LIVE_REFRESH_MS || 45e3));
24580
+ const { makeMultiBar: makeMultiBar2 } = await Promise.resolve().then(() => (init_progressBar(), progressBar_exports));
24581
+ const mbar = makeMultiBar2();
24582
+ const harvestWithin = (lane, line) => {
24583
+ const m = /Σ\s+(\d+)\/(\d+)/.exec(line);
24584
+ if (m && progress[lane] && !progress[lane].done) {
24585
+ progress[lane].within = { done: Number(m[1]), total: Number(m[2]) };
24586
+ earlyHandle.setProgress(progress);
24587
+ }
24588
+ };
24589
+ mbar.setLink(earlyHandle.url);
24590
+ const codingLane = mbar.lane("coding");
24591
+ const codingDone = runPipeline({
24018
24592
  repoRoot: REPO_ROOT,
24019
24593
  model: opts.model,
24020
24594
  conc: opts.conc,
@@ -24022,64 +24596,135 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
24022
24596
  codex: opts.codex,
24023
24597
  idleGapMin: opts.idleGapMin,
24024
24598
  regrade: opts.regrade,
24025
- onStage: (i, total, label, llm) => console.error(`
24026
- \u25B8 [${i}/${total}] ${label}${llm ? " (AI)" : ""}`),
24027
- onLine: (line) => console.error(` ${line}`)
24599
+ onStage: (i, total, label, llm) => {
24600
+ progress.coding = { ...progress.coding, i, total, label, done: false };
24601
+ earlyHandle.setProgress(progress);
24602
+ codingLane.stage(i, total, `${label}${llm ? " (AI)" : ""}`);
24603
+ },
24604
+ onLine: (line) => {
24605
+ harvestWithin("coding", line);
24606
+ codingLane.log(` ${line}`);
24607
+ }
24608
+ }).then(async ({ failed: failed2 }) => {
24609
+ progress.coding = { ...progress.coding ?? { i: 0, total: 0, label: "" }, done: true, failed: failed2.length };
24610
+ earlyHandle.setProgress(progress);
24611
+ await refreshProfile();
24612
+ openBrowser(earlyHandle.url);
24613
+ try {
24614
+ const stored = JSON.parse(await fs31.readFile(path37.join(DATA_ROOT, "notify-email.json"), "utf8"));
24615
+ if (stored.email) {
24616
+ const { requestEmailReminder: requestEmailReminder2 } = await Promise.resolve().then(() => (init_reminderEmail(), reminderEmail_exports));
24617
+ const r = await requestEmailReminder2(stored.email, ["report-ready"], 1);
24618
+ if (r.ok) codingLane.log(` \u2713 We'll also email ${stored.email} that the report is ready.`);
24619
+ }
24620
+ } catch {
24621
+ }
24622
+ return { failed: failed2 };
24028
24623
  });
24624
+ const chatLane = mbar.lane("chat");
24625
+ const chatDone = (async () => {
24626
+ if (!hasChatPipeline(REPO_ROOT)) return null;
24627
+ const manifest = await readManifest(chatDataRoot(REPO_ROOT));
24628
+ if (!manifest || manifest.exports.included.length === 0) {
24629
+ const why = manifest ? "no chat/notes sources are included \u2014 re-run the wizard to add some" : "no exports-manifest.json \u2014 run the wizard (bare `polymath-society`) to approve chat/notes sources";
24630
+ progress.chat = { i: 0, total: 0, label: "", done: true, skipped: why };
24631
+ earlyHandle.setProgress(progress);
24632
+ chatLane.log(` (chat analysis skipped: ${why})`);
24633
+ return null;
24634
+ }
24635
+ chatLane.log(` chat analysis starting over your ${manifest.exports.included.length} approved source(s) \u2014 same engine as the hosted app.`);
24636
+ const chat2 = await runChatPipeline({
24637
+ repoRoot: REPO_ROOT,
24638
+ model: opts.model,
24639
+ overnight: chatOvernight,
24640
+ onStage: (i, total, label, llm) => {
24641
+ progress.chat = { ...progress.chat, i, total, label, done: false };
24642
+ earlyHandle.setProgress(progress);
24643
+ chatLane.stage(i, total, `${label}${llm ? " (AI)" : ""}`);
24644
+ },
24645
+ onLine: (line) => {
24646
+ harvestWithin("chat", line);
24647
+ chatLane.log(` ${line}`);
24648
+ }
24649
+ });
24650
+ progress.chat = { ...progress.chat ?? { i: 0, total: 0, label: "" }, done: true, failed: chat2.failed.length };
24651
+ earlyHandle.setProgress(progress);
24652
+ await refreshProfile();
24653
+ return chat2;
24654
+ })();
24655
+ const [{ failed }, chat] = await Promise.all([codingDone, chatDone]);
24656
+ clearInterval(liveRefresh);
24657
+ mbar.done();
24029
24658
  if (failed.length) {
24030
24659
  console.error(`
24031
- \u26A0\uFE0F ${failed.length} stage(s) FAILED \u2014 the report below is served WITHOUT their output:`);
24660
+ \u26A0\uFE0F ${failed.length} coding stage(s) FAILED \u2014 the report is served WITHOUT their output:`);
24032
24661
  for (const f of failed) console.error(` \u2022 ${f.label} (${f.script}): ${f.error}`);
24033
24662
  console.error(` Fix: run \`serve --grade\` again \u2014 it resumes, redoing ONLY the missing work.
24034
24663
  `);
24035
24664
  } else {
24036
24665
  console.error(`
24037
- \u2713 coding analysis complete in ${Math.round((Date.now() - t0) / 6e4)} min.
24038
- `);
24666
+ \u2713 coding analysis complete in ${Math.round((Date.now() - t0) / 6e4)} min.`);
24039
24667
  }
24040
- if (hasChatPipeline(REPO_ROOT)) {
24041
- const manifest = await readManifest(chatDataRoot(REPO_ROOT));
24042
- if (!manifest || manifest.exports.included.length === 0) {
24043
- console.error(` (chat analysis skipped: ${manifest ? "no chat/notes sources are included \u2014 re-run the wizard to add some" : "no exports-manifest.json \u2014 run the wizard (bare `polymath-society`) to approve chat/notes sources"})
24044
- `);
24045
- } else {
24046
- console.error(`Running the CHAT analysis over your ${manifest.exports.included.length} approved source(s) \u2014 same engine as the hosted app.
24047
- `);
24048
- const c0 = Date.now();
24049
- const chat = await runChatPipeline({
24050
- repoRoot: REPO_ROOT,
24051
- model: opts.model,
24052
- overnight: opts.overnight,
24053
- onStage: (i, total, label, llm) => console.error(`
24054
- \u25B8 [chat ${i}/${total}] ${label}${llm ? " (AI)" : ""}`),
24055
- onLine: (line) => console.error(` ${line}`)
24056
- });
24057
- for (const p of chat.notIngestable) console.error(` \u26A0\uFE0F can't ingest ${p} \u2014 no importer for that source kind`);
24058
- if (chat.failed.length) {
24059
- console.error(`
24668
+ if (chat) {
24669
+ for (const p of chat.notIngestable) console.error(` \u26A0\uFE0F can't ingest ${p} \u2014 no importer for that source kind`);
24670
+ if (chat.failed.length) {
24671
+ console.error(`
24060
24672
  \u26A0\uFE0F ${chat.failed.length} chat stage(s) FAILED \u2014 the chat tab is served WITHOUT their output:`);
24061
- for (const f of chat.failed) console.error(` \u2022 ${f.label} (${f.script}): ${f.error}`);
24062
- console.error(` Fix: run \`serve --grade\` again \u2014 every chat stage resumes from what's on disk.
24673
+ for (const f of chat.failed) console.error(` \u2022 ${f.label} (${f.script}): ${f.error}`);
24674
+ console.error(` Fix: run \`serve --grade\` again \u2014 every chat stage resumes from what's on disk.
24063
24675
  `);
24064
- } else {
24065
- console.error(`
24066
- \u2713 chat analysis complete in ${Math.round((Date.now() - c0) / 6e4)} min.
24676
+ } else {
24677
+ console.error(`\u2713 chat analysis complete in ${Math.round((Date.now() - t0) / 6e4)} min (ran alongside coding).
24067
24678
  `);
24068
- }
24069
24679
  }
24070
24680
  }
24681
+ try {
24682
+ await fs31.writeFile(runMarker, JSON.stringify({ startedAt: (/* @__PURE__ */ new Date()).toISOString(), done: true }));
24683
+ } catch {
24684
+ }
24685
+ }
24686
+ if (!opts.grade && !existsSync7(path37.join(DATA_ROOT, "coding", "sessions.json")) && hasPipeline(REPO_ROOT)) {
24687
+ console.error(`
24688
+ No analysis here yet \u2014 computing the instant deterministic metrics now (free, no AI)\u2026`);
24689
+ const d0 = Date.now();
24690
+ const { makeProgressBar: makeProgressBar2 } = await Promise.resolve().then(() => (init_progressBar(), progressBar_exports));
24691
+ const dbar = makeProgressBar2();
24692
+ const det = await runPipeline({
24693
+ repoRoot: REPO_ROOT,
24694
+ deterministicOnly: true,
24695
+ codex: opts.codex,
24696
+ idleGapMin: opts.idleGapMin,
24697
+ onStage: (i, total, label) => dbar.stage(i, total, label),
24698
+ onLine: (line) => dbar.log(` ${line}`)
24699
+ });
24700
+ dbar.done();
24701
+ if (det.failed.length) {
24702
+ console.error(`
24703
+ \u26A0\uFE0F ${det.failed.length} deterministic stage(s) FAILED \u2014 the report is served without their output:`);
24704
+ for (const f of det.failed) console.error(` \u2022 ${f.label} (${f.script}): ${f.error}`);
24705
+ } else {
24706
+ console.error(`
24707
+ \u2713 deterministic metrics ready in ${Math.round((Date.now() - d0) / 1e3)}s.
24708
+ `);
24709
+ }
24071
24710
  }
24072
24711
  const web = await compileCodingProfile();
24073
24712
  const version = await pkgVersion();
24074
24713
  const shareUrl = opts.shareUrl || process.env.PS_SHARE_URL || DEFAULT_SHARE_URL;
24075
- const dataDir = process.env.POLYMATH_DATA_DIR || path35.join(process.cwd(), ".data");
24714
+ const dataDir = DATA_ROOT;
24076
24715
  const shareSections = buildShareSections(web);
24077
- const handle = await startServer({ profile: { sections: shareSections }, version, shareUrl, port: opts.port, dataDir });
24716
+ let handle;
24717
+ if (earlyHandle) {
24718
+ earlyHandle.setProfile(web);
24719
+ handle = earlyHandle;
24720
+ } else {
24721
+ handle = await startServer({ profile: web, shareSections, version, shareUrl, port: opts.port, dataDir });
24722
+ }
24078
24723
  const w = web;
24079
24724
  const nSessions = w.throughput?.sessions ?? 0;
24080
24725
  const nGraded = w.gradedSessions ?? 0;
24081
24726
  const who = await readIdentity(dataDir);
24082
- if (!existsSync6(path35.join(dataDir, "coding")) && !existsSync6(path35.join(dataDir, "engine-pilot"))) {
24727
+ if (nSessions === 0 && !existsSync7(path37.join(dataDir, "coding")) && !existsSync7(path37.join(dataDir, "engine-pilot"))) {
24083
24728
  console.error(`
24084
24729
  \u26A0\uFE0F ${dataDir} has no analysis artifacts \u2014 this will be an EMPTY report.`);
24085
24730
  console.error(` Run from the directory where you ran the analysis, or set POLYMATH_DATA_DIR.`);
@@ -24091,7 +24736,7 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
24091
24736
  console.error(` ${who ? `signed in as ${who.email}` : "not signed in \u2014 feedback + sharing ask for Google sign-in in the header"}`);
24092
24737
  console.error(` (Ctrl-C to stop)
24093
24738
  `);
24094
- if (opts.open) openBrowser(handle.url);
24739
+ if (opts.open && !earlyHandle) openBrowser(handle.url);
24095
24740
  process.on("SIGINT", async () => {
24096
24741
  await handle.close();
24097
24742
  process.exit(0);
@@ -24105,7 +24750,7 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
24105
24750
  const profile = await analyze({ codex: opts.codex, idleGapMin: opts.idleGapMin, tz: opts.tz, spanHourMin: opts.spanHourMin });
24106
24751
  const payload = opts.cmd === "projects" ? profile.projects : opts.cmd === "flow" ? profile.flow : opts.cmd === "aggregate" ? profile.aggregate : profile;
24107
24752
  if (opts.out) {
24108
- await fs30.writeFile(opts.out, JSON.stringify(payload, null, 2));
24753
+ await fs31.writeFile(opts.out, JSON.stringify(payload, null, 2));
24109
24754
  console.error(`wrote ${opts.cmd} \u2192 ${opts.out}`);
24110
24755
  return;
24111
24756
  }
@@ -24117,7 +24762,7 @@ Running the full analysis on ${backend.name === "cli" ? "Claude Code" : "Codex"}
24117
24762
  }
24118
24763
  async function pkgVersion() {
24119
24764
  try {
24120
- const pkg = JSON.parse(await fs30.readFile(path35.join(__dirname2, "..", "package.json"), "utf-8"));
24765
+ const pkg = JSON.parse(await fs31.readFile(path37.join(__dirname2, "..", "package.json"), "utf-8"));
24121
24766
  return String(pkg.version || "0.0.0");
24122
24767
  } catch {
24123
24768
  return "0.0.0";