polymath-society 0.2.9 → 0.2.11
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 +51 -26
- package/dist/engine/chat-loops.js +10 -0
- package/dist/pipeline/coding-focus.js +27 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2117,7 +2117,8 @@ var init_plan = __esm({
|
|
|
2117
2117
|
// src/wizard.ts
|
|
2118
2118
|
var wizard_exports = {};
|
|
2119
2119
|
__export(wizard_exports, {
|
|
2120
|
-
runWizard: () => runWizard
|
|
2120
|
+
runWizard: () => runWizard,
|
|
2121
|
+
sayExportLinks: () => sayExportLinks
|
|
2121
2122
|
});
|
|
2122
2123
|
import { promises as fs35 } from "fs";
|
|
2123
2124
|
import os10 from "os";
|
|
@@ -2152,6 +2153,24 @@ async function scanJsonlDir(label, dir) {
|
|
|
2152
2153
|
await walk(dir, 0);
|
|
2153
2154
|
return { label, dir, files, newest: newest ? new Date(newest).toISOString().slice(0, 10) : null };
|
|
2154
2155
|
}
|
|
2156
|
+
async function sayExportLinks(say, haveKinds) {
|
|
2157
|
+
const { EXPORT_LINKS: EXPORT_LINKS2, osc8: osc82 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
|
|
2158
|
+
const requestable = EXPORT_LINKS2.filter((l) => l.requestUrl);
|
|
2159
|
+
if (!requestable.length) return;
|
|
2160
|
+
const missing = requestable.filter((l) => !haveKinds.has(l.kind));
|
|
2161
|
+
say();
|
|
2162
|
+
say(` ${bold("Export more of your history")} \u2014 one click each:`);
|
|
2163
|
+
if (missing.length) {
|
|
2164
|
+
say(` ${missing.map((l) => l.label).join(", ")} ${missing.length === 1 ? "is" : "are"} not on this machine yet.`);
|
|
2165
|
+
}
|
|
2166
|
+
for (const l of requestable) {
|
|
2167
|
+
const have = haveKinds.has(l.kind);
|
|
2168
|
+
say(` ${bold(l.label)}${have ? green(" \u2713 already have one") : ""} ${osc82(l.requestUrl, l.requestUrl)}`);
|
|
2169
|
+
say(` ${l.buttonPath} \xB7 ${l.eta}`);
|
|
2170
|
+
}
|
|
2171
|
+
say(` ${dim("Run polymath-society again once a download lands \u2014 it finds the zip on its own.")}`);
|
|
2172
|
+
say();
|
|
2173
|
+
}
|
|
2155
2174
|
function describeExport(f, i, included, hadPrevious) {
|
|
2156
2175
|
const kind = {
|
|
2157
2176
|
chatgpt: "ChatGPT export",
|
|
@@ -2327,20 +2346,7 @@ async function runWizard() {
|
|
|
2327
2346
|
const defaults = found.map(() => true);
|
|
2328
2347
|
say(` Found ${dim("(identified locally from the files themselves)")}:`);
|
|
2329
2348
|
found.forEach((f, i) => say(describeExport(f, i, defaults[i], hadPrevious)));
|
|
2330
|
-
|
|
2331
|
-
const { EXPORT_LINKS: EXPORT_LINKS2, osc8: osc82 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
|
|
2332
|
-
const haveKinds = new Set(found.map((f) => f.kind));
|
|
2333
|
-
const missing = EXPORT_LINKS2.filter((l) => l.requestUrl && !haveKinds.has(l.kind));
|
|
2334
|
-
if (missing.length) {
|
|
2335
|
-
say();
|
|
2336
|
-
say(` ${bold("Not on this machine yet")} \u2014 one click to request each:`);
|
|
2337
|
-
for (const l of missing) {
|
|
2338
|
-
say(` ${bold(l.label)} ${osc82(l.requestUrl, l.requestUrl)}`);
|
|
2339
|
-
say(` ${l.buttonPath} \xB7 ${l.eta}`);
|
|
2340
|
-
}
|
|
2341
|
-
say();
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2349
|
+
await sayExportLinks(say, new Set(found.map((f) => f.kind)));
|
|
2344
2350
|
const include = await pick2(defaults, hadPrevious);
|
|
2345
2351
|
const manifest = {
|
|
2346
2352
|
approvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2360,16 +2366,7 @@ async function runWizard() {
|
|
|
2360
2366
|
}
|
|
2361
2367
|
} else {
|
|
2362
2368
|
await writeManifest(dataDir, { approvedAt: (/* @__PURE__ */ new Date()).toISOString(), agents, exports: { included: [], excluded: [] } });
|
|
2363
|
-
|
|
2364
|
-
const { EXPORT_LINKS: EXPORT_LINKS2, osc8: osc82 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
|
|
2365
|
-
const missing = EXPORT_LINKS2.filter((l) => l.requestUrl);
|
|
2366
|
-
say();
|
|
2367
|
-
say(` ${bold("You can request these exports")} \u2014 one click each:`);
|
|
2368
|
-
for (const l of missing) {
|
|
2369
|
-
say(` ${bold(l.label)} ${osc82(l.requestUrl, l.requestUrl)}`);
|
|
2370
|
-
say(` ${l.buttonPath} \xB7 ${l.eta}`);
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2369
|
+
await sayExportLinks(say, /* @__PURE__ */ new Set());
|
|
2373
2370
|
}
|
|
2374
2371
|
{
|
|
2375
2372
|
const { EXPORT_LINKS: EXPORT_LINKS2 } = await Promise.resolve().then(() => (init_exportLinks(), exportLinks_exports));
|
|
@@ -24866,7 +24863,7 @@ function apportionHeavy(budget) {
|
|
|
24866
24863
|
const grade = Math.max(2, Math.floor(budget * 0.45));
|
|
24867
24864
|
const digest = Math.max(1, Math.floor(budget * 0.17));
|
|
24868
24865
|
const walkthrough = Math.max(1, Math.floor(budget * 0.17));
|
|
24869
|
-
const delegation = Math.max(1, budget - grade - digest - walkthrough -
|
|
24866
|
+
const delegation = Math.max(1, budget - grade - digest - walkthrough - 2);
|
|
24870
24867
|
return { grade, digest, walkthrough, delegation };
|
|
24871
24868
|
}
|
|
24872
24869
|
function stages(o) {
|
|
@@ -24899,6 +24896,14 @@ function stages(o) {
|
|
|
24899
24896
|
{ script: "coding-walkthrough.mts", label: "Per-session walkthroughs", llm: true, args: ["--model", model, "--conc", String(h.walkthrough)], batch: "heavy" },
|
|
24900
24897
|
{ script: "coding-frontier-detail.mts", label: "What you're doing at the frontier", llm: true, args: [], out: "coding/frontier-detail.json", batch: "heavy" },
|
|
24901
24898
|
{ script: "coding-delegation.mts", label: "Delegation patterns", llm: true, args: ["--model", model, "--conc", String(h.delegation)], out: "coding/delegation-rollup.json", batch: "heavy" },
|
|
24899
|
+
// The FULL focus stage: flow-rule judgment + the paste-into-CLAUDE.md block.
|
|
24900
|
+
// The deterministic --skip-llm run above keeps the numbers current every
|
|
24901
|
+
// run; this one produces the AI block — it was MISSING from the graded tier
|
|
24902
|
+
// entirely (688d788 added focus for the numbers-only tier and no one added
|
|
24903
|
+
// the companion), so overnight full runs shipped a report with no flow
|
|
24904
|
+
// rules and nothing failed loud (found 2026-07-15). Both LLM passes are
|
|
24905
|
+
// prompt-hash gated, so an unchanged corpus re-run costs zero calls.
|
|
24906
|
+
{ script: "coding-focus.mts", label: "Flow rules + focus judgment", llm: true, args: [], out: "coding/focus.json", batch: "heavy" },
|
|
24902
24907
|
// ---- corpus aggregate (reads grades; deterministic rollup) ----
|
|
24903
24908
|
{ script: "coding-aggregate.mts", label: "Corpus aggregate \u2014 ability ceiling + flow", llm: false, args: [], out: "coding/aggregate.json" },
|
|
24904
24909
|
// ---- grade readers (grades/* is complete once aggregate ran) ----
|
|
@@ -25256,7 +25261,19 @@ async function runChatPipeline(o) {
|
|
|
25256
25261
|
}
|
|
25257
25262
|
log.event("ingest-export", { ok, ms: Date.now() - stStart, source: f.kind });
|
|
25258
25263
|
}
|
|
25264
|
+
const minNewDocs = Number(process.env.POLYMATH_MIN_NEW_DOCS ?? 10);
|
|
25265
|
+
const signalDocs = async () => {
|
|
25266
|
+
const t = await fs32.readFile(path37.join(dataRoot, "signals", "index.jsonl"), "utf8").catch(() => "");
|
|
25267
|
+
return t ? t.split("\n").filter(Boolean).length : 0;
|
|
25268
|
+
};
|
|
25269
|
+
const docsBeforeTagger = await signalDocs();
|
|
25270
|
+
let upToDate = false;
|
|
25259
25271
|
for (const st of chatEngineStages()) {
|
|
25272
|
+
if (upToDate && st.llm) {
|
|
25273
|
+
o.onStage?.(++idx, total, `${st.label} \u2014 up to date, skipped`, st.llm);
|
|
25274
|
+
log.event(st.script.replace(/\.mts$/, ""), { ok: true, ms: 0, skipped: true });
|
|
25275
|
+
continue;
|
|
25276
|
+
}
|
|
25260
25277
|
o.onStage?.(++idx, total, st.label, st.llm);
|
|
25261
25278
|
const stStart = Date.now();
|
|
25262
25279
|
let ok = true;
|
|
@@ -25270,6 +25287,14 @@ async function runChatPipeline(o) {
|
|
|
25270
25287
|
}
|
|
25271
25288
|
const sha = st.out ? await fileSha(path37.join(dataRoot, st.out)) : null;
|
|
25272
25289
|
log.event(st.script.replace(/\.mts$/, ""), { ok, ms: Date.now() - stStart, ...sha ? { sha } : {} });
|
|
25290
|
+
if (st.script === "run-tagger.mts" && ok && minNewDocs > 0) {
|
|
25291
|
+
const newDocs = await signalDocs() - docsBeforeTagger;
|
|
25292
|
+
const report = await fs32.readFile(path37.join(dataRoot, "engine-pilot", "public-report.json"), "utf8").then((s) => JSON.parse(s)).catch(() => null);
|
|
25293
|
+
if (report?.headline && (report.spikes?.length ?? 0) > 0 && newDocs < minNewDocs) {
|
|
25294
|
+
upToDate = true;
|
|
25295
|
+
o.onLine?.(`\u2713 chat report is up to date \u2014 ${newDocs} new doc(s) since the last full run (below the ${minNewDocs}-doc threshold), so the AI stages are skipped and the existing report is served as-is. POLYMATH_MIN_NEW_DOCS=0 forces a full run.`);
|
|
25296
|
+
}
|
|
25297
|
+
}
|
|
25273
25298
|
}
|
|
25274
25299
|
log.event("pipeline_completed", { ok: failed.length === 0, failedStages: failed.length, ms: Date.now() - t0 });
|
|
25275
25300
|
await log.finish();
|
|
@@ -16902,6 +16902,14 @@ async function run2() {
|
|
|
16902
16902
|
}
|
|
16903
16903
|
}
|
|
16904
16904
|
if (!sigs.length) throw new Error("loops: empty signals index \u2014 refusing to write");
|
|
16905
|
+
const fingerprint = `${sigs.length}:${sigs.reduce((a, s) => s.date > a ? s.date : a, "")}`;
|
|
16906
|
+
if (!process.env.LOOPS_REFRESH) {
|
|
16907
|
+
const prev2 = await fs6.readFile(OUT2, "utf8").then(JSON.parse).catch(() => null);
|
|
16908
|
+
if (prev2?.inputFingerprint === fingerprint && prev2?.loops?.loops?.length) {
|
|
16909
|
+
console.log(`loops: inputs unchanged (${sigs.length} signals) \u2014 keeping ${prev2.generatedAt} (LOOPS_REFRESH=1 to redo)`);
|
|
16910
|
+
return;
|
|
16911
|
+
}
|
|
16912
|
+
}
|
|
16905
16913
|
const artifacts = importArtifactDates(sigs);
|
|
16906
16914
|
const dated = sigs.filter((s) => !artifacts.has(s.date));
|
|
16907
16915
|
const chats = dated.filter((s) => s.source === "chatgpt" || s.source === "claude");
|
|
@@ -16988,6 +16996,8 @@ ${sample.join("\n")}`;
|
|
|
16988
16996
|
rules.sort((a, b) => a.date.localeCompare(b.date));
|
|
16989
16997
|
const out = {
|
|
16990
16998
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16999
|
+
inputFingerprint: fingerprint,
|
|
17000
|
+
// the skip gate above compares against this
|
|
16991
17001
|
totals: { signals: sigs.length, dated: dated.length, chats: chats.length, ruleDeclarations: rules.length, importDatesExcluded: [...artifacts] },
|
|
16992
17002
|
chatsByMonth: monthCounts(chats.map((c) => c.date)),
|
|
16993
17003
|
rules,
|
|
@@ -135,6 +135,19 @@ var require_secure_json_parse = __commonJS({
|
|
|
135
135
|
|
|
136
136
|
// ../../scripts/coding-focus.mts
|
|
137
137
|
import { promises as fs6 } from "fs";
|
|
138
|
+
|
|
139
|
+
// ../../lib/agents/coding/promptCache.ts
|
|
140
|
+
import { createHash } from "crypto";
|
|
141
|
+
function promptSha(parts) {
|
|
142
|
+
const h = createHash("sha256");
|
|
143
|
+
for (const p of parts) {
|
|
144
|
+
h.update(p ?? "");
|
|
145
|
+
h.update("\0");
|
|
146
|
+
}
|
|
147
|
+
return h.digest("hex").slice(0, 16);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ../../scripts/coding-focus.mts
|
|
138
151
|
import path10 from "path";
|
|
139
152
|
|
|
140
153
|
// ../../lib/agents/shared/agent.ts
|
|
@@ -16170,7 +16183,13 @@ async function run2() {
|
|
|
16170
16183
|
|
|
16171
16184
|
SESSIONS:
|
|
16172
16185
|
${JSON.stringify(listing, null, 1)}`;
|
|
16173
|
-
|
|
16186
|
+
const clusterSha = promptSha([cprompt]);
|
|
16187
|
+
if (prev?.features?.length && prev?.featuresPromptSha === clusterSha && !process.env.FOCUS_REFRESH) {
|
|
16188
|
+
out.features = prev.features;
|
|
16189
|
+
out.featuresDaysUnit = prev.featuresDaysUnit ?? "min";
|
|
16190
|
+
out.featuresPromptSha = clusterSha;
|
|
16191
|
+
console.log("focus: cluster inputs unchanged \u2014 reusing features (FOCUS_REFRESH=1 to redo)");
|
|
16192
|
+
} else try {
|
|
16174
16193
|
const cr = await invokeAgent({ prompt: cprompt, systemPrompt: "You produce strict JSON only.", maxTurns: 3, timeoutMs: 48e4, model: "sonnet" });
|
|
16175
16194
|
let cj = cr.json ?? null;
|
|
16176
16195
|
if (!cj) cj = parseModelJson(cr.raw);
|
|
@@ -16205,6 +16224,7 @@ ${JSON.stringify(listing, null, 1)}`;
|
|
|
16205
16224
|
}
|
|
16206
16225
|
out.features = feats.sort((a, b) => Object.keys(b.days).length - Object.keys(a.days).length);
|
|
16207
16226
|
out.featuresDaysUnit = "min";
|
|
16227
|
+
out.featuresPromptSha = clusterSha;
|
|
16208
16228
|
console.log(`focus: ${out.features.length} semantic features from ${recentSess.length} sessions (${stray.length} unmapped \u2192 Everything else)`);
|
|
16209
16229
|
} else console.error(`focus: feature clustering returned no usable JSON (raw ${String(cr.raw || "").length} chars)`);
|
|
16210
16230
|
} catch (e) {
|
|
@@ -16229,7 +16249,11 @@ ${examples}
|
|
|
16229
16249
|
${summary}
|
|
16230
16250
|
|
|
16231
16251
|
Return the Part 5 JSON only.`;
|
|
16232
|
-
|
|
16252
|
+
const judgeSha = promptSha([prompt, process.env.FOCUS_JUDGE_MODEL || "opus"]);
|
|
16253
|
+
if (prev?.llm?.promptSha === judgeSha && !process.env.FOCUS_REFRESH) {
|
|
16254
|
+
out.llm = prev.llm;
|
|
16255
|
+
console.log("focus: judgment inputs unchanged \u2014 reusing llm block (FOCUS_REFRESH=1 to redo)");
|
|
16256
|
+
} else try {
|
|
16233
16257
|
const judgeModel = process.env.FOCUS_JUDGE_MODEL || "opus";
|
|
16234
16258
|
const run22 = await invokeAgent({ prompt, systemPrompt: "You produce strict JSON per the rubric's output contract. No prose outside the JSON.", maxTurns: 3, timeoutMs: 48e4, model: judgeModel });
|
|
16235
16259
|
let parsed = run22.json ?? null;
|
|
@@ -16242,7 +16266,7 @@ Return the Part 5 JSON only.`;
|
|
|
16242
16266
|
The block to append in step 1:
|
|
16243
16267
|
${parsed.claudeMdSnippet}`;
|
|
16244
16268
|
}
|
|
16245
|
-
if (parsed && (parsed.causalFindings?.length || parsed.flowKinds)) out.llm = { ...parsed, model: judgeModel, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
16269
|
+
if (parsed && (parsed.causalFindings?.length || parsed.flowKinds)) out.llm = { ...parsed, model: judgeModel, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), promptSha: judgeSha };
|
|
16246
16270
|
else console.error(`focus: LLM pass returned no usable JSON (raw ${String(run22.raw || "").length} chars) \u2014 deterministic data written, causal block empty`);
|
|
16247
16271
|
} catch (e) {
|
|
16248
16272
|
console.error("focus: LLM pass failed \u2014", e.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polymath-society",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
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",
|