polymath-society 0.2.22 → 0.2.24

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
@@ -22857,6 +22857,7 @@ function apportionHeavy(budget) {
22857
22857
  function stages(o) {
22858
22858
  const model = o.model ?? "sonnet";
22859
22859
  const gradeModel = o.model ?? "haiku";
22860
+ const narrativeModel = o.model ?? "haiku";
22860
22861
  const budget = machineBudget();
22861
22862
  const h = apportionHeavy(budget);
22862
22863
  const conc = String(o.conc ?? h.grade);
@@ -22882,8 +22883,8 @@ function stages(o) {
22882
22883
  // finish inside its shadow). Budget: grade 8 + digest 3 + walkthrough 3 +
22883
22884
  // two single-call stages ≈ 16 peak subprocesses (measured-safe envelope).
22884
22885
  { script: "coding-grade.mts", label: "AI-grade every substantial session", llm: true, args: gradeArgs, batch: "heavy" },
22885
- { script: "coding-day-digest.mts", label: "Day-by-day digests", llm: true, args: ["--model", model, "--conc", String(h.digest)], out: "coding/day-digest.json", batch: "heavy" },
22886
- { script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, args: ["--model", model, "--conc", String(h.walkthrough)], batch: "heavy" },
22886
+ { script: "coding-day-digest.mts", label: "Day-by-day digests", llm: true, args: ["--model", narrativeModel, "--conc", String(h.digest)], out: "coding/day-digest.json", batch: "heavy" },
22887
+ { script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, args: ["--model", narrativeModel, "--conc", String(h.walkthrough)], batch: "heavy" },
22887
22888
  { script: "coding-frontier-detail.mts", label: "What you're doing at the frontier", llm: true, args: [], out: "coding/frontier-detail.json", batch: "heavy" },
22888
22889
  { script: "coding-delegation.mts", label: "Delegation patterns", llm: true, args: ["--model", model, "--conc", String(h.delegation)], out: "coding/delegation-rollup.json", batch: "heavy" },
22889
22890
  // The FULL focus stage: flow-rule judgment + the paste-into-CLAUDE.md block.
@@ -22901,7 +22902,13 @@ function stages(o) {
22901
22902
  // (one call per run; the axis verdicts are the most judgment-heavy output).
22902
22903
  // An explicit user model choice still wins.
22903
22904
  { script: "coding-agglomerate.mts", label: "Agglomerated criteria + verdicts + signature moves", llm: true, args: o.model ? ["--model", o.model] : [], out: "coding/agglomerate.json", batch: "grade-readers" },
22904
- { script: "coding-expertise.mts", label: "Domain-expertise map", llm: true, args: ["--conc", String(Math.max(2, budget - 1))], out: "coding/expertise.json", batch: "grade-readers" },
22905
+ // Domain-expertise map DISABLED (owner call 2026-07-22): the single most
22906
+ // expensive stage of the whole run ($13.88 of $49.34 measured — 129 sonnet
22907
+ // calls) and for most users the hiring-bar filter correctly returns ZERO
22908
+ // domains — $14 for an empty section. The machinery (coding-expertise.mts,
22909
+ // expertise-floor.ts) stays for the planned cheap re-enable (haiku scan or
22910
+ // fold into the grade call); the report renders nothing when the artifact
22911
+ // is absent.
22905
22912
  // ---- final synthesis (reads the finished aggregates + batch outputs) ----
22906
22913
  { script: "coding-coaching.mts", label: "Coaching + feel-seen copy", llm: true, args: [], out: "coding/coaching.json", batch: "synthesis" },
22907
22914
  { script: "coding-gap.mts", label: "Gap-to-close vs best practice", llm: true, args: [], out: "coding/gap.json", batch: "synthesis" },
@@ -23414,6 +23421,76 @@ var init_remind = __esm({
23414
23421
  }
23415
23422
  });
23416
23423
 
23424
+ // src/banner.ts
23425
+ var banner_exports = {};
23426
+ __export(banner_exports, {
23427
+ banner: () => banner,
23428
+ printBanner: () => printBanner
23429
+ });
23430
+ function banner(version, out = process.stderr) {
23431
+ const tty = !!out.isTTY && !process.env.NO_COLOR;
23432
+ const cols = out.columns || 80;
23433
+ if (!tty) return `
23434
+ Polymath Society v${version}
23435
+ ${TAGLINE}
23436
+ ${PRIVACY}
23437
+ `;
23438
+ if (cols >= 74) {
23439
+ const lines = KEYBLOCK.map((l) => ` ${GOLD}${l}${RESET}`);
23440
+ 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}`);
23441
+ lines.push("");
23442
+ lines.push(` ${TAGLINE} ${DIM}v${version}${RESET}`);
23443
+ lines.push(` ${DIM}${PRIVACY}${RESET}`);
23444
+ return `
23445
+ ${lines.join("\n")}
23446
+ `;
23447
+ }
23448
+ const right = ["", "POLYMATH", "SOCIETY", "", `${DIM}v${version}${RESET}`, ""];
23449
+ const mark = MONOGRAM.map((l, i) => ` ${GOLD}${l}${RESET} ${right[i]}`.trimEnd());
23450
+ return `
23451
+ ${mark.join("\n")}
23452
+
23453
+ ${TAGLINE}
23454
+ ${DIM}${PRIVACY}${RESET}
23455
+ `;
23456
+ }
23457
+ async function printBanner(out = process.stderr) {
23458
+ if (bannerPrinted) return;
23459
+ bannerPrinted = true;
23460
+ let version = "0.0.0";
23461
+ try {
23462
+ const { promises: fs39 } = await import("fs");
23463
+ const path45 = await import("path");
23464
+ const { fileURLToPath: fileURLToPath6 } = await import("url");
23465
+ const here = path45.dirname(fileURLToPath6(import.meta.url));
23466
+ const pkg = JSON.parse(await fs39.readFile(path45.join(here, "..", "package.json"), "utf-8"));
23467
+ version = String(pkg.version || version);
23468
+ } catch {
23469
+ }
23470
+ out.write(banner(version, out));
23471
+ }
23472
+ var GOLD, DIM, RESET, KEYBLOCK, MONOGRAM, TAGLINE, PRIVACY, bannerPrinted;
23473
+ var init_banner = __esm({
23474
+ "src/banner.ts"() {
23475
+ "use strict";
23476
+ GOLD = "\x1B[38;5;179m";
23477
+ DIM = "\x1B[2m";
23478
+ RESET = "\x1B[0m";
23479
+ KEYBLOCK = [
23480
+ "\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",
23481
+ "\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",
23482
+ "\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",
23483
+ "\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",
23484
+ "\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",
23485
+ "\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"
23486
+ ];
23487
+ 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 "];
23488
+ TAGLINE = "see how you actually work, from your own agent logs";
23489
+ PRIVACY = "private by construction \xB7 everything runs on this machine";
23490
+ bannerPrinted = false;
23491
+ }
23492
+ });
23493
+
23417
23494
  // src/exportScan.ts
23418
23495
  var exportScan_exports = {};
23419
23496
  __export(exportScan_exports, {
@@ -23905,73 +23982,6 @@ var init_reminderState = __esm({
23905
23982
  }
23906
23983
  });
23907
23984
 
23908
- // src/banner.ts
23909
- var banner_exports = {};
23910
- __export(banner_exports, {
23911
- banner: () => banner,
23912
- printBanner: () => printBanner
23913
- });
23914
- function banner(version, out = process.stderr) {
23915
- const tty = !!out.isTTY && !process.env.NO_COLOR;
23916
- const cols = out.columns || 80;
23917
- if (!tty) return `
23918
- Polymath Society v${version}
23919
- ${TAGLINE}
23920
- ${PRIVACY}
23921
- `;
23922
- if (cols >= 74) {
23923
- const lines = KEYBLOCK.map((l) => ` ${GOLD}${l}${RESET}`);
23924
- 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}`);
23925
- lines.push("");
23926
- lines.push(` ${TAGLINE} ${DIM}v${version}${RESET}`);
23927
- lines.push(` ${DIM}${PRIVACY}${RESET}`);
23928
- return `
23929
- ${lines.join("\n")}
23930
- `;
23931
- }
23932
- const right = ["", "POLYMATH", "SOCIETY", "", `${DIM}v${version}${RESET}`, ""];
23933
- const mark = MONOGRAM.map((l, i) => ` ${GOLD}${l}${RESET} ${right[i]}`.trimEnd());
23934
- return `
23935
- ${mark.join("\n")}
23936
-
23937
- ${TAGLINE}
23938
- ${DIM}${PRIVACY}${RESET}
23939
- `;
23940
- }
23941
- async function printBanner(out = process.stderr) {
23942
- let version = "0.0.0";
23943
- try {
23944
- const { promises: fs39 } = await import("fs");
23945
- const path45 = await import("path");
23946
- const { fileURLToPath: fileURLToPath6 } = await import("url");
23947
- const here = path45.dirname(fileURLToPath6(import.meta.url));
23948
- const pkg = JSON.parse(await fs39.readFile(path45.join(here, "..", "package.json"), "utf-8"));
23949
- version = String(pkg.version || version);
23950
- } catch {
23951
- }
23952
- out.write(banner(version, out));
23953
- }
23954
- var GOLD, DIM, RESET, KEYBLOCK, MONOGRAM, TAGLINE, PRIVACY;
23955
- var init_banner = __esm({
23956
- "src/banner.ts"() {
23957
- "use strict";
23958
- GOLD = "\x1B[38;5;179m";
23959
- DIM = "\x1B[2m";
23960
- RESET = "\x1B[0m";
23961
- KEYBLOCK = [
23962
- "\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",
23963
- "\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",
23964
- "\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",
23965
- "\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",
23966
- "\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",
23967
- "\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"
23968
- ];
23969
- 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 "];
23970
- TAGLINE = "see how you actually work, from your own agent logs";
23971
- PRIVACY = "private by construction \xB7 everything runs on this machine";
23972
- }
23973
- });
23974
-
23975
23985
  // src/plan.ts
23976
23986
  var plan_exports = {};
23977
23987
  __export(plan_exports, {
@@ -24039,7 +24049,7 @@ async function detectPlan(home = os9.homedir()) {
24039
24049
  return { tier, label: LABEL[tier], windowUsd: WINDOW_USD[tier] };
24040
24050
  }
24041
24051
  function estimateLaneCosts(input) {
24042
- const codingUsd = Math.max(5, Math.min(60, Math.round(input.agentLogFiles * 17e-4)));
24052
+ const codingUsd = Math.max(5, Math.min(32, Math.round(input.agentLogFiles * 9e-4)));
24043
24053
  const chatUsd = input.chatIncluded ? Math.round(40 + input.exportTotalMB * 0.8) : 0;
24044
24054
  return { codingUsd, chatUsd };
24045
24055
  }
@@ -24074,19 +24084,16 @@ async function remainingFractions(dataRoot, agentLogFiles) {
24074
24084
  return { coding, chat: clamp(1 - chatDone, 0.1, 1) };
24075
24085
  }
24076
24086
  function scheduleLanes(plan, est) {
24077
- const dayShare = plan.tier === "max_5x" || plan.tier === "gpt_team" ? 0.2 : 0.5;
24078
- const fits = (usd) => usd <= plan.windowUsd * dayShare;
24079
- const coding = fits(est.codingUsd) ? "now" : "overnight";
24080
- const chat = est.chatUsd === 0 || fits(est.chatUsd) ? "now" : "overnight";
24087
+ const chat = est.chatUsd === 0 || est.chatUsd <= plan.windowUsd * 0.9 ? "now" : "overnight";
24081
24088
  return {
24082
- coding,
24089
+ coding: "now",
24083
24090
  chat,
24084
24091
  // No plan name here — the schedule header names the plan once, and
24085
24092
  // repeating it doubled the clumsy unknown-plan label (2026-07-22
24086
24093
  // walkthrough). Phrased for the two-column "Proposed schedule" rows:
24087
24094
  // "<lane> <verdict> — <reason>".
24088
- codingReason: coding === "now" ? `runs now \u2014 fits comfortably inside one 5-hour window` : `starts at 2:00 am tonight \u2014 a full grade would eat most of your daytime window`,
24089
- chatReason: est.chatUsd === 0 ? "not planned \u2014 no chat sources included" : chat === "now" ? `runs now, alongside coding \u2014 your chat corpus is small enough` : `starts at 2:00 am tonight \u2014 a large chat corpus; this protects your daytime quota`
24095
+ codingReason: `runs now \u2014 the report link appears right away and fills in as it goes`,
24096
+ chatReason: est.chatUsd === 0 ? "not planned \u2014 no chat sources included" : chat === "overnight" ? `starts at 2:00 am tonight \u2014 a chat corpus this size is an overnight job; it runs while you sleep` : `runs now, alongside coding`
24090
24097
  };
24091
24098
  }
24092
24099
  var UNSUPPORTED_FULL_TIERS, WINDOW_USD, LABEL;
@@ -24487,8 +24494,8 @@ async function runWizard() {
24487
24494
  reminderState = await recordQueued2(dataDir, reminderState, done.kinds);
24488
24495
  say(` ${green("\u2713")} Request an export above and we'll email ${notifyEmail} when it should be ready.`);
24489
24496
  } else {
24490
- const { spawn: spawn10 } = await import("node:child_process");
24491
- spawn10(process.execPath, [process.argv[1], "remind-exports", "--hours", "48", "--kinds", kinds.join(",")], { detached: true, stdio: "ignore" }).unref();
24497
+ const { spawn: spawn11 } = await import("node:child_process");
24498
+ spawn11(process.execPath, [process.argv[1], "remind-exports", "--hours", "48", "--kinds", kinds.join(",")], { detached: true, stdio: "ignore" }).unref();
24492
24499
  say(dim(` (couldn't reach the server \u2014 set a local notification instead)`));
24493
24500
  }
24494
24501
  }
@@ -24982,6 +24989,8 @@ try {
24982
24989
  import { promises as fs38, existsSync as existsSync7 } from "fs";
24983
24990
  import path44 from "path";
24984
24991
  import { fileURLToPath as fileURLToPath5 } from "url";
24992
+ import { spawnSync as spawnSync3 } from "child_process";
24993
+ import readline2 from "readline";
24985
24994
 
24986
24995
  // src/openBrowser.ts
24987
24996
  import { spawn } from "child_process";
@@ -28161,8 +28170,8 @@ PROJECTS`);
28161
28170
  log(``);
28162
28171
  }
28163
28172
  async function askYesNo(q, def = true) {
28164
- const readline2 = await import("node:readline/promises");
28165
- const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
28173
+ const readline3 = await import("node:readline/promises");
28174
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
28166
28175
  try {
28167
28176
  for (; ; ) {
28168
28177
  const a = (await rl.question(`${q} `)).trim().toLowerCase();
@@ -28208,10 +28217,17 @@ async function main() {
28208
28217
  console.log(USAGE);
28209
28218
  return;
28210
28219
  }
28211
- console.error(` data home: ${DATA_ROOT}`);
28212
- await maybePrintUpdateNotice(opts2);
28213
28220
  const wantWizard = process.env.POLYMATH_WIZARD === "1" || process.env.POLYMATH_WIZARD !== "0" && process.argv.length <= 2 && (ASSUME_TTY || !!process.stdin.isTTY && !!process.stdout.isTTY);
28221
+ if (!wantWizard) {
28222
+ console.error(` data home: ${DATA_ROOT}`);
28223
+ await maybePrintUpdateNotice(opts2);
28224
+ }
28214
28225
  if (wantWizard) {
28226
+ const { printBanner: printBanner2 } = await Promise.resolve().then(() => (init_banner(), banner_exports));
28227
+ await printBanner2();
28228
+ console.error("");
28229
+ await maybePrintUpdateNotice(opts2);
28230
+ console.error(` data home: ${DATA_ROOT}`);
28215
28231
  console.error(" Checking for existing analysis\u2026");
28216
28232
  const prior = process.env.POLYMATH_WIZARD === "1" ? null : await readPriorAnalysis();
28217
28233
  if (prior) {
@@ -28657,11 +28673,64 @@ function compareVersions(a, b) {
28657
28673
  }
28658
28674
  return 0;
28659
28675
  }
28676
+ async function promptAutoUpdate(current, latest) {
28677
+ console.error(`
28678
+ New version available: polymath-society ${current} \u2192 ${latest}`);
28679
+ const answer = await new Promise((resolve2) => {
28680
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
28681
+ const timer = setTimeout(() => {
28682
+ rl.close();
28683
+ resolve2("");
28684
+ }, 15e3);
28685
+ rl.question(` Update now? (Recommended) [Y/n] `, (a) => {
28686
+ clearTimeout(timer);
28687
+ rl.close();
28688
+ resolve2(a.trim().toLowerCase());
28689
+ });
28690
+ });
28691
+ if (answer === "n" || answer === "no") {
28692
+ console.error("");
28693
+ return;
28694
+ }
28695
+ console.error(`
28696
+ Updating\u2026
28697
+ `);
28698
+ const install = spawnSync3("npm", ["install", "-g", "polymath-society@latest"], {
28699
+ stdio: "inherit",
28700
+ shell: process.platform === "win32"
28701
+ });
28702
+ if (install.status !== 0) {
28703
+ console.error(`
28704
+ Update didn't go through \u2014 continuing on ${current}.`);
28705
+ console.error(` You can update manually any time: npm install -g polymath-society@latest
28706
+ `);
28707
+ return;
28708
+ }
28709
+ console.error(`
28710
+ Updated to ${latest}. Relaunching\u2026
28711
+ `);
28712
+ const relaunch = spawnSync3("polymath-society", process.argv.slice(2), {
28713
+ stdio: "inherit",
28714
+ shell: process.platform === "win32"
28715
+ });
28716
+ if (relaunch.error) {
28717
+ console.error(` Updated, but couldn't relaunch automatically \u2014 just run polymath-society again.`);
28718
+ process.exit(0);
28719
+ }
28720
+ process.exit(relaunch.status ?? 0);
28721
+ }
28660
28722
  async function maybePrintUpdateNotice(opts2) {
28661
28723
  if (process.env.POLYMATH_NO_UPDATE_CHECK === "1") return;
28662
28724
  if (!process.stderr.isTTY) return;
28663
28725
  if (opts2.json || opts2.out) return;
28664
28726
  if (opts2.cmd !== "profile" && opts2.cmd !== "serve" && opts2.cmd !== "grade") return;
28727
+ const canPrompt = !!process.stdin.isTTY;
28728
+ const notice = async (current, latest) => {
28729
+ if (canPrompt) return promptAutoUpdate(current, latest);
28730
+ console.error(` update available: polymath-society ${current} \u2192 ${latest}`);
28731
+ console.error(` run: npm install -g polymath-society@latest
28732
+ `);
28733
+ };
28665
28734
  const cacheFile = path44.join(DATA_ROOT, "update-check.json");
28666
28735
  const now = Date.now();
28667
28736
  const ttlMs = 12 * 60 * 60 * 1e3;
@@ -28669,11 +28738,7 @@ async function maybePrintUpdateNotice(opts2) {
28669
28738
  const cached2 = JSON.parse(await fs38.readFile(cacheFile, "utf8"));
28670
28739
  if (cached2.checkedAt && now - cached2.checkedAt < ttlMs) {
28671
28740
  const current = await pkgVersion();
28672
- if (cached2.latest && compareVersions(cached2.latest, current) > 0) {
28673
- console.error(` update available: polymath-society ${current} \u2192 ${cached2.latest}`);
28674
- console.error(` run: npm install -g polymath-society@latest
28675
- `);
28676
- }
28741
+ if (cached2.latest && compareVersions(cached2.latest, current) > 0) await notice(current, cached2.latest);
28677
28742
  return;
28678
28743
  }
28679
28744
  } catch {
@@ -28691,11 +28756,7 @@ async function maybePrintUpdateNotice(opts2) {
28691
28756
  if (!latest) return;
28692
28757
  await fs38.writeFile(cacheFile, JSON.stringify({ checkedAt: now, latest }, null, 2), "utf8").catch(() => {
28693
28758
  });
28694
- if (compareVersions(latest, current) > 0) {
28695
- console.error(` update available: polymath-society ${current} \u2192 ${latest}`);
28696
- console.error(` run: npm install -g polymath-society@latest
28697
- `);
28698
- }
28759
+ if (compareVersions(latest, current) > 0) await notice(current, latest);
28699
28760
  } catch {
28700
28761
  } finally {
28701
28762
  clearTimeout(timer);
@@ -15418,6 +15418,7 @@ function tick(ok) {
15418
15418
  if (!p) return;
15419
15419
  if (ok) p.done++;
15420
15420
  else p.failed++;
15421
+ console.log(` \u03A3 ${p.done + p.failed}/${p.total}`);
15421
15422
  if ((p.done + p.failed) % 25 === 0) progress("progress");
15422
15423
  }
15423
15424
  function progress(event = "progress") {
@@ -15327,6 +15327,7 @@ function tick(ok) {
15327
15327
  if (!p) return;
15328
15328
  if (ok) p.done++;
15329
15329
  else p.failed++;
15330
+ console.log(` \u03A3 ${p.done + p.failed}/${p.total}`);
15330
15331
  if ((p.done + p.failed) % 25 === 0) progress("progress");
15331
15332
  }
15332
15333
  function progress(event = "progress") {
@@ -16193,7 +16194,7 @@ ${lines.join("\n")}`);
16193
16194
  }
16194
16195
  return blocks.join("\n\n");
16195
16196
  }
16196
- var SYSTEM = "You read all the messages a person sent on ONE day across several coding-agent conversations, and say \u2014 per conversation \u2014 what they actually DID that day, semantically and specifically. Their words only; ignore mechanical scaffolding. One tight line per conversation (e.g. 'built the full prototype of the apprenticeship app', 'tuned the intelligence rubric with examples from their own read on people', 'set up an EC2 box to run the analysis overnight'). No flattery, no filler.";
16197
+ var SYSTEM = "You read all the messages a person sent on ONE day across several coding-agent conversations, and say \u2014 per conversation \u2014 what they actually DID that day, semantically and specifically. Their words only; ignore mechanical scaffolding. One tight line per conversation. HARD RULES: (1) NAME the concrete feature, bug, decision, or artifact in play ('added retry handling to the export queue', 'cut the sidebar's self-analysis tab but kept the public report') \u2014 never a category ('reviewed features', 'UI changes'). (2) A line must be so specific it could only describe THAT conversation \u2014 if it would fit another day's work, rewrite it. (3) BANNED verbs: 'worked on', 'iterated on', 'refined', 'reviewed', 'continued', 'various'. (4) Even a 1-2 message conversation gets a real read of what the person said or asked \u2014 never a meta-comment like 'repeated earlier context'. No flattery, no filler.";
16197
16198
  async function digestDay(dc, opts = {}) {
16198
16199
  const sandbox = await fs6.mkdtemp(path10.join(os3.tmpdir(), "ps-coding-day-"));
16199
16200
  try {
@@ -16205,7 +16206,7 @@ Output a SINGLE fenced \`\`\`json block:
16205
16206
  \`\`\`json
16206
16207
  { "overall": "", "conversations": [ { "sessionId": "<8-char id from a header>", "summary": "" } ] }
16207
16208
  \`\`\``;
16208
- const run2 = await invokeAgent({ prompt, systemPrompt: SYSTEM, addDir: sandbox, allowedTools: ["Read"], maxTurns: 4, timeoutMs: 3e5, model: opts.model ?? "sonnet" });
16209
+ const run2 = await invokeAgent({ prompt, systemPrompt: SYSTEM, addDir: sandbox, allowedTools: ["Read"], maxTurns: 4, timeoutMs: 3e5, model: opts.model ?? "haiku" });
16209
16210
  const raw = run2.json ?? {};
16210
16211
  const summaries = new Map((raw.conversations ?? []).map((c) => [String(c.sessionId ?? "").slice(0, 8), String(c.summary ?? "")]));
16211
16212
  return {
@@ -16570,7 +16571,7 @@ var GRADES = path11.join(CODING_DIR, "grades");
16570
16571
 
16571
16572
  // ../../scripts/coding-day-digest.mts
16572
16573
  var argv = process.argv.slice(2);
16573
- var model = argv.indexOf("--model") >= 0 ? argv[argv.indexOf("--model") + 1] : "sonnet";
16574
+ var model = argv.indexOf("--model") >= 0 ? argv[argv.indexOf("--model") + 1] : "haiku";
16574
16575
  var REDO = argv.includes("--redo");
16575
16576
  var CONC = Number(argv.indexOf("--conc") >= 0 ? argv[argv.indexOf("--conc") + 1] : "8");
16576
16577
  var CODING = CODING_DIR;
@@ -15327,6 +15327,7 @@ function tick(ok) {
15327
15327
  if (!p) return;
15328
15328
  if (ok) p.done++;
15329
15329
  else p.failed++;
15330
+ console.log(` \u03A3 ${p.done + p.failed}/${p.total}`);
15330
15331
  if ((p.done + p.failed) % 25 === 0) progress("progress");
15331
15332
  }
15332
15333
  function progress(event = "progress") {
@@ -15327,6 +15327,7 @@ function tick(ok) {
15327
15327
  if (!p) return;
15328
15328
  if (ok) p.done++;
15329
15329
  else p.failed++;
15330
+ console.log(` \u03A3 ${p.done + p.failed}/${p.total}`);
15330
15331
  if ((p.done + p.failed) % 25 === 0) progress("progress");
15331
15332
  }
15332
15333
  function progress(event = "progress") {
@@ -15327,6 +15327,7 @@ function tick(ok) {
15327
15327
  if (!p) return;
15328
15328
  if (ok) p.done++;
15329
15329
  else p.failed++;
15330
+ console.log(` \u03A3 ${p.done + p.failed}/${p.total}`);
15330
15331
  if ((p.done + p.failed) % 25 === 0) progress("progress");
15331
15332
  }
15332
15333
  function progress(event = "progress") {
@@ -16237,7 +16238,7 @@ function buildTranscript(msgs, tz) {
16237
16238
  if (text2.length > MAXC) text2 = text2.slice(0, MAXC) + "\n\u2026[transcript truncated]";
16238
16239
  return { text: text2, turnT, userTurns: n };
16239
16240
  }
16240
- var SYSTEM = "You reconstruct, IN ORDER, what a person actually tried in one coding-agent session \u2014 the arc of attempts, realizations, dead-ends, and course-corrections, told as their story. You read the back-and-forth (YOU: = the person, AI: = the model, collapsed). You write specific, concrete phases grounded in what really happened \u2014 never generic ('iterated on the feature'). Capture the THINKING: what they tried, why it didn't satisfy them, what they realized, what they switched to. Honest about messiness and dead-ends.";
16241
+ var SYSTEM = "You reconstruct, IN ORDER, what a person actually tried in one coding-agent session \u2014 the arc of attempts, realizations, dead-ends, and course-corrections, told as their story. You read the back-and-forth (YOU: = the person, AI: = the model, collapsed). Every phase must be so specific that it could ONLY describe this session \u2014 a reader who saw ten sessions could pick this one out from the phase alone. Capture the THINKING: what they tried, why it didn't satisfy them, what they realized, what they switched to. Honest about messiness and dead-ends.";
16241
16242
  function buildPrompt(rec, transcript) {
16242
16243
  return `Below is a coding session, with the person's turns numbered [#N HH:MM].
16243
16244
 
@@ -16245,11 +16246,18 @@ SESSION: "${rec.title}"
16245
16246
 
16246
16247
  ${transcript}
16247
16248
 
16248
- Reconstruct what happened as an ORDERED list of 5\u201314 phases \u2014 the person's actual arc. Each phase:
16249
- - "what": 1\u20132 sentences, concrete and specific, in narrative voice (e.g. "Started by trying to show percentiles instead of raw scores, but nothing anchored what a percentile meant, so pivoted to comparing against big-tech engineers." / "Realized the output was AI slop \u2014 they'd only given a couple of examples, not a real spec, and asked the AI to generate more from that.").
16250
- - "startTurn": the turn number [#N] where this phase begins.
16249
+ Reconstruct what happened as an ORDERED list of phases \u2014 the person's actual arc. ONE phase per distinct move the person made: a new demand, a pushback, a scope change, a realization, a decision, a dead-end. If a turn contains two separate moves, that is two phases. Short sessions may have 5; long many-turn sessions often need 12\u201320. Never merge distinct moves to stay short.
16251
16250
 
16252
- Order phases by startTurn (ascending). Capture realizations and dead-ends, not just actions.
16251
+ Each phase:
16252
+ - "what": 1\u20132 sentences. VOICE: narrative, past tense, the person as the implied subject \u2014 start with the verb of their move ("Opened by demanding\u2026", "Pushed back when\u2026", "Caught a real gap\u2026", "Dropped the integration after\u2026"). NEVER start with "User" or "The user".
16253
+ - HARD RULES for "what":
16254
+ 1. NAME the concrete thing \u2014 the specific feature, file, bug, number, or behavior in play ("the login redirect that looped back to /home", "the CSV export that dropped its header row"), never a category ("an issue", "the feature", "some bugs").
16255
+ 2. CARRY the person's own key words when they reveal intent or feeling \u2014 short quoted fragments ('ship it as is', 'why is this still broken') are the highest-signal content.
16256
+ 3. STATE the why or the turn-of-thought when the transcript shows it: what dissatisfied them, what they realized, what they traded away. An action with no why is only half a phase.
16257
+ 4. BANNED: 'worked on', 'iterated on', 'continued', 'discussed', 'made progress', 'addressed issues', 'various fixes' \u2014 if a sentence would fit any other session, rewrite it until it wouldn't.
16258
+ - "startTurn": the turn number [#N] where this phase begins. Use the number printed in the transcript \u2014 never invent one.
16259
+
16260
+ Order phases by startTurn (ascending). Cover the WHOLE session \u2014 later turns matter as much as early ones. Capture realizations and dead-ends, not just actions.
16253
16261
 
16254
16262
  Output a SINGLE fenced \`\`\`json block:
16255
16263
  \`\`\`json
@@ -16287,7 +16295,9 @@ async function buildWalkthrough(rec, opts) {
16287
16295
  allowedTools: ["Read"],
16288
16296
  maxTurns: 6,
16289
16297
  timeoutMs: 3e5,
16290
- model: opts.model ?? "sonnet"
16298
+ // haiku default (2026-07-22): with the explicit-rules prompt above it
16299
+ // matches sonnet on real sessions at ~1/3 the cost; opts.model overrides.
16300
+ model: opts.model ?? "haiku"
16291
16301
  });
16292
16302
  const phases = validate(run2.json, turnT, times, sessionEnd);
16293
16303
  if (phases.length === 0) return null;
@@ -16679,7 +16689,7 @@ var flag = (n, d) => {
16679
16689
  const i = argv.indexOf(`--${n}`);
16680
16690
  return i >= 0 ? argv[i + 1] : d;
16681
16691
  };
16682
- var MODEL = flag("model", "sonnet");
16692
+ var MODEL = flag("model", "haiku");
16683
16693
  var CONC = Number(flag("conc", "8"));
16684
16694
  var REGEN = argv.includes("--regen");
16685
16695
  var CODING = CODING_DIR;
package/dist/web/app.js CHANGED
@@ -9886,58 +9886,6 @@ function CodingAccountabilityCard({ a }) {
9886
9886
  ] })
9887
9887
  ] });
9888
9888
  }
9889
- function Pill({ label, tone = "faint" }) {
9890
- const c = { good: "bg-accentSoft text-accent", accent: "bg-accentSoft text-accent", faint: "border border-line bg-canvas text-faint" }[tone];
9891
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `rounded px-1.5 py-0.5 text-[11px] font-semibold ${c}`, children: label });
9892
- }
9893
- function ExpertiseMapPanel({ x, coaching }) {
9894
- const tone = (d) => d === "deep" ? "good" : d === "working" ? "accent" : "faint";
9895
- const domainLi = (dm, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("details", { className: "group", children: [
9896
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("summary", { className: "grid cursor-pointer list-none grid-cols-1 items-baseline gap-x-4 gap-y-0.5 text-[13px] [&::-webkit-details-marker]:hidden sm:grid-cols-[1.05fr_1fr]", children: [
9897
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex min-w-0 items-baseline gap-2", children: [
9898
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-accent", children: "\u25B8" }),
9899
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Pill, { label: dm.depth, tone: tone(dm.depth) }),
9900
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold text-ink", children: dm.domain })
9901
- ] }),
9902
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[12px] leading-relaxed text-faint", children: [
9903
- dm.dateRange && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-medium", children: dm.dateRange }),
9904
- dm.whatTheyKnow && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-muted", children: [
9905
- " \u2014 ",
9906
- dm.whatTheyKnow
9907
- ] })
9908
- ] })
9909
- ] }),
9910
- dm.why && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-5 mt-2 text-[12.5px] leading-relaxed text-ink", children: [
9911
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold text-accent", children: "Why \u2014 " }),
9912
- dm.why
9913
- ] }),
9914
- dm.bullets?.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "ml-5 mt-2 space-y-1", children: dm.bullets.map((b, j) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "flex gap-2 text-[12.5px] leading-relaxed text-ink", children: [
9915
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "mt-[7px] h-1 w-1 flex-none rounded-full bg-accentSoft" }),
9916
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: b })
9917
- ] }, j)) }) : dm.evidence?.slice(0, 3).map((e, j) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ml-5 mt-2 border-l-2 border-line pl-2.5", children: [
9918
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-[11px] uppercase tracking-wide text-faint", children: [
9919
- e.kind,
9920
- " \xB7 ",
9921
- e.date
9922
- ] }),
9923
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-[12.5px] text-muted", children: e.knowledge })
9924
- ] }, j))
9925
- ] }) }, i);
9926
- const tech = x.domains.filter((d) => d.technical !== false);
9927
- const built = tech.filter((d) => d.kind !== "directs");
9928
- const directs = tech.filter((d) => d.kind === "directs");
9929
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "mb-5", children: [
9930
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-[12px] text-faint", children: "hard technical / systems domains where you have real, accumulated depth \u2014 not your eval/product work, not smart-generalist problem-solving \xB7 expand any line for the proof" }),
9931
- tech.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
9932
- built.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SubSection, { label: "Built & studied deeply", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-1.5", children: built.map(domainLi) }) }),
9933
- directs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(SubSection, { label: "Directs the AI with authority", children: [
9934
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-2 text-[11.5px] leading-relaxed text-faint", children: "you haven\u2019t necessarily built these here, but you command the AI in them with mechanism-level authority it defers to" }),
9935
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-1.5", children: directs.map(domainLi) })
9936
- ] })
9937
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "mt-2 text-[13px] leading-relaxed text-muted", children: "No hard technical / systems domain cleared the bar \u2014 you\u2019re a very capable generalist builder, but deep specialized domains (vector DBs, distributed systems, kernels, RAG internals\u2026) aren\u2019t where your accumulated expertise lives. That\u2019s the honest picture." }),
9938
- tech.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Coaching, { text: coaching })
9939
- ] });
9940
- }
9941
9889
  var DO_THIS = {
9942
9890
  worktrees: "Replace `claude` with `claude --worktree <name>` \u2014 each session gets its own isolated checkout. That's the whole change; there's no branching workflow to adopt.",
9943
9891
  subagents: "Run `/agents` in Claude Code to save a reusable specialist (e.g. a per-criterion grader with your rubric baked in) to `.claude/agents/` \u2014 or just tell Claude \u201Cuse 4 subagents, one per criterion\u201D to fan out on the spot.",
@@ -11022,10 +10970,6 @@ function CodeAnalysisView({ data: dataProp } = {}) {
11022
10970
  data.frontierArsenal && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FrontierPanel, { f: data.frontierArsenal, detail: data.frontierDetail, coaching: data.coaching?.coaching?.frontier, gap: data.gap, parallelism: data.parallelism }),
11023
10971
  data.delegationRollup && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DelegationPanel, { r: data.delegationRollup, takes: data.criteria?.find((c) => c.key === "delegation")?.takes })
11024
10972
  ] }), ["delegation", "frontier"]),
11025
- data.expertiseMap && fs("coding:expertise", "Expertise", /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
11026
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SectionDivider, { label: "Expertise" }),
11027
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ExpertiseMapPanel, { x: data.expertiseMap, coaching: data.coaching?.coaching?.expertise })
11028
- ] }), ["expertise"]),
11029
10973
  (data.gradedSessions ?? 0) > 0 && (data.agglomeration?.verdicts || data.aggregate?.ability) && fs("coding:qualitatives", "The qualitatives", /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
11030
10974
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SectionDivider, { label: "The qualitatives", note: "Our best read, with the specific chats behind it. Coding logs are a narrow window, so thin evidence reads as a range: your real level can sit above or below what shows up here." }),
11031
10975
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AbilityPanel, { a: data.aggregate?.ability, verdicts: data.agglomeration?.verdicts, criteria: data.criteria, feelSeen: data.coaching?.feelSeen, agencyScore: (data.stackUp ?? []).find((r) => r.label === "Agency")?.score ?? null })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polymath-society",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "Deterministic metrics from your local Claude Code / Codex agent logs — parallelism, flow, throughput, projects. Zero LLM, zero server, on-device.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",