polymath-society 0.2.4

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.
Files changed (45) hide show
  1. package/LICENSE +52 -0
  2. package/README.md +311 -0
  3. package/dist/DATA-MAP.md +109 -0
  4. package/dist/cli.js +24136 -0
  5. package/dist/engine/closing-rubric.md +50 -0
  6. package/dist/engine/exp-allfacets.js +16832 -0
  7. package/dist/engine/exp-person.js +16922 -0
  8. package/dist/engine/exp-pipeline.js +16700 -0
  9. package/dist/engine/feel-seen-rubric.md +192 -0
  10. package/dist/engine/ingest-export.js +1423 -0
  11. package/dist/engine/peak-demos.js +16752 -0
  12. package/dist/engine/person-dimension-summary.js +16744 -0
  13. package/dist/engine/person-facet-lines.js +16784 -0
  14. package/dist/engine/person-headline.js +16733 -0
  15. package/dist/engine/person-report.js +16845 -0
  16. package/dist/engine/person-self-image.js +16689 -0
  17. package/dist/engine/public-report-guidelines.md +80 -0
  18. package/dist/engine/public-report.js +17284 -0
  19. package/dist/engine/run-analysis.js +16035 -0
  20. package/dist/engine/run-tagger.js +16092 -0
  21. package/dist/engine/top-companies.md +41 -0
  22. package/dist/engine/writing-well.md +56 -0
  23. package/dist/index.js +22021 -0
  24. package/dist/pipeline/TECHNIQUES.md +406 -0
  25. package/dist/pipeline/WRITING.md +48 -0
  26. package/dist/pipeline/coding-agglomerate.js +15876 -0
  27. package/dist/pipeline/coding-aggregate.js +440 -0
  28. package/dist/pipeline/coding-build.js +1168 -0
  29. package/dist/pipeline/coding-coaching.js +16893 -0
  30. package/dist/pipeline/coding-day-digest.js +15869 -0
  31. package/dist/pipeline/coding-delegation.js +16292 -0
  32. package/dist/pipeline/coding-expertise.js +15925 -0
  33. package/dist/pipeline/coding-flow.js +313 -0
  34. package/dist/pipeline/coding-frontier-detail.js +15878 -0
  35. package/dist/pipeline/coding-frontier.js +51 -0
  36. package/dist/pipeline/coding-gap-dist.js +293 -0
  37. package/dist/pipeline/coding-gap.js +17108 -0
  38. package/dist/pipeline/coding-grade.js +16195 -0
  39. package/dist/pipeline/coding-nutshell.js +15725 -0
  40. package/dist/pipeline/coding-projects.js +104 -0
  41. package/dist/pipeline/coding-walkthrough.js +15947 -0
  42. package/dist/web/app.js +12024 -0
  43. package/dist/web/index.html +1 -0
  44. package/dist/web/styles.css +1 -0
  45. package/package.json +61 -0
@@ -0,0 +1,1168 @@
1
+ import{createRequire as __cr}from'module';const require=__cr(import.meta.url);
2
+
3
+ // ../../scripts/coding-build.mts
4
+ import { promises as fs3 } from "fs";
5
+ import path2 from "path";
6
+
7
+ // ../coding-core/dist/raw.js
8
+ import { promises as fs } from "fs";
9
+ import path from "path";
10
+ import os from "os";
11
+
12
+ // ../coding-core/dist/injected.js
13
+ var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
14
+ var INJECTED_RES = [
15
+ /<task-notification>[\s\S]*?<\/task-notification>/g,
16
+ /<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g,
17
+ /<local-command-caveat>[\s\S]*?<\/local-command-caveat>/g,
18
+ /<ide_opened_file>[\s\S]*?<\/ide_opened_file>/g
19
+ ];
20
+ var HARNESS_TURN_RES = [
21
+ /^Non user activity$/i,
22
+ /^<<autonomous-loop(-dynamic)?>>$/,
23
+ /^This session is being continued from a previous conversation/,
24
+ // the loop skill's expanded wake-up prompt — older CLI versions wrote it as a
25
+ // plain user turn with no isMeta flag (found counting 601 words, 2026-07-07)
26
+ /^(-{3,}\s*)?#\s*Autonomous loop (check|tick)/
27
+ ];
28
+ function stripInjected(t) {
29
+ for (const re of INJECTED_RES)
30
+ t = t.replace(re, "");
31
+ return t.replace(SYSTEM_REMINDER_RE, "");
32
+ }
33
+ function isHarnessTurn(t) {
34
+ const s = t.trim();
35
+ return HARNESS_TURN_RES.some((re) => re.test(s));
36
+ }
37
+ function humanTypedText(t) {
38
+ const s = stripInjected(t).trim();
39
+ return s && !isHarnessTurn(s) ? s : "";
40
+ }
41
+ function isHarnessEntry(e) {
42
+ return e.isMeta === true || e.isCompactSummary === true;
43
+ }
44
+
45
+ // ../coding-core/dist/raw.js
46
+ var SOURCE_ROOT_ENV_VARS = [
47
+ "CLAUDE_PROJECTS_DIR",
48
+ "CODEX_SESSIONS_DIR",
49
+ "CURSOR_WORKSPACE_DIR",
50
+ "CURSOR_PROJECTS_DIR"
51
+ ];
52
+ var anyRootOverridden = () => SOURCE_ROOT_ENV_VARS.some((v) => !!process.env[v]);
53
+ function resolveSourceRoot(envVar, ...defaultSegments) {
54
+ const explicit = process.env[envVar];
55
+ if (explicit)
56
+ return explicit;
57
+ if (anyRootOverridden())
58
+ return null;
59
+ return path.join(os.homedir(), ...defaultSegments);
60
+ }
61
+ var claudeProjectsRoot = () => resolveSourceRoot("CLAUDE_PROJECTS_DIR", ".claude", "projects");
62
+ var codexSessionsRoot = () => resolveSourceRoot("CODEX_SESSIONS_DIR", ".codex", "sessions");
63
+ var cursorWorkspaceRoot = () => resolveSourceRoot("CURSOR_WORKSPACE_DIR", "Library", "Application Support", "Cursor", "User", "workspaceStorage");
64
+ var cursorProjectsRoot = () => resolveSourceRoot("CURSOR_PROJECTS_DIR", ".cursor", "projects");
65
+ var SKIP_DIR_RE = /(private-var-folders|-T-ps-|--data(-|$)|-sources(-|$)|--conversations|-node-modules-|-tmp(-|$)|-polymath-rehearsal-|-polymath-overnight-)/;
66
+ var AGENT_CWD_RE = /(\/\.data(\/|$)|\.polymath-society|\/sources(\/|$)|\/var\/folders\/|\/T\/ps-|\/timeline(\/|$)|onboarding-digest|engine-pilot|polymath-rehearsal|polymath-overnight)/;
67
+ var PERSONA_RE = /^You are (a|an|the|\w+ing)\b/;
68
+ var OUTPUT_CONTRACT_RES = [
69
+ /\b(output|return|respond|reply|answer|emit)\b[^\n.]{0,80}\b(only|single|exactly)\b[\s\S]{0,160}?(```|fenced|json)\b/i,
70
+ /\b(reply|respond|answer) with only\b/i,
71
+ /\bONLY a (fenced|```)/i,
72
+ /output a single fenced/i
73
+ ];
74
+ var CAPS_HEADER_RE = /^[A-Z]{2,}[^\n]{0,100}?(?::|\s[–—-])\s/gm;
75
+ function machineShape(fh) {
76
+ if (PERSONA_RE.test(fh))
77
+ return "shape:persona-opener";
78
+ if (OUTPUT_CONTRACT_RES.some((re) => re.test(fh)))
79
+ return "shape:output-contract";
80
+ const headers = new Set((fh.match(CAPS_HEADER_RE) ?? []).map((h) => h.split(/[\s:(]/)[0]));
81
+ if (headers.size >= 2)
82
+ return "shape:caps-scaffold";
83
+ return null;
84
+ }
85
+ function extractText(content) {
86
+ if (typeof content === "string")
87
+ return content;
88
+ if (Array.isArray(content)) {
89
+ const parts = [];
90
+ for (const item of content) {
91
+ if (item && typeof item === "object" && item.type === "text") {
92
+ parts.push(item.text || "");
93
+ } else if (typeof item === "string")
94
+ parts.push(item);
95
+ }
96
+ return parts.join("\n");
97
+ }
98
+ return "";
99
+ }
100
+ function humanText(content) {
101
+ if (Array.isArray(content) && content.some((c) => c && typeof c === "object" && c.type === "tool_result")) {
102
+ const t2 = humanTypedText(extractText(content));
103
+ return t2.length > 0 && !content.every((c) => c.type === "tool_result") ? t2 : null;
104
+ }
105
+ const t = humanTypedText(extractText(content));
106
+ return t.length > 0 ? t : null;
107
+ }
108
+ var PING_RE = /^(reply\s*(with|only|:)|say (ok|the word)|acknowledge (session|setup|request)|ok|okay|k|yes|sure|got it|ls|auth_ok|ready|done)\b/i;
109
+ function classify(s) {
110
+ if (s.cwd && AGENT_CWD_RE.test(s.cwd))
111
+ return { klass: "agent", reason: `cwd:${s.cwd}` };
112
+ if (s.humanTurns === 0)
113
+ return { klass: "agent", reason: "no-human-text" };
114
+ if (s.humanTurns <= 2 && s.humanChars > 8e3)
115
+ return { klass: "agent", reason: "mega-prompt" };
116
+ const fh = s.firstHuman.trim();
117
+ if (s.humanTurns <= 2 && fh.length <= 50 && PING_RE.test(fh))
118
+ return { klass: "agent", reason: "ping/ack" };
119
+ if (s.humanTurns <= 2) {
120
+ const shape = machineShape(fh);
121
+ if (shape)
122
+ return { klass: "agent", reason: shape };
123
+ }
124
+ return { klass: "interactive", reason: "human-session" };
125
+ }
126
+ function pushDedup(arr, v) {
127
+ if (v && arr[arr.length - 1] !== v)
128
+ arr.push(v);
129
+ }
130
+ function blank(sessionId, source, file) {
131
+ return {
132
+ sessionId,
133
+ source,
134
+ file,
135
+ cwd: null,
136
+ gitBranch: null,
137
+ entrypoint: null,
138
+ originator: null,
139
+ versions: [],
140
+ events: [],
141
+ titles: [],
142
+ modes: [],
143
+ permissionModes: [],
144
+ collaborationModes: [],
145
+ attachments: 0,
146
+ imageAttachments: 0,
147
+ queueOps: 0,
148
+ subagentSpawns: 0,
149
+ sidechainTurns: 0,
150
+ mcpTools: [],
151
+ toolCounts: {},
152
+ models: {},
153
+ tokens: { input: 0, output: 0, cacheRead: 0, cacheCreation: 0 },
154
+ humanTurns: 0,
155
+ humanChars: 0,
156
+ aiTurns: 0,
157
+ firstHuman: "",
158
+ klass: "interactive",
159
+ klassReason: ""
160
+ };
161
+ }
162
+ var ms = (ts) => {
163
+ if (typeof ts !== "string")
164
+ return null;
165
+ const n = Date.parse(ts);
166
+ return Number.isFinite(n) ? n : null;
167
+ };
168
+ function parseClaudeCode(raw, sessionId, file) {
169
+ const s = blank(sessionId, "claude-code", file);
170
+ const seen = /* @__PURE__ */ new Set();
171
+ for (const lineRaw of raw.split("\n")) {
172
+ const line = lineRaw.trim();
173
+ if (!line)
174
+ continue;
175
+ let e;
176
+ try {
177
+ e = JSON.parse(line);
178
+ } catch {
179
+ continue;
180
+ }
181
+ if (typeof e.cwd === "string" && !s.cwd)
182
+ s.cwd = e.cwd;
183
+ if (typeof e.gitBranch === "string" && !s.gitBranch)
184
+ s.gitBranch = e.gitBranch;
185
+ if (typeof e.entrypoint === "string" && !s.entrypoint)
186
+ s.entrypoint = e.entrypoint;
187
+ if (typeof e.version === "string" && !s.versions.includes(e.version))
188
+ s.versions.push(e.version);
189
+ if (typeof e.permissionMode === "string")
190
+ pushDedup(s.permissionModes, e.permissionMode);
191
+ if (e.isSidechain === true && e.type === "assistant")
192
+ s.sidechainTurns++;
193
+ const t = ms(e.timestamp);
194
+ const type = e.type;
195
+ if (type === "ai-title") {
196
+ pushDedup(s.titles, e.aiTitle);
197
+ continue;
198
+ }
199
+ if (type === "mode") {
200
+ pushDedup(s.modes, e.mode ?? null);
201
+ continue;
202
+ }
203
+ if (type === "queue-operation") {
204
+ if (e.operation === "enqueue" && typeof e.content === "string" && humanTypedText(e.content)) {
205
+ s.queueOps++;
206
+ if (t)
207
+ s.events.push({ t, k: "queue" });
208
+ }
209
+ continue;
210
+ }
211
+ if (type === "attachment") {
212
+ s.attachments++;
213
+ const at = e.attachment?.type;
214
+ if (at === "image" || at === "pasted_image")
215
+ s.imageAttachments++;
216
+ continue;
217
+ }
218
+ if (type === "user") {
219
+ if (isHarnessEntry(e))
220
+ continue;
221
+ const msg = e.message ?? {};
222
+ const dedupKey = typeof e.uuid === "string" ? `u:${e.uuid}` : "";
223
+ if (dedupKey && seen.has(dedupKey))
224
+ continue;
225
+ if (dedupKey)
226
+ seen.add(dedupKey);
227
+ const ht = humanText(msg.content);
228
+ if (ht) {
229
+ s.humanTurns++;
230
+ s.humanChars += ht.length;
231
+ if (!s.firstHuman)
232
+ s.firstHuman = ht.slice(0, 4e3);
233
+ if (t)
234
+ s.events.push({ t, k: "human" });
235
+ }
236
+ continue;
237
+ }
238
+ if (type === "assistant") {
239
+ const msg = e.message ?? {};
240
+ s.aiTurns++;
241
+ if (t)
242
+ s.events.push({ t, k: "ai" });
243
+ if (typeof msg.model === "string")
244
+ s.models[msg.model] = (s.models[msg.model] ?? 0) + 1;
245
+ const u = msg.usage ?? {};
246
+ s.tokens.input += u.input_tokens ?? 0;
247
+ s.tokens.output += u.output_tokens ?? 0;
248
+ s.tokens.cacheRead += u.cache_read_input_tokens ?? 0;
249
+ s.tokens.cacheCreation += u.cache_creation_input_tokens ?? 0;
250
+ if (Array.isArray(msg.content)) {
251
+ for (const item of msg.content) {
252
+ if (item && typeof item === "object" && item.type === "tool_use") {
253
+ const name = String(item.name ?? "tool");
254
+ s.toolCounts[name] = (s.toolCounts[name] ?? 0) + 1;
255
+ if (name === "Agent" || name === "Task")
256
+ s.subagentSpawns++;
257
+ if (name.startsWith("mcp__") && !s.mcpTools.includes(name))
258
+ s.mcpTools.push(name);
259
+ if (t)
260
+ s.events.push({ t, k: "tool" });
261
+ }
262
+ }
263
+ }
264
+ continue;
265
+ }
266
+ }
267
+ if (s.events.length === 0 && s.humanTurns === 0)
268
+ return null;
269
+ s.events.sort((a, b) => a.t - b.t);
270
+ const c = classify(s);
271
+ s.klass = c.klass;
272
+ s.klassReason = c.reason;
273
+ return s;
274
+ }
275
+ function codexText(content) {
276
+ if (typeof content === "string")
277
+ return content;
278
+ if (Array.isArray(content)) {
279
+ const parts = [];
280
+ for (const item of content) {
281
+ if (typeof item === "string") {
282
+ parts.push(item);
283
+ continue;
284
+ }
285
+ if (item && typeof item === "object") {
286
+ const it = item;
287
+ if (it.type === "input_text" || it.type === "output_text" || it.type === "text")
288
+ parts.push(it.text || "");
289
+ }
290
+ }
291
+ return parts.join("\n");
292
+ }
293
+ return "";
294
+ }
295
+ var CODEX_INJECTED_RE = /^\s*<(environment_context|user_instructions)>/;
296
+ function parseCodex(raw, file) {
297
+ let sessionId = path.basename(file, ".jsonl");
298
+ const s = blank(sessionId, "codex", file);
299
+ for (const lineRaw of raw.split("\n")) {
300
+ const line = lineRaw.trim();
301
+ if (!line)
302
+ continue;
303
+ let e;
304
+ try {
305
+ e = JSON.parse(line);
306
+ } catch {
307
+ continue;
308
+ }
309
+ const t = ms(e.timestamp);
310
+ const p = e.payload ?? {};
311
+ if (e.type === "session_meta") {
312
+ if (typeof p.id === "string") {
313
+ s.sessionId = p.id;
314
+ sessionId = p.id;
315
+ }
316
+ if (typeof p.cwd === "string")
317
+ s.cwd = p.cwd;
318
+ if (typeof p.originator === "string")
319
+ s.originator = p.originator;
320
+ if (typeof p.cli_version === "string" && !s.versions.includes(p.cli_version))
321
+ s.versions.push(p.cli_version);
322
+ continue;
323
+ }
324
+ if (e.type === "event_msg" && p.type === "task_started") {
325
+ if (typeof p.collaboration_mode_kind === "string")
326
+ pushDedup(s.collaborationModes, p.collaboration_mode_kind);
327
+ continue;
328
+ }
329
+ if (e.type === "response_item") {
330
+ const ptype = p.type;
331
+ if (ptype === "message") {
332
+ const role = p.role;
333
+ const text = codexText(p.content).replace(SYSTEM_REMINDER_RE, "").trim();
334
+ if (role === "user") {
335
+ if (text && !CODEX_INJECTED_RE.test(text)) {
336
+ s.humanTurns++;
337
+ s.humanChars += text.length;
338
+ if (!s.firstHuman)
339
+ s.firstHuman = text.slice(0, 4e3);
340
+ if (t)
341
+ s.events.push({ t, k: "human" });
342
+ }
343
+ } else if (role === "assistant") {
344
+ s.aiTurns++;
345
+ if (t)
346
+ s.events.push({ t, k: "ai" });
347
+ }
348
+ continue;
349
+ }
350
+ if (ptype === "function_call" || ptype === "local_shell_call" || ptype === "custom_tool_call") {
351
+ const name = String(p.name ?? ptype);
352
+ s.toolCounts[name] = (s.toolCounts[name] ?? 0) + 1;
353
+ if (t)
354
+ s.events.push({ t, k: "tool" });
355
+ continue;
356
+ }
357
+ }
358
+ }
359
+ if (s.events.length === 0 && s.humanTurns === 0)
360
+ return null;
361
+ s.events.sort((a, b) => a.t - b.t);
362
+ const c = classify(s);
363
+ s.klass = c.klass;
364
+ s.klassReason = c.reason;
365
+ return s;
366
+ }
367
+ async function readClaudeCode() {
368
+ const base = claudeProjectsRoot();
369
+ const out = [];
370
+ if (!base)
371
+ return out;
372
+ let dirs = [];
373
+ try {
374
+ dirs = await fs.readdir(base);
375
+ } catch {
376
+ return out;
377
+ }
378
+ for (const dir of dirs) {
379
+ if (SKIP_DIR_RE.test(dir))
380
+ continue;
381
+ const full = path.join(base, dir);
382
+ let files = [];
383
+ try {
384
+ files = (await fs.readdir(full)).filter((f) => f.endsWith(".jsonl"));
385
+ } catch {
386
+ continue;
387
+ }
388
+ for (const f of files) {
389
+ const file = path.join(full, f);
390
+ let raw;
391
+ try {
392
+ raw = await fs.readFile(file, "utf-8");
393
+ } catch {
394
+ continue;
395
+ }
396
+ const s = parseClaudeCode(raw, path.basename(f, ".jsonl"), file);
397
+ if (s)
398
+ out.push(s);
399
+ }
400
+ }
401
+ return out;
402
+ }
403
+ async function walkJsonl(dir, acc) {
404
+ let entries = [];
405
+ try {
406
+ entries = await fs.readdir(dir, { withFileTypes: true });
407
+ } catch {
408
+ return;
409
+ }
410
+ for (const ent of entries) {
411
+ const full = path.join(dir, ent.name);
412
+ if (ent.isDirectory())
413
+ await walkJsonl(full, acc);
414
+ else if (ent.name.endsWith(".jsonl"))
415
+ acc.push(full);
416
+ }
417
+ }
418
+ async function readCodex() {
419
+ const base = codexSessionsRoot();
420
+ if (!base)
421
+ return [];
422
+ const files = [];
423
+ await walkJsonl(base, files);
424
+ const out = [];
425
+ for (const file of files) {
426
+ let raw;
427
+ try {
428
+ raw = await fs.readFile(file, "utf-8");
429
+ } catch {
430
+ continue;
431
+ }
432
+ const s = parseCodex(raw, file);
433
+ if (s)
434
+ out.push(s);
435
+ }
436
+ return out;
437
+ }
438
+ var CURSOR_USER_QUERY_RE = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/;
439
+ function fileUriToPath(uri) {
440
+ if (typeof uri !== "string" || !uri)
441
+ return null;
442
+ let s = uri.replace(/^file:\/\//, "");
443
+ try {
444
+ s = decodeURIComponent(s);
445
+ } catch {
446
+ }
447
+ return s || null;
448
+ }
449
+ function cursorSlug(folderPath) {
450
+ return folderPath.replace(/^\//, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/-+$/, "");
451
+ }
452
+ var NODE_SQLITE = "node:sqlite";
453
+ async function openCursorDb(dbPath) {
454
+ let mod;
455
+ try {
456
+ mod = await import(NODE_SQLITE);
457
+ } catch {
458
+ return null;
459
+ }
460
+ const DatabaseSync = mod?.DatabaseSync;
461
+ if (!DatabaseSync)
462
+ return null;
463
+ let db;
464
+ try {
465
+ db = new DatabaseSync(dbPath, { readOnly: true });
466
+ } catch {
467
+ try {
468
+ db = new DatabaseSync(dbPath);
469
+ } catch {
470
+ return null;
471
+ }
472
+ }
473
+ return {
474
+ get(key) {
475
+ try {
476
+ const row = db.prepare("SELECT value FROM ItemTable WHERE key = ?").get(key);
477
+ if (!row || row.value == null)
478
+ return [];
479
+ const txt = typeof row.value === "string" ? row.value : Buffer.from(row.value).toString("utf-8");
480
+ const parsed = JSON.parse(txt);
481
+ return Array.isArray(parsed) ? parsed : Object.values(parsed ?? {});
482
+ } catch {
483
+ return [];
484
+ }
485
+ },
486
+ close() {
487
+ try {
488
+ db.close();
489
+ } catch {
490
+ }
491
+ }
492
+ };
493
+ }
494
+ async function readCursorWorkspaces() {
495
+ const base = cursorWorkspaceRoot();
496
+ const out = /* @__PURE__ */ new Map();
497
+ if (!base)
498
+ return out;
499
+ let dirs = [];
500
+ try {
501
+ dirs = await fs.readdir(base, { withFileTypes: true });
502
+ } catch {
503
+ return out;
504
+ }
505
+ for (const d of dirs) {
506
+ if (!d.isDirectory())
507
+ continue;
508
+ const wsDir = path.join(base, d.name);
509
+ let folder = null;
510
+ try {
511
+ folder = fileUriToPath(JSON.parse(await fs.readFile(path.join(wsDir, "workspace.json"), "utf-8")).folder);
512
+ } catch {
513
+ }
514
+ if (!folder)
515
+ continue;
516
+ const dbFile = path.join(wsDir, "state.vscdb");
517
+ try {
518
+ await fs.access(dbFile);
519
+ } catch {
520
+ continue;
521
+ }
522
+ const db = await openCursorDb(dbFile);
523
+ if (!db)
524
+ continue;
525
+ const tsByText = /* @__PURE__ */ new Map();
526
+ const generations = [];
527
+ const prompts = [];
528
+ try {
529
+ for (const item of db.get("aiService.generations")) {
530
+ if (!item || typeof item !== "object")
531
+ continue;
532
+ const it = item;
533
+ const text = String(it.textDescription ?? "").trim();
534
+ const t = Number(it.unixMs ?? 0);
535
+ if (!text || !t)
536
+ continue;
537
+ generations.push({ text, t });
538
+ const prev = tsByText.get(text);
539
+ if (prev == null || t < prev)
540
+ tsByText.set(text, t);
541
+ }
542
+ for (const item of db.get("aiService.prompts")) {
543
+ if (!item || typeof item !== "object")
544
+ continue;
545
+ const text = String(item.text ?? "").trim();
546
+ if (text)
547
+ prompts.push(text);
548
+ }
549
+ } finally {
550
+ db.close();
551
+ }
552
+ generations.sort((a, b) => a.t - b.t);
553
+ const slug = cursorSlug(folder);
554
+ const existing = out.get(slug);
555
+ if (existing) {
556
+ for (const g of generations)
557
+ existing.generations.push(g);
558
+ for (const [k, v] of tsByText) {
559
+ const p = existing.tsByText.get(k);
560
+ if (p == null || v < p)
561
+ existing.tsByText.set(k, v);
562
+ }
563
+ for (const p of prompts)
564
+ existing.prompts.push(p);
565
+ existing.generations.sort((a, b) => a.t - b.t);
566
+ } else {
567
+ out.set(slug, { folder, slug, tsByText, generations, prompts });
568
+ }
569
+ }
570
+ return out;
571
+ }
572
+ function parseCursorTranscript(raw, composerId, file, ws, fileMtime) {
573
+ const s = blank(composerId, "cursor", file);
574
+ s.cwd = ws.folder;
575
+ let cursor = ws.generations.length ? ws.generations[0].t : fileMtime;
576
+ let humanIdx = 0;
577
+ for (const lineRaw of raw.split("\n")) {
578
+ const line = lineRaw.trim();
579
+ if (!line)
580
+ continue;
581
+ let e;
582
+ try {
583
+ e = JSON.parse(line);
584
+ } catch {
585
+ continue;
586
+ }
587
+ const role = e.role;
588
+ let text = extractText(e.message?.content).replace(SYSTEM_REMINDER_RE, "").trim();
589
+ const m = text.match(CURSOR_USER_QUERY_RE);
590
+ if (m)
591
+ text = m[1].trim();
592
+ if (!text)
593
+ continue;
594
+ if (role === "user") {
595
+ const ts = ws.tsByText.get(text) ?? (humanIdx < ws.generations.length ? ws.generations[humanIdx].t : cursor + 1);
596
+ cursor = Math.max(cursor, ts);
597
+ humanIdx++;
598
+ s.humanTurns++;
599
+ s.humanChars += text.length;
600
+ if (!s.firstHuman)
601
+ s.firstHuman = text.slice(0, 4e3);
602
+ s.events.push({ t: cursor, k: "human" });
603
+ } else if (role === "assistant") {
604
+ cursor += 1;
605
+ s.aiTurns++;
606
+ s.events.push({ t: cursor, k: "ai" });
607
+ }
608
+ }
609
+ if (s.events.length === 0 && s.humanTurns === 0)
610
+ return null;
611
+ s.events.sort((a, b) => a.t - b.t);
612
+ const c = classify(s);
613
+ s.klass = c.klass;
614
+ s.klassReason = c.reason;
615
+ return s;
616
+ }
617
+ function cursorWorkspaceSession(ws, idx) {
618
+ if (ws.generations.length === 0)
619
+ return null;
620
+ const s = blank(`cursor-${cursorSlug(ws.folder).slice(0, 12)}-${idx}`, "cursor", `cursor-workspace:${ws.folder}`);
621
+ s.cwd = ws.folder;
622
+ for (const g of ws.generations) {
623
+ s.humanTurns++;
624
+ s.humanChars += g.text.length;
625
+ if (!s.firstHuman)
626
+ s.firstHuman = g.text.slice(0, 4e3);
627
+ s.aiTurns++;
628
+ s.events.push({ t: g.t, k: "human" });
629
+ s.events.push({ t: g.t, k: "ai" });
630
+ }
631
+ if (s.events.length === 0)
632
+ return null;
633
+ s.events.sort((a, b) => a.t - b.t);
634
+ const c = classify(s);
635
+ s.klass = c.klass;
636
+ s.klassReason = c.reason;
637
+ return s;
638
+ }
639
+ async function readCursor() {
640
+ const workspaces = await readCursorWorkspaces();
641
+ const out = [];
642
+ const projects = cursorProjectsRoot();
643
+ const usedSlugs = /* @__PURE__ */ new Set();
644
+ let dirs = [];
645
+ if (projects) {
646
+ try {
647
+ dirs = await fs.readdir(projects, { withFileTypes: true });
648
+ } catch {
649
+ dirs = [];
650
+ }
651
+ }
652
+ for (const d of dirs) {
653
+ if (!d.isDirectory())
654
+ continue;
655
+ const slug = d.name;
656
+ if (SKIP_DIR_RE.test(slug))
657
+ continue;
658
+ const transcriptsDir = path.join(projects, slug, "agent-transcripts");
659
+ let composers = [];
660
+ try {
661
+ composers = await fs.readdir(transcriptsDir, { withFileTypes: true });
662
+ } catch {
663
+ continue;
664
+ }
665
+ const ws = workspaces.get(slug);
666
+ if (!ws)
667
+ continue;
668
+ usedSlugs.add(slug);
669
+ for (const c of composers) {
670
+ if (!c.isDirectory())
671
+ continue;
672
+ const composerId = c.name;
673
+ const jf = path.join(transcriptsDir, composerId, `${composerId}.jsonl`);
674
+ let raw;
675
+ let mtime = Date.now();
676
+ try {
677
+ raw = await fs.readFile(jf, "utf-8");
678
+ } catch {
679
+ continue;
680
+ }
681
+ try {
682
+ mtime = (await fs.stat(jf)).mtimeMs;
683
+ } catch {
684
+ }
685
+ const s = parseCursorTranscript(raw, composerId, jf, ws, mtime);
686
+ if (s)
687
+ out.push(s);
688
+ }
689
+ }
690
+ let i = 0;
691
+ for (const ws of workspaces.values()) {
692
+ if (usedSlugs.has(ws.slug))
693
+ continue;
694
+ const s = cursorWorkspaceSession(ws, i++);
695
+ if (s)
696
+ out.push(s);
697
+ }
698
+ return out;
699
+ }
700
+ var FANOUT_PREFIX = 80;
701
+ var FANOUT_MIN_SESSIONS = 3;
702
+ function demoteTemplateFanouts(sessions) {
703
+ const groups = /* @__PURE__ */ new Map();
704
+ for (const s of sessions) {
705
+ if (s.klass !== "interactive" || s.humanTurns > 2)
706
+ continue;
707
+ const key = s.firstHuman.replace(/\s+/g, " ").trim().slice(0, FANOUT_PREFIX);
708
+ if (key.length < FANOUT_PREFIX)
709
+ continue;
710
+ (groups.get(key) ?? groups.set(key, []).get(key)).push(s);
711
+ }
712
+ for (const g of groups.values()) {
713
+ if (g.length < FANOUT_MIN_SESSIONS)
714
+ continue;
715
+ for (const s of g) {
716
+ s.klass = "agent";
717
+ s.klassReason = `template-fanout(x${g.length})`;
718
+ }
719
+ }
720
+ }
721
+ async function readAllSessions(opts = {}) {
722
+ const [cc, cx, cr] = await Promise.all([
723
+ readClaudeCode(),
724
+ opts.codex === false ? Promise.resolve([]) : readCodex(),
725
+ opts.cursor === false ? Promise.resolve([]) : readCursor()
726
+ ]);
727
+ const all = [...cc, ...cx, ...cr];
728
+ demoteTemplateFanouts(all);
729
+ return all;
730
+ }
731
+
732
+ // ../coding-core/dist/deterministic.js
733
+ var DEFAULT_IDLE_GAP_MIN = 5;
734
+ var DEFAULT_TZ = process.env.CODING_TZ || "America/Los_Angeles";
735
+ function activeIntervals(times, idleGapMs) {
736
+ if (times.length === 0)
737
+ return [];
738
+ const xs = [...times].sort((a, b) => a - b);
739
+ const out = [];
740
+ let start = xs[0];
741
+ let prev = xs[0];
742
+ for (let i = 1; i < xs.length; i++) {
743
+ if (xs[i] - prev > idleGapMs) {
744
+ out.push([start, prev]);
745
+ start = xs[i];
746
+ }
747
+ prev = xs[i];
748
+ }
749
+ out.push([start, prev]);
750
+ return out;
751
+ }
752
+ var MIN = 6e4;
753
+ var round1 = (x) => Math.round(x * 10) / 10;
754
+ var round2 = (x) => Math.round(x * 100) / 100;
755
+ function localDate(ms2, tz) {
756
+ return new Date(ms2).toLocaleDateString("en-CA", { timeZone: tz });
757
+ }
758
+ function localTime(ms2, tz) {
759
+ return new Date(ms2).toLocaleTimeString("en-GB", { timeZone: tz, hour12: false });
760
+ }
761
+ function toSessionRecords(sessions, idleGapMin2 = DEFAULT_IDLE_GAP_MIN) {
762
+ const idleGapMs = idleGapMin2 * MIN;
763
+ return sessions.map((s) => {
764
+ const times = s.events.map((e) => e.t);
765
+ const intervals = activeIntervals(times, idleGapMs);
766
+ const start = times.length ? Math.min(...times) : 0;
767
+ const end = times.length ? Math.max(...times) : 0;
768
+ const activeMs = intervals.reduce((a, [x, y]) => a + (y - x), 0);
769
+ const toolCalls = Object.values(s.toolCounts).reduce((a, b) => a + b, 0);
770
+ return {
771
+ sessionId: s.sessionId,
772
+ source: s.source,
773
+ project: s.cwd,
774
+ gitBranch: s.gitBranch,
775
+ title: s.titles[s.titles.length - 1] || s.firstHuman.slice(0, 60) || "(untitled)",
776
+ titleHistory: s.titles,
777
+ renamed: s.titles.length > 1,
778
+ start: start ? new Date(start).toISOString() : "",
779
+ end: end ? new Date(end).toISOString() : "",
780
+ durationMin: round1((end - start) / MIN),
781
+ activeMin: round1(activeMs / MIN),
782
+ idleSplits: Math.max(0, intervals.length - 1),
783
+ humanTurns: s.humanTurns,
784
+ humanChars: s.humanChars,
785
+ aiTurns: s.aiTurns,
786
+ toolCalls,
787
+ toolCounts: s.toolCounts,
788
+ models: s.models,
789
+ tokens: s.tokens,
790
+ modes: s.modes,
791
+ permissionModes: s.permissionModes,
792
+ collaborationModes: s.collaborationModes,
793
+ queueOps: s.queueOps,
794
+ attachments: s.attachments,
795
+ imageAttachments: s.imageAttachments,
796
+ subagentSpawns: s.subagentSpawns,
797
+ sidechainTurns: s.sidechainTurns,
798
+ mcpTools: s.mcpTools,
799
+ klass: s.klass,
800
+ klassReason: s.klassReason,
801
+ file: s.file
802
+ };
803
+ });
804
+ }
805
+ function detectRenames(sessions) {
806
+ const out = [];
807
+ for (const s of sessions) {
808
+ for (let i = 1; i < s.titles.length; i++) {
809
+ out.push({ sessionId: s.sessionId, index: i - 1, from: s.titles[i - 1], to: s.titles[i] });
810
+ }
811
+ }
812
+ return out;
813
+ }
814
+ function buildConcurrency(sessions, records, opts = {}) {
815
+ const idleGapMin2 = opts.idleGapMin ?? DEFAULT_IDLE_GAP_MIN;
816
+ const tz = opts.tz ?? DEFAULT_TZ;
817
+ const idleGapMs = idleGapMin2 * MIN;
818
+ const titleById = new Map(records.map((r) => [r.sessionId, r.title]));
819
+ const intervals = [];
820
+ for (const s of sessions) {
821
+ const title = titleById.get(s.sessionId) ?? "(untitled)";
822
+ for (const [start, end] of activeIntervals(s.events.map((e) => e.t), idleGapMs)) {
823
+ intervals.push({ id: s.sessionId, title, start, end: end === start ? end + 1e3 : end });
824
+ }
825
+ }
826
+ const pts = intervals.flatMap((iv) => [{ t: iv.start, d: 1, iv }, { t: iv.end, d: -1, iv }]).sort((a, b) => a.t - b.t || a.d - b.d);
827
+ const active = /* @__PURE__ */ new Map();
828
+ const segments = [];
829
+ const levelHist = {};
830
+ let prevT = pts.length ? pts[0].t : 0;
831
+ const flush = (upto) => {
832
+ const level = active.size;
833
+ if (level > 0 && upto > prevT) {
834
+ const minutes = (upto - prevT) / MIN;
835
+ levelHist[level] = (levelHist[level] ?? 0) + minutes;
836
+ if (level >= 2) {
837
+ segments.push({
838
+ start: new Date(prevT).toISOString(),
839
+ end: new Date(upto).toISOString(),
840
+ minutes: round2(minutes),
841
+ level,
842
+ sessions: [...active.entries()].map(([id, title]) => ({ id, title }))
843
+ });
844
+ }
845
+ }
846
+ };
847
+ for (const p of pts) {
848
+ flush(p.t);
849
+ if (p.d === 1)
850
+ active.set(p.iv.id, p.iv.title);
851
+ else
852
+ active.delete(p.iv.id);
853
+ prevT = p.t;
854
+ }
855
+ for (const k of Object.keys(levelHist))
856
+ levelHist[k] = round1(levelHist[k]);
857
+ const maxConcurrency = Object.keys(levelHist).reduce((m, k) => Math.max(m, Number(k)), 0);
858
+ return {
859
+ idleGapMin: idleGapMin2,
860
+ tz,
861
+ maxConcurrency,
862
+ levelHistogramMin: levelHist,
863
+ segments,
864
+ perDay: perDayMetrics(intervals, records, tz)
865
+ };
866
+ }
867
+ function perDayMetrics(intervals, records, tz) {
868
+ const byDay = /* @__PURE__ */ new Map();
869
+ for (const iv of intervals) {
870
+ let cur = iv.start;
871
+ while (cur < iv.end) {
872
+ const day = localDate(cur, tz);
873
+ const dayEnd = nextLocalMidnight(cur, tz);
874
+ const segEnd = Math.min(iv.end, dayEnd);
875
+ (byDay.get(day) ?? byDay.set(day, []).get(day)).push({ iv, start: cur, end: segEnd });
876
+ cur = segEnd;
877
+ }
878
+ }
879
+ const startsByDay = /* @__PURE__ */ new Map();
880
+ const humanByDay = /* @__PURE__ */ new Map();
881
+ const toolsByDay = /* @__PURE__ */ new Map();
882
+ for (const r of records) {
883
+ if (!r.start)
884
+ continue;
885
+ const day = localDate(Date.parse(r.start), tz);
886
+ startsByDay.set(day, (startsByDay.get(day) ?? 0) + 1);
887
+ humanByDay.set(day, (humanByDay.get(day) ?? 0) + r.humanTurns);
888
+ toolsByDay.set(day, (toolsByDay.get(day) ?? 0) + r.toolCalls);
889
+ }
890
+ const out = [];
891
+ for (const [date, segs] of [...byDay.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
892
+ const pts = segs.flatMap((s) => [{ t: s.start, d: 1 }, { t: s.end, d: -1 }]).sort((a, b) => a.t - b.t || a.d - b.d);
893
+ let live = 0, prevT = pts.length ? pts[0].t : 0, activeMs = 0, overlapMs = 0, peak = 0;
894
+ for (const p of pts) {
895
+ if (p.t > prevT) {
896
+ if (live >= 1)
897
+ activeMs += p.t - prevT;
898
+ if (live >= 2)
899
+ overlapMs += p.t - prevT;
900
+ }
901
+ live += p.d;
902
+ peak = Math.max(peak, live);
903
+ prevT = p.t;
904
+ }
905
+ const starts = segs.map((s) => s.iv.start);
906
+ const ends = segs.map((s) => s.iv.end);
907
+ const firstStartMs = Math.min(...starts);
908
+ const lastEndMs = Math.max(...ends);
909
+ out.push({
910
+ date,
911
+ sessionsStarted: startsByDay.get(date) ?? 0,
912
+ activeMin: round1(activeMs / MIN),
913
+ overlapMin: round1(overlapMs / MIN),
914
+ overlapRatio: activeMs > 0 ? round2(overlapMs / activeMs) : 0,
915
+ peakConcurrency: peak,
916
+ firstStart: localTime(firstStartMs, tz),
917
+ lastEnd: localTime(lastEndMs, tz),
918
+ spanMin: round1((lastEndMs - firstStartMs) / MIN),
919
+ humanTurns: humanByDay.get(date) ?? 0,
920
+ toolCalls: toolsByDay.get(date) ?? 0
921
+ });
922
+ }
923
+ return out;
924
+ }
925
+ function buildDayChart(sessions, records, opts = {}) {
926
+ const idleGapMs = (opts.idleGapMin ?? DEFAULT_IDLE_GAP_MIN) * MIN;
927
+ const tz = opts.tz ?? DEFAULT_TZ;
928
+ const titleById = new Map(records.map((r) => [r.sessionId, r.title]));
929
+ const byDate = /* @__PURE__ */ new Map();
930
+ for (const s of sessions) {
931
+ for (const [start, rawEnd] of activeIntervals(s.events.map((e) => e.t), idleGapMs)) {
932
+ const end = rawEnd === start ? start + 3e4 : rawEnd;
933
+ let cur = start;
934
+ while (cur < end) {
935
+ const date = localDate(cur, tz);
936
+ const segEnd = Math.min(end, nextLocalMidnight(cur, tz));
937
+ const day = byDate.get(date) ?? byDate.set(date, /* @__PURE__ */ new Map()).get(date);
938
+ const conv = day.get(s.sessionId) ?? day.set(s.sessionId, { sessionId: s.sessionId, title: titleById.get(s.sessionId) ?? "(untitled)", intervals: [] }).get(s.sessionId);
939
+ conv.intervals.push([cur, segEnd]);
940
+ cur = segEnd;
941
+ }
942
+ }
943
+ }
944
+ const out = [];
945
+ for (const [date, convs] of byDate) {
946
+ const all = [...convs.values()].flatMap((c) => c.intervals);
947
+ if (!all.length)
948
+ continue;
949
+ const start = Math.min(...all.map((i) => i[0]));
950
+ const end = Math.max(...all.map((i) => i[1]));
951
+ const conversations = [...convs.values()].sort((a, b) => Math.min(...a.intervals.map((i) => i[0])) - Math.min(...b.intervals.map((i) => i[0])));
952
+ out.push({ date, start, end, conversations });
953
+ }
954
+ return out.sort((a, b) => b.date.localeCompare(a.date));
955
+ }
956
+ function nextLocalMidnight(fromMs, tz) {
957
+ const day = localDate(fromMs, tz);
958
+ for (let h = 1; h <= 26; h++) {
959
+ const probe = fromMs + h * 60 * MIN;
960
+ if (localDate(probe, tz) !== day) {
961
+ let lo = fromMs, hi = probe;
962
+ while (hi - lo > MIN) {
963
+ const mid = (lo + hi) / 2;
964
+ if (localDate(mid, tz) === day)
965
+ lo = mid;
966
+ else
967
+ hi = mid;
968
+ }
969
+ return hi;
970
+ }
971
+ }
972
+ return fromMs + 24 * 60 * MIN;
973
+ }
974
+
975
+ // ../coding-core/dist/gate.js
976
+ var GATE = {
977
+ minHumanChars: 800,
978
+ // the user must have actually said something substantial
979
+ minHumanTurns: 3,
980
+ // …over enough back-and-forth to reveal how they work
981
+ trivialHumanChars: 300,
982
+ // below this = a one-liner, definitely trivial
983
+ trivialHumanTurns: 2
984
+ };
985
+ function isGradable(s) {
986
+ return s.humanChars >= GATE.minHumanChars && s.humanTurns >= GATE.minHumanTurns;
987
+ }
988
+ function isTrivial(s) {
989
+ return s.humanTurns < GATE.trivialHumanTurns || s.humanChars < GATE.trivialHumanChars;
990
+ }
991
+ function partition(sessions) {
992
+ const gradable = [], thin = [], trivial = [];
993
+ for (const s of sessions)
994
+ (isGradable(s) ? gradable : isTrivial(s) ? trivial : thin).push(s);
995
+ return { gradable, thin, trivial };
996
+ }
997
+
998
+ // ../coding-core/dist/messages.js
999
+ import { promises as fs2 } from "fs";
1000
+ function extractText2(content) {
1001
+ if (typeof content === "string")
1002
+ return content;
1003
+ if (Array.isArray(content))
1004
+ return content.filter((c) => c && typeof c === "object" && c.type === "text").map((c) => c.text || "").join("\n");
1005
+ return "";
1006
+ }
1007
+ function isToolResultOnly(content) {
1008
+ return Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
1009
+ }
1010
+ async function extractUserMessages(file) {
1011
+ let raw;
1012
+ try {
1013
+ raw = await fs2.readFile(file, "utf-8");
1014
+ } catch {
1015
+ return [];
1016
+ }
1017
+ const out = [];
1018
+ const userTexts = /* @__PURE__ */ new Set();
1019
+ const seenUuids = /* @__PURE__ */ new Set();
1020
+ let lastTs = null;
1021
+ for (const line of raw.split("\n")) {
1022
+ const s = line.trim();
1023
+ if (!s)
1024
+ continue;
1025
+ let e;
1026
+ try {
1027
+ e = JSON.parse(s);
1028
+ } catch {
1029
+ continue;
1030
+ }
1031
+ const t = typeof e.timestamp === "string" ? Date.parse(e.timestamp) : NaN;
1032
+ const gapMs = Number.isFinite(t) && lastTs != null ? t - lastTs : NaN;
1033
+ if (Number.isFinite(t))
1034
+ lastTs = t;
1035
+ if (e.type === "queue-operation" && e.operation === "enqueue" && typeof e.content === "string") {
1036
+ const qt = humanTypedText(e.content);
1037
+ if (qt && Number.isFinite(t))
1038
+ out.push({ t, text: qt, gapMs, q: true });
1039
+ continue;
1040
+ }
1041
+ if (e.type === "user") {
1042
+ if (isHarnessEntry(e))
1043
+ continue;
1044
+ const uuid = typeof e.uuid === "string" ? e.uuid : "";
1045
+ if (uuid && seenUuids.has(uuid))
1046
+ continue;
1047
+ const content = e.message?.content;
1048
+ if (isToolResultOnly(content))
1049
+ continue;
1050
+ const text = humanTypedText(extractText2(content));
1051
+ if (text && Number.isFinite(t)) {
1052
+ if (uuid)
1053
+ seenUuids.add(uuid);
1054
+ userTexts.add(text);
1055
+ out.push({ t, text, gapMs, uuid: uuid || void 0 });
1056
+ }
1057
+ }
1058
+ }
1059
+ return out.filter((m) => !(m.q && userTexts.has(m.text))).map(({ q, ...m }) => m).sort((a, b) => a.t - b.t);
1060
+ }
1061
+
1062
+ // ../coding-core/dist/dedup.js
1063
+ var THRESHOLD = 0.9;
1064
+ var MIN_MSGS = 3;
1065
+ async function markDuplicates(records) {
1066
+ const sets = /* @__PURE__ */ new Map();
1067
+ await Promise.all(records.map(async (r) => {
1068
+ const m = await extractUserMessages(r.file).catch(() => []);
1069
+ sets.set(r.sessionId, new Set(m.map((x) => x.text.trim()).filter(Boolean)));
1070
+ }));
1071
+ const sorted = [...records].sort((a, b) => sets.get(b.sessionId).size - sets.get(a.sessionId).size);
1072
+ let count = 0;
1073
+ for (let i = 0; i < sorted.length; i++) {
1074
+ const A = sorted[i];
1075
+ const aSet = sets.get(A.sessionId);
1076
+ if (aSet.size < MIN_MSGS)
1077
+ continue;
1078
+ for (let j = 0; j < i; j++) {
1079
+ const B = sorted[j];
1080
+ if (B.duplicateOf)
1081
+ continue;
1082
+ const bSet = sets.get(B.sessionId);
1083
+ let inB = 0;
1084
+ for (const t of aSet)
1085
+ if (bSet.has(t))
1086
+ inB++;
1087
+ if (inB / aSet.size >= THRESHOLD) {
1088
+ A.duplicateOf = B.sessionId;
1089
+ count++;
1090
+ break;
1091
+ }
1092
+ }
1093
+ }
1094
+ return count;
1095
+ }
1096
+
1097
+ // ../../scripts/coding-build.mts
1098
+ var args = process.argv.slice(2);
1099
+ var codex = !args.includes("--no-codex");
1100
+ var idleGapMin = Number(args[args.indexOf("--idle") + 1]) || DEFAULT_IDLE_GAP_MIN;
1101
+ var OUT = path2.join(process.cwd(), ".data", "coding");
1102
+ function fmt(n) {
1103
+ return n.toLocaleString("en-US");
1104
+ }
1105
+ async function main() {
1106
+ console.log(`[coding] reading raw logs (codex=${codex}, idleGap=${idleGapMin}m, tz=${DEFAULT_TZ}) \u2026`);
1107
+ const t0 = Date.now();
1108
+ const raw = await readAllSessions({ codex });
1109
+ console.log(`[coding] parsed ${fmt(raw.length)} sessions in ${((Date.now() - t0) / 1e3).toFixed(1)}s`);
1110
+ const interactive = raw.filter((s) => s.klass === "interactive");
1111
+ const agent = raw.filter((s) => s.klass === "agent");
1112
+ console.log(`[coding] interactive=${fmt(interactive.length)} agent/excluded=${fmt(agent.length)}`);
1113
+ const reasons = /* @__PURE__ */ new Map();
1114
+ for (const s of agent) {
1115
+ const key = s.klassReason.startsWith("cwd:") ? "cwd:<agent-dir>" : s.klassReason;
1116
+ reasons.set(key, (reasons.get(key) ?? 0) + 1);
1117
+ }
1118
+ console.log(`[coding] excluded-by-reason:`, Object.fromEntries([...reasons.entries()].sort((a, b) => b[1] - a[1])));
1119
+ const records = toSessionRecords(interactive, idleGapMin);
1120
+ const dupCount = await markDuplicates(records);
1121
+ const dupSet = new Set(records.filter((r) => r.duplicateOf).map((r) => r.sessionId));
1122
+ const canonRaw = interactive.filter((s) => !dupSet.has(s.sessionId));
1123
+ const canonRecords = records.filter((r) => !r.duplicateOf);
1124
+ console.log(`[coding] duplicate (resume-copy) sessions: ${dupCount} \u2192 excluded from all counts`);
1125
+ const conc = buildConcurrency(canonRaw, canonRecords, { idleGapMin, tz: DEFAULT_TZ });
1126
+ const renames = detectRenames(canonRaw);
1127
+ const dayChart = buildDayChart(canonRaw, canonRecords, { idleGapMin, tz: DEFAULT_TZ });
1128
+ await fs3.mkdir(OUT, { recursive: true });
1129
+ await fs3.writeFile(path2.join(OUT, "sessions.json"), JSON.stringify(records, null, 2));
1130
+ await fs3.writeFile(path2.join(OUT, "concurrency.json"), JSON.stringify(conc, null, 2));
1131
+ await fs3.writeFile(path2.join(OUT, "renames.json"), JSON.stringify(renames, null, 2));
1132
+ await fs3.writeFile(path2.join(OUT, "day-chart.json"), JSON.stringify(dayChart));
1133
+ await fs3.writeFile(
1134
+ path2.join(OUT, "_classified.json"),
1135
+ JSON.stringify(raw.map((s) => ({ id: s.sessionId, source: s.source, klass: s.klass, reason: s.klassReason, cwd: s.cwd, firstHuman: s.firstHuman.slice(0, 120) })), null, 2)
1136
+ );
1137
+ const bySource = (k) => records.filter((r) => r.source === k).length;
1138
+ const totalActive = records.reduce((a, r) => a + r.activeMin, 0);
1139
+ const totalTools = records.reduce((a, r) => a + r.toolCalls, 0);
1140
+ const totalQueue = records.reduce((a, r) => a + r.queueOps, 0);
1141
+ const totalSub = records.reduce((a, r) => a + r.subagentSpawns, 0);
1142
+ const models = /* @__PURE__ */ new Map();
1143
+ for (const r of records) for (const [m, n] of Object.entries(r.models)) models.set(m, (models.get(m) ?? 0) + n);
1144
+ const dates = records.map((r) => r.start).filter(Boolean).sort();
1145
+ const topConcDays = [...conc.perDay].sort((a, b) => b.peakConcurrency - a.peakConcurrency || b.overlapMin - a.overlapMin).slice(0, 8);
1146
+ const split = partition(records);
1147
+ console.log(`
1148
+ ========== DETERMINISTIC CODING SUMMARY ==========`);
1149
+ console.log(`interactive sessions: ${fmt(records.length)} (claude-code=${bySource("claude-code")}, codex=${bySource("codex")})`);
1150
+ console.log(`gradable: ${fmt(split.gradable.length)} \xB7 thin: ${fmt(split.thin.length)} \xB7 trivial (never graded): ${fmt(split.trivial.length)} \u2014 all ${fmt(records.length)} still count for parallelism/throughput`);
1151
+ if (dates.length) console.log(`date range: ${dates[0].slice(0, 10)} \u2192 ${dates[dates.length - 1].slice(0, 10)}`);
1152
+ console.log(`active work time: ${(totalActive / 60).toFixed(1)} h tool calls: ${fmt(totalTools)} subagents spawned: ${fmt(totalSub)} queue-ahead ops: ${fmt(totalQueue)}`);
1153
+ console.log(`renames: ${fmt(renames.length)} sessions with >1 title: ${fmt(records.filter((r) => r.renamed).length)}`);
1154
+ console.log(`models:`, Object.fromEntries([...models.entries()].sort((a, b) => b[1] - a[1])));
1155
+ console.log(`
1156
+ PARALLELISM (idle-gap ${idleGapMin}m, tz ${conc.tz})`);
1157
+ console.log(` max concurrent sessions: ${conc.maxConcurrency}`);
1158
+ console.log(` minutes spent at each concurrency level:`, conc.levelHistogramMin);
1159
+ console.log(` highest-parallelism days:`);
1160
+ for (const d of topConcDays)
1161
+ console.log(` ${d.date} peak\xD7${d.peakConcurrency} active ${(d.activeMin / 60).toFixed(1)}h overlap ${(d.overlapMin / 60).toFixed(1)}h (${Math.round(d.overlapRatio * 100)}%) ${d.firstStart}\u2192${d.lastEnd} ${d.sessionsStarted} sessions`);
1162
+ console.log(`
1163
+ wrote \u2192 ${path2.relative(process.cwd(), OUT)}/{sessions,concurrency,renames,_classified}.json`);
1164
+ }
1165
+ main().catch((e) => {
1166
+ console.error(e);
1167
+ process.exit(1);
1168
+ });