polymath-society 0.2.30 → 0.2.31
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 +301 -234
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -22828,15 +22828,167 @@ var init_publicCodingPageApi = __esm({
|
|
|
22828
22828
|
}
|
|
22829
22829
|
});
|
|
22830
22830
|
|
|
22831
|
+
// src/plan.ts
|
|
22832
|
+
var plan_exports = {};
|
|
22833
|
+
__export(plan_exports, {
|
|
22834
|
+
UNSUPPORTED_FULL_TIERS: () => UNSUPPORTED_FULL_TIERS,
|
|
22835
|
+
detectCodexPlan: () => detectCodexPlan,
|
|
22836
|
+
detectPlan: () => detectPlan,
|
|
22837
|
+
estimateLaneCosts: () => estimateLaneCosts,
|
|
22838
|
+
opusAvailable: () => opusAvailable,
|
|
22839
|
+
remainingFractions: () => remainingFractions,
|
|
22840
|
+
scheduleLanes: () => scheduleLanes,
|
|
22841
|
+
supportsFullAnalysis: () => supportsFullAnalysis,
|
|
22842
|
+
tierFromChatgptPlan: () => tierFromChatgptPlan,
|
|
22843
|
+
tierFromStrings: () => tierFromStrings
|
|
22844
|
+
});
|
|
22845
|
+
import { promises as fs32 } from "fs";
|
|
22846
|
+
import os8 from "os";
|
|
22847
|
+
import path38 from "path";
|
|
22848
|
+
function supportsFullAnalysis(tier) {
|
|
22849
|
+
if (process.env.POLYMATH_FORCE_FULL === "1") return true;
|
|
22850
|
+
return !UNSUPPORTED_FULL_TIERS.includes(tier);
|
|
22851
|
+
}
|
|
22852
|
+
function opusAvailable(tier) {
|
|
22853
|
+
return tier === "max_20x" || tier === "max_5x";
|
|
22854
|
+
}
|
|
22855
|
+
function tierFromStrings(...hints) {
|
|
22856
|
+
for (const h of hints) {
|
|
22857
|
+
if (!h) continue;
|
|
22858
|
+
const s = h.toLowerCase();
|
|
22859
|
+
if (s.includes("max_20x") || s.includes("max20x")) return "max_20x";
|
|
22860
|
+
if (s.includes("max_5x") || s.includes("max5x")) return "max_5x";
|
|
22861
|
+
if (s.includes("pro")) return "pro";
|
|
22862
|
+
if (s.includes("claude_max")) return "max_5x";
|
|
22863
|
+
}
|
|
22864
|
+
return "unknown";
|
|
22865
|
+
}
|
|
22866
|
+
function tierFromChatgptPlan(planType) {
|
|
22867
|
+
const s = (planType ?? "").toLowerCase();
|
|
22868
|
+
if (s === "pro") return "gpt_pro";
|
|
22869
|
+
if (s === "plus") return "gpt_plus";
|
|
22870
|
+
if (s === "team" || s === "enterprise" || s === "business") return "gpt_team";
|
|
22871
|
+
if (s === "free") return "gpt_free";
|
|
22872
|
+
return "unknown";
|
|
22873
|
+
}
|
|
22874
|
+
async function detectCodexPlan(home = os8.homedir()) {
|
|
22875
|
+
try {
|
|
22876
|
+
const raw = JSON.parse(await fs32.readFile(path38.join(home, ".codex", "auth.json"), "utf8"));
|
|
22877
|
+
for (const tok of [raw.tokens?.id_token, raw.tokens?.access_token]) {
|
|
22878
|
+
if (!tok) continue;
|
|
22879
|
+
try {
|
|
22880
|
+
const payload = JSON.parse(Buffer.from(tok.split(".")[1], "base64url").toString("utf8"));
|
|
22881
|
+
const t = tierFromChatgptPlan(payload["https://api.openai.com/auth"]?.chatgpt_plan_type);
|
|
22882
|
+
if (t !== "unknown") return t;
|
|
22883
|
+
} catch {
|
|
22884
|
+
}
|
|
22885
|
+
}
|
|
22886
|
+
} catch {
|
|
22887
|
+
}
|
|
22888
|
+
return "unknown";
|
|
22889
|
+
}
|
|
22890
|
+
async function detectPlan(home = os8.homedir()) {
|
|
22891
|
+
let tier = "unknown";
|
|
22892
|
+
try {
|
|
22893
|
+
const raw = JSON.parse(await fs32.readFile(path38.join(home, ".claude.json"), "utf8"));
|
|
22894
|
+
const oa = raw.oauthAccount ?? {};
|
|
22895
|
+
tier = tierFromStrings(oa.userRateLimitTier, oa.organizationRateLimitTier, oa.organizationType);
|
|
22896
|
+
} catch {
|
|
22897
|
+
}
|
|
22898
|
+
if (tier === "unknown") tier = await detectCodexPlan(home);
|
|
22899
|
+
return { tier, label: LABEL[tier], windowUsd: WINDOW_USD[tier] };
|
|
22900
|
+
}
|
|
22901
|
+
function estimateLaneCosts(input) {
|
|
22902
|
+
const codingUsd = Math.max(5, Math.min(32, Math.round(input.agentLogFiles * 9e-4)));
|
|
22903
|
+
const chatUsd = input.chatIncluded ? Math.round(40 + input.exportTotalMB * 0.8) : 0;
|
|
22904
|
+
return { codingUsd, chatUsd };
|
|
22905
|
+
}
|
|
22906
|
+
async function remainingFractions(dataRoot, agentLogFiles) {
|
|
22907
|
+
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
22908
|
+
let coding = 1;
|
|
22909
|
+
try {
|
|
22910
|
+
const graded = (await fs32.readdir(path38.join(dataRoot, "coding", "grades"))).filter((f) => f.endsWith(".json")).length;
|
|
22911
|
+
const expected = Math.max(1, agentLogFiles * 56e-4);
|
|
22912
|
+
coding = clamp(1 - graded / expected, 0.05, 1);
|
|
22913
|
+
} catch {
|
|
22914
|
+
}
|
|
22915
|
+
let chatDone = 0;
|
|
22916
|
+
try {
|
|
22917
|
+
const st = await fs32.stat(path38.join(dataRoot, "signals", "index.jsonl"));
|
|
22918
|
+
if (st.size > 0) chatDone += 0.3;
|
|
22919
|
+
} catch {
|
|
22920
|
+
}
|
|
22921
|
+
try {
|
|
22922
|
+
const lenses = await fs32.readdir(path38.join(dataRoot, "engine-pilot", "grades"));
|
|
22923
|
+
let withPerson = 0;
|
|
22924
|
+
for (const l of lenses) {
|
|
22925
|
+
try {
|
|
22926
|
+
await fs32.stat(path38.join(dataRoot, "engine-pilot", "grades", l, "_person.json"));
|
|
22927
|
+
withPerson++;
|
|
22928
|
+
} catch {
|
|
22929
|
+
}
|
|
22930
|
+
}
|
|
22931
|
+
chatDone += 0.6 * Math.min(1, withPerson / 5);
|
|
22932
|
+
} catch {
|
|
22933
|
+
}
|
|
22934
|
+
return { coding, chat: clamp(1 - chatDone, 0.1, 1) };
|
|
22935
|
+
}
|
|
22936
|
+
function scheduleLanes(plan, est) {
|
|
22937
|
+
const chat = est.chatUsd === 0 || est.chatUsd <= plan.windowUsd * 0.9 ? "now" : "overnight";
|
|
22938
|
+
return {
|
|
22939
|
+
coding: "now",
|
|
22940
|
+
chat,
|
|
22941
|
+
// No plan name here — the schedule header names the plan once, and
|
|
22942
|
+
// repeating it doubled the clumsy unknown-plan label (2026-07-22
|
|
22943
|
+
// walkthrough). Phrased for the two-column "Proposed schedule" rows:
|
|
22944
|
+
// "<lane> <verdict> — <reason>".
|
|
22945
|
+
codingReason: `runs now \u2014 the report link appears right away and fills in as it goes`,
|
|
22946
|
+
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`
|
|
22947
|
+
};
|
|
22948
|
+
}
|
|
22949
|
+
var UNSUPPORTED_FULL_TIERS, WINDOW_USD, LABEL;
|
|
22950
|
+
var init_plan = __esm({
|
|
22951
|
+
"src/plan.ts"() {
|
|
22952
|
+
"use strict";
|
|
22953
|
+
UNSUPPORTED_FULL_TIERS = ["gpt_plus", "gpt_free"];
|
|
22954
|
+
WINDOW_USD = {
|
|
22955
|
+
max_20x: 300,
|
|
22956
|
+
max_5x: 75,
|
|
22957
|
+
pro: 15,
|
|
22958
|
+
// ChatGPT tiers, mapped to rough Claude-window parity by monthly price
|
|
22959
|
+
// ($200 Pro ≈ Max 20x; $20 Plus ≈ Claude Pro; Team between; free ≈ nothing).
|
|
22960
|
+
gpt_pro: 300,
|
|
22961
|
+
gpt_plus: 20,
|
|
22962
|
+
gpt_team: 50,
|
|
22963
|
+
gpt_free: 5,
|
|
22964
|
+
unknown: 15
|
|
22965
|
+
// conservative: schedule like Pro when we can't tell
|
|
22966
|
+
};
|
|
22967
|
+
LABEL = {
|
|
22968
|
+
max_20x: "Claude Max 20x",
|
|
22969
|
+
max_5x: "Claude Max 5x",
|
|
22970
|
+
pro: "Claude Pro",
|
|
22971
|
+
gpt_pro: "ChatGPT Pro (Codex)",
|
|
22972
|
+
gpt_plus: "ChatGPT Plus (Codex)",
|
|
22973
|
+
gpt_team: "ChatGPT Team (Codex)",
|
|
22974
|
+
gpt_free: "ChatGPT Free (Codex)",
|
|
22975
|
+
unknown: "an undetected plan (scheduling conservatively)"
|
|
22976
|
+
};
|
|
22977
|
+
}
|
|
22978
|
+
});
|
|
22979
|
+
|
|
22831
22980
|
// src/pipeline.ts
|
|
22832
22981
|
var pipeline_exports = {};
|
|
22833
22982
|
__export(pipeline_exports, {
|
|
22983
|
+
TAIL_MODEL_ENV: () => TAIL_MODEL_ENV,
|
|
22984
|
+
applyTailModelEnv: () => applyTailModelEnv,
|
|
22834
22985
|
apportionHeavy: () => apportionHeavy,
|
|
22835
22986
|
codingDataRoot: () => codingDataRoot,
|
|
22836
22987
|
evalUpToDate: () => evalUpToDate,
|
|
22837
22988
|
expectedLlmArtifacts: () => expectedLlmArtifacts,
|
|
22838
22989
|
hasPipeline: () => hasPipeline,
|
|
22839
22990
|
machineBudget: () => machineBudget,
|
|
22991
|
+
resolveTailModel: () => resolveTailModel,
|
|
22840
22992
|
runPipeline: () => runPipeline,
|
|
22841
22993
|
stages: () => stages,
|
|
22842
22994
|
startKeepAwake: () => startKeepAwake,
|
|
@@ -22845,11 +22997,11 @@ __export(pipeline_exports, {
|
|
|
22845
22997
|
upToDateSkip: () => upToDateSkip
|
|
22846
22998
|
});
|
|
22847
22999
|
import { spawn as spawn7 } from "child_process";
|
|
22848
|
-
import
|
|
22849
|
-
import
|
|
22850
|
-
import { existsSync as existsSync5, promises as
|
|
23000
|
+
import os9 from "os";
|
|
23001
|
+
import path39 from "path";
|
|
23002
|
+
import { existsSync as existsSync5, promises as fs33 } from "fs";
|
|
22851
23003
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
22852
|
-
function machineBudget(totalMemBytes =
|
|
23004
|
+
function machineBudget(totalMemBytes = os9.totalmem(), cpus = os9.availableParallelism?.() ?? os9.cpus().length, envOverride = process.env.POLYMATH_CONC) {
|
|
22853
23005
|
const envB = Number(envOverride);
|
|
22854
23006
|
if (Number.isFinite(envB) && envB >= 1) return Math.min(32, Math.floor(envB));
|
|
22855
23007
|
const byMem = Math.floor(totalMemBytes / 2 ** 30 * 0.35 / 0.45);
|
|
@@ -22876,6 +23028,8 @@ function stages(o) {
|
|
|
22876
23028
|
if (o.idleGapMin != null) buildArgs.push("--idle", String(o.idleGapMin));
|
|
22877
23029
|
const gradeArgs = ["--all", "--model", gradeModel, "--conc", conc];
|
|
22878
23030
|
if (o.regrade) gradeArgs.push("--regrade");
|
|
23031
|
+
const tailModel = o.model ?? o.tailModel;
|
|
23032
|
+
const tailArgs = tailModel ? ["--model", tailModel] : [];
|
|
22879
23033
|
return [
|
|
22880
23034
|
// ---- deterministic foundation (fast, free) ----
|
|
22881
23035
|
{ script: "coding-build.mts", label: "Scrape + classify your sessions", llm: false, args: buildArgs, out: "coding/sessions.json" },
|
|
@@ -22907,10 +23061,10 @@ function stages(o) {
|
|
|
22907
23061
|
// ---- corpus aggregate (reads grades; deterministic rollup) ----
|
|
22908
23062
|
{ script: "coding-aggregate.mts", label: "Corpus aggregate \u2014 ability ceiling + flow", llm: false, args: [], out: "coding/aggregate.json" },
|
|
22909
23063
|
// ---- grade readers (grades/* is complete once aggregate ran) ----
|
|
22910
|
-
//
|
|
22911
|
-
//
|
|
22912
|
-
//
|
|
22913
|
-
{ script: "coding-agglomerate.mts", label: "Agglomerated criteria + verdicts + signature moves", llm: true, weight: 4, args:
|
|
23064
|
+
// The agglomerate script defaults itself to Opus (one call per run; the
|
|
23065
|
+
// axis verdicts are the most judgment-heavy output). tailArgs overrides
|
|
23066
|
+
// that on plans that can't run Opus; an explicit user model choice wins.
|
|
23067
|
+
{ script: "coding-agglomerate.mts", label: "Agglomerated criteria + verdicts + signature moves", llm: true, weight: 4, args: [...tailArgs], out: "coding/agglomerate.json", batch: "grade-readers" },
|
|
22914
23068
|
// Domain-expertise map DISABLED (owner call 2026-07-22): the single most
|
|
22915
23069
|
// expensive stage of the whole run ($13.88 of $49.34 measured — 129 sonnet
|
|
22916
23070
|
// calls) and for most users the hiring-bar filter correctly returns ZERO
|
|
@@ -22924,34 +23078,44 @@ function stages(o) {
|
|
|
22924
23078
|
// Work brief — the hiring-manager narrative + continuous timeline. Consumes
|
|
22925
23079
|
// day-digest.json (heavy batch, already complete) + concurrency/projects;
|
|
22926
23080
|
// disjoint output, so it rides the synthesis batch. Defaults itself to Opus
|
|
22927
|
-
// (the journey's significance calls are judgment-heavy);
|
|
22928
|
-
// choice
|
|
22929
|
-
{ script: "coding-workbrief.mts", label: "Work brief \u2014 narrative timeline", llm: true, weight: 3, args:
|
|
23081
|
+
// (the journey's significance calls are judgment-heavy); tailArgs / an
|
|
23082
|
+
// explicit model choice win. Prompt-hash gated + up-to-date-skip like its siblings.
|
|
23083
|
+
{ script: "coding-workbrief.mts", label: "Work brief \u2014 narrative timeline", llm: true, weight: 3, args: [...tailArgs], out: "coding/workbrief.json", batch: "synthesis" },
|
|
22930
23084
|
// Growth reads grades/* + deterministic artifacts only (no per-session AI
|
|
22931
23085
|
// calls — it deliberately ignores the AI recency window and covers the full
|
|
22932
23086
|
// history); disjoint output, so it rides the synthesis batch. Like
|
|
22933
|
-
// agglomerate it defaults itself to Opus; an explicit model choice
|
|
22934
|
-
{ script: "coding-growth.mts", label: "Growth arcs + learning rate", llm: true, weight: 3, args:
|
|
23087
|
+
// agglomerate it defaults itself to Opus; tailArgs / an explicit model choice win.
|
|
23088
|
+
{ script: "coding-growth.mts", label: "Growth arcs + learning rate", llm: true, weight: 3, args: [...tailArgs], out: "coding/growth.json", batch: "synthesis" },
|
|
22935
23089
|
// nutshell stays LAST + alone: it merges INTO aggregate.json while the two
|
|
22936
23090
|
// above read the compiled profile — racing them re-reads mid-write.
|
|
22937
23091
|
{ script: "coding-nutshell.mts", label: "Big-picture nutshell", llm: true, weight: 1, args: [], out: "coding/aggregate.json" }
|
|
22938
23092
|
// merges bigPicture INTO aggregate.json
|
|
22939
23093
|
];
|
|
22940
23094
|
}
|
|
23095
|
+
function applyTailModelEnv(env, tailModel) {
|
|
23096
|
+
if (!tailModel) return;
|
|
23097
|
+
for (const key2 of Object.values(TAIL_MODEL_ENV)) if (!env[key2]) env[key2] = tailModel;
|
|
23098
|
+
}
|
|
23099
|
+
async function resolveTailModel(o, home) {
|
|
23100
|
+
if (o.model || o.tailModel) return o.tailModel;
|
|
23101
|
+
const { detectPlan: detectPlan2, opusAvailable: opusAvailable2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
|
|
23102
|
+
const { tier } = await detectPlan2(home);
|
|
23103
|
+
return opusAvailable2(tier) ? void 0 : "sonnet";
|
|
23104
|
+
}
|
|
22941
23105
|
function bundledPipelineDir() {
|
|
22942
|
-
const d =
|
|
22943
|
-
return existsSync5(
|
|
23106
|
+
const d = path39.join(MODULE_DIR2, "pipeline");
|
|
23107
|
+
return existsSync5(path39.join(d, "coding-build.js")) ? d : null;
|
|
22944
23108
|
}
|
|
22945
23109
|
function stageInvocation(repoRoot, scriptRel, args) {
|
|
22946
|
-
const scriptAbs =
|
|
23110
|
+
const scriptAbs = path39.join(repoRoot, "scripts", scriptRel);
|
|
22947
23111
|
if (existsSync5(scriptAbs)) {
|
|
22948
|
-
const local =
|
|
23112
|
+
const local = path39.join(repoRoot, "node_modules", ".bin", "tsx");
|
|
22949
23113
|
if (existsSync5(local)) return { cmd: local, argv: [scriptAbs, ...args], cwd: repoRoot };
|
|
22950
23114
|
return { cmd: "npx", argv: ["tsx", scriptAbs, ...args], cwd: repoRoot };
|
|
22951
23115
|
}
|
|
22952
23116
|
const bundled = bundledPipelineDir();
|
|
22953
23117
|
if (bundled) {
|
|
22954
|
-
const js =
|
|
23118
|
+
const js = path39.join(bundled, scriptRel.replace(/\.mts$/, ".js"));
|
|
22955
23119
|
return { cmd: process.execPath, argv: [js, ...args], cwd: process.cwd() };
|
|
22956
23120
|
}
|
|
22957
23121
|
throw new Error(`pipeline stage ${scriptRel}: neither repo scripts/ nor bundled dist/pipeline/ found`);
|
|
@@ -23007,17 +23171,17 @@ function runStage(repoRoot, st, env, onLine) {
|
|
|
23007
23171
|
});
|
|
23008
23172
|
}
|
|
23009
23173
|
function hasPipeline(repoRoot) {
|
|
23010
|
-
return existsSync5(
|
|
23174
|
+
return existsSync5(path39.join(repoRoot, "scripts", "coding-build.mts")) || bundledPipelineDir() != null;
|
|
23011
23175
|
}
|
|
23012
23176
|
function codingDataRoot(repoRoot) {
|
|
23013
|
-
if (!process.env.POLYMATH_DATA_DIR && existsSync5(
|
|
23014
|
-
return
|
|
23177
|
+
if (!process.env.POLYMATH_DATA_DIR && existsSync5(path39.join(repoRoot, "scripts", "coding-build.mts"))) {
|
|
23178
|
+
return path39.join(repoRoot, ".data");
|
|
23015
23179
|
}
|
|
23016
23180
|
return resolveDataRoot();
|
|
23017
23181
|
}
|
|
23018
23182
|
async function finalCodingArtifacts(codingDir) {
|
|
23019
|
-
const files = await
|
|
23020
|
-
return files.filter((f) => f.endsWith(".json") && f !== "report-run.json" && !f.startsWith(".")).sort().map((f) =>
|
|
23183
|
+
const files = await fs33.readdir(codingDir).catch(() => []);
|
|
23184
|
+
return files.filter((f) => f.endsWith(".json") && f !== "report-run.json" && !f.startsWith(".")).sort().map((f) => path39.join(codingDir, f));
|
|
23021
23185
|
}
|
|
23022
23186
|
function upToDateSkip(i) {
|
|
23023
23187
|
const pending2 = i.gradableIds.filter((id) => !i.gradedIds.has(id)).length;
|
|
@@ -23030,22 +23194,22 @@ function expectedLlmArtifacts(o) {
|
|
|
23030
23194
|
}
|
|
23031
23195
|
async function evalUpToDate(codingDir, threshold, regrade, opts2) {
|
|
23032
23196
|
try {
|
|
23033
|
-
const dataDir =
|
|
23197
|
+
const dataDir = path39.dirname(codingDir);
|
|
23034
23198
|
const missing = [];
|
|
23035
23199
|
for (const rel of expectedLlmArtifacts(opts2 ?? {})) {
|
|
23036
|
-
const ok = await
|
|
23200
|
+
const ok = await fs33.stat(path39.join(dataDir, rel)).then(() => true).catch(() => false);
|
|
23037
23201
|
if (!ok) missing.push(rel);
|
|
23038
23202
|
}
|
|
23039
23203
|
const { partition: partition2, aiWindowCutoff: aiWindowCutoff2, capGradable: capGradable2 } = await Promise.resolve().then(() => (init_gate2(), gate_exports));
|
|
23040
|
-
const sessions = JSON.parse(await
|
|
23204
|
+
const sessions = JSON.parse(await fs33.readFile(path39.join(codingDir, "sessions.json"), "utf8"));
|
|
23041
23205
|
const live = sessions.filter((s) => s.klass === "interactive" && !s.duplicateOf);
|
|
23042
23206
|
const cutoff = aiWindowCutoff2(live);
|
|
23043
23207
|
const { gradable: uncapped } = partition2(cutoff ? live.filter((s) => (s.start ?? "") >= cutoff) : live);
|
|
23044
23208
|
const gradable = capGradable2(uncapped);
|
|
23045
23209
|
const graded = new Set(
|
|
23046
|
-
(await
|
|
23210
|
+
(await fs33.readdir(path39.join(codingDir, "grades")).catch(() => [])).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5))
|
|
23047
23211
|
);
|
|
23048
|
-
const agg = JSON.parse(await
|
|
23212
|
+
const agg = JSON.parse(await fs33.readFile(path39.join(codingDir, "aggregate.json"), "utf8").catch(() => "{}"));
|
|
23049
23213
|
const hasPriorRun = typeof agg.bigPicture === "string" && agg.bigPicture.trim().length > 0;
|
|
23050
23214
|
return {
|
|
23051
23215
|
...upToDateSkip({
|
|
@@ -23110,14 +23274,19 @@ async function runPipeline(o) {
|
|
|
23110
23274
|
const dataRoot = codingDataRoot(o.repoRoot);
|
|
23111
23275
|
const env = { ...process.env, POLYMATH_RESILIENT: "1", POLYMATH_DATA_DIR: dataRoot };
|
|
23112
23276
|
if (!/max-old-space-size/.test(env.NODE_OPTIONS ?? "")) {
|
|
23113
|
-
const headroomMb = Math.min(12288, Math.max(4096, Math.round(
|
|
23277
|
+
const headroomMb = Math.min(12288, Math.max(4096, Math.round(os9.totalmem() / 2 ** 20 * 0.6)));
|
|
23114
23278
|
env.NODE_OPTIONS = `${env.NODE_OPTIONS ?? ""} --max-old-space-size=${headroomMb}`.trim();
|
|
23115
23279
|
}
|
|
23116
23280
|
if (!o.overnight) env.POLYMATH_RESILIENT_NO_WINDOW = "1";
|
|
23117
23281
|
const stopKeepAwake = o.overnight ? startKeepAwake() : () => {
|
|
23118
23282
|
};
|
|
23119
|
-
const
|
|
23120
|
-
|
|
23283
|
+
const tailModel = await resolveTailModel(o);
|
|
23284
|
+
applyTailModelEnv(env, tailModel);
|
|
23285
|
+
if (tailModel && !o.deterministicOnly) {
|
|
23286
|
+
o.onLine?.(`\u24D8 synthesis stages run on ${tailModel} \u2014 this plan doesn't include Opus (Max plans keep the Opus default)`);
|
|
23287
|
+
}
|
|
23288
|
+
const list = stages({ ...o, tailModel }).filter((st) => !o.deterministicOnly || !st.llm);
|
|
23289
|
+
const codingDir = path39.join(dataRoot, "coding");
|
|
23121
23290
|
const central = centralConfig();
|
|
23122
23291
|
const t0 = Date.now();
|
|
23123
23292
|
const log = await startRunLog(
|
|
@@ -23126,7 +23295,7 @@ async function runPipeline(o) {
|
|
|
23126
23295
|
supabaseUrl: central.supabaseUrl,
|
|
23127
23296
|
supabaseAnonKey: central.supabaseAnonKey,
|
|
23128
23297
|
artifacts: () => finalCodingArtifacts(codingDir),
|
|
23129
|
-
refPath:
|
|
23298
|
+
refPath: path39.join(codingDir, "report-run.json")
|
|
23130
23299
|
// next to the served artifacts
|
|
23131
23300
|
}
|
|
23132
23301
|
);
|
|
@@ -23163,7 +23332,7 @@ async function runPipeline(o) {
|
|
|
23163
23332
|
failed.push({ label: st.label, script: st.script, error });
|
|
23164
23333
|
o.onLine?.(`\u26A0\uFE0F stage FAILED (continuing): ${st.label} \u2014 ${error}`);
|
|
23165
23334
|
}
|
|
23166
|
-
const sha = st.out ? await fileSha(
|
|
23335
|
+
const sha = st.out ? await fileSha(path39.join(dataRoot, st.out)) : null;
|
|
23167
23336
|
log.event(st.script.replace(/\.mts$/, ""), { ok, ms: Date.now() - stStart, ...sha ? { sha } : {} });
|
|
23168
23337
|
stageTimes.push({ script: st.script, label: st.label, ms: Date.now() - stStart, ok });
|
|
23169
23338
|
o.onStageDone?.(st.label, st.llm, ok, st.script);
|
|
@@ -23199,7 +23368,7 @@ async function runPipeline(o) {
|
|
|
23199
23368
|
model: o.model ?? "sonnet(+haiku grade)",
|
|
23200
23369
|
budget: machineBudget()
|
|
23201
23370
|
};
|
|
23202
|
-
await
|
|
23371
|
+
await fs33.appendFile(path39.join(codingDir, "run-times.jsonl"), JSON.stringify(record) + "\n");
|
|
23203
23372
|
const slowest = stageTimes.filter((s) => !s.skipped).sort((a, b) => b.ms - a.ms).slice(0, 3).map((s) => `${s.script.replace(/^coding-|\.mts$/g, "")} ${fmt(s.ms)}`).join(" \xB7 ");
|
|
23204
23373
|
o.onLine?.(`\u23F1 run took ${fmt(totalMs)} (${record.kind}) \u2014 slowest: ${slowest} \xB7 ledger: .data/coding/run-times.jsonl`);
|
|
23205
23374
|
} catch {
|
|
@@ -23207,14 +23376,18 @@ async function runPipeline(o) {
|
|
|
23207
23376
|
stopKeepAwake();
|
|
23208
23377
|
return { failed, ...skippedLlm ? { skippedLlm } : {} };
|
|
23209
23378
|
}
|
|
23210
|
-
var MODULE_DIR2, liveStages, killHooked;
|
|
23379
|
+
var MODULE_DIR2, TAIL_MODEL_ENV, liveStages, killHooked;
|
|
23211
23380
|
var init_pipeline2 = __esm({
|
|
23212
23381
|
"src/pipeline.ts"() {
|
|
23213
23382
|
"use strict";
|
|
23214
23383
|
init_central();
|
|
23215
23384
|
init_dataHome();
|
|
23216
23385
|
init_runLog();
|
|
23217
|
-
MODULE_DIR2 =
|
|
23386
|
+
MODULE_DIR2 = path39.dirname(fileURLToPath3(import.meta.url));
|
|
23387
|
+
TAIL_MODEL_ENV = {
|
|
23388
|
+
"coding-coaching.mts": "AGENT_MODEL",
|
|
23389
|
+
"coding-focus.mts": "FOCUS_JUDGE_MODEL"
|
|
23390
|
+
};
|
|
23218
23391
|
liveStages = /* @__PURE__ */ new Set();
|
|
23219
23392
|
killHooked = false;
|
|
23220
23393
|
}
|
|
@@ -23230,9 +23403,9 @@ __export(chatPipeline_exports, {
|
|
|
23230
23403
|
runChatPipeline: () => runChatPipeline
|
|
23231
23404
|
});
|
|
23232
23405
|
import { spawn as spawn8 } from "child_process";
|
|
23233
|
-
import
|
|
23234
|
-
import
|
|
23235
|
-
import { existsSync as existsSync6, promises as
|
|
23406
|
+
import os10 from "os";
|
|
23407
|
+
import path40 from "path";
|
|
23408
|
+
import { existsSync as existsSync6, promises as fs34 } from "fs";
|
|
23236
23409
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
23237
23410
|
function chatEngineStages() {
|
|
23238
23411
|
return [
|
|
@@ -23252,40 +23425,40 @@ function chatEngineStages() {
|
|
|
23252
23425
|
];
|
|
23253
23426
|
}
|
|
23254
23427
|
function bundledEngineDir() {
|
|
23255
|
-
const d =
|
|
23256
|
-
return existsSync6(
|
|
23428
|
+
const d = path40.join(MODULE_DIR3, "engine");
|
|
23429
|
+
return existsSync6(path40.join(d, "run-tagger.js")) ? d : null;
|
|
23257
23430
|
}
|
|
23258
23431
|
function stageInvocation2(repoRoot, scriptRel, args) {
|
|
23259
|
-
const scriptAbs =
|
|
23432
|
+
const scriptAbs = path40.join(repoRoot, "scripts", scriptRel);
|
|
23260
23433
|
if (existsSync6(scriptAbs)) {
|
|
23261
|
-
const local =
|
|
23434
|
+
const local = path40.join(repoRoot, "node_modules", ".bin", "tsx");
|
|
23262
23435
|
if (existsSync6(local)) return { cmd: local, argv: [scriptAbs, ...args], cwd: repoRoot };
|
|
23263
23436
|
return { cmd: "npx", argv: ["tsx", scriptAbs, ...args], cwd: repoRoot };
|
|
23264
23437
|
}
|
|
23265
23438
|
const bundled = bundledEngineDir();
|
|
23266
23439
|
if (bundled) {
|
|
23267
|
-
const js =
|
|
23440
|
+
const js = path40.join(bundled, scriptRel.replace(/\.mts$/, ".js"));
|
|
23268
23441
|
return { cmd: process.execPath, argv: [js, ...args], cwd: process.cwd() };
|
|
23269
23442
|
}
|
|
23270
23443
|
throw new Error(`chat-engine stage ${scriptRel}: neither repo scripts/ nor bundled dist/engine/ found`);
|
|
23271
23444
|
}
|
|
23272
23445
|
function hasChatPipeline(repoRoot) {
|
|
23273
|
-
return existsSync6(
|
|
23446
|
+
return existsSync6(path40.join(repoRoot, "scripts", "run-tagger.mts")) || bundledEngineDir() != null;
|
|
23274
23447
|
}
|
|
23275
23448
|
function chatDataRoot(repoRoot) {
|
|
23276
|
-
if (!process.env.POLYMATH_DATA_DIR && existsSync6(
|
|
23277
|
-
return
|
|
23449
|
+
if (!process.env.POLYMATH_DATA_DIR && existsSync6(path40.join(repoRoot, "scripts", "run-tagger.mts"))) {
|
|
23450
|
+
return path40.join(repoRoot, ".data");
|
|
23278
23451
|
}
|
|
23279
23452
|
return resolveDataRoot();
|
|
23280
23453
|
}
|
|
23281
23454
|
async function finalChatArtifacts(dataRoot) {
|
|
23282
|
-
const gradesDir =
|
|
23455
|
+
const gradesDir = path40.join(dataRoot, "engine-pilot", "grades");
|
|
23283
23456
|
const out = [];
|
|
23284
|
-
for (const lens of (await
|
|
23285
|
-
const p =
|
|
23457
|
+
for (const lens of (await fs34.readdir(gradesDir).catch(() => [])).sort()) {
|
|
23458
|
+
const p = path40.join(gradesDir, lens, "_person.json");
|
|
23286
23459
|
if (existsSync6(p)) out.push(p);
|
|
23287
23460
|
}
|
|
23288
|
-
out.push(
|
|
23461
|
+
out.push(path40.join(dataRoot, "engine-pilot", "public-report.json"));
|
|
23289
23462
|
return out;
|
|
23290
23463
|
}
|
|
23291
23464
|
function runStage2(repoRoot, st, env, onLine) {
|
|
@@ -23335,7 +23508,7 @@ async function runChatPipeline(o) {
|
|
|
23335
23508
|
};
|
|
23336
23509
|
if (!o.overnight) env.POLYMATH_RESILIENT_NO_WINDOW = "1";
|
|
23337
23510
|
if (!/max-old-space-size/.test(env.NODE_OPTIONS ?? "")) {
|
|
23338
|
-
const headroomMb = Math.min(12288, Math.max(4096, Math.round(
|
|
23511
|
+
const headroomMb = Math.min(12288, Math.max(4096, Math.round(os10.totalmem() / 2 ** 20 * 0.6)));
|
|
23339
23512
|
env.NODE_OPTIONS = `${env.NODE_OPTIONS ?? ""} --max-old-space-size=${headroomMb}`.trim();
|
|
23340
23513
|
}
|
|
23341
23514
|
const failed = [];
|
|
@@ -23354,12 +23527,12 @@ async function runChatPipeline(o) {
|
|
|
23354
23527
|
supabaseUrl: central.supabaseUrl,
|
|
23355
23528
|
supabaseAnonKey: central.supabaseAnonKey,
|
|
23356
23529
|
artifacts: () => finalChatArtifacts(dataRoot),
|
|
23357
|
-
refPath:
|
|
23530
|
+
refPath: path40.join(dataRoot, "report-run.json")
|
|
23358
23531
|
}
|
|
23359
23532
|
);
|
|
23360
23533
|
let idx = 0;
|
|
23361
23534
|
for (const f of ingestable) {
|
|
23362
|
-
const st = { script: "ingest-export.mts", label: `Ingest ${f.kind} \u2014 ${
|
|
23535
|
+
const st = { script: "ingest-export.mts", label: `Ingest ${f.kind} \u2014 ${path40.basename(f.path)}`, llm: false, args: [f.kind, f.path] };
|
|
23363
23536
|
o.onStage?.(++idx, total, st.label, false);
|
|
23364
23537
|
const stStart = Date.now();
|
|
23365
23538
|
let ok = true;
|
|
@@ -23376,7 +23549,7 @@ async function runChatPipeline(o) {
|
|
|
23376
23549
|
}
|
|
23377
23550
|
const minNewDocs = Number(process.env.POLYMATH_MIN_NEW_DOCS ?? 10);
|
|
23378
23551
|
const signalDocs = async () => {
|
|
23379
|
-
const t = await
|
|
23552
|
+
const t = await fs34.readFile(path40.join(dataRoot, "signals", "index.jsonl"), "utf8").catch(() => "");
|
|
23380
23553
|
return t ? t.split("\n").filter(Boolean).length : 0;
|
|
23381
23554
|
};
|
|
23382
23555
|
const docsBeforeTagger = await signalDocs();
|
|
@@ -23398,11 +23571,11 @@ async function runChatPipeline(o) {
|
|
|
23398
23571
|
failed.push({ label: st.label, script: st.script, error });
|
|
23399
23572
|
o.onLine?.(`\u26A0\uFE0F stage FAILED (continuing): ${st.label} \u2014 ${error}`);
|
|
23400
23573
|
}
|
|
23401
|
-
const sha = st.out ? await fileSha(
|
|
23574
|
+
const sha = st.out ? await fileSha(path40.join(dataRoot, st.out)) : null;
|
|
23402
23575
|
log.event(st.script.replace(/\.mts$/, ""), { ok, ms: Date.now() - stStart, ...sha ? { sha } : {} });
|
|
23403
23576
|
if (st.script === "run-tagger.mts" && ok && minNewDocs > 0) {
|
|
23404
23577
|
const newDocs = await signalDocs() - docsBeforeTagger;
|
|
23405
|
-
const report = await
|
|
23578
|
+
const report = await fs34.readFile(path40.join(dataRoot, "engine-pilot", "public-report.json"), "utf8").then((s) => JSON.parse(s)).catch(() => null);
|
|
23406
23579
|
if (report?.headline && (report.spikes?.length ?? 0) > 0 && newDocs < minNewDocs) {
|
|
23407
23580
|
upToDate = true;
|
|
23408
23581
|
o.onUpToDate?.();
|
|
@@ -23425,7 +23598,7 @@ var init_chatPipeline = __esm({
|
|
|
23425
23598
|
init_central();
|
|
23426
23599
|
init_dataHome();
|
|
23427
23600
|
init_runLog();
|
|
23428
|
-
MODULE_DIR3 =
|
|
23601
|
+
MODULE_DIR3 = path40.dirname(fileURLToPath4(import.meta.url));
|
|
23429
23602
|
INGESTABLE_KINDS = ["chatgpt", "claude", "notion", "obsidian"];
|
|
23430
23603
|
}
|
|
23431
23604
|
});
|
|
@@ -23635,10 +23808,10 @@ __export(exportScan_exports, {
|
|
|
23635
23808
|
scanForExports: () => scanForExports,
|
|
23636
23809
|
scanForObsidianVaults: () => scanForObsidianVaults
|
|
23637
23810
|
});
|
|
23638
|
-
import { promises as
|
|
23811
|
+
import { promises as fs35 } from "fs";
|
|
23639
23812
|
import { execFile as execFile2 } from "child_process";
|
|
23640
23813
|
import { promisify as promisify2 } from "util";
|
|
23641
|
-
import
|
|
23814
|
+
import path41 from "path";
|
|
23642
23815
|
function latestPerSource(found) {
|
|
23643
23816
|
const newest = /* @__PURE__ */ new Map();
|
|
23644
23817
|
const keyOf = (f) => f.kind === "obsidian" || f.kind === "unknown" ? null : `${f.kind}:${f.account ?? "(unknown-account)"}`;
|
|
@@ -23708,7 +23881,7 @@ async function identifyZip(zip, stat) {
|
|
|
23708
23881
|
note: ""
|
|
23709
23882
|
};
|
|
23710
23883
|
if (!entries) {
|
|
23711
|
-
const n =
|
|
23884
|
+
const n = path41.basename(zip).toLowerCase();
|
|
23712
23885
|
if (n.includes("chatgpt") || n.includes("openai")) {
|
|
23713
23886
|
found.kind = "chatgpt";
|
|
23714
23887
|
found.note = "matched by filename only (no unzip binary to verify)";
|
|
@@ -23728,7 +23901,7 @@ async function identifyZip(zip, stat) {
|
|
|
23728
23901
|
}
|
|
23729
23902
|
let { kind, note } = classify2(entries);
|
|
23730
23903
|
if (kind === "unknown") {
|
|
23731
|
-
const nb =
|
|
23904
|
+
const nb = path41.basename(zip).toLowerCase();
|
|
23732
23905
|
const nameHint = nb.includes("notion") || /(^|[-_ ])export-/.test(nb);
|
|
23733
23906
|
const inner = entries.filter((e) => e.toLowerCase().endsWith(".zip"));
|
|
23734
23907
|
const partZips = inner.some((e) => /export[^/]*part[-_ ]?\d+\.zip$/i.test(e.toLowerCase()));
|
|
@@ -23757,17 +23930,17 @@ async function identifyZip(zip, stat) {
|
|
|
23757
23930
|
async function identifyDir(dir) {
|
|
23758
23931
|
let names;
|
|
23759
23932
|
try {
|
|
23760
|
-
names = await
|
|
23933
|
+
names = await fs35.readdir(dir);
|
|
23761
23934
|
} catch {
|
|
23762
23935
|
return null;
|
|
23763
23936
|
}
|
|
23764
23937
|
const { kind, note } = classify2(names);
|
|
23765
23938
|
if (kind === "unknown") return null;
|
|
23766
|
-
const st = await
|
|
23939
|
+
const st = await fs35.stat(dir);
|
|
23767
23940
|
const found = { kind, path: dir, account: null, sizeMB: 0, modified: st.mtime.toISOString().slice(0, 10), note };
|
|
23768
23941
|
const read = async (n) => {
|
|
23769
23942
|
try {
|
|
23770
|
-
return await
|
|
23943
|
+
return await fs35.readFile(path41.join(dir, n), "utf8");
|
|
23771
23944
|
} catch {
|
|
23772
23945
|
return null;
|
|
23773
23946
|
}
|
|
@@ -23780,7 +23953,7 @@ async function identifyPath(p) {
|
|
|
23780
23953
|
if (/\/\.data\//.test(p)) return null;
|
|
23781
23954
|
let st;
|
|
23782
23955
|
try {
|
|
23783
|
-
st = await
|
|
23956
|
+
st = await fs35.stat(p);
|
|
23784
23957
|
} catch {
|
|
23785
23958
|
return null;
|
|
23786
23959
|
}
|
|
@@ -23842,14 +24015,14 @@ async function countMdNotes(root, cap = 2e3) {
|
|
|
23842
24015
|
if (n >= cap || depth > 6) return;
|
|
23843
24016
|
let entries;
|
|
23844
24017
|
try {
|
|
23845
|
-
entries = await
|
|
24018
|
+
entries = await fs35.readdir(d, { withFileTypes: true });
|
|
23846
24019
|
} catch {
|
|
23847
24020
|
return;
|
|
23848
24021
|
}
|
|
23849
24022
|
for (const e of entries) {
|
|
23850
24023
|
if (n >= cap) return;
|
|
23851
24024
|
if (e.name.startsWith(".")) continue;
|
|
23852
|
-
if (e.isDirectory()) await walk(
|
|
24025
|
+
if (e.isDirectory()) await walk(path41.join(d, e.name), depth + 1);
|
|
23853
24026
|
else if (e.name.endsWith(".md")) n++;
|
|
23854
24027
|
}
|
|
23855
24028
|
}
|
|
@@ -23858,12 +24031,12 @@ async function countMdNotes(root, cap = 2e3) {
|
|
|
23858
24031
|
}
|
|
23859
24032
|
async function identifyObsidianVault(dir) {
|
|
23860
24033
|
try {
|
|
23861
|
-
const st = await
|
|
24034
|
+
const st = await fs35.stat(path41.join(dir, ".obsidian"));
|
|
23862
24035
|
if (!st.isDirectory()) return null;
|
|
23863
24036
|
} catch {
|
|
23864
24037
|
return null;
|
|
23865
24038
|
}
|
|
23866
|
-
const rootSt = await
|
|
24039
|
+
const rootSt = await fs35.stat(dir).catch(() => null);
|
|
23867
24040
|
if (!rootSt) return null;
|
|
23868
24041
|
const notes = await countMdNotes(dir);
|
|
23869
24042
|
return {
|
|
@@ -23885,21 +24058,21 @@ async function scanForObsidianVaults(home) {
|
|
|
23885
24058
|
}
|
|
23886
24059
|
};
|
|
23887
24060
|
const roots = [
|
|
23888
|
-
|
|
23889
|
-
|
|
23890
|
-
|
|
24061
|
+
path41.join(home, "Library", "Mobile Documents", "iCloud~md~obsidian", "Documents"),
|
|
24062
|
+
path41.join(home, "Documents"),
|
|
24063
|
+
path41.join(home, "Desktop"),
|
|
23891
24064
|
home
|
|
23892
24065
|
];
|
|
23893
24066
|
for (const root of roots) {
|
|
23894
24067
|
let names = [];
|
|
23895
24068
|
try {
|
|
23896
|
-
names = await
|
|
24069
|
+
names = await fs35.readdir(root);
|
|
23897
24070
|
} catch {
|
|
23898
24071
|
continue;
|
|
23899
24072
|
}
|
|
23900
24073
|
for (const name17 of names) {
|
|
23901
24074
|
if (name17.startsWith(".")) continue;
|
|
23902
|
-
add(await identifyObsidianVault(
|
|
24075
|
+
add(await identifyObsidianVault(path41.join(root, name17)));
|
|
23903
24076
|
}
|
|
23904
24077
|
}
|
|
23905
24078
|
return out;
|
|
@@ -23908,16 +24081,16 @@ async function scanForExports(root) {
|
|
|
23908
24081
|
const out = [];
|
|
23909
24082
|
let names;
|
|
23910
24083
|
try {
|
|
23911
|
-
names = await
|
|
24084
|
+
names = await fs35.readdir(root);
|
|
23912
24085
|
} catch {
|
|
23913
24086
|
return out;
|
|
23914
24087
|
}
|
|
23915
24088
|
for (const name17 of names) {
|
|
23916
24089
|
if (name17.startsWith(".")) continue;
|
|
23917
|
-
const p =
|
|
24090
|
+
const p = path41.join(root, name17);
|
|
23918
24091
|
let st;
|
|
23919
24092
|
try {
|
|
23920
|
-
st = await
|
|
24093
|
+
st = await fs35.stat(p);
|
|
23921
24094
|
} catch {
|
|
23922
24095
|
continue;
|
|
23923
24096
|
}
|
|
@@ -24045,16 +24218,16 @@ __export(reminderState_exports, {
|
|
|
24045
24218
|
recordQueued: () => recordQueued,
|
|
24046
24219
|
trackedLinkUrl: () => trackedLinkUrl
|
|
24047
24220
|
});
|
|
24048
|
-
import { promises as
|
|
24049
|
-
import
|
|
24221
|
+
import { promises as fs36 } from "node:fs";
|
|
24222
|
+
import path42 from "node:path";
|
|
24050
24223
|
import crypto4 from "node:crypto";
|
|
24051
24224
|
function newToken() {
|
|
24052
24225
|
return crypto4.randomBytes(12).toString("base64url");
|
|
24053
24226
|
}
|
|
24054
24227
|
async function ensureReminderState(dataDir) {
|
|
24055
|
-
const file2 =
|
|
24228
|
+
const file2 = path42.join(dataDir, FILE);
|
|
24056
24229
|
try {
|
|
24057
|
-
const s = JSON.parse(await
|
|
24230
|
+
const s = JSON.parse(await fs36.readFile(file2, "utf8"));
|
|
24058
24231
|
if (typeof s.token === "string" && s.token.length >= 8) {
|
|
24059
24232
|
return {
|
|
24060
24233
|
token: s.token,
|
|
@@ -24068,8 +24241,8 @@ async function ensureReminderState(dataDir) {
|
|
|
24068
24241
|
}
|
|
24069
24242
|
const state = { token: newToken(), queuedKinds: [] };
|
|
24070
24243
|
try {
|
|
24071
|
-
await
|
|
24072
|
-
await
|
|
24244
|
+
await fs36.mkdir(dataDir, { recursive: true });
|
|
24245
|
+
await fs36.writeFile(file2, JSON.stringify(state));
|
|
24073
24246
|
} catch {
|
|
24074
24247
|
}
|
|
24075
24248
|
return state;
|
|
@@ -24084,8 +24257,8 @@ async function recordQueued(dataDir, state, kinds) {
|
|
|
24084
24257
|
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24085
24258
|
};
|
|
24086
24259
|
try {
|
|
24087
|
-
await
|
|
24088
|
-
await
|
|
24260
|
+
await fs36.mkdir(dataDir, { recursive: true });
|
|
24261
|
+
await fs36.writeFile(path42.join(dataDir, FILE), JSON.stringify(next));
|
|
24089
24262
|
} catch {
|
|
24090
24263
|
}
|
|
24091
24264
|
return next;
|
|
@@ -24098,8 +24271,8 @@ async function recordOffered(dataDir, state, kinds) {
|
|
|
24098
24271
|
offeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24099
24272
|
};
|
|
24100
24273
|
try {
|
|
24101
|
-
await
|
|
24102
|
-
await
|
|
24274
|
+
await fs36.mkdir(dataDir, { recursive: true });
|
|
24275
|
+
await fs36.writeFile(path42.join(dataDir, FILE), JSON.stringify(next));
|
|
24103
24276
|
} catch {
|
|
24104
24277
|
}
|
|
24105
24278
|
return next;
|
|
@@ -24116,151 +24289,6 @@ var init_reminderState = __esm({
|
|
|
24116
24289
|
}
|
|
24117
24290
|
});
|
|
24118
24291
|
|
|
24119
|
-
// src/plan.ts
|
|
24120
|
-
var plan_exports = {};
|
|
24121
|
-
__export(plan_exports, {
|
|
24122
|
-
UNSUPPORTED_FULL_TIERS: () => UNSUPPORTED_FULL_TIERS,
|
|
24123
|
-
detectCodexPlan: () => detectCodexPlan,
|
|
24124
|
-
detectPlan: () => detectPlan,
|
|
24125
|
-
estimateLaneCosts: () => estimateLaneCosts,
|
|
24126
|
-
remainingFractions: () => remainingFractions,
|
|
24127
|
-
scheduleLanes: () => scheduleLanes,
|
|
24128
|
-
supportsFullAnalysis: () => supportsFullAnalysis,
|
|
24129
|
-
tierFromChatgptPlan: () => tierFromChatgptPlan,
|
|
24130
|
-
tierFromStrings: () => tierFromStrings
|
|
24131
|
-
});
|
|
24132
|
-
import { promises as fs36 } from "fs";
|
|
24133
|
-
import os10 from "os";
|
|
24134
|
-
import path42 from "path";
|
|
24135
|
-
function supportsFullAnalysis(tier) {
|
|
24136
|
-
if (process.env.POLYMATH_FORCE_FULL === "1") return true;
|
|
24137
|
-
return !UNSUPPORTED_FULL_TIERS.includes(tier);
|
|
24138
|
-
}
|
|
24139
|
-
function tierFromStrings(...hints) {
|
|
24140
|
-
for (const h of hints) {
|
|
24141
|
-
if (!h) continue;
|
|
24142
|
-
const s = h.toLowerCase();
|
|
24143
|
-
if (s.includes("max_20x") || s.includes("max20x")) return "max_20x";
|
|
24144
|
-
if (s.includes("max_5x") || s.includes("max5x")) return "max_5x";
|
|
24145
|
-
if (s.includes("pro")) return "pro";
|
|
24146
|
-
if (s.includes("claude_max")) return "max_5x";
|
|
24147
|
-
}
|
|
24148
|
-
return "unknown";
|
|
24149
|
-
}
|
|
24150
|
-
function tierFromChatgptPlan(planType) {
|
|
24151
|
-
const s = (planType ?? "").toLowerCase();
|
|
24152
|
-
if (s === "pro") return "gpt_pro";
|
|
24153
|
-
if (s === "plus") return "gpt_plus";
|
|
24154
|
-
if (s === "team" || s === "enterprise" || s === "business") return "gpt_team";
|
|
24155
|
-
if (s === "free") return "gpt_free";
|
|
24156
|
-
return "unknown";
|
|
24157
|
-
}
|
|
24158
|
-
async function detectCodexPlan(home = os10.homedir()) {
|
|
24159
|
-
try {
|
|
24160
|
-
const raw = JSON.parse(await fs36.readFile(path42.join(home, ".codex", "auth.json"), "utf8"));
|
|
24161
|
-
for (const tok of [raw.tokens?.id_token, raw.tokens?.access_token]) {
|
|
24162
|
-
if (!tok) continue;
|
|
24163
|
-
try {
|
|
24164
|
-
const payload = JSON.parse(Buffer.from(tok.split(".")[1], "base64url").toString("utf8"));
|
|
24165
|
-
const t = tierFromChatgptPlan(payload["https://api.openai.com/auth"]?.chatgpt_plan_type);
|
|
24166
|
-
if (t !== "unknown") return t;
|
|
24167
|
-
} catch {
|
|
24168
|
-
}
|
|
24169
|
-
}
|
|
24170
|
-
} catch {
|
|
24171
|
-
}
|
|
24172
|
-
return "unknown";
|
|
24173
|
-
}
|
|
24174
|
-
async function detectPlan(home = os10.homedir()) {
|
|
24175
|
-
let tier = "unknown";
|
|
24176
|
-
try {
|
|
24177
|
-
const raw = JSON.parse(await fs36.readFile(path42.join(home, ".claude.json"), "utf8"));
|
|
24178
|
-
const oa = raw.oauthAccount ?? {};
|
|
24179
|
-
tier = tierFromStrings(oa.userRateLimitTier, oa.organizationRateLimitTier, oa.organizationType);
|
|
24180
|
-
} catch {
|
|
24181
|
-
}
|
|
24182
|
-
if (tier === "unknown") tier = await detectCodexPlan(home);
|
|
24183
|
-
return { tier, label: LABEL[tier], windowUsd: WINDOW_USD[tier] };
|
|
24184
|
-
}
|
|
24185
|
-
function estimateLaneCosts(input) {
|
|
24186
|
-
const codingUsd = Math.max(5, Math.min(32, Math.round(input.agentLogFiles * 9e-4)));
|
|
24187
|
-
const chatUsd = input.chatIncluded ? Math.round(40 + input.exportTotalMB * 0.8) : 0;
|
|
24188
|
-
return { codingUsd, chatUsd };
|
|
24189
|
-
}
|
|
24190
|
-
async function remainingFractions(dataRoot, agentLogFiles) {
|
|
24191
|
-
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
24192
|
-
let coding = 1;
|
|
24193
|
-
try {
|
|
24194
|
-
const graded = (await fs36.readdir(path42.join(dataRoot, "coding", "grades"))).filter((f) => f.endsWith(".json")).length;
|
|
24195
|
-
const expected = Math.max(1, agentLogFiles * 56e-4);
|
|
24196
|
-
coding = clamp(1 - graded / expected, 0.05, 1);
|
|
24197
|
-
} catch {
|
|
24198
|
-
}
|
|
24199
|
-
let chatDone = 0;
|
|
24200
|
-
try {
|
|
24201
|
-
const st = await fs36.stat(path42.join(dataRoot, "signals", "index.jsonl"));
|
|
24202
|
-
if (st.size > 0) chatDone += 0.3;
|
|
24203
|
-
} catch {
|
|
24204
|
-
}
|
|
24205
|
-
try {
|
|
24206
|
-
const lenses = await fs36.readdir(path42.join(dataRoot, "engine-pilot", "grades"));
|
|
24207
|
-
let withPerson = 0;
|
|
24208
|
-
for (const l of lenses) {
|
|
24209
|
-
try {
|
|
24210
|
-
await fs36.stat(path42.join(dataRoot, "engine-pilot", "grades", l, "_person.json"));
|
|
24211
|
-
withPerson++;
|
|
24212
|
-
} catch {
|
|
24213
|
-
}
|
|
24214
|
-
}
|
|
24215
|
-
chatDone += 0.6 * Math.min(1, withPerson / 5);
|
|
24216
|
-
} catch {
|
|
24217
|
-
}
|
|
24218
|
-
return { coding, chat: clamp(1 - chatDone, 0.1, 1) };
|
|
24219
|
-
}
|
|
24220
|
-
function scheduleLanes(plan, est) {
|
|
24221
|
-
const chat = est.chatUsd === 0 || est.chatUsd <= plan.windowUsd * 0.9 ? "now" : "overnight";
|
|
24222
|
-
return {
|
|
24223
|
-
coding: "now",
|
|
24224
|
-
chat,
|
|
24225
|
-
// No plan name here — the schedule header names the plan once, and
|
|
24226
|
-
// repeating it doubled the clumsy unknown-plan label (2026-07-22
|
|
24227
|
-
// walkthrough). Phrased for the two-column "Proposed schedule" rows:
|
|
24228
|
-
// "<lane> <verdict> — <reason>".
|
|
24229
|
-
codingReason: `runs now \u2014 the report link appears right away and fills in as it goes`,
|
|
24230
|
-
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`
|
|
24231
|
-
};
|
|
24232
|
-
}
|
|
24233
|
-
var UNSUPPORTED_FULL_TIERS, WINDOW_USD, LABEL;
|
|
24234
|
-
var init_plan = __esm({
|
|
24235
|
-
"src/plan.ts"() {
|
|
24236
|
-
"use strict";
|
|
24237
|
-
UNSUPPORTED_FULL_TIERS = ["pro", "gpt_plus", "gpt_free"];
|
|
24238
|
-
WINDOW_USD = {
|
|
24239
|
-
max_20x: 300,
|
|
24240
|
-
max_5x: 75,
|
|
24241
|
-
pro: 15,
|
|
24242
|
-
// ChatGPT tiers, mapped to rough Claude-window parity by monthly price
|
|
24243
|
-
// ($200 Pro ≈ Max 20x; $20 Plus ≈ Claude Pro; Team between; free ≈ nothing).
|
|
24244
|
-
gpt_pro: 300,
|
|
24245
|
-
gpt_plus: 20,
|
|
24246
|
-
gpt_team: 50,
|
|
24247
|
-
gpt_free: 5,
|
|
24248
|
-
unknown: 15
|
|
24249
|
-
// conservative: schedule like Pro when we can't tell
|
|
24250
|
-
};
|
|
24251
|
-
LABEL = {
|
|
24252
|
-
max_20x: "Claude Max 20x",
|
|
24253
|
-
max_5x: "Claude Max 5x",
|
|
24254
|
-
pro: "Claude Pro",
|
|
24255
|
-
gpt_pro: "ChatGPT Pro (Codex)",
|
|
24256
|
-
gpt_plus: "ChatGPT Plus (Codex)",
|
|
24257
|
-
gpt_team: "ChatGPT Team (Codex)",
|
|
24258
|
-
gpt_free: "ChatGPT Free (Codex)",
|
|
24259
|
-
unknown: "an undetected plan (scheduling conservatively)"
|
|
24260
|
-
};
|
|
24261
|
-
}
|
|
24262
|
-
});
|
|
24263
|
-
|
|
24264
24292
|
// src/wizard.ts
|
|
24265
24293
|
var wizard_exports = {};
|
|
24266
24294
|
__export(wizard_exports, {
|
|
@@ -24654,13 +24682,52 @@ async function runWizard() {
|
|
|
24654
24682
|
say(` ${bold("Proposed schedule")} \u2014 sized to your plan (${plan.label}):`);
|
|
24655
24683
|
say();
|
|
24656
24684
|
say(` ${bold("Your analysis")} runs now \u2014 everything measured from your logs: words/day, flow state, focus, projects, parallelism`);
|
|
24657
|
-
const planPhrase = plan.tier === "
|
|
24685
|
+
const planPhrase = plan.tier === "gpt_plus" ? "the $20 ChatGPT Plus plan" : "the free ChatGPT plan";
|
|
24658
24686
|
say(` ${bold("AI sections")} not supported on ${planPhrase} just yet \u2014 coming soon! Only the AI-graded sections (judgement, agency, taste, growth) won't be visible`);
|
|
24659
24687
|
say();
|
|
24660
24688
|
await ask(` Press Enter to run your analysis`).catch(() => "");
|
|
24661
24689
|
say(dim(" On it. The report link appears right away.\n"));
|
|
24662
24690
|
return { cmd: "serve", grade: false, overnight: false, chatOvernight: false, open: true };
|
|
24663
24691
|
}
|
|
24692
|
+
if (plan.tier === "pro") {
|
|
24693
|
+
say(` ${bold("Proposed schedule")} \u2014 sized to your plan (${plan.label}):`);
|
|
24694
|
+
if (resumed) say(dim(" (an earlier run already processed most of this \u2014 only what's new is scheduled; nothing done is redone)"));
|
|
24695
|
+
say();
|
|
24696
|
+
say(` ${bold("Deterministic sections")} run now \u2014 everything measured from your logs: words/day, flow state, focus, projects, parallelism`);
|
|
24697
|
+
say();
|
|
24698
|
+
say(` ${bold("AI sections")} run overnight, starting 1:00 am \u2014 grades, judgement, agency, taste, growth`);
|
|
24699
|
+
say();
|
|
24700
|
+
say(` Why overnight: a full AI run costs about two of your plan's 5-hour usage windows.`);
|
|
24701
|
+
say(` Running it at night spends windows you're asleep for \u2014 it hits the limit around`);
|
|
24702
|
+
say(` halfway, pauses, and resumes on its own when the window resets (~6:00 am).`);
|
|
24703
|
+
say();
|
|
24704
|
+
say(` ${bold("1) Run on this schedule \u2014 RECOMMENDED")}`);
|
|
24705
|
+
say(` \u2022 keep your laptop plugged in`);
|
|
24706
|
+
say(` \u2022 keep the lid open \u2014 closed means sleep, and the run stalls until morning`);
|
|
24707
|
+
say(` ${bold("2)")} Run the AI sections now instead \u2014 you'll likely hit your plan's usage limit partway;`);
|
|
24708
|
+
say(` the run pauses and resumes by itself when the limit resets (~5h), and the report`);
|
|
24709
|
+
say(` link works the whole time, filling in as stages finish`);
|
|
24710
|
+
say();
|
|
24711
|
+
const proDepth = await ask(` Choose ${dim("[1/2, default 1]")}: `) || "1";
|
|
24712
|
+
const proOvernight = proDepth !== "2";
|
|
24713
|
+
if (proOvernight) {
|
|
24714
|
+
if (!process.env.POLYMATH_NIGHTLY_START) process.env.POLYMATH_NIGHTLY_START = "1";
|
|
24715
|
+
say(` ${bold("VERY IMPORTANT for the overnight part:")}`);
|
|
24716
|
+
say(` \u2022 keep your laptop plugged in`);
|
|
24717
|
+
say(` \u2022 keep the lid open \u2014 closed means sleep, and the run stalls until morning`);
|
|
24718
|
+
say(dim(` (the report page shows this too)`));
|
|
24719
|
+
}
|
|
24720
|
+
say(dim(" On it. The report link appears right away and fills in as stages finish \u2014 Ctrl-C anytime.\n"));
|
|
24721
|
+
return {
|
|
24722
|
+
cmd: "serve",
|
|
24723
|
+
grade: true,
|
|
24724
|
+
overnight: proOvernight,
|
|
24725
|
+
// The chat corpus is unbounded (~$390 measured on 450 MB) — on a $15
|
|
24726
|
+
// window it is ALWAYS an overnight job, whatever the coding choice.
|
|
24727
|
+
chatOvernight: est.chatUsd > 0 ? true : proOvernight,
|
|
24728
|
+
open: true
|
|
24729
|
+
};
|
|
24730
|
+
}
|
|
24664
24731
|
const planBit = plan.label.includes("undetected") ? "your corpus (plan undetected \u2014 scheduling conservatively)" : `your plan (${plan.label}) and your corpus`;
|
|
24665
24732
|
say(` ${bold("Proposed schedule")} \u2014 sized to ${planBit}:`);
|
|
24666
24733
|
if (resumed) say(dim(" (an earlier run already processed most of this \u2014 only what's new is scheduled; nothing done is redone)"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polymath-society",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
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",
|