dreamcontext 0.5.3 → 0.5.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.
package/dist/index.js CHANGED
@@ -245,7 +245,7 @@ var init_platform_defaults = __esm({
245
245
 
246
246
  // src/lib/manifest.ts
247
247
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync, statSync } from "fs";
248
- import { dirname as dirname2, join as join3 } from "path";
248
+ import { basename, dirname as dirname2, join as join3 } from "path";
249
249
  import { fileURLToPath } from "url";
250
250
  function dreamcontextVersion() {
251
251
  if (cachedVersion) return cachedVersion;
@@ -367,7 +367,7 @@ function walk(dir, relBase, out) {
367
367
  }
368
368
  }
369
369
  }
370
- function bootstrapManifestFromScan(projectRoot) {
370
+ function bootstrapManifestFromScan(projectRoot, known) {
371
371
  const m = emptyManifest();
372
372
  m.version = PRE_MANIFEST_VERSION;
373
373
  const claudeSkillsRoot = join3(projectRoot, ".claude", "skills");
@@ -376,6 +376,7 @@ function bootstrapManifestFromScan(projectRoot) {
376
376
  for (const entry of readdirSync(claudeSkillsRoot)) {
377
377
  const sub = join3(claudeSkillsRoot, entry);
378
378
  if (!statSync(sub).isDirectory()) continue;
379
+ if (!known.skillDirs.has(entry)) continue;
379
380
  const files = [];
380
381
  walk(sub, "", files);
381
382
  const kind = entry === "dreamcontext" ? "core" : "pack-skill";
@@ -388,6 +389,7 @@ function bootstrapManifestFromScan(projectRoot) {
388
389
  if (existsSync3(claudeAgentsDir)) {
389
390
  for (const f of readdirSync(claudeAgentsDir)) {
390
391
  if (!f.endsWith(".md")) continue;
392
+ if (!known.agentNames.has(basename(f, ".md"))) continue;
391
393
  recordFile(m, `.claude/agents/${f}`, PRE_MANIFEST_VERSION, "agent");
392
394
  }
393
395
  }
@@ -401,6 +403,7 @@ function bootstrapManifestFromScan(projectRoot) {
401
403
  for (const entry of readdirSync(agentsSkillsRoot)) {
402
404
  const sub = join3(agentsSkillsRoot, entry);
403
405
  if (!statSync(sub).isDirectory()) continue;
406
+ if (!known.skillDirs.has(entry)) continue;
404
407
  const files = [];
405
408
  walk(sub, "", files);
406
409
  const kind = entry === "dreamcontext" ? "core" : "pack-skill";
@@ -414,6 +417,7 @@ function bootstrapManifestFromScan(projectRoot) {
414
417
  recordPlatform(m, "codex");
415
418
  for (const f of readdirSync(codexAgentsDir)) {
416
419
  if (!f.endsWith(".toml")) continue;
420
+ if (!known.agentNames.has(basename(f, ".toml"))) continue;
417
421
  recordFile(m, `.codex/agents/${f}`, PRE_MANIFEST_VERSION, "agent");
418
422
  }
419
423
  }
@@ -486,8 +490,8 @@ var init_setup_config = __esm({
486
490
  });
487
491
 
488
492
  // src/lib/catalog.ts
489
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
490
- import { join as join5 } from "path";
493
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
494
+ import { basename as basename2, join as join5 } from "path";
491
495
  import { fileURLToPath as fileURLToPath2 } from "url";
492
496
  function platformSkillRoot(projectRoot, platform) {
493
497
  if (platform === "claude") return join5(projectRoot, ".claude", "skills");
@@ -522,6 +526,26 @@ function loadCatalog() {
522
526
  return null;
523
527
  }
524
528
  }
529
+ function knownArtifactNames() {
530
+ const agentNames = /* @__PURE__ */ new Set();
531
+ const skillDirs = /* @__PURE__ */ new Set(["dreamcontext"]);
532
+ const agentsDir = findPackageDir("agents");
533
+ if (agentsDir) {
534
+ try {
535
+ for (const f of readdirSync2(agentsDir)) {
536
+ if (f.endsWith(".md")) agentNames.add(basename2(f, ".md"));
537
+ }
538
+ } catch {
539
+ }
540
+ }
541
+ const loaded = loadCatalog();
542
+ if (loaded) {
543
+ for (const a of loaded.catalog.agents) agentNames.add(a.name);
544
+ for (const p of loaded.catalog.packs) skillDirs.add(p.name);
545
+ for (const s of loaded.catalog.standalone) skillDirs.add(s.name);
546
+ }
547
+ return { agentNames, skillDirs };
548
+ }
525
549
  var __dirname2;
526
550
  var init_catalog = __esm({
527
551
  "src/lib/catalog.ts"() {
@@ -537,17 +561,17 @@ import {
537
561
  mkdirSync as mkdirSync4,
538
562
  readFileSync as readFileSync5,
539
563
  writeFileSync as writeFileSync4,
540
- readdirSync as readdirSync2,
564
+ readdirSync as readdirSync3,
541
565
  cpSync,
542
566
  rmSync
543
567
  } from "fs";
544
- import { basename, dirname as dirname4, join as join6, resolve as resolve2, sep } from "path";
568
+ import { basename as basename3, dirname as dirname4, join as join6, resolve as resolve2, sep } from "path";
545
569
  import matter from "gray-matter";
546
570
  function parseAgentFile(agentPath) {
547
571
  const raw = readFileSync5(agentPath, "utf-8");
548
572
  const parsed = matter(raw);
549
573
  const data = parsed.data;
550
- const name = typeof data.name === "string" && data.name.trim().length > 0 ? data.name.trim() : basename(agentPath, ".md");
574
+ const name = typeof data.name === "string" && data.name.trim().length > 0 ? data.name.trim() : basename3(agentPath, ".md");
551
575
  const description = typeof data.description === "string" ? data.description.replace(/\s+/g, " ").trim() : "";
552
576
  const model = typeof data.model === "string" && data.model.trim().length > 0 ? data.model.trim() : "sonnet";
553
577
  return {
@@ -576,7 +600,7 @@ function installAgentForPlatform(platform, projectRoot, agentPath, agentName) {
576
600
  if (platform === "claude") {
577
601
  const agentsDestDir = join6(projectRoot, ".claude", "agents");
578
602
  mkdirSync4(agentsDestDir, { recursive: true });
579
- const file = agentName ? `${agentName}.md` : basename(agentPath);
603
+ const file = agentName ? `${agentName}.md` : basename3(agentPath);
580
604
  const dest = join6(agentsDestDir, file);
581
605
  writeFileSync4(dest, readFileSync5(agentPath, "utf-8"), "utf-8");
582
606
  return `.claude/agents/${file}`;
@@ -615,7 +639,7 @@ function installPackFiles(pack, packsDir, projectRoot, catalog, platform, manife
615
639
  if (existsSync6(refSrcDir)) {
616
640
  const refDestDir = join6(dirname4(subDest), "references");
617
641
  cpSync(refSrcDir, refDestDir, { recursive: true });
618
- const refFiles = readdirSync2(refSrcDir).filter((f) => f.endsWith(".md"));
642
+ const refFiles = readdirSync3(refSrcDir).filter((f) => f.endsWith(".md"));
619
643
  for (const rf of refFiles) {
620
644
  const refRel = `${skillRootRel}/${pack.name}/${dirname4(sub.file) === "." ? "" : dirname4(sub.file) + "/"}references/${rf}`;
621
645
  recordIfManifest(manifest, refRel, "pack-skill");
@@ -798,7 +822,7 @@ var init_install_packs = __esm({
798
822
  });
799
823
 
800
824
  // src/cli/commands/install-skill.ts
801
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync3, cpSync as cpSync2 } from "fs";
825
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync4, cpSync as cpSync2 } from "fs";
802
826
  import { dirname as dirname5, join as join7 } from "path";
803
827
  import chalk2 from "chalk";
804
828
  import { checkbox, confirm } from "@inquirer/prompts";
@@ -974,7 +998,7 @@ function ensureClaudeHooks(projectRoot) {
974
998
  settings.hooks[spec.event] = [];
975
999
  }
976
1000
  const exists = settings.hooks[spec.event].some(
977
- (group) => group.hooks?.some((h) => h.command === spec.command)
1001
+ (group) => (group.matcher ?? "") === (spec.matcher ?? "") && group.hooks?.some((h) => h.command === spec.command)
978
1002
  );
979
1003
  if (!exists) {
980
1004
  const group = {
@@ -1141,7 +1165,7 @@ function installSingleSkill(skillName, projectRoot, platforms) {
1141
1165
  if (existsSync7(refSrcDir)) {
1142
1166
  const refDestDir = join7(dirname5(subDest), "references");
1143
1167
  cpSync2(refSrcDir, refDestDir, { recursive: true });
1144
- const refFiles = readdirSync3(refSrcDir).filter((f) => f.endsWith(".md"));
1168
+ const refFiles = readdirSync4(refSrcDir).filter((f) => f.endsWith(".md"));
1145
1169
  for (const rf of refFiles) {
1146
1170
  const refRel = `${skillRootRel}/${pack.name}/${dirname5(sub.file) === "." ? "" : dirname5(sub.file) + "/"}references/${rf}`;
1147
1171
  recordFile(manifest, refRel, dreamcontextVersion(), "pack-skill");
@@ -1266,7 +1290,7 @@ async function installCoreForPlatform(platform, projectRoot, manifest) {
1266
1290
  installed.push(platformPrefixed(platform, coreSkillRel));
1267
1291
  const agentsSourceDir = findPackageDir("agents");
1268
1292
  if (agentsSourceDir) {
1269
- const agentFiles = readdirSync3(agentsSourceDir).filter((f) => f.endsWith(".md"));
1293
+ const agentFiles = readdirSync4(agentsSourceDir).filter((f) => f.endsWith(".md"));
1270
1294
  for (const file of agentFiles) {
1271
1295
  const source = join7(agentsSourceDir, file);
1272
1296
  const agentRel = installAgentForPlatform(platform, projectRoot, source);
@@ -1409,6 +1433,9 @@ var init_install_skill = __esm({
1409
1433
  { event: "Stop", command: STOP_HOOK, timeout: 5 },
1410
1434
  { event: "SubagentStart", command: SUBAGENT_START_HOOK, timeout: 5 },
1411
1435
  { event: "PreToolUse", command: PRE_TOOL_USE_HOOK, timeout: 5, matcher: "Agent" },
1436
+ // Second PreToolUse entry: gates direct writes to protected files (e.g. marketing/.env).
1437
+ // The hook.ts action already branches on tool_name, so the same command serves both gates.
1438
+ { event: "PreToolUse", command: PRE_TOOL_USE_HOOK, timeout: 5, matcher: "Edit|Write|MultiEdit" },
1412
1439
  { event: "UserPromptSubmit", command: USER_PROMPT_SUBMIT_HOOK, timeout: 120 },
1413
1440
  { event: "PostToolUse", command: POST_TOOL_USE_HOOK, timeout: 30, matcher: "Edit|Write" },
1414
1441
  { event: "PreCompact", command: PRE_COMPACT_HOOK, timeout: 5 }
@@ -1716,7 +1743,7 @@ var init_exports = {};
1716
1743
  __export(init_exports, {
1717
1744
  registerInitCommand: () => registerInitCommand
1718
1745
  });
1719
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync8, copyFileSync as copyFileSync2, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
1746
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync8, copyFileSync as copyFileSync2, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
1720
1747
  import { join as join9 } from "path";
1721
1748
  import { input, confirm as confirm2, checkbox as checkbox2 } from "@inquirer/prompts";
1722
1749
  import chalk4 from "chalk";
@@ -1736,7 +1763,7 @@ function copyObsidianConfig(destDir) {
1736
1763
  const src = getTemplateDir("obsidian");
1737
1764
  if (!existsSync9(src)) return false;
1738
1765
  mkdirSync6(destDir, { recursive: true });
1739
- for (const entry of readdirSync4(src)) {
1766
+ for (const entry of readdirSync5(src)) {
1740
1767
  const from = join9(src, entry);
1741
1768
  const to = join9(destDir, entry);
1742
1769
  if (statSync2(from).isFile()) {
@@ -2271,7 +2298,7 @@ var init_frontmatter = __esm({
2271
2298
  });
2272
2299
 
2273
2300
  // src/lib/release-discovery.ts
2274
- import { join as join10, basename as basename2 } from "path";
2301
+ import { join as join10, basename as basename4 } from "path";
2275
2302
  import { existsSync as existsSync11 } from "fs";
2276
2303
  import fg from "fast-glob";
2277
2304
  function changelogFingerprint(entry) {
@@ -2305,8 +2332,8 @@ function findUnreleasedTasks(root) {
2305
2332
  if (releasedTaskIds.has(id)) continue;
2306
2333
  result.push({
2307
2334
  id,
2308
- slug: basename2(file, ".md"),
2309
- name: String(data.name ?? basename2(file, ".md")),
2335
+ slug: basename4(file, ".md"),
2336
+ name: String(data.name ?? basename4(file, ".md")),
2310
2337
  description: String(data.description ?? "")
2311
2338
  });
2312
2339
  } catch {
@@ -2325,7 +2352,7 @@ function findUnreleasedFeatures(root) {
2325
2352
  if (data.released_version !== null && data.released_version !== void 0) continue;
2326
2353
  result.push({
2327
2354
  id: String(data.id ?? ""),
2328
- slug: basename2(file, ".md"),
2355
+ slug: basename4(file, ".md"),
2329
2356
  status: String(data.status ?? "planning")
2330
2357
  });
2331
2358
  } catch {
@@ -2857,7 +2884,7 @@ var init_markdown = __esm({
2857
2884
 
2858
2885
  // src/cli/commands/features.ts
2859
2886
  import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
2860
- import { join as join14, basename as basename3 } from "path";
2887
+ import { join as join14, basename as basename5 } from "path";
2861
2888
  import { input as input4 } from "@inquirer/prompts";
2862
2889
  import fg3 from "fast-glob";
2863
2890
  function getFeaturesDir() {
@@ -2870,18 +2897,18 @@ function findFeatureFile(name) {
2870
2897
  const exact = join14(dir, `${slug}.md`);
2871
2898
  if (existsSync14(exact)) return exact;
2872
2899
  const files = fg3.sync("*.md", { cwd: dir, absolute: true });
2873
- const exactGlob = files.find((f) => basename3(f, ".md") === slug);
2900
+ const exactGlob = files.find((f) => basename5(f, ".md") === slug);
2874
2901
  if (exactGlob) return exactGlob;
2875
- const prefixMatches = files.filter((f) => basename3(f, ".md").startsWith(slug));
2902
+ const prefixMatches = files.filter((f) => basename5(f, ".md").startsWith(slug));
2876
2903
  if (prefixMatches.length === 1) return prefixMatches[0];
2877
2904
  if (prefixMatches.length > 1) {
2878
- error(`Ambiguous feature name "${name}". Did you mean: ${prefixMatches.map((f) => basename3(f, ".md")).join(", ")}?`);
2905
+ error(`Ambiguous feature name "${name}". Did you mean: ${prefixMatches.map((f) => basename5(f, ".md")).join(", ")}?`);
2879
2906
  return null;
2880
2907
  }
2881
- const substringMatches = files.filter((f) => basename3(f, ".md").includes(slug));
2908
+ const substringMatches = files.filter((f) => basename5(f, ".md").includes(slug));
2882
2909
  if (substringMatches.length === 1) return substringMatches[0];
2883
2910
  if (substringMatches.length > 1) {
2884
- error(`Ambiguous feature name "${name}". Did you mean: ${substringMatches.map((f) => basename3(f, ".md")).join(", ")}?`);
2911
+ error(`Ambiguous feature name "${name}". Did you mean: ${substringMatches.map((f) => basename5(f, ".md")).join(", ")}?`);
2885
2912
  return null;
2886
2913
  }
2887
2914
  return null;
@@ -2997,7 +3024,7 @@ function registerFeaturesCommand(program) {
2997
3024
  try {
2998
3025
  insertToSection(file, sectionName, content, position);
2999
3026
  updateFrontmatterFields(file, { updated: today() });
3000
- success(`Inserted into ${sectionName} in ${basename3(file)}`);
3027
+ success(`Inserted into ${sectionName} in ${basename5(file)}`);
3001
3028
  } catch (err) {
3002
3029
  error(err.message);
3003
3030
  }
@@ -3016,7 +3043,7 @@ var init_features = __esm({
3016
3043
 
3017
3044
  // src/lib/knowledge-index.ts
3018
3045
  import { existsSync as existsSync15 } from "fs";
3019
- import { join as join15, basename as basename4 } from "path";
3046
+ import { join as join15, basename as basename6 } from "path";
3020
3047
  import fg4 from "fast-glob";
3021
3048
  function buildKnowledgeIndex(contextRoot) {
3022
3049
  const knowledgeDir = join15(contextRoot, "knowledge");
@@ -3027,8 +3054,8 @@ function buildKnowledgeIndex(contextRoot) {
3027
3054
  try {
3028
3055
  const { data, content } = readFrontmatter(file);
3029
3056
  const entry = {
3030
- slug: basename4(file, ".md"),
3031
- name: String(data.name ?? basename4(file, ".md")),
3057
+ slug: basename6(file, ".md"),
3058
+ name: String(data.name ?? basename6(file, ".md")),
3032
3059
  description: String(data.description ?? ""),
3033
3060
  tags: Array.isArray(data.tags) ? data.tags.map(String) : [],
3034
3061
  date: String(data.date ?? ""),
@@ -3162,6 +3189,13 @@ function writeSleepState(root, state) {
3162
3189
  const filePath = getSleepPath(root);
3163
3190
  writeJsonObject(filePath, state);
3164
3191
  }
3192
+ function bumpKnowledgeAccess(state, slug) {
3193
+ if (!state.knowledge_access[slug]) {
3194
+ state.knowledge_access[slug] = { last_accessed: today(), count: 0 };
3195
+ }
3196
+ state.knowledge_access[slug].last_accessed = today();
3197
+ state.knowledge_access[slug].count++;
3198
+ }
3165
3199
  function getSleepinessLevel(debt) {
3166
3200
  if (debt <= 3) return "Alert";
3167
3201
  if (debt <= 6) return "Drowsy";
@@ -3474,12 +3508,7 @@ ${content || "(Content to be added)"}
3474
3508
  return;
3475
3509
  }
3476
3510
  const state = readSleepState(root);
3477
- const now = today();
3478
- if (!state.knowledge_access[slug]) {
3479
- state.knowledge_access[slug] = { last_accessed: now, count: 0 };
3480
- }
3481
- state.knowledge_access[slug].last_accessed = now;
3482
- state.knowledge_access[slug].count++;
3511
+ bumpKnowledgeAccess(state, slug);
3483
3512
  writeSleepState(root, state);
3484
3513
  success(`Touched: ${slug} (access count: ${state.knowledge_access[slug].count})`);
3485
3514
  });
@@ -3576,7 +3605,7 @@ var init_rice = __esm({
3576
3605
 
3577
3606
  // src/cli/commands/tasks.ts
3578
3607
  import { existsSync as existsSync18, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
3579
- import { join as join18, basename as basename5 } from "path";
3608
+ import { join as join18, basename as basename7 } from "path";
3580
3609
  import { input as input6 } from "@inquirer/prompts";
3581
3610
  import chalk8 from "chalk";
3582
3611
  import fg5 from "fast-glob";
@@ -3590,18 +3619,18 @@ function findTaskFile(name) {
3590
3619
  const exact = join18(dir, `${slug}.md`);
3591
3620
  if (existsSync18(exact)) return exact;
3592
3621
  const files = fg5.sync("*.md", { cwd: dir, absolute: true });
3593
- const exactGlob = files.find((f) => basename5(f, ".md") === slug);
3622
+ const exactGlob = files.find((f) => basename7(f, ".md") === slug);
3594
3623
  if (exactGlob) return exactGlob;
3595
- const prefixMatches = files.filter((f) => basename5(f, ".md").startsWith(slug));
3624
+ const prefixMatches = files.filter((f) => basename7(f, ".md").startsWith(slug));
3596
3625
  if (prefixMatches.length === 1) return prefixMatches[0];
3597
3626
  if (prefixMatches.length > 1) {
3598
- error(`Ambiguous task name "${name}". Did you mean: ${prefixMatches.map((f) => basename5(f, ".md")).join(", ")}?`);
3627
+ error(`Ambiguous task name "${name}". Did you mean: ${prefixMatches.map((f) => basename7(f, ".md")).join(", ")}?`);
3599
3628
  return null;
3600
3629
  }
3601
- const substringMatches = files.filter((f) => basename5(f, ".md").includes(slug));
3630
+ const substringMatches = files.filter((f) => basename7(f, ".md").includes(slug));
3602
3631
  if (substringMatches.length === 1) return substringMatches[0];
3603
3632
  if (substringMatches.length > 1) {
3604
- error(`Ambiguous task name "${name}". Did you mean: ${substringMatches.map((f) => basename5(f, ".md")).join(", ")}?`);
3633
+ error(`Ambiguous task name "${name}". Did you mean: ${substringMatches.map((f) => basename7(f, ".md")).join(", ")}?`);
3605
3634
  return null;
3606
3635
  }
3607
3636
  return null;
@@ -3700,7 +3729,7 @@ function registerTasksCommand(program) {
3700
3729
  try {
3701
3730
  const { data } = readFrontmatter(file);
3702
3731
  const status = String(data.status ?? "unknown");
3703
- const name = basename5(file, ".md");
3732
+ const name = basename7(file, ".md");
3704
3733
  const priority = String(data.priority ?? "-");
3705
3734
  const updated = String(data.updated_at ?? data.created_at ?? "-");
3706
3735
  if (opts.status) {
@@ -3780,7 +3809,7 @@ function registerTasksCommand(program) {
3780
3809
  error(`Task not found: ${name}`);
3781
3810
  return;
3782
3811
  }
3783
- const slug = basename5(file, ".md");
3812
+ const slug = basename7(file, ".md");
3784
3813
  const { data } = readFrontmatter(file);
3785
3814
  const existing = normalizeRice(data.rice);
3786
3815
  const hasFlag = opts.reach !== void 0 || opts.impact !== void 0 || opts.confidence !== void 0 || opts.effort !== void 0;
@@ -3866,7 +3895,7 @@ function registerTasksCommand(program) {
3866
3895
  try {
3867
3896
  insertToSection(file, sectionName, content, position, true);
3868
3897
  updateFrontmatterFields(file, { updated_at: today() });
3869
- success(`Inserted into ${sectionName} in ${basename5(file)}`);
3898
+ success(`Inserted into ${sectionName} in ${basename7(file)}`);
3870
3899
  } catch (err) {
3871
3900
  error(err.message);
3872
3901
  }
@@ -3895,7 +3924,7 @@ function registerTasksCommand(program) {
3895
3924
  status: "completed",
3896
3925
  updated_at: today()
3897
3926
  });
3898
- success(`Task completed: ${basename5(file, ".md")}`);
3927
+ success(`Task completed: ${basename7(file, ".md")}`);
3899
3928
  });
3900
3929
  tasks.command("status").argument("<name>").argument("<new-status>", "todo, in_progress, in_review, or completed").argument("[reason...]", "Optional reason for the status change").description("Change a task's status (logs the change in the changelog)").action(async (name, newStatus, reasonParts) => {
3901
3930
  const validStatuses = ["todo", "in_progress", "in_review", "completed"];
@@ -3919,7 +3948,7 @@ function registerTasksCommand(program) {
3919
3948
  writeFileSync12(file, existing.trimEnd() + "\n\n" + logContent + "\n", "utf-8");
3920
3949
  }
3921
3950
  updateFrontmatterFields(file, { status: newStatus, updated_at: today() });
3922
- success(`Task ${basename5(file, ".md")} \u2192 ${newStatus}`);
3951
+ success(`Task ${basename7(file, ".md")} \u2192 ${newStatus}`);
3923
3952
  });
3924
3953
  tasks.command("log").argument("<name>").argument("[content...]", "Log entry content").description("Add a changelog entry to a task (cross-session continuity)").action(async (name, contentParts) => {
3925
3954
  const file = findTaskFile(name);
@@ -3946,7 +3975,7 @@ function registerTasksCommand(program) {
3946
3975
  writeFileSync12(file, existing.trimEnd() + "\n\n" + logContent + "\n", "utf-8");
3947
3976
  }
3948
3977
  updateFrontmatterFields(file, { updated_at: today() });
3949
- success(`Log entry added to ${basename5(file, ".md")}`);
3978
+ success(`Log entry added to ${basename7(file, ".md")}`);
3950
3979
  });
3951
3980
  tasks.command("doctor").argument("[name]", "Task name (omit to check every task)").description("Validate Workflow flowchart is in sync with Acceptance Criteria").action((name) => {
3952
3981
  const dir = getStateDir();
@@ -3960,9 +3989,9 @@ function registerTasksCommand(program) {
3960
3989
  })() : fg5.sync("*.md", { cwd: dir, absolute: true });
3961
3990
  if (files.length === 0) return;
3962
3991
  let drift = 0;
3963
- console.log(header(name ? `Workflow check: ${basename5(files[0], ".md")}` : "Workflow check"));
3992
+ console.log(header(name ? `Workflow check: ${basename7(files[0], ".md")}` : "Workflow check"));
3964
3993
  for (const file of files) {
3965
- const slug = basename5(file, ".md");
3994
+ const slug = basename7(file, ".md");
3966
3995
  const issues = checkWorkflow(file);
3967
3996
  if (issues.length === 0) {
3968
3997
  console.log(` ${chalk8.green("ok")} ${slug}`);
@@ -4035,7 +4064,7 @@ var init_tasks = __esm({
4035
4064
  });
4036
4065
 
4037
4066
  // src/cli/commands/update.ts
4038
- import { existsSync as existsSync19, readdirSync as readdirSync5, rmSync as rmSync2 } from "fs";
4067
+ import { existsSync as existsSync19, readdirSync as readdirSync6, rmSync as rmSync2 } from "fs";
4039
4068
  import { join as join19 } from "path";
4040
4069
  import chalk9 from "chalk";
4041
4070
  import { confirm as confirm4 } from "@inquirer/prompts";
@@ -4055,7 +4084,7 @@ function detectInstalledPacks(projectRoot, platforms) {
4055
4084
  for (const platform of platforms) {
4056
4085
  const root = platformSkillRoot(projectRoot, platform);
4057
4086
  if (!existsSync19(root)) continue;
4058
- for (const name of readdirSync5(root)) {
4087
+ for (const name of readdirSync6(root)) {
4059
4088
  if (name === "dreamcontext") continue;
4060
4089
  if (!knownNames.has(name)) continue;
4061
4090
  if (existsSync19(join19(root, name, "SKILL.md"))) found.add(name);
@@ -4072,26 +4101,42 @@ async function pruneStaleFiles(projectRoot, oldManifest, newManifest, isFirstRun
4072
4101
  warn(`Skipped ${unsafe.length} stale path(s) outside safe prefixes (.claude/, .agents/, .codex/):`);
4073
4102
  for (const p of unsafe) console.log(` ${chalk9.dim("\u2022")} ${chalk9.dim(p)}`);
4074
4103
  }
4075
- if (candidates.length === 0) return { removed: [], flagged: [], cancelled: false };
4076
- if (isFirstRun) {
4104
+ if (candidates.length === 0) return { removed: [], keep: [] };
4105
+ const heuristic = candidates.filter(
4106
+ (p) => oldManifest.files[p]?.version === PRE_MANIFEST_VERSION
4107
+ );
4108
+ const owned = candidates.filter(
4109
+ (p) => oldManifest.files[p]?.version !== PRE_MANIFEST_VERSION
4110
+ );
4111
+ if (heuristic.length > 0) {
4077
4112
  console.log();
4078
- warn(`First update after upgrade: ${candidates.length} stale file(s) detected (not removed).`);
4079
- console.log(chalk9.dim(" Re-run `dreamcontext update` to clean them up."));
4080
- for (const p of candidates) console.log(` ${chalk9.dim("\u2022")} ${chalk9.dim(p)}`);
4081
- return { removed: [], flagged: candidates, cancelled: true };
4113
+ warn(`${heuristic.length} legacy file(s) detected, not removed (review manually):`);
4114
+ for (const p of heuristic) console.log(` ${chalk9.dim("\u2022")} ${chalk9.dim(p)}`);
4115
+ }
4116
+ if (isFirstRun) {
4117
+ if (owned.length > 0) {
4118
+ console.log();
4119
+ warn(`First update after upgrade: ${owned.length} stale file(s) detected (not removed).`);
4120
+ console.log(chalk9.dim(" Re-run `dreamcontext update` to clean them up."));
4121
+ for (const p of owned) console.log(` ${chalk9.dim("\u2022")} ${chalk9.dim(p)}`);
4122
+ }
4123
+ return { removed: [], keep: candidates };
4124
+ }
4125
+ if (owned.length === 0) {
4126
+ return { removed: [], keep: candidates };
4082
4127
  }
4083
4128
  console.log();
4084
- console.log(`Stale file(s) detected (${candidates.length}):`);
4085
- for (const p of candidates) console.log(` ${chalk9.yellow("-")} ${p}`);
4129
+ console.log(`Stale file(s) detected (${owned.length}):`);
4130
+ for (const p of owned) console.log(` ${chalk9.yellow("-")} ${p}`);
4086
4131
  if (!yes && process.stdin.isTTY) {
4087
4132
  const ok = await confirm4({ message: "Delete these files?", default: true });
4088
4133
  if (!ok) {
4089
4134
  info("Skipped deletions.");
4090
- return { removed: [], flagged: candidates, cancelled: true };
4135
+ return { removed: [], keep: candidates };
4091
4136
  }
4092
4137
  }
4093
4138
  const removed = [];
4094
- for (const rel of candidates) {
4139
+ for (const rel of owned) {
4095
4140
  const abs = join19(projectRoot, rel);
4096
4141
  try {
4097
4142
  rmSync2(abs, { force: true });
@@ -4101,7 +4146,7 @@ async function pruneStaleFiles(projectRoot, oldManifest, newManifest, isFirstRun
4101
4146
  warn(`Could not delete ${rel}: ${msg}`);
4102
4147
  }
4103
4148
  }
4104
- return { removed, flagged: [], cancelled: false };
4149
+ return { removed, keep: candidates.filter((c) => !removed.includes(c)) };
4105
4150
  }
4106
4151
  function registerUpdateCommand(program) {
4107
4152
  program.command("update").description("Refresh installed dreamcontext files (core skill, agents, hooks, packs, root instructions) to the latest shipped version").option("--packs-only", "Only refresh installed packs, skip core skill/agents/hooks").option("--core-only", "Only refresh core skill/agents/hooks, skip packs").option("-y, --yes", "Skip confirmation prompts when deleting stale files").action(async (opts) => {
@@ -4117,7 +4162,7 @@ function registerUpdateCommand(program) {
4117
4162
  let isFirstRun = false;
4118
4163
  if (!oldManifest) {
4119
4164
  isFirstRun = true;
4120
- oldManifest = bootstrapManifestFromScan(projectRoot);
4165
+ oldManifest = bootstrapManifestFromScan(projectRoot, knownArtifactNames());
4121
4166
  info(chalk9.dim(`No manifest found \u2014 bootstrapped baseline from ${Object.keys(oldManifest.files).length} existing files.`));
4122
4167
  }
4123
4168
  const newManifest = getOrCreateManifest(projectRoot);
@@ -4172,12 +4217,10 @@ function registerUpdateCommand(program) {
4172
4217
  isFirstRun,
4173
4218
  !!opts.yes
4174
4219
  );
4175
- if (pruneResult.cancelled) {
4176
- for (const path of pruneResult.flagged) {
4177
- const entry = oldManifest.files[path];
4178
- if (entry && !newManifest.files[path]) {
4179
- newManifest.files[path] = entry;
4180
- }
4220
+ for (const path of pruneResult.keep) {
4221
+ const entry = oldManifest.files[path];
4222
+ if (entry && !newManifest.files[path]) {
4223
+ newManifest.files[path] = entry;
4181
4224
  }
4182
4225
  }
4183
4226
  writeManifest(projectRoot, newManifest);
@@ -4197,13 +4240,14 @@ var init_update = __esm({
4197
4240
  init_format();
4198
4241
  init_platforms();
4199
4242
  init_install_skill();
4243
+ init_catalog();
4200
4244
  init_manifest();
4201
4245
  }
4202
4246
  });
4203
4247
 
4204
4248
  // src/lib/core-index.ts
4205
4249
  import { existsSync as existsSync20 } from "fs";
4206
- import { join as join20, basename as basename6 } from "path";
4250
+ import { join as join20, basename as basename8 } from "path";
4207
4251
  import fg6 from "fast-glob";
4208
4252
  function buildCoreIndex(contextRoot) {
4209
4253
  const coreDir = join20(contextRoot, "core");
@@ -4211,7 +4255,7 @@ function buildCoreIndex(contextRoot) {
4211
4255
  const files = fg6.sync("[3-9]*", { cwd: coreDir, absolute: true });
4212
4256
  const entries = [];
4213
4257
  for (const file of files) {
4214
- const filename = basename6(file);
4258
+ const filename = basename8(file);
4215
4259
  const relativePath = `_dream_context/core/${filename}`;
4216
4260
  if (filename.endsWith(".json")) {
4217
4261
  entries.push({
@@ -4355,7 +4399,7 @@ import {
4355
4399
  writeFileSync as writeFileSync13,
4356
4400
  renameSync,
4357
4401
  unlinkSync,
4358
- readdirSync as readdirSync6
4402
+ readdirSync as readdirSync7
4359
4403
  } from "fs";
4360
4404
  import { dirname as dirname6, join as join22 } from "path";
4361
4405
  import { randomBytes } from "crypto";
@@ -4533,7 +4577,7 @@ var init_store = __esm({
4533
4577
  });
4534
4578
 
4535
4579
  // src/lib/marketing/cohort.ts
4536
- import { existsSync as existsSync22, readFileSync as readFileSync15, readdirSync as readdirSync7 } from "fs";
4580
+ import { existsSync as existsSync22, readFileSync as readFileSync15, readdirSync as readdirSync8 } from "fs";
4537
4581
  import { join as join23 } from "path";
4538
4582
  import { customAlphabet } from "nanoid";
4539
4583
  function newCohortId() {
@@ -4558,7 +4602,7 @@ function loadCohort(id) {
4558
4602
  function listCohorts() {
4559
4603
  const dir = MARKETING_PATHS.cohortsDir();
4560
4604
  if (!existsSync22(dir)) return [];
4561
- const files = readdirSync7(dir).filter((f) => f.endsWith(".json"));
4605
+ const files = readdirSync8(dir).filter((f) => f.endsWith(".json"));
4562
4606
  const cohorts = [];
4563
4607
  for (const f of files) {
4564
4608
  try {
@@ -5012,7 +5056,7 @@ var init_version_check = __esm({
5012
5056
 
5013
5057
  // src/cli/commands/snapshot.ts
5014
5058
  import { readFileSync as readFileSync19, existsSync as existsSync26, statSync as statSync3 } from "fs";
5015
- import { join as join25, basename as basename7, dirname as dirname9 } from "path";
5059
+ import { join as join25, basename as basename9, dirname as dirname9 } from "path";
5016
5060
  import fg7 from "fast-glob";
5017
5061
  function extractFirstParagraph(content) {
5018
5062
  const lines = content.split("\n");
@@ -5051,7 +5095,7 @@ function getActiveTaskLines(root) {
5051
5095
  const { data } = readFrontmatter(file);
5052
5096
  const status = String(data.status ?? "unknown");
5053
5097
  if (status === "completed") continue;
5054
- const name = basename7(file, ".md");
5098
+ const name = basename9(file, ".md");
5055
5099
  const priority = String(data.priority ?? "-");
5056
5100
  const updated = String(data.updated_at ?? data.created_at ?? "");
5057
5101
  let line = `- ${name} (status: ${status}, priority: ${priority}, updated: ${updated})`;
@@ -5252,7 +5296,7 @@ function generateSnapshot() {
5252
5296
  try {
5253
5297
  const { data } = readFrontmatter(file);
5254
5298
  if (String(data.status ?? "") === "completed") continue;
5255
- taskNames.push(basename7(file, ".md").toLowerCase());
5299
+ taskNames.push(basename9(file, ".md").toLowerCase());
5256
5300
  if (Array.isArray(data.tags)) {
5257
5301
  taskTags.push(...data.tags.map((t) => String(t).toLowerCase()));
5258
5302
  }
@@ -5364,7 +5408,7 @@ function generateSnapshot() {
5364
5408
  for (const file of featureFiles) {
5365
5409
  try {
5366
5410
  const { data } = readFrontmatter(file);
5367
- const name = basename7(file, ".md");
5411
+ const name = basename9(file, ".md");
5368
5412
  const status = String(data.status ?? "unknown");
5369
5413
  const tags = Array.isArray(data.tags) ? data.tags.join(", ") : "";
5370
5414
  let why = "";
@@ -5511,6 +5555,10 @@ function generateSubagentBriefing() {
5511
5555
  parts.push("and knowledge index below BEFORE using Glob, Grep, or searching code. If any feature");
5512
5556
  parts.push("name or tag matches your task, Read that feature file first. Searching the codebase");
5513
5557
  parts.push("without checking context wastes tokens and duplicates existing documentation.\n");
5558
+ parts.push('RECALL: For any "where / why / what do we know about X" question, run');
5559
+ parts.push('`dreamcontext memory recall "<keywords>"` (BM25 over knowledge/features/tasks/');
5560
+ parts.push("memory/changelog, <100ms, zero token overhead) BEFORE Glob/Grep \u2014 it frequently");
5561
+ parts.push("beats blind exploration outright.\n");
5514
5562
  const soulPath = join25(root, "core", "0.soul.md");
5515
5563
  if (existsSync26(soulPath)) {
5516
5564
  const { data, content } = readFrontmatter(soulPath);
@@ -5538,7 +5586,7 @@ function generateSubagentBriefing() {
5538
5586
  for (const file of featureFiles) {
5539
5587
  try {
5540
5588
  const { data } = readFrontmatter(file);
5541
- const name = basename7(file, ".md");
5589
+ const name = basename9(file, ".md");
5542
5590
  const status = String(data.status ?? "unknown");
5543
5591
  const tags = Array.isArray(data.tags) ? data.tags.join(", ") : "";
5544
5592
  let why = "";
@@ -5602,11 +5650,11 @@ function generateSubagentBriefing() {
5602
5650
  const coreDir = join25(root, "core");
5603
5651
  if (existsSync26(coreDir)) {
5604
5652
  const allCoreFiles = fg7.sync(["[0-9]*"], { cwd: coreDir, absolute: true });
5605
- allCoreFiles.sort((a, b) => basename7(a).localeCompare(basename7(b), void 0, { numeric: true }));
5653
+ allCoreFiles.sort((a, b) => basename9(a).localeCompare(basename9(b), void 0, { numeric: true }));
5606
5654
  if (allCoreFiles.length > 0) {
5607
5655
  parts.push("## Core Files\n");
5608
5656
  for (const file of allCoreFiles) {
5609
- const filename = basename7(file);
5657
+ const filename = basename9(file);
5610
5658
  const relativePath = `_dream_context/core/${filename}`;
5611
5659
  if (filename.endsWith(".json")) {
5612
5660
  const name = filename.replace(/^\d+\./, "").replace(/\.\w+$/, "").replace(/_/g, " ");
@@ -5677,96 +5725,450 @@ var init_snapshot2 = __esm({
5677
5725
  }
5678
5726
  });
5679
5727
 
5680
- // src/lib/marketing/path-guards.ts
5681
- import { existsSync as existsSync27, realpathSync } from "fs";
5682
- import { basename as basename8, dirname as dirname10, resolve as resolve3, sep as sep2 } from "path";
5683
- function isMarketingEnvPath(filePath, from) {
5684
- if (!filePath) return false;
5685
- const root = resolveContextRoot(from);
5686
- if (!root) return false;
5687
- const realRoot = realpathOrSelf(root);
5688
- const target = resolve3(realRoot, "marketing", ".env");
5689
- const realCandidate = realpathOfNearestAncestor(resolve3(filePath));
5690
- if (realCandidate === target) return true;
5691
- if (!realCandidate.startsWith(realRoot + sep2)) return false;
5692
- return realCandidate.endsWith(`${sep2}marketing${sep2}.env`);
5693
- }
5694
- function realpathOrSelf(p) {
5728
+ // src/cli/commands/transcript.ts
5729
+ import { readFileSync as readFileSync20, existsSync as existsSync27, statSync as statSync4 } from "fs";
5730
+ function distillTranscript(transcriptPath, sinceTimestamp) {
5731
+ const result = {
5732
+ userMessages: [],
5733
+ agentDecisions: [],
5734
+ codeChanges: [],
5735
+ errors: [],
5736
+ bookmarks: []
5737
+ };
5738
+ if (!existsSync27(transcriptPath)) return result;
5695
5739
  try {
5696
- return existsSync27(p) ? realpathSync(p) : p;
5740
+ const stat = statSync4(transcriptPath);
5741
+ if (stat.size === 0 || stat.size > MAX_TRANSCRIPT_BYTES) return result;
5742
+ const content = readFileSync20(transcriptPath, "utf-8");
5743
+ const lines = content.split("\n").filter((l) => l.trim());
5744
+ for (const line of lines) {
5745
+ let entry;
5746
+ try {
5747
+ entry = JSON.parse(line);
5748
+ } catch {
5749
+ continue;
5750
+ }
5751
+ if (sinceTimestamp && entry.timestamp && entry.timestamp <= sinceTimestamp) continue;
5752
+ if (!entry.message) continue;
5753
+ const msg = entry.message;
5754
+ if (msg.role === "user") {
5755
+ let text = "";
5756
+ if (typeof msg.content === "string") {
5757
+ text = msg.content;
5758
+ } else if (Array.isArray(msg.content)) {
5759
+ for (const block of msg.content) {
5760
+ if (typeof block === "string") {
5761
+ text += block + " ";
5762
+ } else if (block && typeof block === "object") {
5763
+ if (block.type === "text" && typeof block.text === "string") {
5764
+ text += block.text + " ";
5765
+ } else if (block.type === "tool_result" && Array.isArray(block.content)) {
5766
+ const content2 = block.content;
5767
+ for (const contentBlock of content2) {
5768
+ if (typeof contentBlock === "string") {
5769
+ text += contentBlock + " ";
5770
+ } else if (contentBlock && typeof contentBlock === "object" && contentBlock.type === "text") {
5771
+ text += (contentBlock.text || "") + " ";
5772
+ }
5773
+ }
5774
+ }
5775
+ }
5776
+ }
5777
+ }
5778
+ const trimmed = text.trim();
5779
+ if (trimmed && trimmed.length > 0) {
5780
+ result.userMessages.push(trimmed);
5781
+ }
5782
+ continue;
5783
+ }
5784
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
5785
+ for (const block of msg.content) {
5786
+ if (block.type === "text" && block.text) {
5787
+ const text = block.text.trim();
5788
+ if (text.length > 0) {
5789
+ result.agentDecisions.push(text);
5790
+ }
5791
+ }
5792
+ if (block.type === "thinking" && block.thinking) {
5793
+ const thinking = (typeof block.thinking === "string" ? block.thinking : "").trim();
5794
+ if (thinking.length > 0) {
5795
+ result.agentDecisions.push(`[thinking] ${thinking}`);
5796
+ }
5797
+ }
5798
+ if (block.type === "tool_use" && block.name) {
5799
+ const toolName = block.name;
5800
+ if (toolName === "Bash" && block.input) {
5801
+ const cmd = typeof block.input.command === "string" ? block.input.command : "";
5802
+ if (cmd.includes("dreamcontext bookmark")) {
5803
+ result.bookmarks.push(cmd);
5804
+ continue;
5805
+ }
5806
+ }
5807
+ if (CHANGE_TOOLS.has(toolName) && block.input) {
5808
+ const filePath = typeof block.input.file_path === "string" ? block.input.file_path : "";
5809
+ if (toolName === "Write") {
5810
+ const content2 = typeof block.input.content === "string" ? block.input.content : "";
5811
+ const lines2 = content2.split("\n").length;
5812
+ result.codeChanges.push(`WRITE ${filePath} (${lines2} lines)
5813
+ ${content2}`);
5814
+ } else if (toolName === "Edit") {
5815
+ const oldStr = typeof block.input.old_string === "string" ? block.input.old_string : "";
5816
+ const newStr = typeof block.input.new_string === "string" ? block.input.new_string : "";
5817
+ result.codeChanges.push(`EDIT ${filePath}
5818
+ --- OLD ---
5819
+ ${oldStr}
5820
+ --- NEW ---
5821
+ ${newStr}`);
5822
+ } else if (toolName === "NotebookEdit") {
5823
+ const nbPath = typeof block.input.notebook_path === "string" ? block.input.notebook_path : "";
5824
+ result.codeChanges.push(`NOTEBOOK_EDIT ${nbPath}`);
5825
+ }
5826
+ continue;
5827
+ }
5828
+ if (toolName === "Bash" && block.input) {
5829
+ const cmd = typeof block.input.command === "string" ? block.input.command : "";
5830
+ if (/\b(npm install|npm i |yarn add|pnpm add|pip install|git |mkdir |rm |mv |cp |chmod |chown |sed |awk )/.test(cmd)) {
5831
+ result.codeChanges.push(`BASH ${cmd}`);
5832
+ }
5833
+ continue;
5834
+ }
5835
+ if (NOISE_TOOLS.has(toolName)) continue;
5836
+ if (toolName === "Task" && block.input) {
5837
+ const prompt = typeof block.input.prompt === "string" ? block.input.prompt : "";
5838
+ if (prompt.length > 20) {
5839
+ result.agentDecisions.push(`[subagent-task] ${prompt}`);
5840
+ }
5841
+ continue;
5842
+ }
5843
+ }
5844
+ }
5845
+ continue;
5846
+ }
5847
+ if (msg.role === "tool" && Array.isArray(msg.content)) {
5848
+ for (const block of msg.content) {
5849
+ if (block.type === "tool_result") {
5850
+ const text = typeof block.text === "string" ? block.text : "";
5851
+ if (/error|Error|ERROR|failed|Failed|FAILED|exception|Exception/.test(text)) {
5852
+ result.errors.push(text);
5853
+ }
5854
+ if (text.length > 20 && text.includes("[subagent]")) {
5855
+ result.agentDecisions.push(`[subagent-result] ${text}`);
5856
+ }
5857
+ }
5858
+ }
5859
+ }
5860
+ if (entry.subagent?.result) {
5861
+ const subResult = entry.subagent.result.trim();
5862
+ if (subResult.length > 20) {
5863
+ result.agentDecisions.push(`[subagent] ${subResult}`);
5864
+ }
5865
+ }
5866
+ }
5697
5867
  } catch {
5698
- return p;
5699
5868
  }
5869
+ return result;
5700
5870
  }
5701
- function realpathOfNearestAncestor(p) {
5702
- let current = p;
5703
- const tail = [];
5704
- while (current && current !== sep2 && !existsSync27(current)) {
5705
- tail.unshift(basename8(current));
5706
- current = dirname10(current);
5871
+ function formatDistilled(sessionId, distilled, sinceTimestamp) {
5872
+ const suffix = sinceTimestamp ? ` (since ${sinceTimestamp})` : "";
5873
+ const parts = [`## Session ${sessionId} -- Distilled Transcript${suffix}
5874
+ `];
5875
+ if (distilled.userMessages.length > 0) {
5876
+ parts.push("### User Messages");
5877
+ for (const m of distilled.userMessages) {
5878
+ parts.push(`- "${m}"`);
5879
+ }
5880
+ parts.push("");
5707
5881
  }
5708
- if (!current || current === sep2) return p;
5709
- try {
5710
- const real = realpathSync(current);
5711
- return tail.length === 0 ? real : real + sep2 + tail.join(sep2);
5712
- } catch {
5713
- return p;
5882
+ if (distilled.agentDecisions.length > 0) {
5883
+ parts.push("### Agent Decisions & Reasoning");
5884
+ for (const d of distilled.agentDecisions) {
5885
+ parts.push(`- ${d}`);
5886
+ }
5887
+ parts.push("");
5714
5888
  }
5715
- }
5716
- var init_path_guards = __esm({
5717
- "src/lib/marketing/path-guards.ts"() {
5718
- "use strict";
5719
- init_context_path();
5889
+ if (distilled.codeChanges.length > 0) {
5890
+ parts.push("### Code Changes");
5891
+ for (const c of distilled.codeChanges) {
5892
+ parts.push(`- ${c}`);
5893
+ }
5894
+ parts.push("");
5720
5895
  }
5721
- });
5722
-
5723
- // src/lib/recall.ts
5724
- import { existsSync as existsSync28, readFileSync as readFileSync20 } from "fs";
5725
- import { join as join26, basename as basename9, dirname as dirname11, relative } from "path";
5726
- import fg8 from "fast-glob";
5727
- function tokenize(text) {
5728
- return text.toLowerCase().replace(/[^a-z0-9çğıöşü_\-\s]/g, " ").split(/[\s_\-]+/).filter((t) => t.length > 1 && !STOPWORDS.has(t));
5729
- }
5730
- function loadMarkdownDocs(dir, type, contextRoot) {
5731
- if (!existsSync28(dir)) return [];
5732
- const files = fg8.sync("*.md", { cwd: dir, absolute: true });
5733
- const out = [];
5734
- for (const file of files) {
5735
- try {
5736
- const { data, content } = readFrontmatter(file);
5737
- const slug = basename9(file, ".md");
5738
- const title = String(data.name ?? data.title ?? slug);
5739
- const description = String(data.description ?? data.summary ?? "");
5740
- const tags = Array.isArray(data.tags) ? data.tags.map(String) : [];
5741
- const body = content.trim();
5742
- const haystack = [title, description, tags.join(" "), body].join(" ");
5743
- const tokens = tokenize(haystack);
5744
- const termFreq = /* @__PURE__ */ new Map();
5745
- for (const t of tokens) termFreq.set(t, (termFreq.get(t) ?? 0) + 1);
5746
- out.push({
5747
- type,
5748
- path: file,
5749
- relPath: file.replace(contextRoot + "/", ""),
5750
- slug,
5751
- title,
5752
- description,
5753
- tags,
5754
- body,
5755
- tokens,
5756
- tokenSet: new Set(tokens),
5757
- termFreq
5758
- });
5759
- } catch {
5896
+ if (distilled.errors.length > 0) {
5897
+ parts.push("### Errors & Issues");
5898
+ for (const e of distilled.errors) {
5899
+ parts.push(`- ${e}`);
5760
5900
  }
5901
+ parts.push("");
5761
5902
  }
5762
- return out;
5903
+ if (distilled.bookmarks.length > 0) {
5904
+ parts.push("### Bookmarks");
5905
+ for (const b of distilled.bookmarks) {
5906
+ parts.push(`- ${b}`);
5907
+ }
5908
+ parts.push("");
5909
+ }
5910
+ return parts.join("\n");
5911
+ }
5912
+ function registerTranscriptCommand(program) {
5913
+ const transcript = program.command("transcript").description("Process session transcripts");
5914
+ transcript.command("distill").argument("<session_id>", "Session ID to distill").option("--since <timestamp>", "Only include content after this ISO timestamp").option("--full", "Show full transcript (skip auto-filter by last consolidation)").description("Extract high-signal content from a session transcript (pure structural filtering)").action((sessionId, opts) => {
5915
+ const root = ensureContextRoot();
5916
+ const state = readSleepState(root);
5917
+ const session = state.sessions.find((s) => s.session_id === sessionId);
5918
+ if (!session) {
5919
+ error(`Session not found: ${sessionId}`);
5920
+ return;
5921
+ }
5922
+ if (!session.transcript_path) {
5923
+ error(`No transcript path for session: ${sessionId}`);
5924
+ return;
5925
+ }
5926
+ if (!existsSync27(session.transcript_path)) {
5927
+ error(`Transcript file not found: ${session.transcript_path}`);
5928
+ return;
5929
+ }
5930
+ let sinceTimestamp;
5931
+ if (opts.since) {
5932
+ sinceTimestamp = opts.since;
5933
+ } else if (!opts.full) {
5934
+ const history = readSleepHistory(root);
5935
+ const lastConsolidation = history.find(
5936
+ (h) => Array.isArray(h.session_ids) && h.session_ids.includes(sessionId)
5937
+ );
5938
+ if (lastConsolidation?.consolidated_at) {
5939
+ sinceTimestamp = lastConsolidation.consolidated_at;
5940
+ info(`Auto-filtering: showing content after last consolidation (${lastConsolidation.consolidated_at})`);
5941
+ }
5942
+ }
5943
+ const distilled = distillTranscript(session.transcript_path, sinceTimestamp);
5944
+ console.log(formatDistilled(sessionId, distilled, sinceTimestamp));
5945
+ });
5946
+ }
5947
+ var MAX_TRANSCRIPT_BYTES, NOISE_TOOLS, CHANGE_TOOLS;
5948
+ var init_transcript = __esm({
5949
+ "src/cli/commands/transcript.ts"() {
5950
+ "use strict";
5951
+ init_context_path();
5952
+ init_sleep();
5953
+ init_format();
5954
+ MAX_TRANSCRIPT_BYTES = 50 * 1024 * 1024;
5955
+ NOISE_TOOLS = /* @__PURE__ */ new Set([
5956
+ "Read",
5957
+ "Glob",
5958
+ "Grep",
5959
+ "WebFetch",
5960
+ "WebSearch",
5961
+ "ListMcpResourcesTool",
5962
+ "ReadMcpResourceTool",
5963
+ "ToolSearch"
5964
+ ]);
5965
+ CHANGE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "NotebookEdit"]);
5966
+ }
5967
+ });
5968
+
5969
+ // src/lib/recall-synonyms.ts
5970
+ function expandQueryTerms(queryTerms, stem) {
5971
+ const primary = new Set(queryTerms);
5972
+ const expansions = /* @__PURE__ */ new Map();
5973
+ for (const [surfaceKey, syns] of SYNONYMS) {
5974
+ const stemmedKey = stem(surfaceKey);
5975
+ if (!primary.has(stemmedKey)) continue;
5976
+ for (const syn of syns) {
5977
+ const stemmedSyn = stem(syn);
5978
+ if (primary.has(stemmedSyn)) continue;
5979
+ if (!expansions.has(stemmedSyn)) {
5980
+ expansions.set(stemmedSyn, SYNONYM_WEIGHT);
5981
+ }
5982
+ }
5983
+ }
5984
+ return expansions;
5985
+ }
5986
+ var SYNONYM_GROUPS, SYNONYMS, SYNONYM_WEIGHT;
5987
+ var init_recall_synonyms = __esm({
5988
+ "src/lib/recall-synonyms.ts"() {
5989
+ "use strict";
5990
+ SYNONYM_GROUPS = [
5991
+ // Authentication / authorization
5992
+ ["auth", "authentication", "authorization", "login", "signin"],
5993
+ // Databases
5994
+ ["db", "database", "postgres", "postgresql", "sqlite"],
5995
+ // Recall / search / retrieval (core project concept) + TR
5996
+ ["recall", "search", "retrieval", "retrieve", "lookup", "\xE7a\u011F\u0131rma", "arama"],
5997
+ // Sleep / consolidation (core project concept) + TR (uyku / konsolidasyon)
5998
+ ["sleep", "consolidation", "consolidate", "consolidating", "uyku", "konsolidasyon", "konsolidasyonu"],
5999
+ // Bookmark / salience / ripple (core project concept)
6000
+ ["bookmark", "ripple", "salience", "salient"],
6001
+ // Hook / hooks
6002
+ ["hook", "hooks"],
6003
+ // Memory / remember (core project concept) + TR (hafıza / bellek / beyin)
6004
+ ["memory", "remember", "recollection", "haf\u0131za", "bellek", "beyin", "beynini"],
6005
+ // Vector / embeddings / semantic (the path NOT taken — mem0 decision)
6006
+ ["vector", "embedding", "embeddings", "semantic"],
6007
+ // Keyword / bm25 (the path taken)
6008
+ ["keyword", "bm25"],
6009
+ // Vault / registry / project-folder (multivault concept)
6010
+ ["vault", "registry", "multivault"],
6011
+ // Positioning / tagline / slogan / messaging (branding concept) + TR
6012
+ ["positioning", "position", "tagline", "slogan", "slogan\u0131", "messaging", "konumland\u0131rma", "konumland\u0131rmas\u0131"],
6013
+ // Council / debate / personas (council-skill concept)
6014
+ ["council", "debate", "persona", "personas"],
6015
+ // Snapshot / inject / bootstrap (context-snapshot concept) + TR (oturum / enjekte)
6016
+ ["snapshot", "inject", "bootstrap", "session", "oturum", "enjekte"],
6017
+ // RICE / prioritization / scoring / backlog (rice concept) + TR
6018
+ ["rice", "prioritization", "prioritize", "backlog", "scoring", "\xF6nceliklendirme", "puanlama", "say\u0131sal"],
6019
+ // Manifest / install / upgrade / cleanup (install-update concept)
6020
+ ["manifest", "install", "upgrade", "uninstall"],
6021
+ // Security / vulnerability (dashboard-server-security) + TR (güvenlik / açık)
6022
+ ["security", "vulnerability", "vulnerabilities", "hardening", "g\xFCvenlik", "a\xE7\u0131k"],
6023
+ // Server (dashboard server) + TR (sunucu)
6024
+ ["server", "sunucu", "sunucusu"],
6025
+ // Task / görev
6026
+ ["task", "g\xF6rev"],
6027
+ // Debt / borç (sleep debt)
6028
+ ["debt", "bor\xE7"]
6029
+ ];
6030
+ SYNONYMS = (() => {
6031
+ const map = /* @__PURE__ */ new Map();
6032
+ for (const group of SYNONYM_GROUPS) {
6033
+ for (const term of group) {
6034
+ const expansions = map.get(term) ?? /* @__PURE__ */ new Set();
6035
+ for (const other of group) {
6036
+ if (other !== term) expansions.add(other);
6037
+ }
6038
+ map.set(term, expansions);
6039
+ }
6040
+ }
6041
+ return map;
6042
+ })();
6043
+ SYNONYM_WEIGHT = 0.5;
6044
+ }
6045
+ });
6046
+
6047
+ // src/lib/recall.ts
6048
+ import { existsSync as existsSync28, readFileSync as readFileSync21 } from "fs";
6049
+ import { join as join26, basename as basename10, dirname as dirname10, relative } from "path";
6050
+ import fg8 from "fast-glob";
6051
+ function docKey(doc) {
6052
+ return `${doc.type}/${doc.slug}`;
6053
+ }
6054
+ function stemEn(token) {
6055
+ if (token.length <= 4) return token;
6056
+ if (token.endsWith("ing") && token.length > 5) return token.slice(0, -3);
6057
+ if (token.endsWith("ed") && token.length > 4) return token.slice(0, -2);
6058
+ if (token.endsWith("es") && token.length > 4) return token.slice(0, -2);
6059
+ if (token.endsWith("s") && !token.endsWith("ss") && token.length > 4) return token.slice(0, -1);
6060
+ return token;
6061
+ }
6062
+ function stemTr(token) {
6063
+ if (token.length <= 4) return token;
6064
+ for (const suf of TR_SUFFIXES) {
6065
+ if (token.length - suf.length > 2 && token.endsWith(suf)) {
6066
+ return token.slice(0, -suf.length);
6067
+ }
6068
+ }
6069
+ return token;
6070
+ }
6071
+ function stemToken(token) {
6072
+ return stemTr(stemEn(token));
6073
+ }
6074
+ function tokenize(text) {
6075
+ return text.toLowerCase().replace(/[^a-z0-9çğıöşü_\-\s]/g, " ").split(/[\s_\-]+/).filter((t) => t.length > 1 && !STOPWORDS.has(t)).map(stemToken).filter((t) => t.length > 1 && !STOPWORDS.has(t));
6076
+ }
6077
+ function parseLinks(body) {
6078
+ const out = [];
6079
+ let m;
6080
+ WIKILINK_RE.lastIndex = 0;
6081
+ while ((m = WIKILINK_RE.exec(body)) !== null) {
6082
+ const slug = m[1].trim().split("|")[0].split("#")[0].trim();
6083
+ if (slug) out.push(slug);
6084
+ }
6085
+ return out;
6086
+ }
6087
+ function buildFields(f) {
6088
+ const titleToks = tokenize(f.title);
6089
+ const descToks = tokenize(f.description);
6090
+ const tagToks = tokenize(f.tags.join(" "));
6091
+ const bodyToks = tokenize(f.body);
6092
+ const tokens = [...titleToks, ...descToks, ...tagToks, ...bodyToks];
6093
+ const termFreq = /* @__PURE__ */ new Map();
6094
+ for (const t of tokens) termFreq.set(t, (termFreq.get(t) ?? 0) + 1);
6095
+ const fieldFreq = /* @__PURE__ */ new Map();
6096
+ const addWeighted = (toks, w) => {
6097
+ for (const t of toks) fieldFreq.set(t, (fieldFreq.get(t) ?? 0) + w);
6098
+ };
6099
+ addWeighted(titleToks, FIELD_WEIGHTS.title);
6100
+ addWeighted(descToks, FIELD_WEIGHTS.description);
6101
+ addWeighted(tagToks, FIELD_WEIGHTS.tags);
6102
+ addWeighted(bodyToks, FIELD_WEIGHTS.body);
6103
+ const identityTokens = Array.from(
6104
+ /* @__PURE__ */ new Set([...tokenize(f.slug ?? ""), ...titleToks])
6105
+ );
6106
+ return {
6107
+ tokens,
6108
+ termFreq,
6109
+ fieldFreq,
6110
+ fieldLen: tokens.length,
6111
+ links: parseLinks(f.body),
6112
+ identityTokens
6113
+ };
6114
+ }
6115
+ function readUpdatedAt(data) {
6116
+ const v = data.updated_at ?? data.updated ?? data.date;
6117
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
6118
+ }
6119
+ function readStatus(data) {
6120
+ const v = data.status;
6121
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
6122
+ }
6123
+ function productFromRelPath(relPath) {
6124
+ const m = relPath.replace(/\\/g, "/").match(/(?:^|\/)knowledge\/products\/([^/]+)\//);
6125
+ return m ? m[1] : void 0;
6126
+ }
6127
+ function loadMarkdownDocs(dir, type, contextRoot) {
6128
+ if (!existsSync28(dir)) return [];
6129
+ const files = fg8.sync("**/*.md", { cwd: dir, absolute: true });
6130
+ const out = [];
6131
+ for (const file of files) {
6132
+ try {
6133
+ const { data, content } = readFrontmatter(file);
6134
+ const slug = basename10(file, ".md");
6135
+ const title = String(data.name ?? data.title ?? slug);
6136
+ const description = String(data.description ?? data.summary ?? "");
6137
+ const tags = Array.isArray(data.tags) ? data.tags.map(String) : [];
6138
+ const body = content.trim();
6139
+ const relPath = file.replace(contextRoot + "/", "");
6140
+ const fields = buildFields({ slug, title, description, tags, body });
6141
+ out.push({
6142
+ type,
6143
+ path: file,
6144
+ relPath,
6145
+ slug,
6146
+ title,
6147
+ description,
6148
+ tags,
6149
+ body,
6150
+ tokens: fields.tokens,
6151
+ tokenSet: new Set(fields.tokens),
6152
+ termFreq: fields.termFreq,
6153
+ fieldFreq: fields.fieldFreq,
6154
+ fieldLen: fields.fieldLen,
6155
+ links: fields.links,
6156
+ identityTokens: fields.identityTokens,
6157
+ status: readStatus(data),
6158
+ updatedAt: readUpdatedAt(data),
6159
+ product: productFromRelPath(relPath)
6160
+ });
6161
+ } catch {
6162
+ }
6163
+ }
6164
+ return out;
5763
6165
  }
5764
6166
  function loadChangelogEntries(contextRoot) {
5765
6167
  const path = join26(contextRoot, "core", "CHANGELOG.json");
5766
6168
  if (!existsSync28(path)) return [];
5767
6169
  let entries = [];
5768
6170
  try {
5769
- const raw = readFileSync20(path, "utf-8");
6171
+ const raw = readFileSync21(path, "utf-8");
5770
6172
  const parsed = JSON.parse(raw);
5771
6173
  if (Array.isArray(parsed)) entries = parsed;
5772
6174
  } catch {
@@ -5784,10 +6186,13 @@ function loadChangelogEntries(contextRoot) {
5784
6186
  if (!description && !summary) continue;
5785
6187
  const slug = `changelog#${date}-${scope || type}-${i}`;
5786
6188
  const title = `${date} [${type}] ${scope}${summary ? ` \u2014 ${summary}` : ""}`.trim();
5787
- const haystack = [title, summary, description, refs.join(" ")].join(" ");
5788
- const tokens = tokenize(haystack);
5789
- const termFreq = /* @__PURE__ */ new Map();
5790
- for (const t of tokens) termFreq.set(t, (termFreq.get(t) ?? 0) + 1);
6189
+ const tags = [type, scope].filter(Boolean);
6190
+ const fields = buildFields({
6191
+ title,
6192
+ description: summary,
6193
+ tags,
6194
+ body: [description, refs.join(" ")].join(" ").trim()
6195
+ });
5791
6196
  out.push({
5792
6197
  type: "changelog",
5793
6198
  path,
@@ -5795,11 +6200,16 @@ function loadChangelogEntries(contextRoot) {
5795
6200
  slug,
5796
6201
  title,
5797
6202
  description: summary || description.slice(0, 200),
5798
- tags: [type, scope].filter(Boolean),
6203
+ tags,
5799
6204
  body: description,
5800
- tokens,
5801
- tokenSet: new Set(tokens),
5802
- termFreq
6205
+ tokens: fields.tokens,
6206
+ tokenSet: new Set(fields.tokens),
6207
+ termFreq: fields.termFreq,
6208
+ fieldFreq: fields.fieldFreq,
6209
+ fieldLen: fields.fieldLen,
6210
+ links: fields.links,
6211
+ identityTokens: [],
6212
+ updatedAt: date || void 0
5803
6213
  });
5804
6214
  }
5805
6215
  return out;
@@ -5807,7 +6217,7 @@ function loadChangelogEntries(contextRoot) {
5807
6217
  function loadMemoryFile(contextRoot) {
5808
6218
  const path = join26(contextRoot, "core", "2.memory.md");
5809
6219
  if (!existsSync28(path)) return [];
5810
- const raw = readFileSync20(path, "utf-8");
6220
+ const raw = readFileSync21(path, "utf-8");
5811
6221
  const sections = raw.split(/^##\s+/m).slice(1);
5812
6222
  const out = [];
5813
6223
  for (let i = 0; i < sections.length; i++) {
@@ -5816,22 +6226,70 @@ function loadMemoryFile(contextRoot) {
5816
6226
  const heading = (firstNl >= 0 ? sec.slice(0, firstNl) : sec).trim();
5817
6227
  const body = (firstNl >= 0 ? sec.slice(firstNl + 1) : "").trim();
5818
6228
  if (!body) continue;
5819
- const haystack = [heading, body].join(" ");
5820
- const tokens = tokenize(haystack);
5821
- const termFreq = /* @__PURE__ */ new Map();
5822
- for (const t of tokens) termFreq.set(t, (termFreq.get(t) ?? 0) + 1);
6229
+ const title = heading || `memory entry ${i + 1}`;
6230
+ const fields = buildFields({ title, description: "", tags: [], body });
5823
6231
  out.push({
5824
6232
  type: "memory",
5825
6233
  path,
5826
6234
  relPath: "core/2.memory.md",
5827
6235
  slug: `memory#${i}`,
5828
- title: heading || `memory entry ${i + 1}`,
6236
+ title,
5829
6237
  description: "",
5830
6238
  tags: [],
5831
6239
  body,
5832
- tokens,
5833
- tokenSet: new Set(tokens),
5834
- termFreq
6240
+ tokens: fields.tokens,
6241
+ tokenSet: new Set(fields.tokens),
6242
+ termFreq: fields.termFreq,
6243
+ fieldFreq: fields.fieldFreq,
6244
+ fieldLen: fields.fieldLen,
6245
+ links: fields.links,
6246
+ identityTokens: fields.identityTokens
6247
+ });
6248
+ }
6249
+ return out;
6250
+ }
6251
+ function loadBookmarkDocs(contextRoot) {
6252
+ const path = join26(contextRoot, "state", ".sleep.json");
6253
+ if (!existsSync28(path)) return [];
6254
+ let bookmarks = [];
6255
+ try {
6256
+ const parsed = JSON.parse(readFileSync21(path, "utf-8"));
6257
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.bookmarks)) {
6258
+ bookmarks = parsed.bookmarks;
6259
+ }
6260
+ } catch {
6261
+ return [];
6262
+ }
6263
+ const out = [];
6264
+ for (const b of bookmarks) {
6265
+ const id = typeof b.id === "string" ? b.id : "";
6266
+ const message = typeof b.message === "string" ? b.message.trim() : "";
6267
+ if (!id || !message) continue;
6268
+ const slug = `bookmark#${id}`;
6269
+ const title = message.length > 80 ? message.slice(0, 80) : message;
6270
+ const tags = typeof b.task_slug === "string" && b.task_slug ? [b.task_slug] : [];
6271
+ const fields = buildFields({ title, description: "", tags, body: message });
6272
+ out.push({
6273
+ type: "memory",
6274
+ path,
6275
+ relPath: "state/.sleep.json",
6276
+ slug,
6277
+ title,
6278
+ description: "",
6279
+ tags,
6280
+ body: message,
6281
+ tokens: fields.tokens,
6282
+ tokenSet: new Set(fields.tokens),
6283
+ termFreq: fields.termFreq,
6284
+ fieldFreq: fields.fieldFreq,
6285
+ fieldLen: fields.fieldLen,
6286
+ links: fields.links,
6287
+ // Synthetic `bookmark#…` slug is not a canonical identity — exclude from
6288
+ // the identity boost (mirrors the changelog loader's choice).
6289
+ identityTokens: [],
6290
+ updatedAt: typeof b.created_at === "string" ? b.created_at : void 0,
6291
+ // C2/C3: auto/explicit bookmarks are continuous captures → rank-penalised.
6292
+ capture: true
5835
6293
  });
5836
6294
  }
5837
6295
  return out;
@@ -5844,15 +6302,12 @@ function loadSkillDocs(skillsRoot) {
5844
6302
  try {
5845
6303
  const { data, content } = readFrontmatter(file);
5846
6304
  if (data.alwaysApply === true) continue;
5847
- const slug = typeof data.name === "string" && data.name ? data.name : basename9(dirname11(file));
6305
+ const slug = typeof data.name === "string" && data.name ? data.name : basename10(dirname10(file));
5848
6306
  const title = slug;
5849
6307
  const description = typeof data.description === "string" ? data.description : "";
5850
6308
  const tags = Array.isArray(data.tags) ? data.tags.map(String) : [];
5851
6309
  const body = content.trim();
5852
- const haystack = [title, description, tags.join(" "), body].join(" ");
5853
- const tokens = tokenize(haystack);
5854
- const termFreq = /* @__PURE__ */ new Map();
5855
- for (const t of tokens) termFreq.set(t, (termFreq.get(t) ?? 0) + 1);
6310
+ const fields = buildFields({ slug, title, description, tags, body });
5856
6311
  out.push({
5857
6312
  type: "skill",
5858
6313
  path: file,
@@ -5862,9 +6317,13 @@ function loadSkillDocs(skillsRoot) {
5862
6317
  description,
5863
6318
  tags,
5864
6319
  body,
5865
- tokens,
5866
- tokenSet: new Set(tokens),
5867
- termFreq
6320
+ tokens: fields.tokens,
6321
+ tokenSet: new Set(fields.tokens),
6322
+ termFreq: fields.termFreq,
6323
+ fieldFreq: fields.fieldFreq,
6324
+ fieldLen: fields.fieldLen,
6325
+ links: fields.links,
6326
+ identityTokens: fields.identityTokens
5868
6327
  });
5869
6328
  } catch {
5870
6329
  }
@@ -5882,46 +6341,123 @@ function buildCorpus(contextRoot, opts = {}) {
5882
6341
  }
5883
6342
  if (types.has("task")) {
5884
6343
  docs.push(...loadMarkdownDocs(join26(contextRoot, "state"), "task", contextRoot));
6344
+ docs.push(...loadDigestDocs(contextRoot));
5885
6345
  }
5886
6346
  if (types.has("memory")) {
5887
6347
  docs.push(...loadMemoryFile(contextRoot));
6348
+ docs.push(...loadBookmarkDocs(contextRoot));
5888
6349
  }
5889
6350
  if (types.has("changelog")) {
5890
6351
  docs.push(...loadChangelogEntries(contextRoot));
5891
6352
  }
5892
6353
  return docs;
5893
6354
  }
5894
- function bm25Search(query, corpus, topK = 5) {
6355
+ function recencyMultiplier(updatedAt, now, halfLifeDays = 120) {
6356
+ const minMult = 0.85;
6357
+ if (!updatedAt) return minMult + (1 - minMult) * 0.5;
6358
+ const t = Date.parse(updatedAt);
6359
+ if (Number.isNaN(t)) return minMult + (1 - minMult) * 0.5;
6360
+ const ageDays = Math.max(0, (now.getTime() - t) / 864e5);
6361
+ const decay = Math.pow(0.5, ageDays / halfLifeDays);
6362
+ return minMult + (1 - minMult) * decay;
6363
+ }
6364
+ function statusMultiplier(status) {
6365
+ if (!status) return 1;
6366
+ return STATUS_PENALTY[status.toLowerCase()] ?? 1;
6367
+ }
6368
+ function buildLinkAdjacency(corpus) {
6369
+ const adj = /* @__PURE__ */ new Map();
6370
+ const present = new Set(corpus.map((d) => d.slug));
6371
+ for (const d of corpus) {
6372
+ const set = adj.get(d.slug) ?? /* @__PURE__ */ new Set();
6373
+ for (const target of d.links ?? []) {
6374
+ if (present.has(target) && target !== d.slug) set.add(target);
6375
+ }
6376
+ adj.set(d.slug, set);
6377
+ }
6378
+ return adj;
6379
+ }
6380
+ function bm25Search(query, corpus, topK = 5, opts = {}) {
5895
6381
  if (corpus.length === 0) return [];
6382
+ const now = opts.now ?? /* @__PURE__ */ new Date();
5896
6383
  const queryTerms = Array.from(new Set(tokenize(query)));
5897
6384
  if (queryTerms.length === 0) return [];
6385
+ const synonymTerms = expandQueryTerms(queryTerms, stemToken);
6386
+ const allTerms = new Set(queryTerms);
6387
+ for (const t of synonymTerms.keys()) allTerms.add(t);
5898
6388
  const N = corpus.length;
5899
6389
  const avgdl = corpus.reduce((s, d) => s + d.tokens.length, 0) / N;
6390
+ const avgFieldLen = corpus.reduce((s, d) => s + (d.fieldLen ?? d.tokens.length), 0) / N;
5900
6391
  const df = {};
5901
- for (const term of queryTerms) {
6392
+ for (const term of allTerms) {
5902
6393
  let count = 0;
5903
6394
  for (const d of corpus) if (d.tokenSet.has(term)) count++;
5904
6395
  df[term] = count;
5905
6396
  }
6397
+ const idfOf = (term) => {
6398
+ const dfT = df[term] ?? 0;
6399
+ return Math.log(1 + (N - dfT + 0.5) / (dfT + 0.5));
6400
+ };
6401
+ const bm25fTerm = (doc, term, dl) => {
6402
+ const tf = doc.fieldFreq?.get(term) ?? doc.termFreq.get(term) ?? 0;
6403
+ if (tf === 0) return 0;
6404
+ const num = tf * (K1 + 1);
6405
+ const den = tf + K1 * (1 - B + B * (dl / avgFieldLen));
6406
+ return idfOf(term) * (num / den);
6407
+ };
6408
+ const queryTermSet = new Set(queryTerms);
5906
6409
  const scored = [];
5907
6410
  for (const doc of corpus) {
5908
- let score = 0;
5909
- const dl = doc.tokens.length || 1;
6411
+ let rawScore = 0;
6412
+ const dlFlat = doc.tokens.length || 1;
5910
6413
  for (const term of queryTerms) {
5911
6414
  const tf = doc.termFreq.get(term) ?? 0;
5912
6415
  if (tf === 0) continue;
5913
- const dfT = df[term];
5914
- const idf = Math.log(1 + (N - dfT + 0.5) / (dfT + 0.5));
5915
6416
  const num = tf * (K1 + 1);
5916
- const den = tf + K1 * (1 - B + B * (dl / avgdl));
5917
- score += idf * (num / den);
5918
- }
5919
- if (score > 0) {
5920
- scored.push({ doc, score, snippet: extractSnippet(doc, queryTerms) });
6417
+ const den = tf + K1 * (1 - B + B * (dlFlat / avgdl));
6418
+ rawScore += idfOf(term) * (num / den);
6419
+ }
6420
+ const dlField = doc.fieldLen ?? dlFlat;
6421
+ let fieldPrimary = 0;
6422
+ for (const term of queryTerms) fieldPrimary += bm25fTerm(doc, term, dlField || 1);
6423
+ const fieldBonus = FIELD_WEIGHT_BONUS * Math.max(0, fieldPrimary - rawScore);
6424
+ let synonymContrib = 0;
6425
+ for (const [term, w] of synonymTerms) synonymContrib += w * bm25fTerm(doc, term, dlField || 1);
6426
+ const idToks = doc.identityTokens ?? [];
6427
+ let idHits = 0;
6428
+ for (const t of idToks) if (queryTermSet.has(t)) idHits++;
6429
+ const qCoverage = queryTerms.length > 0 ? idHits / queryTerms.length : 0;
6430
+ const identityBoost = IDENTITY_BOOST * qCoverage * qCoverage * Math.max(rawScore, 1);
6431
+ const rankBase = rawScore + fieldBonus + synonymContrib + identityBoost;
6432
+ if (rankBase <= 0) continue;
6433
+ const rankScore = rankBase * statusMultiplier(doc.status) * recencyMultiplier(doc.updatedAt, now) * (doc.capture ? CAPTURE_RANK_PENALTY : 1);
6434
+ scored.push({
6435
+ hit: { doc, score: rawScore, rankScore, snippet: extractSnippet(doc, queryTerms) },
6436
+ rawRank: rankBase
6437
+ });
6438
+ }
6439
+ if (opts.linkAware) {
6440
+ const adj = buildLinkAdjacency(corpus);
6441
+ const seed = /* @__PURE__ */ new Map();
6442
+ for (const s of scored) seed.set(s.hit.doc.slug, s.rawRank);
6443
+ for (const s of scored) {
6444
+ let boost = 0;
6445
+ const neighbours = adj.get(s.hit.doc.slug) ?? /* @__PURE__ */ new Set();
6446
+ for (const n1 of neighbours) {
6447
+ boost += LINK_DECAY * (seed.get(n1) ?? 0);
6448
+ for (const n2 of adj.get(n1) ?? /* @__PURE__ */ new Set()) {
6449
+ if (n2 === s.hit.doc.slug) continue;
6450
+ boost += LINK_DECAY * LINK_DECAY * (seed.get(n2) ?? 0);
6451
+ }
6452
+ }
6453
+ if (boost > 0) {
6454
+ s.hit.rankScore += boost * statusMultiplier(s.hit.doc.status) * recencyMultiplier(s.hit.doc.updatedAt, now) * (s.hit.doc.capture ? CAPTURE_RANK_PENALTY : 1);
6455
+ }
5921
6456
  }
5922
6457
  }
5923
- scored.sort((a, b) => b.score - a.score);
5924
- return scored.slice(0, topK);
6458
+ const hits = scored.map((s) => s.hit);
6459
+ hits.sort((a, b) => b.rankScore - a.rankScore);
6460
+ return hits.slice(0, topK);
5925
6461
  }
5926
6462
  function extractSnippet(doc, queryTerms) {
5927
6463
  const lines = doc.body.split("\n");
@@ -5943,11 +6479,13 @@ function extractSnippet(doc, queryTerms) {
5943
6479
  const end = Math.min(lines.length, bestIdx + 2);
5944
6480
  return lines.slice(start, end).join("\n").trim();
5945
6481
  }
5946
- var STOPWORDS, K1, B;
6482
+ var STOPWORDS, TR_SUFFIXES, FIELD_WEIGHTS, WIKILINK_RE, K1, B, FIELD_WEIGHT_BONUS, IDENTITY_BOOST, CAPTURE_RANK_PENALTY, STATUS_PENALTY, LINK_DECAY;
5947
6483
  var init_recall = __esm({
5948
6484
  "src/lib/recall.ts"() {
5949
6485
  "use strict";
5950
6486
  init_frontmatter();
6487
+ init_recall_synonyms();
6488
+ init_session_digest();
5951
6489
  STOPWORDS = /* @__PURE__ */ new Set([
5952
6490
  // English
5953
6491
  "the",
@@ -6039,8 +6577,276 @@ var init_recall = __esm({
6039
6577
  "siz",
6040
6578
  "onlar"
6041
6579
  ]);
6580
+ TR_SUFFIXES = [
6581
+ "lerinden",
6582
+ "lar\u0131ndan",
6583
+ "lerine",
6584
+ "lar\u0131na",
6585
+ "leri",
6586
+ "lar\u0131",
6587
+ "ler",
6588
+ "lar",
6589
+ "den",
6590
+ "dan",
6591
+ "nin",
6592
+ "n\u0131n",
6593
+ "nun",
6594
+ "n\xFCn",
6595
+ "de",
6596
+ "da",
6597
+ "yi",
6598
+ "y\u0131",
6599
+ "yu",
6600
+ "y\xFC"
6601
+ ];
6602
+ FIELD_WEIGHTS = { title: 3, tags: 2, description: 2, body: 1 };
6603
+ WIKILINK_RE = /\[\[([^\]]+)\]\]/g;
6042
6604
  K1 = 1.5;
6043
6605
  B = 0.75;
6606
+ FIELD_WEIGHT_BONUS = 0.5;
6607
+ IDENTITY_BOOST = 1.5;
6608
+ CAPTURE_RANK_PENALTY = 0.5;
6609
+ STATUS_PENALTY = { completed: 0.85 };
6610
+ LINK_DECAY = 0.3;
6611
+ }
6612
+ });
6613
+
6614
+ // src/lib/session-digest.ts
6615
+ import { existsSync as existsSync29, mkdirSync as mkdirSync10, writeFileSync as writeFileSync16 } from "fs";
6616
+ import { join as join27 } from "path";
6617
+ import fg9 from "fast-glob";
6618
+ function coerceCreatedAt(value) {
6619
+ if (typeof value === "string" && value.trim()) return value.trim();
6620
+ if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();
6621
+ return void 0;
6622
+ }
6623
+ function clampLine(text) {
6624
+ const oneLine = text.replace(/\s+/g, " ").trim();
6625
+ if (oneLine.length <= MAX_LINE_CHARS) return oneLine;
6626
+ return oneLine.slice(0, MAX_LINE_CHARS - 1) + "\u2026";
6627
+ }
6628
+ function codeChangeHeader(change) {
6629
+ const firstLine = change.split("\n", 1)[0] ?? "";
6630
+ return clampLine(firstLine);
6631
+ }
6632
+ function buildDigest(distilled, opts = {}) {
6633
+ const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
6634
+ const sections = ["# Session Digest", ""];
6635
+ const userMessages = distilled.userMessages.slice(0, MAX_USER_MESSAGES);
6636
+ if (userMessages.length > 0) {
6637
+ sections.push("## User Messages");
6638
+ for (const m of userMessages) sections.push(`- ${clampLine(m)}`);
6639
+ sections.push("");
6640
+ }
6641
+ const decisions = distilled.agentDecisions.filter((d) => !d.startsWith("[thinking]")).slice(0, MAX_DECISIONS);
6642
+ if (decisions.length > 0) {
6643
+ sections.push("## Decisions");
6644
+ for (const d of decisions) sections.push(`- ${clampLine(d)}`);
6645
+ sections.push("");
6646
+ }
6647
+ const errors = distilled.errors.slice(0, MAX_ERRORS);
6648
+ if (errors.length > 0) {
6649
+ sections.push("## Errors");
6650
+ for (const e of errors) sections.push(`- ${clampLine(e)}`);
6651
+ sections.push("");
6652
+ }
6653
+ const codeChanges = distilled.codeChanges.slice(0, MAX_CODE_CHANGES);
6654
+ if (codeChanges.length > 0) {
6655
+ sections.push("## Code Changes");
6656
+ for (const c of codeChanges) sections.push(`- ${codeChangeHeader(c)}`);
6657
+ sections.push("");
6658
+ }
6659
+ return enforceByteCap(sections, maxBytes);
6660
+ }
6661
+ function enforceByteCap(lines, maxBytes) {
6662
+ const byteLen = (s) => Buffer.byteLength(s, "utf-8");
6663
+ let working = [...lines];
6664
+ let out = working.join("\n");
6665
+ while (byteLen(out) > maxBytes && working.length > 1) {
6666
+ working.pop();
6667
+ out = working.join("\n");
6668
+ }
6669
+ if (byteLen(out) > maxBytes) {
6670
+ out = Buffer.from(out, "utf-8").subarray(0, maxBytes).toString("utf-8");
6671
+ }
6672
+ return out.trimEnd() + "\n";
6673
+ }
6674
+ function digestsDir(root) {
6675
+ return join27(root, "state", DIGESTS_DIRNAME);
6676
+ }
6677
+ function digestPath(root, sessionId) {
6678
+ const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
6679
+ return join27(digestsDir(root), `${safe}.md`);
6680
+ }
6681
+ function digestExists(root, sessionId) {
6682
+ return existsSync29(digestPath(root, sessionId));
6683
+ }
6684
+ function writeDigest(root, sessionId, md) {
6685
+ const dir = digestsDir(root);
6686
+ mkdirSync10(dir, { recursive: true });
6687
+ const file = digestPath(root, sessionId);
6688
+ const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
6689
+ const frontmatter = [
6690
+ "---",
6691
+ "type: session-digest",
6692
+ `session_id: ${safeId}`,
6693
+ `created_at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
6694
+ "---",
6695
+ ""
6696
+ ].join("\n");
6697
+ writeFileSync16(file, frontmatter + md, "utf-8");
6698
+ return file;
6699
+ }
6700
+ function loadDigestDocs(root) {
6701
+ const dir = digestsDir(root);
6702
+ if (!existsSync29(dir)) return [];
6703
+ const files = fg9.sync("*.md", { cwd: dir, absolute: true });
6704
+ const parsed = [];
6705
+ for (const file of files) {
6706
+ try {
6707
+ const { data, content } = readFrontmatter(file);
6708
+ const sessionId = typeof data.session_id === "string" ? data.session_id : file;
6709
+ const slug = `digest#${sessionId}`;
6710
+ const title = `Session digest ${sessionId}`;
6711
+ const body = content.trim();
6712
+ if (!body) continue;
6713
+ const relPath = join27("state", DIGESTS_DIRNAME, `${sessionId}.md`);
6714
+ const createdAt = coerceCreatedAt(data.created_at);
6715
+ const fields = buildFields({ slug, title, description: "", tags: [], body });
6716
+ const ms = createdAt ? Date.parse(createdAt) : NaN;
6717
+ parsed.push({
6718
+ createdMs: Number.isNaN(ms) ? 0 : ms,
6719
+ doc: {
6720
+ type: "task",
6721
+ path: file,
6722
+ relPath,
6723
+ slug,
6724
+ title,
6725
+ description: "",
6726
+ tags: [],
6727
+ body,
6728
+ tokens: fields.tokens,
6729
+ tokenSet: new Set(fields.tokens),
6730
+ termFreq: fields.termFreq,
6731
+ fieldFreq: fields.fieldFreq,
6732
+ fieldLen: fields.fieldLen,
6733
+ links: fields.links,
6734
+ identityTokens: fields.identityTokens,
6735
+ updatedAt: createdAt,
6736
+ // C3: session digests are continuous captures → rank-penalised.
6737
+ capture: true
6738
+ }
6739
+ });
6740
+ } catch {
6741
+ }
6742
+ }
6743
+ parsed.sort((a, b) => b.createdMs - a.createdMs || a.doc.slug.localeCompare(b.doc.slug));
6744
+ return parsed.slice(0, MAX_INDEXED_DIGESTS).map((p) => p.doc);
6745
+ }
6746
+ var DEFAULT_MAX_BYTES, MAX_USER_MESSAGES, MAX_DECISIONS, MAX_ERRORS, MAX_CODE_CHANGES, MAX_LINE_CHARS, DIGESTS_DIRNAME, MAX_INDEXED_DIGESTS;
6747
+ var init_session_digest = __esm({
6748
+ "src/lib/session-digest.ts"() {
6749
+ "use strict";
6750
+ init_frontmatter();
6751
+ init_recall();
6752
+ DEFAULT_MAX_BYTES = 8e3;
6753
+ MAX_USER_MESSAGES = 12;
6754
+ MAX_DECISIONS = 12;
6755
+ MAX_ERRORS = 6;
6756
+ MAX_CODE_CHANGES = 10;
6757
+ MAX_LINE_CHARS = 240;
6758
+ DIGESTS_DIRNAME = ".session-digests";
6759
+ MAX_INDEXED_DIGESTS = 50;
6760
+ }
6761
+ });
6762
+
6763
+ // src/lib/salience.ts
6764
+ function clamp(text) {
6765
+ const oneLine = text.replace(/\s+/g, " ").trim();
6766
+ return oneLine.length <= MAX_MESSAGE_CHARS ? oneLine : oneLine.slice(0, MAX_MESSAGE_CHARS - 1) + "\u2026";
6767
+ }
6768
+ function detectSalience(distilled) {
6769
+ const moments = [];
6770
+ const seen = /* @__PURE__ */ new Set();
6771
+ const push = (message, salience) => {
6772
+ const clamped = clamp(message);
6773
+ if (!clamped) return;
6774
+ const key = `${salience}::${clamped.toLowerCase()}`;
6775
+ if (seen.has(key)) return;
6776
+ seen.add(key);
6777
+ moments.push({ message: clamped, salience });
6778
+ };
6779
+ for (const msg of distilled.userMessages) {
6780
+ if (CORRECTION_RE.test(msg)) {
6781
+ push(`User correction: ${msg}`, 2);
6782
+ }
6783
+ }
6784
+ if (distilled.errors.length > 0 && distilled.codeChanges.length > 0) {
6785
+ const firstError = distilled.errors[0];
6786
+ push(`Error resolved by code change: ${firstError}`, 1);
6787
+ }
6788
+ const decisionSources = [
6789
+ ...distilled.agentDecisions.filter((d) => !d.startsWith("[thinking]")),
6790
+ ...distilled.userMessages
6791
+ ];
6792
+ for (const src of decisionSources) {
6793
+ if (DECISION_RE.test(src)) {
6794
+ push(`Decision: ${src}`, 2);
6795
+ }
6796
+ }
6797
+ return moments.slice(0, MAX_MOMENTS);
6798
+ }
6799
+ var CORRECTION_RE, DECISION_RE, MAX_MOMENTS, MAX_MESSAGE_CHARS;
6800
+ var init_salience = __esm({
6801
+ "src/lib/salience.ts"() {
6802
+ "use strict";
6803
+ CORRECTION_RE = /\b(no|actually|wrong|instead|hayır|yanlış|değil)\b/i;
6804
+ DECISION_RE = /\b(decided|chose|switched to|will use|karar|seçtik)\b/i;
6805
+ MAX_MOMENTS = 5;
6806
+ MAX_MESSAGE_CHARS = 200;
6807
+ }
6808
+ });
6809
+
6810
+ // src/lib/marketing/path-guards.ts
6811
+ import { existsSync as existsSync30, realpathSync } from "fs";
6812
+ import { basename as basename11, dirname as dirname11, resolve as resolve3, sep as sep2 } from "path";
6813
+ function isMarketingEnvPath(filePath, from) {
6814
+ if (!filePath) return false;
6815
+ const root = resolveContextRoot(from);
6816
+ if (!root) return false;
6817
+ const realRoot = realpathOrSelf(root);
6818
+ const target = resolve3(realRoot, "marketing", ".env");
6819
+ const realCandidate = realpathOfNearestAncestor(resolve3(filePath));
6820
+ if (realCandidate === target) return true;
6821
+ if (!realCandidate.startsWith(realRoot + sep2)) return false;
6822
+ return realCandidate.endsWith(`${sep2}marketing${sep2}.env`);
6823
+ }
6824
+ function realpathOrSelf(p) {
6825
+ try {
6826
+ return existsSync30(p) ? realpathSync(p) : p;
6827
+ } catch {
6828
+ return p;
6829
+ }
6830
+ }
6831
+ function realpathOfNearestAncestor(p) {
6832
+ let current = p;
6833
+ const tail = [];
6834
+ while (current && current !== sep2 && !existsSync30(current)) {
6835
+ tail.unshift(basename11(current));
6836
+ current = dirname11(current);
6837
+ }
6838
+ if (!current || current === sep2) return p;
6839
+ try {
6840
+ const real = realpathSync(current);
6841
+ return tail.length === 0 ? real : real + sep2 + tail.join(sep2);
6842
+ } catch {
6843
+ return p;
6844
+ }
6845
+ }
6846
+ var init_path_guards = __esm({
6847
+ "src/lib/marketing/path-guards.ts"() {
6848
+ "use strict";
6849
+ init_context_path();
6044
6850
  }
6045
6851
  });
6046
6852
 
@@ -6062,8 +6868,23 @@ function haikuRecall(rawPrompt, contextRoot, opts) {
6062
6868
  try {
6063
6869
  const corpus = buildCorpus(contextRoot);
6064
6870
  if (corpus.length === 0) return null;
6871
+ const PREPASS_TOP_K = 100;
6065
6872
  const MAX_INDEX_CHARS = 8e3;
6066
- const fullIndex = buildCorpusIndex(corpus);
6873
+ const ranked = bm25Search(rawPrompt, corpus, PREPASS_TOP_K);
6874
+ let subset;
6875
+ if (ranked.length > 0) {
6876
+ const seen = /* @__PURE__ */ new Set();
6877
+ subset = [];
6878
+ for (const h of ranked) {
6879
+ const key = docKey(h.doc);
6880
+ if (seen.has(key)) continue;
6881
+ seen.add(key);
6882
+ subset.push(h.doc);
6883
+ }
6884
+ } else {
6885
+ subset = corpus.slice(0, PREPASS_TOP_K);
6886
+ }
6887
+ const fullIndex = buildCorpusIndex(subset);
6067
6888
  const index = fullIndex.length > MAX_INDEX_CHARS ? fullIndex.slice(0, MAX_INDEX_CHARS) + "\n[...truncated]" : fullIndex;
6068
6889
  const systemPrompt = SYSTEM_TEMPLATE.replace("{INDEX}", index);
6069
6890
  const executor = opts?.executor ?? defaultExecutor;
@@ -6093,7 +6914,7 @@ function haikuRecall(rawPrompt, contextRoot, opts) {
6093
6914
  }
6094
6915
  const doc = lookup.get(key);
6095
6916
  if (!doc) continue;
6096
- hits.push({ doc, score: 0, snippet: reason });
6917
+ hits.push({ doc, score: 0, rankScore: 0, snippet: reason });
6097
6918
  if (hits.length >= 3) break;
6098
6919
  }
6099
6920
  return hits;
@@ -6152,13 +6973,13 @@ If pure greeting: {"skip":true}`;
6152
6973
  });
6153
6974
 
6154
6975
  // src/cli/commands/hook.ts
6155
- import { readFileSync as readFileSync21, existsSync as existsSync29, statSync as statSync4 } from "fs";
6976
+ import { readFileSync as readFileSync23, existsSync as existsSync31, statSync as statSync5 } from "fs";
6156
6977
  import { execFileSync as execFileSync3 } from "child_process";
6157
- import { dirname as dirname12, resolve as resolve4, join as join27, extname, basename as basename10, relative as relative2 } from "path";
6978
+ import { dirname as dirname12, resolve as resolve4, join as join28, extname, basename as basename12, relative as relative2 } from "path";
6158
6979
  function readStdin() {
6159
6980
  if (process.stdin.isTTY) return null;
6160
6981
  try {
6161
- const raw = readFileSync21(0, "utf-8");
6982
+ const raw = readFileSync23(0, "utf-8");
6162
6983
  if (!raw.trim()) return null;
6163
6984
  const parsed = JSON.parse(raw);
6164
6985
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
@@ -6168,11 +6989,11 @@ function readStdin() {
6168
6989
  }
6169
6990
  }
6170
6991
  function analyzeTranscript(transcriptPath) {
6171
- if (!existsSync29(transcriptPath)) return ZERO_ANALYSIS;
6992
+ if (!existsSync31(transcriptPath)) return ZERO_ANALYSIS;
6172
6993
  try {
6173
- const stat = statSync4(transcriptPath);
6174
- if (stat.size === 0 || stat.size > MAX_TRANSCRIPT_BYTES) return ZERO_ANALYSIS;
6175
- const content = readFileSync21(transcriptPath, "utf-8");
6994
+ const stat = statSync5(transcriptPath);
6995
+ if (stat.size === 0 || stat.size > MAX_TRANSCRIPT_BYTES2) return ZERO_ANALYSIS;
6996
+ const content = readFileSync23(transcriptPath, "utf-8");
6176
6997
  const changeMatches = content.match(/"name"\s*:\s*"(?:Write|Edit)"/g);
6177
6998
  const toolMatches = content.match(/"name"\s*:\s*"[A-Za-z_]+"/g);
6178
6999
  const slugs = /* @__PURE__ */ new Set();
@@ -6213,22 +7034,22 @@ function findProjectConfig(filePath) {
6213
7034
  for (let i = 0; i <= MAX_WALK_LEVELS; i++) {
6214
7035
  if (!formatter) {
6215
7036
  for (const name of BIOME_CONFIGS) {
6216
- if (existsSync29(join27(dir, name))) {
6217
- formatter = { type: "biome", configPath: join27(dir, name), projectRoot: dir };
7037
+ if (existsSync31(join28(dir, name))) {
7038
+ formatter = { type: "biome", configPath: join28(dir, name), projectRoot: dir };
6218
7039
  break;
6219
7040
  }
6220
7041
  }
6221
7042
  if (!formatter) {
6222
7043
  for (const name of PRETTIER_CONFIGS) {
6223
- if (existsSync29(join27(dir, name))) {
6224
- formatter = { type: "prettier", configPath: join27(dir, name), projectRoot: dir };
7044
+ if (existsSync31(join28(dir, name))) {
7045
+ formatter = { type: "prettier", configPath: join28(dir, name), projectRoot: dir };
6225
7046
  break;
6226
7047
  }
6227
7048
  }
6228
7049
  }
6229
7050
  }
6230
- if (!tsconfig && existsSync29(join27(dir, "tsconfig.json"))) {
6231
- tsconfig = join27(dir, "tsconfig.json");
7051
+ if (!tsconfig && existsSync31(join28(dir, "tsconfig.json"))) {
7052
+ tsconfig = join28(dir, "tsconfig.json");
6232
7053
  }
6233
7054
  if (formatter && tsconfig) break;
6234
7055
  const parent = dirname12(dir);
@@ -6238,8 +7059,8 @@ function findProjectConfig(filePath) {
6238
7059
  return { formatter, tsconfig };
6239
7060
  }
6240
7061
  function resolveLocalBin(binName, projectRoot) {
6241
- const localBin = join27(projectRoot, "node_modules", ".bin", binName);
6242
- return existsSync29(localBin) ? localBin : null;
7062
+ const localBin = join28(projectRoot, "node_modules", ".bin", binName);
7063
+ return existsSync31(localBin) ? localBin : null;
6243
7064
  }
6244
7065
  function runFormatter(detection, filePath) {
6245
7066
  try {
@@ -6320,7 +7141,7 @@ function runTscCheckWithConfig(filePath, tsconfigPath) {
6320
7141
  }
6321
7142
  }
6322
7143
  if (relevantErrors.length === 0) return null;
6323
- return `TypeScript errors in ${basename10(filePath)}:
7144
+ return `TypeScript errors in ${basename12(filePath)}:
6324
7145
  ${relevantErrors.join("\n")}`;
6325
7146
  }
6326
7147
  function getConsolidationDirective(state) {
@@ -6465,6 +7286,34 @@ function registerHookCommand(program) {
6465
7286
  state.debt += score;
6466
7287
  dirty = true;
6467
7288
  }
7289
+ for (const session of state.sessions) {
7290
+ if (!session.transcript_path) continue;
7291
+ if (digestExists(root, session.session_id)) continue;
7292
+ try {
7293
+ const distilled = distillTranscript(session.transcript_path);
7294
+ const md = buildDigest(distilled);
7295
+ writeDigest(root, session.session_id, md);
7296
+ const taskSlug = session.task_slugs?.[0] ?? null;
7297
+ for (const moment of detectSalience(distilled)) {
7298
+ const exists = state.bookmarks.some((b) => b.message === moment.message);
7299
+ if (exists) continue;
7300
+ const bookmark = {
7301
+ id: generateId("bm"),
7302
+ message: moment.message,
7303
+ salience: moment.salience,
7304
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
7305
+ session_id: session.session_id,
7306
+ task_slug: taskSlug
7307
+ };
7308
+ state.bookmarks.unshift(bookmark);
7309
+ dirty = true;
7310
+ }
7311
+ } catch (digestErr) {
7312
+ if (process.env.DREAMCONTEXT_DEBUG) {
7313
+ console.error("[digest] error:", digestErr.message ?? digestErr);
7314
+ }
7315
+ }
7316
+ }
6468
7317
  if (dirty) {
6469
7318
  writeSleepState(root, state);
6470
7319
  }
@@ -6501,7 +7350,7 @@ function registerHookCommand(program) {
6501
7350
  }
6502
7351
  if (toolName !== "Agent") process.exit(0);
6503
7352
  const subagentType = typeof toolInput.subagent_type === "string" ? toolInput.subagent_type : "";
6504
- if (subagentType !== "Explore") process.exit(0);
7353
+ if (subagentType.trim().toLowerCase() !== "explore") process.exit(0);
6505
7354
  const root = resolveContextRoot();
6506
7355
  if (!root) process.exit(0);
6507
7356
  console.log(JSON.stringify({
@@ -6606,7 +7455,7 @@ function registerHookCommand(program) {
6606
7455
  const corpus = buildCorpus(root);
6607
7456
  hits = bm25Search(prompt, corpus, 3);
6608
7457
  }
6609
- if (hits.length > 0 && (mode === "Haiku" || hits[0].score >= 2)) {
7458
+ if (hits.length > 0 && (mode === "Haiku" || hits.some((h) => h.score >= 2))) {
6610
7459
  const lines = ["", `\u2014 Memory recall (${mode}, top ${hits.length}) \u2014`];
6611
7460
  for (const h of hits) {
6612
7461
  lines.push(` [${h.doc.type}] ${h.doc.relPath}`);
@@ -6617,6 +7466,14 @@ function registerHookCommand(program) {
6617
7466
  }
6618
7467
  console.log(lines.join("\n"));
6619
7468
  hadRecallHits = true;
7469
+ let bumped = false;
7470
+ for (const h of hits) {
7471
+ if (h.doc.type === "knowledge") {
7472
+ bumpKnowledgeAccess(state, h.doc.slug);
7473
+ bumped = true;
7474
+ }
7475
+ }
7476
+ if (bumped) writeSleepState(root, state);
6620
7477
  }
6621
7478
  }
6622
7479
  }
@@ -6628,7 +7485,7 @@ function registerHookCommand(program) {
6628
7485
  try {
6629
7486
  const prompt = String(input9.prompt ?? "");
6630
7487
  if (prompt.trim().length >= 8) {
6631
- const skillsRoot = join27(process.cwd(), ".claude", "skills");
7488
+ const skillsRoot = join28(process.cwd(), ".claude", "skills");
6632
7489
  const docs = loadSkillDocs(skillsRoot);
6633
7490
  if (docs.length > 0 && bm25Search(prompt, docs, 5).some((h) => h.score >= SKILL_SCORE_THRESHOLD)) {
6634
7491
  gatedSkills = true;
@@ -6665,7 +7522,7 @@ function registerHookCommand(program) {
6665
7522
  if (config.formatter) {
6666
7523
  const result = runFormatter(config.formatter, filePath);
6667
7524
  if (result.success) {
6668
- messages.push(`Formatted ${basename10(filePath)} with ${config.formatter.type}.`);
7525
+ messages.push(`Formatted ${basename12(filePath)} with ${config.formatter.type}.`);
6669
7526
  }
6670
7527
  }
6671
7528
  if (config.tsconfig) {
@@ -6702,12 +7559,16 @@ function registerHookCommand(program) {
6702
7559
  writeSleepState(root, state);
6703
7560
  });
6704
7561
  }
6705
- var MAX_TRANSCRIPT_BYTES, SKILL_SCORE_THRESHOLD, ZERO_ANALYSIS, JS_TS_EXTENSIONS, MAX_WALK_LEVELS, BIOME_CONFIGS, PRETTIER_CONFIGS;
7562
+ var MAX_TRANSCRIPT_BYTES2, SKILL_SCORE_THRESHOLD, ZERO_ANALYSIS, JS_TS_EXTENSIONS, MAX_WALK_LEVELS, BIOME_CONFIGS, PRETTIER_CONFIGS;
6706
7563
  var init_hook = __esm({
6707
7564
  "src/cli/commands/hook.ts"() {
6708
7565
  "use strict";
6709
7566
  init_context_path();
6710
7567
  init_sleep();
7568
+ init_transcript();
7569
+ init_session_digest();
7570
+ init_salience();
7571
+ init_id();
6711
7572
  init_snapshot2();
6712
7573
  init_snapshot();
6713
7574
  init_path_guards();
@@ -6715,7 +7576,7 @@ var init_hook = __esm({
6715
7576
  init_recall_query_extractor();
6716
7577
  init_version_check();
6717
7578
  init_install_skill();
6718
- MAX_TRANSCRIPT_BYTES = 50 * 1024 * 1024;
7579
+ MAX_TRANSCRIPT_BYTES2 = 50 * 1024 * 1024;
6719
7580
  SKILL_SCORE_THRESHOLD = 1;
6720
7581
  ZERO_ANALYSIS = { changeCount: 0, toolCount: 0, taskSlugs: [] };
6721
7582
  JS_TS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"]);
@@ -6735,19 +7596,19 @@ var init_hook = __esm({
6735
7596
  });
6736
7597
 
6737
7598
  // src/cli/commands/doctor.ts
6738
- import { existsSync as existsSync30, readFileSync as readFileSync22, readdirSync as readdirSync8, statSync as statSync5 } from "fs";
6739
- import { join as join28 } from "path";
7599
+ import { existsSync as existsSync32, readFileSync as readFileSync24, readdirSync as readdirSync9, statSync as statSync6 } from "fs";
7600
+ import { join as join29 } from "path";
6740
7601
  import chalk10 from "chalk";
6741
7602
  function checkFile(root, relPath, label, required) {
6742
- const fullPath = join28(root, relPath);
6743
- if (!existsSync30(fullPath)) {
7603
+ const fullPath = join29(root, relPath);
7604
+ if (!existsSync32(fullPath)) {
6744
7605
  return {
6745
7606
  name: label,
6746
7607
  status: required ? "error" : "warn",
6747
7608
  message: required ? `Missing: ${relPath}` : `Optional file not found: ${relPath}`
6748
7609
  };
6749
7610
  }
6750
- const stat = statSync5(fullPath);
7611
+ const stat = statSync6(fullPath);
6751
7612
  if (stat.size === 0) {
6752
7613
  return {
6753
7614
  name: label,
@@ -6756,7 +7617,7 @@ function checkFile(root, relPath, label, required) {
6756
7617
  };
6757
7618
  }
6758
7619
  if (relPath.endsWith(".md")) {
6759
- const content = readFileSync22(fullPath, "utf-8");
7620
+ const content = readFileSync24(fullPath, "utf-8");
6760
7621
  if (content.includes("(Add your") || content.includes("{{") || content.includes("(To be defined)")) {
6761
7622
  return {
6762
7623
  name: label,
@@ -6768,12 +7629,12 @@ function checkFile(root, relPath, label, required) {
6768
7629
  return { name: label, status: "ok", message: relPath };
6769
7630
  }
6770
7631
  function checkJson(root, relPath, label) {
6771
- const fullPath = join28(root, relPath);
6772
- if (!existsSync30(fullPath)) {
7632
+ const fullPath = join29(root, relPath);
7633
+ if (!existsSync32(fullPath)) {
6773
7634
  return { name: label, status: "error", message: `Missing: ${relPath}` };
6774
7635
  }
6775
7636
  try {
6776
- const content = readFileSync22(fullPath, "utf-8");
7637
+ const content = readFileSync24(fullPath, "utf-8");
6777
7638
  JSON.parse(content);
6778
7639
  return { name: label, status: "ok", message: relPath };
6779
7640
  } catch {
@@ -6781,8 +7642,8 @@ function checkJson(root, relPath, label) {
6781
7642
  }
6782
7643
  }
6783
7644
  function checkDirectory(root, relPath, label) {
6784
- const fullPath = join28(root, relPath);
6785
- if (!existsSync30(fullPath)) {
7645
+ const fullPath = join29(root, relPath);
7646
+ if (!existsSync32(fullPath)) {
6786
7647
  return { name: label, status: "error", message: `Missing directory: ${relPath}` };
6787
7648
  }
6788
7649
  return { name: label, status: "ok", message: relPath };
@@ -6790,11 +7651,11 @@ function checkDirectory(root, relPath, label) {
6790
7651
  function checkDataStructures(root) {
6791
7652
  const results = [];
6792
7653
  const dirRel = "core/data-structures";
6793
- const dirAbs = join28(root, dirRel);
7654
+ const dirAbs = join29(root, dirRel);
6794
7655
  const legacyRel = "core/5.data_structures.sql";
6795
- const legacyAbs = join28(root, legacyRel);
6796
- const legacyExists = existsSync30(legacyAbs);
6797
- if (!existsSync30(dirAbs)) {
7656
+ const legacyAbs = join29(root, legacyRel);
7657
+ const legacyExists = existsSync32(legacyAbs);
7658
+ if (!existsSync32(dirAbs)) {
6798
7659
  if (legacyExists) {
6799
7660
  results.push({
6800
7661
  name: "Data structures",
@@ -6812,7 +7673,7 @@ function checkDataStructures(root) {
6812
7673
  }
6813
7674
  let mdCount = 0;
6814
7675
  try {
6815
- mdCount = readdirSync8(dirAbs).filter((f) => f.endsWith(".md")).length;
7676
+ mdCount = readdirSync9(dirAbs).filter((f) => f.endsWith(".md")).length;
6816
7677
  } catch {
6817
7678
  results.push({
6818
7679
  name: "Data structures",
@@ -6869,13 +7730,13 @@ function registerDoctorCommand(program) {
6869
7730
  checkFile(root, "core/4.tech_stack.md", "Tech stack", false),
6870
7731
  ...checkDataStructures(root),
6871
7732
  // Sleep state (optional — created on first Stop hook)
6872
- ...existsSync30(join28(root, "state", ".sleep.json")) ? [checkJson(root, "state/.sleep.json", "Sleep state")] : [],
6873
- ...existsSync30(join28(root, "state", ".platforms.json")) ? [checkJson(root, "state/.platforms.json", "Platform defaults")] : []
7733
+ ...existsSync32(join29(root, "state", ".sleep.json")) ? [checkJson(root, "state/.sleep.json", "Sleep state")] : [],
7734
+ ...existsSync32(join29(root, "state", ".platforms.json")) ? [checkJson(root, "state/.platforms.json", "Platform defaults")] : []
6874
7735
  ];
6875
- const sleepPath = join28(root, "state", ".sleep.json");
6876
- if (existsSync30(sleepPath)) {
7736
+ const sleepPath = join29(root, "state", ".sleep.json");
7737
+ if (existsSync32(sleepPath)) {
6877
7738
  try {
6878
- const parsed = JSON.parse(readFileSync22(sleepPath, "utf-8"));
7739
+ const parsed = JSON.parse(readFileSync24(sleepPath, "utf-8"));
6879
7740
  if (typeof parsed.debt !== "number" || parsed.debt < 0) {
6880
7741
  results.push({ name: "Sleep debt", status: "warn", message: "Invalid debt value in .sleep.json" });
6881
7742
  }
@@ -6912,17 +7773,17 @@ var init_doctor = __esm({
6912
7773
  });
6913
7774
 
6914
7775
  // src/lib/vaults.ts
6915
- import { existsSync as existsSync31, mkdirSync as mkdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync16 } from "fs";
6916
- import { dirname as dirname13, join as join29, resolve as resolve5 } from "path";
7776
+ import { existsSync as existsSync33, mkdirSync as mkdirSync11, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "fs";
7777
+ import { dirname as dirname13, join as join30, resolve as resolve5 } from "path";
6917
7778
  import { homedir } from "os";
6918
7779
  function vaultsFilePath(home = homedir()) {
6919
- return join29(home, ".dreamcontext", "vaults.json");
7780
+ return join30(home, ".dreamcontext", "vaults.json");
6920
7781
  }
6921
7782
  function listVaults(home) {
6922
7783
  const filePath = vaultsFilePath(home);
6923
- if (!existsSync31(filePath)) return [];
7784
+ if (!existsSync33(filePath)) return [];
6924
7785
  try {
6925
- const raw = readFileSync23(filePath, "utf-8");
7786
+ const raw = readFileSync25(filePath, "utf-8");
6926
7787
  const parsed = JSON.parse(raw);
6927
7788
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.vaults)) {
6928
7789
  return [];
@@ -6936,15 +7797,15 @@ function listVaults(home) {
6936
7797
  }
6937
7798
  }
6938
7799
  function writeRegistry(filePath, registry) {
6939
- mkdirSync10(dirname13(filePath), { recursive: true });
6940
- writeFileSync16(filePath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
7800
+ mkdirSync11(dirname13(filePath), { recursive: true });
7801
+ writeFileSync17(filePath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
6941
7802
  }
6942
7803
  function addVault(name, dirPath, home) {
6943
7804
  const resolved = resolve5(dirPath);
6944
- if (!existsSync31(resolved)) {
7805
+ if (!existsSync33(resolved)) {
6945
7806
  throw new VaultError(`Path does not exist: ${resolved}`);
6946
7807
  }
6947
- if (!existsSync31(join29(resolved, "_dream_context"))) {
7808
+ if (!existsSync33(join30(resolved, "_dream_context"))) {
6948
7809
  throw new VaultError(
6949
7810
  `Path is not a dreamcontext project (no _dream_context/ directory): ${resolved}`
6950
7811
  );
@@ -6966,15 +7827,15 @@ function resolveVaultContextRoot(arg, home = homedir()) {
6966
7827
  const vaults = listVaults(home);
6967
7828
  const named = vaults.find((v) => v.name === arg);
6968
7829
  const resolved = named ? named.path : resolve5(arg);
6969
- if (!existsSync31(resolved)) {
7830
+ if (!existsSync33(resolved)) {
6970
7831
  throw new VaultError(`Vault path does not exist: ${resolved}`);
6971
7832
  }
6972
- if (!existsSync31(join29(resolved, "_dream_context"))) {
7833
+ if (!existsSync33(join30(resolved, "_dream_context"))) {
6973
7834
  throw new VaultError(
6974
7835
  `Path is not a dreamcontext project (no _dream_context/ directory): ${resolved}`
6975
7836
  );
6976
7837
  }
6977
- return join29(resolved, "_dream_context");
7838
+ return join30(resolved, "_dream_context");
6978
7839
  }
6979
7840
  function removeVault(name, home) {
6980
7841
  const existing = listVaults(home);
@@ -7123,23 +7984,23 @@ var init_middleware = __esm({
7123
7984
  });
7124
7985
 
7125
7986
  // src/server/static.ts
7126
- import { existsSync as existsSync32, readFileSync as readFileSync24, statSync as statSync6 } from "fs";
7127
- import { join as join30, extname as extname2 } from "path";
7987
+ import { existsSync as existsSync34, readFileSync as readFileSync26, statSync as statSync7 } from "fs";
7988
+ import { join as join31, extname as extname2 } from "path";
7128
7989
  function serveStatic(req, res, staticDir) {
7129
7990
  const url = new URL(req.url || "/", `http://${req.headers.host}`);
7130
- let filePath = join30(staticDir, url.pathname);
7991
+ let filePath = join31(staticDir, url.pathname);
7131
7992
  if (!filePath.startsWith(staticDir)) {
7132
7993
  res.writeHead(403, { "Content-Type": "text/plain" });
7133
7994
  res.end("Forbidden");
7134
7995
  return;
7135
7996
  }
7136
- if (existsSync32(filePath) && statSync6(filePath).isDirectory()) {
7137
- filePath = join30(filePath, "index.html");
7997
+ if (existsSync34(filePath) && statSync7(filePath).isDirectory()) {
7998
+ filePath = join31(filePath, "index.html");
7138
7999
  }
7139
- if (existsSync32(filePath) && statSync6(filePath).isFile()) {
8000
+ if (existsSync34(filePath) && statSync7(filePath).isFile()) {
7140
8001
  const ext = extname2(filePath);
7141
8002
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
7142
- const content = readFileSync24(filePath);
8003
+ const content = readFileSync26(filePath);
7143
8004
  res.writeHead(200, {
7144
8005
  "Content-Type": contentType,
7145
8006
  "Content-Length": content.length,
@@ -7148,9 +8009,9 @@ function serveStatic(req, res, staticDir) {
7148
8009
  res.end(content);
7149
8010
  return;
7150
8011
  }
7151
- const indexPath2 = join30(staticDir, "index.html");
7152
- if (existsSync32(indexPath2)) {
7153
- const content = readFileSync24(indexPath2);
8012
+ const indexPath2 = join31(staticDir, "index.html");
8013
+ if (existsSync34(indexPath2)) {
8014
+ const content = readFileSync26(indexPath2);
7154
8015
  res.writeHead(200, {
7155
8016
  "Content-Type": "text/html; charset=utf-8",
7156
8017
  "Content-Length": content.length,
@@ -7289,9 +8150,9 @@ var init_change_tracker = __esm({
7289
8150
  });
7290
8151
 
7291
8152
  // src/server/routes/tasks.ts
7292
- import { existsSync as existsSync33, writeFileSync as writeFileSync17 } from "fs";
7293
- import { join as join31, basename as basename11 } from "path";
7294
- import fg9 from "fast-glob";
8153
+ import { existsSync as existsSync35, writeFileSync as writeFileSync18 } from "fs";
8154
+ import { join as join32, basename as basename13 } from "path";
8155
+ import fg10 from "fast-glob";
7295
8156
  function readSectionSafe(filePath, sectionName) {
7296
8157
  try {
7297
8158
  return readSection(filePath, sectionName) ?? "";
@@ -7300,7 +8161,7 @@ function readSectionSafe(filePath, sectionName) {
7300
8161
  }
7301
8162
  }
7302
8163
  function readTask(filePath) {
7303
- const slug = basename11(filePath, ".md");
8164
+ const slug = basename13(filePath, ".md");
7304
8165
  const { data, content } = readFrontmatter(filePath);
7305
8166
  let sections = [];
7306
8167
  try {
@@ -7337,15 +8198,15 @@ function readTask(filePath) {
7337
8198
  };
7338
8199
  }
7339
8200
  function getStateDir2(contextRoot) {
7340
- return join31(contextRoot, "state");
8201
+ return join32(contextRoot, "state");
7341
8202
  }
7342
8203
  function resolveTaskPath(contextRoot, slug) {
7343
8204
  return safeChildPath2(getStateDir2(contextRoot), `${slug}.md`);
7344
8205
  }
7345
8206
  function getTaskFiles(contextRoot) {
7346
8207
  const stateDir = getStateDir2(contextRoot);
7347
- if (!existsSync33(stateDir)) return [];
7348
- return fg9.sync("*.md", { cwd: stateDir, absolute: true });
8208
+ if (!existsSync35(stateDir)) return [];
8209
+ return fg10.sync("*.md", { cwd: stateDir, absolute: true });
7349
8210
  }
7350
8211
  async function handleTasksList(_req, res, _params, contextRoot) {
7351
8212
  const files = getTaskFiles(contextRoot);
@@ -7390,8 +8251,8 @@ async function handleTasksCreate(req, res, _params, contextRoot) {
7390
8251
  }
7391
8252
  const slug = slugify(name.trim());
7392
8253
  const stateDir = getStateDir2(contextRoot);
7393
- const filePath = join31(stateDir, `${slug}.md`);
7394
- if (existsSync33(filePath)) {
8254
+ const filePath = join32(stateDir, `${slug}.md`);
8255
+ if (existsSync35(filePath)) {
7395
8256
  sendError(res, 409, "already_exists", `Task already exists: ${slug}`);
7396
8257
  return;
7397
8258
  }
@@ -7449,7 +8310,7 @@ ${why || "(To be defined)"}
7449
8310
  ### ${dateStr} - Created
7450
8311
  - Task created.
7451
8312
  `;
7452
- writeFileSync17(filePath, content, "utf-8");
8313
+ writeFileSync18(filePath, content, "utf-8");
7453
8314
  recordDashboardChange(contextRoot, {
7454
8315
  entity: "task",
7455
8316
  action: "create",
@@ -7466,7 +8327,7 @@ async function handleTasksGet(_req, res, params, contextRoot) {
7466
8327
  sendError(res, 400, "invalid_path", `Invalid task slug: ${slug}`);
7467
8328
  return;
7468
8329
  }
7469
- if (!existsSync33(filePath)) {
8330
+ if (!existsSync35(filePath)) {
7470
8331
  sendError(res, 404, "not_found", `Task not found: ${slug}`);
7471
8332
  return;
7472
8333
  }
@@ -7480,7 +8341,7 @@ async function handleTasksUpdate(req, res, params, contextRoot) {
7480
8341
  sendError(res, 400, "invalid_path", `Invalid task slug: ${slug}`);
7481
8342
  return;
7482
8343
  }
7483
- if (!existsSync33(filePath)) {
8344
+ if (!existsSync35(filePath)) {
7484
8345
  sendError(res, 404, "not_found", `Task not found: ${slug}`);
7485
8346
  return;
7486
8347
  }
@@ -7607,7 +8468,7 @@ async function handleTasksChangelog(req, res, params, contextRoot) {
7607
8468
  sendError(res, 400, "invalid_path", `Invalid task slug: ${slug}`);
7608
8469
  return;
7609
8470
  }
7610
- if (!existsSync33(filePath)) {
8471
+ if (!existsSync35(filePath)) {
7611
8472
  sendError(res, 404, "not_found", `Task not found: ${slug}`);
7612
8473
  return;
7613
8474
  }
@@ -7641,7 +8502,7 @@ async function handleTasksInsert(req, res, params, contextRoot) {
7641
8502
  sendError(res, 400, "invalid_path", `Invalid task slug: ${slug}`);
7642
8503
  return;
7643
8504
  }
7644
- if (!existsSync33(filePath)) {
8505
+ if (!existsSync35(filePath)) {
7645
8506
  sendError(res, 404, "not_found", `Task not found: ${slug}`);
7646
8507
  return;
7647
8508
  }
@@ -7754,20 +8615,20 @@ var init_sleep2 = __esm({
7754
8615
  });
7755
8616
 
7756
8617
  // src/server/routes/core.ts
7757
- import { existsSync as existsSync34, readdirSync as readdirSync9 } from "fs";
7758
- import { join as join32 } from "path";
8618
+ import { existsSync as existsSync36, readdirSync as readdirSync10 } from "fs";
8619
+ import { join as join33 } from "path";
7759
8620
  function getCoreDir(contextRoot) {
7760
- return join32(contextRoot, "core");
8621
+ return join33(contextRoot, "core");
7761
8622
  }
7762
8623
  async function handleCoreList(_req, res, _params, contextRoot) {
7763
8624
  const coreDir = getCoreDir(contextRoot);
7764
- if (!existsSync34(coreDir)) {
8625
+ if (!existsSync36(coreDir)) {
7765
8626
  sendJson(res, 200, { files: [] });
7766
8627
  return;
7767
8628
  }
7768
- const entries = readdirSync9(coreDir, { withFileTypes: true });
8629
+ const entries = readdirSync10(coreDir, { withFileTypes: true });
7769
8630
  const files = entries.filter((e) => e.isFile() && (e.name.endsWith(".md") || e.name.endsWith(".sql") || e.name.endsWith(".json"))).map((e) => {
7770
- const filePath = join32(coreDir, e.name);
8631
+ const filePath = join33(coreDir, e.name);
7771
8632
  let frontmatter = {};
7772
8633
  if (e.name.endsWith(".md")) {
7773
8634
  try {
@@ -7791,13 +8652,13 @@ async function handleCoreGet(_req, res, params, contextRoot) {
7791
8652
  sendError(res, 400, "invalid_path", "Invalid filename.");
7792
8653
  return;
7793
8654
  }
7794
- if (!existsSync34(filePath)) {
8655
+ if (!existsSync36(filePath)) {
7795
8656
  sendError(res, 404, "not_found", `Core file not found: ${filename}`);
7796
8657
  return;
7797
8658
  }
7798
8659
  if (filename.endsWith(".json")) {
7799
- const { readFileSync: readFileSync45 } = await import("fs");
7800
- const raw2 = readFileSync45(filePath, "utf-8");
8660
+ const { readFileSync: readFileSync46 } = await import("fs");
8661
+ const raw2 = readFileSync46(filePath, "utf-8");
7801
8662
  try {
7802
8663
  const data = JSON.parse(raw2);
7803
8664
  sendJson(res, 200, { filename, type: "json", data });
@@ -7831,8 +8692,8 @@ async function handleCoreGet(_req, res, params, contextRoot) {
7831
8692
  });
7832
8693
  return;
7833
8694
  }
7834
- const { readFileSync: readFileSync44 } = await import("fs");
7835
- const raw = readFileSync44(filePath, "utf-8");
8695
+ const { readFileSync: readFileSync45 } = await import("fs");
8696
+ const raw = readFileSync45(filePath, "utf-8");
7836
8697
  sendJson(res, 200, { filename, type: "text", content: raw });
7837
8698
  }
7838
8699
  async function handleCoreUpdate(req, res, params, contextRoot) {
@@ -7842,7 +8703,7 @@ async function handleCoreUpdate(req, res, params, contextRoot) {
7842
8703
  sendError(res, 400, "invalid_path", "Invalid filename.");
7843
8704
  return;
7844
8705
  }
7845
- if (!existsSync34(filePath)) {
8706
+ if (!existsSync36(filePath)) {
7846
8707
  sendError(res, 404, "not_found", `Core file not found: ${filename}`);
7847
8708
  return;
7848
8709
  }
@@ -7882,8 +8743,8 @@ async function handleCoreUpdate(req, res, params, contextRoot) {
7882
8743
  return;
7883
8744
  }
7884
8745
  changedParts.push("content");
7885
- const { writeFileSync: writeFileSync28 } = await import("fs");
7886
- writeFileSync28(filePath, body.content, "utf-8");
8746
+ const { writeFileSync: writeFileSync29 } = await import("fs");
8747
+ writeFileSync29(filePath, body.content, "utf-8");
7887
8748
  }
7888
8749
  const what = changedParts.length > 0 ? changedParts.join(" and ") : "file";
7889
8750
  recordDashboardChange(contextRoot, {
@@ -7906,10 +8767,10 @@ var init_core2 = __esm({
7906
8767
  });
7907
8768
 
7908
8769
  // src/server/routes/knowledge.ts
7909
- import { existsSync as existsSync35 } from "fs";
7910
- import { join as join33 } from "path";
8770
+ import { existsSync as existsSync37 } from "fs";
8771
+ import { join as join34 } from "path";
7911
8772
  function getKnowledgeDir2(contextRoot) {
7912
- return join33(contextRoot, "knowledge");
8773
+ return join34(contextRoot, "knowledge");
7913
8774
  }
7914
8775
  async function handleKnowledgeList(_req, res, _params, contextRoot) {
7915
8776
  const entries = buildKnowledgeIndex(contextRoot);
@@ -7922,7 +8783,7 @@ async function handleKnowledgeGet(_req, res, params, contextRoot) {
7922
8783
  sendError(res, 400, "invalid_path", `Invalid knowledge slug: ${slug}`);
7923
8784
  return;
7924
8785
  }
7925
- if (!existsSync35(filePath)) {
8786
+ if (!existsSync37(filePath)) {
7926
8787
  sendError(res, 404, "not_found", `Knowledge file not found: ${slug}`);
7927
8788
  return;
7928
8789
  }
@@ -7946,7 +8807,7 @@ async function handleKnowledgeUpdate(req, res, params, contextRoot) {
7946
8807
  sendError(res, 400, "invalid_path", `Invalid knowledge slug: ${slug}`);
7947
8808
  return;
7948
8809
  }
7949
- if (!existsSync35(filePath)) {
8810
+ if (!existsSync37(filePath)) {
7950
8811
  sendError(res, 404, "not_found", `Knowledge file not found: ${slug}`);
7951
8812
  return;
7952
8813
  }
@@ -8003,21 +8864,21 @@ var init_knowledge2 = __esm({
8003
8864
  });
8004
8865
 
8005
8866
  // src/server/routes/features.ts
8006
- import { existsSync as existsSync36 } from "fs";
8007
- import { join as join34, basename as basename12 } from "path";
8008
- import fg10 from "fast-glob";
8867
+ import { existsSync as existsSync38 } from "fs";
8868
+ import { join as join35, basename as basename14 } from "path";
8869
+ import fg11 from "fast-glob";
8009
8870
  function getFeaturesDir2(contextRoot) {
8010
- return join34(contextRoot, "core", "features");
8871
+ return join35(contextRoot, "core", "features");
8011
8872
  }
8012
8873
  async function handleFeaturesList(_req, res, _params, contextRoot) {
8013
8874
  const featuresDir = getFeaturesDir2(contextRoot);
8014
- if (!existsSync36(featuresDir)) {
8875
+ if (!existsSync38(featuresDir)) {
8015
8876
  sendJson(res, 200, { features: [] });
8016
8877
  return;
8017
8878
  }
8018
- const files = fg10.sync("*.md", { cwd: featuresDir, absolute: true });
8879
+ const files = fg11.sync("*.md", { cwd: featuresDir, absolute: true });
8019
8880
  const features = files.map((file) => {
8020
- const slug = basename12(file, ".md");
8881
+ const slug = basename14(file, ".md");
8021
8882
  const { data } = readFrontmatter(file);
8022
8883
  return {
8023
8884
  slug,
@@ -8038,7 +8899,7 @@ async function handleFeaturesGet(_req, res, params, contextRoot) {
8038
8899
  sendError(res, 400, "invalid_path", `Invalid feature slug: ${slug}`);
8039
8900
  return;
8040
8901
  }
8041
- if (!existsSync36(filePath)) {
8902
+ if (!existsSync38(filePath)) {
8042
8903
  sendError(res, 404, "not_found", `Feature not found: ${slug}`);
8043
8904
  return;
8044
8905
  }
@@ -8077,11 +8938,11 @@ var init_features2 = __esm({
8077
8938
  });
8078
8939
 
8079
8940
  // src/server/routes/changelog.ts
8080
- import { existsSync as existsSync37 } from "fs";
8081
- import { join as join35 } from "path";
8941
+ import { existsSync as existsSync39 } from "fs";
8942
+ import { join as join36 } from "path";
8082
8943
  async function handleChangelogGet(_req, res, _params, contextRoot) {
8083
- const filePath = join35(contextRoot, "core", "CHANGELOG.json");
8084
- if (!existsSync37(filePath)) {
8944
+ const filePath = join36(contextRoot, "core", "CHANGELOG.json");
8945
+ if (!existsSync39(filePath)) {
8085
8946
  sendJson(res, 200, { entries: [] });
8086
8947
  return;
8087
8948
  }
@@ -8093,8 +8954,8 @@ async function handleChangelogGet(_req, res, _params, contextRoot) {
8093
8954
  }
8094
8955
  }
8095
8956
  async function handleReleasesGet(_req, res, _params, contextRoot) {
8096
- const filePath = join35(contextRoot, "core", "RELEASES.json");
8097
- if (!existsSync37(filePath)) {
8957
+ const filePath = join36(contextRoot, "core", "RELEASES.json");
8958
+ if (!existsSync39(filePath)) {
8098
8959
  sendJson(res, 200, { entries: [] });
8099
8960
  return;
8100
8961
  }
@@ -8148,7 +9009,7 @@ async function handleReleasesCreate(req, res, _params, contextRoot) {
8148
9009
  tasks: Array.isArray(body.tasks) ? body.tasks : [],
8149
9010
  changelog: Array.isArray(body.changelog) ? body.changelog : []
8150
9011
  };
8151
- const filePath = join35(contextRoot, "core", "RELEASES.json");
9012
+ const filePath = join36(contextRoot, "core", "RELEASES.json");
8152
9013
  insertToJsonArray(filePath, release);
8153
9014
  if (release.features.length > 0) {
8154
9015
  backPopulateFeatures(contextRoot, release.features, release.version);
@@ -8167,8 +9028,8 @@ async function handleReleasesUpdate(req, res, params, contextRoot) {
8167
9028
  sendError(res, 400, "invalid_body", "Request body must be JSON.");
8168
9029
  return;
8169
9030
  }
8170
- const filePath = join35(contextRoot, "core", "RELEASES.json");
8171
- if (!existsSync37(filePath)) {
9031
+ const filePath = join36(contextRoot, "core", "RELEASES.json");
9032
+ if (!existsSync39(filePath)) {
8172
9033
  sendError(res, 404, "not_found", `Release not found: ${params.version}`);
8173
9034
  return;
8174
9035
  }
@@ -8222,9 +9083,9 @@ var init_changelog = __esm({
8222
9083
  });
8223
9084
 
8224
9085
  // src/lib/graph.ts
8225
- import { existsSync as existsSync38, readFileSync as readFileSync25 } from "fs";
8226
- import { join as join36, basename as basename13 } from "path";
8227
- import fg11 from "fast-glob";
9086
+ import { existsSync as existsSync40, readFileSync as readFileSync27 } from "fs";
9087
+ import { join as join37, basename as basename15 } from "path";
9088
+ import fg12 from "fast-glob";
8228
9089
  function buildGraph(contextRoot) {
8229
9090
  const nodes = [];
8230
9091
  const links = [];
@@ -8238,22 +9099,22 @@ function buildGraph(contextRoot) {
8238
9099
  byId.set(node.id, node);
8239
9100
  if (slug) bySlug.set(slug, node);
8240
9101
  };
8241
- const coreDir = join36(contextRoot, "core");
9102
+ const coreDir = join37(contextRoot, "core");
8242
9103
  const foundational = [
8243
9104
  { prefix: "0", group: "soul" },
8244
9105
  { prefix: "1", group: "user" },
8245
9106
  { prefix: "2", group: "memory" }
8246
9107
  ];
8247
9108
  for (const { prefix, group } of foundational) {
8248
- if (!existsSync38(coreDir)) continue;
8249
- const matches = fg11.sync(`${prefix}.*.md`, { cwd: coreDir, absolute: true });
9109
+ if (!existsSync40(coreDir)) continue;
9110
+ const matches = fg12.sync(`${prefix}.*.md`, { cwd: coreDir, absolute: true });
8250
9111
  for (const file of matches) {
8251
9112
  try {
8252
9113
  const { data } = readFrontmatter(file);
8253
- const filename = basename13(file);
9114
+ const filename = basename15(file);
8254
9115
  addNode({
8255
9116
  id: `core/${filename}`,
8256
- label: basename13(filename, ".md"),
9117
+ label: basename15(filename, ".md"),
8257
9118
  group,
8258
9119
  path: `core/${filename}`,
8259
9120
  meta: {
@@ -8265,15 +9126,15 @@ function buildGraph(contextRoot) {
8265
9126
  }
8266
9127
  }
8267
9128
  }
8268
- if (existsSync38(coreDir)) {
8269
- const others = fg11.sync("[3-9]*.md", { cwd: coreDir, absolute: true });
9129
+ if (existsSync40(coreDir)) {
9130
+ const others = fg12.sync("[3-9]*.md", { cwd: coreDir, absolute: true });
8270
9131
  for (const file of others) {
8271
- const filename = basename13(file);
9132
+ const filename = basename15(file);
8272
9133
  try {
8273
9134
  const { data } = readFrontmatter(file);
8274
9135
  addNode({
8275
9136
  id: `core/${filename}`,
8276
- label: basename13(filename, ".md"),
9137
+ label: basename15(filename, ".md"),
8277
9138
  group: "core",
8278
9139
  path: `core/${filename}`,
8279
9140
  meta: {
@@ -8285,11 +9146,11 @@ function buildGraph(contextRoot) {
8285
9146
  }
8286
9147
  }
8287
9148
  }
8288
- const featuresDir = join36(coreDir, "features");
8289
- if (existsSync38(featuresDir)) {
8290
- const files = fg11.sync("*.md", { cwd: featuresDir, absolute: true });
9149
+ const featuresDir = join37(coreDir, "features");
9150
+ if (existsSync40(featuresDir)) {
9151
+ const files = fg12.sync("*.md", { cwd: featuresDir, absolute: true });
8291
9152
  for (const file of files) {
8292
- const fileSlug = basename13(file, ".md");
9153
+ const fileSlug = basename15(file, ".md");
8293
9154
  try {
8294
9155
  const { data } = readFrontmatter(file);
8295
9156
  const id = data.id ? String(data.id) : `feature/${fileSlug}`;
@@ -8316,11 +9177,11 @@ function buildGraph(contextRoot) {
8316
9177
  }
8317
9178
  }
8318
9179
  }
8319
- const stateDir = join36(contextRoot, "state");
8320
- if (existsSync38(stateDir)) {
8321
- const files = fg11.sync("*.md", { cwd: stateDir, absolute: true });
9180
+ const stateDir = join37(contextRoot, "state");
9181
+ if (existsSync40(stateDir)) {
9182
+ const files = fg12.sync("*.md", { cwd: stateDir, absolute: true });
8322
9183
  for (const file of files) {
8323
- const fileSlug = basename13(file, ".md");
9184
+ const fileSlug = basename15(file, ".md");
8324
9185
  try {
8325
9186
  const { data } = readFrontmatter(file);
8326
9187
  const id = data.id ? String(data.id) : `task/${fileSlug}`;
@@ -8368,11 +9229,11 @@ function buildGraph(contextRoot) {
8368
9229
  entry.slug
8369
9230
  );
8370
9231
  }
8371
- const inboxDir = join36(contextRoot, "inbox");
8372
- if (existsSync38(inboxDir)) {
8373
- const files = fg11.sync("*.md", { cwd: inboxDir, absolute: true });
9232
+ const inboxDir = join37(contextRoot, "inbox");
9233
+ if (existsSync40(inboxDir)) {
9234
+ const files = fg12.sync("*.md", { cwd: inboxDir, absolute: true });
8374
9235
  for (const file of files) {
8375
- const slug = basename13(file, ".md");
9236
+ const slug = basename15(file, ".md");
8376
9237
  let label = slug;
8377
9238
  let description = "";
8378
9239
  try {
@@ -8390,11 +9251,11 @@ function buildGraph(contextRoot) {
8390
9251
  });
8391
9252
  }
8392
9253
  }
8393
- const releasesPath = join36(coreDir, "RELEASES.json");
9254
+ const releasesPath = join37(coreDir, "RELEASES.json");
8394
9255
  let releases = [];
8395
- if (existsSync38(releasesPath)) {
9256
+ if (existsSync40(releasesPath)) {
8396
9257
  try {
8397
- const parsed = JSON.parse(readFileSync25(releasesPath, "utf-8"));
9258
+ const parsed = JSON.parse(readFileSync27(releasesPath, "utf-8"));
8398
9259
  if (Array.isArray(parsed)) releases = parsed;
8399
9260
  } catch {
8400
9261
  }
@@ -8488,7 +9349,7 @@ var init_graph = __esm({
8488
9349
  });
8489
9350
 
8490
9351
  // src/server/routes/graph.ts
8491
- import { existsSync as existsSync39, readFileSync as readFileSync26 } from "fs";
9352
+ import { existsSync as existsSync41, readFileSync as readFileSync28 } from "fs";
8492
9353
  import { normalize, resolve as resolve7, sep as sep4 } from "path";
8493
9354
  async function handleGraphGet(_req, res, _params, contextRoot) {
8494
9355
  try {
@@ -8512,7 +9373,7 @@ async function handleGraphContentGet(req, res, _params, contextRoot) {
8512
9373
  sendError(res, 400, "invalid_path", "Path escapes context root.");
8513
9374
  return;
8514
9375
  }
8515
- if (!existsSync39(absTarget)) {
9376
+ if (!existsSync41(absTarget)) {
8516
9377
  sendError(res, 404, "not_found", `Path not found: ${rawPath}`);
8517
9378
  return;
8518
9379
  }
@@ -8533,7 +9394,7 @@ async function handleGraphContentGet(req, res, _params, contextRoot) {
8533
9394
  }
8534
9395
  if (absTarget.endsWith(".json")) {
8535
9396
  try {
8536
- const raw = readFileSync26(absTarget, "utf-8");
9397
+ const raw = readFileSync28(absTarget, "utf-8");
8537
9398
  try {
8538
9399
  sendJson(res, 200, { path: rawPath, type: "json", data: JSON.parse(raw) });
8539
9400
  } catch {
@@ -8546,7 +9407,7 @@ async function handleGraphContentGet(req, res, _params, contextRoot) {
8546
9407
  return;
8547
9408
  }
8548
9409
  try {
8549
- const content = readFileSync26(absTarget, "utf-8");
9410
+ const content = readFileSync28(absTarget, "utf-8");
8550
9411
  sendJson(res, 200, { path: rawPath, type: "text", content });
8551
9412
  } catch (err) {
8552
9413
  const message = err instanceof Error ? err.message : "Failed to read file";
@@ -8563,12 +9424,12 @@ var init_graph2 = __esm({
8563
9424
  });
8564
9425
 
8565
9426
  // src/lib/council.ts
8566
- import { existsSync as existsSync40, mkdirSync as mkdirSync11, readFileSync as readFileSync27 } from "fs";
8567
- import { join as join37, resolve as resolve8, sep as sep5 } from "path";
9427
+ import { existsSync as existsSync42, mkdirSync as mkdirSync12, readFileSync as readFileSync29 } from "fs";
9428
+ import { join as join38, resolve as resolve8, sep as sep5 } from "path";
8568
9429
  import { fileURLToPath as fileURLToPath6 } from "url";
8569
9430
  function getCouncilDir() {
8570
9431
  const root = ensureContextRoot();
8571
- return join37(root, "council");
9432
+ return join38(root, "council");
8572
9433
  }
8573
9434
  function assertSafeSegment(kind, id) {
8574
9435
  if (!id || typeof id !== "string") {
@@ -8591,29 +9452,29 @@ function assertWithinCouncil(target) {
8591
9452
  }
8592
9453
  function getDebateDir(debateId) {
8593
9454
  assertSafeSegment("debate_id", debateId);
8594
- const target = join37(getCouncilDir(), debateId);
9455
+ const target = join38(getCouncilDir(), debateId);
8595
9456
  return assertWithinCouncil(target);
8596
9457
  }
8597
9458
  function getPersonaDir(debateId, personaSlug) {
8598
9459
  assertSafeSegment("persona_slug", personaSlug);
8599
- const target = join37(getDebateDir(debateId), personaSlug);
9460
+ const target = join38(getDebateDir(debateId), personaSlug);
8600
9461
  return assertWithinCouncil(target);
8601
9462
  }
8602
9463
  function getCouncilIndexPath() {
8603
- return join37(getCouncilDir(), "index.json");
9464
+ return join38(getCouncilDir(), "index.json");
8604
9465
  }
8605
9466
  function ensureCouncilDir() {
8606
9467
  const dir = getCouncilDir();
8607
- mkdirSync11(dir, { recursive: true });
9468
+ mkdirSync12(dir, { recursive: true });
8608
9469
  const indexPath2 = getCouncilIndexPath();
8609
- if (!existsSync40(indexPath2)) {
9470
+ if (!existsSync42(indexPath2)) {
8610
9471
  writeJsonArray(indexPath2, []);
8611
9472
  }
8612
9473
  return dir;
8613
9474
  }
8614
9475
  function ensureDebateExists(debateId) {
8615
9476
  const dir = getDebateDir(debateId);
8616
- if (!existsSync40(dir)) {
9477
+ if (!existsSync42(dir)) {
8617
9478
  throw new Error(`Debate not found: ${debateId}`);
8618
9479
  }
8619
9480
  return dir;
@@ -8621,19 +9482,19 @@ function ensureDebateExists(debateId) {
8621
9482
  function ensurePersonaExists(debateId, personaSlug) {
8622
9483
  ensureDebateExists(debateId);
8623
9484
  const dir = getPersonaDir(debateId, personaSlug);
8624
- if (!existsSync40(dir)) {
9485
+ if (!existsSync42(dir)) {
8625
9486
  throw new Error(`Persona not found in ${debateId}: ${personaSlug}`);
8626
9487
  }
8627
9488
  return dir;
8628
9489
  }
8629
9490
  function readDebateFrontmatter(debateId) {
8630
9491
  ensureDebateExists(debateId);
8631
- const { data } = readFrontmatter(join37(getDebateDir(debateId), "debate.md"));
9492
+ const { data } = readFrontmatter(join38(getDebateDir(debateId), "debate.md"));
8632
9493
  return data;
8633
9494
  }
8634
9495
  function loadCouncilIndex() {
8635
9496
  const indexPath2 = getCouncilIndexPath();
8636
- if (!existsSync40(indexPath2)) return [];
9497
+ if (!existsSync42(indexPath2)) return [];
8637
9498
  return readJsonArray(indexPath2);
8638
9499
  }
8639
9500
  function saveCouncilIndex(entries) {
@@ -8651,17 +9512,17 @@ function upsertCouncilIndex(entry) {
8651
9512
  }
8652
9513
  function loadTemplate(filename) {
8653
9514
  const candidates = [
8654
- join37(__dirname6, "templates", filename),
9515
+ join38(__dirname6, "templates", filename),
8655
9516
  // bundled: dist/templates/
8656
- join37(__dirname6, "..", "templates", filename),
9517
+ join38(__dirname6, "..", "templates", filename),
8657
9518
  // alt layout
8658
- join37(__dirname6, "..", "src", "templates", filename),
9519
+ join38(__dirname6, "..", "src", "templates", filename),
8659
9520
  // dev fallback
8660
- join37(__dirname6, "..", "..", "src", "templates", filename)
9521
+ join38(__dirname6, "..", "..", "src", "templates", filename)
8661
9522
  ];
8662
9523
  for (const path of candidates) {
8663
- if (existsSync40(path)) {
8664
- return readFileSync27(path, "utf-8");
9524
+ if (existsSync42(path)) {
9525
+ return readFileSync29(path, "utf-8");
8665
9526
  }
8666
9527
  }
8667
9528
  throw new Error(`Template not found: ${filename}`);
@@ -8735,8 +9596,8 @@ function parseReportRounds(reportContent) {
8735
9596
  return entries;
8736
9597
  }
8737
9598
  function getPersonaRoundSummary(debateId, personaSlug, round) {
8738
- const reportPath = join37(getPersonaDir(debateId, personaSlug), "report.md");
8739
- if (!existsSync40(reportPath)) return null;
9599
+ const reportPath = join38(getPersonaDir(debateId, personaSlug), "report.md");
9600
+ if (!existsSync42(reportPath)) return null;
8740
9601
  const { content } = readFrontmatter(reportPath);
8741
9602
  const entries = parseReportRounds(content);
8742
9603
  const entry = entries.find((e) => e.round === round);
@@ -8770,10 +9631,10 @@ var init_council = __esm({
8770
9631
  });
8771
9632
 
8772
9633
  // src/server/routes/council.ts
8773
- import { existsSync as existsSync41 } from "fs";
8774
- import { join as join38, resolve as resolve9, sep as sep6 } from "path";
9634
+ import { existsSync as existsSync43 } from "fs";
9635
+ import { join as join39, resolve as resolve9, sep as sep6 } from "path";
8775
9636
  function getCouncilDir2(contextRoot) {
8776
- return join38(contextRoot, "council");
9637
+ return join39(contextRoot, "council");
8777
9638
  }
8778
9639
  function assertSafeSegment2(id) {
8779
9640
  if (!id || typeof id !== "string") return false;
@@ -8826,14 +9687,14 @@ function toParsedRound(entry) {
8826
9687
  };
8827
9688
  }
8828
9689
  function loadPersonaDetail(debateDir, slug) {
8829
- const personaDir = join38(debateDir, slug);
8830
- if (!existsSync41(personaDir)) return null;
8831
- const personaFile = join38(personaDir, "context-and-persona.md");
8832
- const reportFile = join38(personaDir, "report.md");
9690
+ const personaDir = join39(debateDir, slug);
9691
+ if (!existsSync43(personaDir)) return null;
9692
+ const personaFile = join39(personaDir, "context-and-persona.md");
9693
+ const reportFile = join39(personaDir, "report.md");
8833
9694
  let fm = { name: slug, model: "unknown", aspects: [], round_entries: 0 };
8834
9695
  let persona = "";
8835
9696
  let crossContext = {};
8836
- if (existsSync41(personaFile)) {
9697
+ if (existsSync43(personaFile)) {
8837
9698
  const parsed = readFrontmatter(personaFile);
8838
9699
  fm = {
8839
9700
  name: parsed.data.name ?? slug,
@@ -8846,13 +9707,13 @@ function loadPersonaDetail(debateDir, slug) {
8846
9707
  crossContext = split.crossContext;
8847
9708
  }
8848
9709
  let rounds = [];
8849
- if (existsSync41(reportFile)) {
9710
+ if (existsSync43(reportFile)) {
8850
9711
  const { content } = readFrontmatter(reportFile);
8851
9712
  rounds = parseReportRounds(content).map(toParsedRound);
8852
9713
  }
8853
- const researchIndex = join38(personaDir, "researches", "index.json");
9714
+ const researchIndex = join39(personaDir, "researches", "index.json");
8854
9715
  let researches = [];
8855
- if (existsSync41(researchIndex)) {
9716
+ if (existsSync43(researchIndex)) {
8856
9717
  try {
8857
9718
  researches = readJsonArray(researchIndex);
8858
9719
  } catch {
@@ -8862,17 +9723,17 @@ function loadPersonaDetail(debateDir, slug) {
8862
9723
  return { slug, frontmatter: fm, persona, crossContext, rounds, researches };
8863
9724
  }
8864
9725
  async function handleCouncilList(_req, res, _params, contextRoot) {
8865
- const indexPath2 = join38(getCouncilDir2(contextRoot), "index.json");
8866
- if (!existsSync41(indexPath2)) {
9726
+ const indexPath2 = join39(getCouncilDir2(contextRoot), "index.json");
9727
+ if (!existsSync43(indexPath2)) {
8867
9728
  sendJson(res, 200, { debates: [] });
8868
9729
  return;
8869
9730
  }
8870
9731
  try {
8871
9732
  const debates = readJsonArray(indexPath2);
8872
9733
  const enriched = debates.map((d) => {
8873
- const debateFile = join38(getCouncilDir2(contextRoot), d.id, "debate.md");
9734
+ const debateFile = join39(getCouncilDir2(contextRoot), d.id, "debate.md");
8874
9735
  let personaSlugs = [];
8875
- if (existsSync41(debateFile)) {
9736
+ if (existsSync43(debateFile)) {
8876
9737
  try {
8877
9738
  const fm = readFrontmatter(debateFile).data;
8878
9739
  if (Array.isArray(fm.personas)) personaSlugs = fm.personas;
@@ -8895,14 +9756,14 @@ async function handleCouncilGet(_req, res, params, contextRoot) {
8895
9756
  return;
8896
9757
  }
8897
9758
  const councilDir = getCouncilDir2(contextRoot);
8898
- const debateDirUnchecked = join38(councilDir, debateId);
9759
+ const debateDirUnchecked = join39(councilDir, debateId);
8899
9760
  const debateDir = assertWithin(councilDir, debateDirUnchecked);
8900
- if (!debateDir || !existsSync41(debateDir)) {
9761
+ if (!debateDir || !existsSync43(debateDir)) {
8901
9762
  sendError(res, 404, "not_found", `Debate not found: ${debateId}`);
8902
9763
  return;
8903
9764
  }
8904
- const debateFile = join38(debateDir, "debate.md");
8905
- if (!existsSync41(debateFile)) {
9765
+ const debateFile = join39(debateDir, "debate.md");
9766
+ if (!existsSync43(debateFile)) {
8906
9767
  sendError(res, 404, "not_found", `Debate metadata missing: ${debateId}`);
8907
9768
  return;
8908
9769
  }
@@ -8919,11 +9780,11 @@ async function handleCouncilGet(_req, res, params, contextRoot) {
8919
9780
  created_at: String(fmRaw.created_at ?? ""),
8920
9781
  updated_at: String(fmRaw.updated_at ?? "")
8921
9782
  };
8922
- const roundLogFile = join38(debateDir, "round-log.md");
8923
- const roundLog = existsSync41(roundLogFile) ? readFrontmatter(roundLogFile).content : null;
8924
- const finalReportFile = join38(debateDir, "final-report.md");
9783
+ const roundLogFile = join39(debateDir, "round-log.md");
9784
+ const roundLog = existsSync43(roundLogFile) ? readFrontmatter(roundLogFile).content : null;
9785
+ const finalReportFile = join39(debateDir, "final-report.md");
8925
9786
  let finalReport = null;
8926
- if (existsSync41(finalReportFile)) {
9787
+ if (existsSync43(finalReportFile)) {
8927
9788
  const parsed = readFrontmatter(finalReportFile);
8928
9789
  finalReport = { frontmatter: parsed.data, content: parsed.content };
8929
9790
  }
@@ -8951,9 +9812,9 @@ async function handleCouncilResearchGet(_req, res, params, contextRoot) {
8951
9812
  const councilDir = getCouncilDir2(contextRoot);
8952
9813
  const researchFile = assertWithin(
8953
9814
  councilDir,
8954
- join38(councilDir, debateId, personaSlug, "researches", `${researchSlug}.md`)
9815
+ join39(councilDir, debateId, personaSlug, "researches", `${researchSlug}.md`)
8955
9816
  );
8956
- if (!researchFile || !existsSync41(researchFile)) {
9817
+ if (!researchFile || !existsSync43(researchFile)) {
8957
9818
  sendError(res, 404, "not_found", `Research not found: ${researchSlug}`);
8958
9819
  return;
8959
9820
  }
@@ -9279,434 +10140,193 @@ var init_server = __esm({
9279
10140
  init_changelog();
9280
10141
  init_graph2();
9281
10142
  init_council2();
9282
- init_config();
9283
- init_packs();
9284
- init_packs_install();
9285
- init_version_check2();
9286
- }
9287
- });
9288
-
9289
- // src/cli/commands/dashboard.ts
9290
- function registerDashboardCommand(program) {
9291
- program.command("dashboard").description("Open the web dashboard in your browser").option("-p, --port <port>", "Port number", "4173").option("--host <host>", "Interface to bind (default loopback). Use 0.0.0.0 to expose on your network.", "127.0.0.1").option("--no-open", "Do not open browser automatically").option("--vault <path>", "Open a specific vault by registered name or path").action(async (opts) => {
9292
- let contextRoot;
9293
- if (opts.vault !== void 0) {
9294
- try {
9295
- contextRoot = resolveVaultContextRoot(opts.vault);
9296
- } catch (err) {
9297
- if (err instanceof VaultError) {
9298
- error(err.message);
9299
- } else {
9300
- error(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
9301
- }
9302
- process.exitCode = 1;
9303
- return;
9304
- }
9305
- } else {
9306
- contextRoot = ensureContextRoot();
9307
- }
9308
- const port = parseInt(opts.port, 10);
9309
- if (isNaN(port) || port < 1 || port > 65535) {
9310
- throw new Error("Invalid port number. Must be between 1 and 65535.");
9311
- }
9312
- await startDashboardServer({ port, contextRoot, open: opts.open, host: opts.host });
9313
- });
9314
- }
9315
- var init_dashboard = __esm({
9316
- "src/cli/commands/dashboard.ts"() {
9317
- "use strict";
9318
- init_context_path();
9319
- init_vaults();
9320
- init_server();
9321
- init_format();
9322
- }
9323
- });
9324
-
9325
- // src/cli/commands/bookmark.ts
9326
- import chalk11 from "chalk";
9327
- function registerBookmarkCommand(program) {
9328
- const bookmark = program.command("bookmark").description("Tag important moments for consolidation (awake ripples)");
9329
- bookmark.command("add").argument("<message...>", "What to remember").option("-s, --salience <level>", "Importance level (1=notable, 2=significant, 3=critical)", "2").option("-t, --task <slug>", "Associated task slug").description("Create a bookmark").action((messageParts, opts) => {
9330
- const salience = parseInt(opts.salience, 10);
9331
- if (![1, 2, 3].includes(salience)) {
9332
- error("Salience must be 1, 2, or 3.");
9333
- return;
9334
- }
9335
- const message = messageParts.join(" ").trim();
9336
- if (!message) {
9337
- error("Message is required.");
9338
- return;
9339
- }
9340
- const root = ensureContextRoot();
9341
- const state = readSleepState(root);
9342
- state.bookmarks.unshift({
9343
- id: generateId("bm"),
9344
- message,
9345
- salience,
9346
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
9347
- session_id: null,
9348
- // linked by stop hook later
9349
- task_slug: opts.task ?? null
9350
- });
9351
- writeSleepState(root, state);
9352
- const taskRef = opts.task ? chalk11.dim(` (task: ${opts.task})`) : "";
9353
- success(`${SALIENCE_LABELS[salience]} Bookmarked: ${message}${taskRef}`);
9354
- });
9355
- bookmark.command("list").description("Show current bookmarks").action(() => {
9356
- const root = ensureContextRoot();
9357
- const state = readSleepState(root);
9358
- if (state.bookmarks.length === 0) {
9359
- console.log(chalk11.dim("No bookmarks."));
9360
- return;
9361
- }
9362
- console.log(header("Bookmarks"));
9363
- for (const b of state.bookmarks) {
9364
- const stars = SALIENCE_LABELS[b.salience] || "\u2605";
9365
- const time = chalk11.dim(b.created_at.split("T")[0]);
9366
- const taskRef = b.task_slug ? chalk11.cyan(` [${b.task_slug}]`) : "";
9367
- console.log(` ${stars} ${time} ${b.message}${taskRef}`);
9368
- }
9369
- });
9370
- bookmark.command("clear").description("Remove all bookmarks").action(() => {
9371
- const root = ensureContextRoot();
9372
- const state = readSleepState(root);
9373
- const count = state.bookmarks.length;
9374
- state.bookmarks = [];
9375
- writeSleepState(root, state);
9376
- success(`Cleared ${count} bookmark(s).`);
9377
- });
9378
- }
9379
- var SALIENCE_LABELS;
9380
- var init_bookmark = __esm({
9381
- "src/cli/commands/bookmark.ts"() {
9382
- "use strict";
9383
- init_context_path();
9384
- init_sleep();
9385
- init_id();
9386
- init_format();
9387
- SALIENCE_LABELS = {
9388
- 1: "\u2605",
9389
- 2: "\u2605\u2605",
9390
- 3: "\u2605\u2605\u2605"
9391
- };
9392
- }
9393
- });
9394
-
9395
- // src/cli/commands/trigger.ts
9396
- import chalk12 from "chalk";
9397
- function registerTriggerCommand(program) {
9398
- const trigger = program.command("trigger").description("Manage contextual reminders (prospective memory)");
9399
- trigger.command("add").argument("<when>", "Context keyword(s) that activate this trigger").argument("<remind...>", "What to remind about").option("-m, --max-fires <count>", "Auto-expire after N triggers", "3").option("-s, --source <source>", "Source reference (e.g. memory.md#section)").description("Create a contextual trigger").action((when, remindParts, opts) => {
9400
- const maxFires = parseInt(opts.maxFires, 10);
9401
- if (isNaN(maxFires) || maxFires < 1) {
9402
- error("max-fires must be a positive integer.");
9403
- return;
9404
- }
9405
- const remind = remindParts.join(" ").trim();
9406
- if (!remind) {
9407
- error("Reminder message is required.");
9408
- return;
9409
- }
9410
- const root = ensureContextRoot();
9411
- const state = readSleepState(root);
9412
- state.triggers.push({
9413
- id: generateId("trg"),
9414
- when: when.trim(),
9415
- remind,
9416
- source: opts.source || null,
9417
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
9418
- fired_count: 0,
9419
- max_fires: maxFires
9420
- });
9421
- writeSleepState(root, state);
9422
- success(`Trigger created: when "${when}" -> ${remind}`);
9423
- });
9424
- trigger.command("list").description("Show active triggers").action(() => {
9425
- const root = ensureContextRoot();
9426
- const state = readSleepState(root);
9427
- const active = state.triggers.filter((t) => t.fired_count < t.max_fires);
9428
- if (active.length === 0) {
9429
- info("No active triggers.");
9430
- return;
9431
- }
9432
- console.log(header("Active Triggers"));
9433
- for (const t of active) {
9434
- const remaining = t.max_fires - t.fired_count;
9435
- console.log(` ${chalk12.magentaBright(t.id)} ${chalk12.dim(`(${remaining} fires left)`)}`);
9436
- console.log(` When: ${chalk12.white(t.when)}`);
9437
- console.log(` Remind: ${t.remind}`);
9438
- if (t.source) {
9439
- console.log(` Source: ${chalk12.dim(t.source)}`);
9440
- }
9441
- }
9442
- });
9443
- trigger.command("remove").argument("<id>", "Trigger ID to remove").description("Remove a trigger").action((id) => {
9444
- const root = ensureContextRoot();
9445
- const state = readSleepState(root);
9446
- const before = state.triggers.length;
9447
- state.triggers = state.triggers.filter((t) => t.id !== id);
9448
- if (state.triggers.length === before) {
9449
- error(`Trigger not found: ${id}`);
9450
- return;
9451
- }
9452
- writeSleepState(root, state);
9453
- success(`Trigger removed: ${id}`);
9454
- });
9455
- }
9456
- var init_trigger = __esm({
9457
- "src/cli/commands/trigger.ts"() {
9458
- "use strict";
9459
- init_context_path();
9460
- init_sleep();
9461
- init_id();
9462
- init_format();
9463
- }
9464
- });
9465
-
9466
- // src/cli/commands/transcript.ts
9467
- import { readFileSync as readFileSync28, existsSync as existsSync42, statSync as statSync7 } from "fs";
9468
- function distillTranscript(transcriptPath, sinceTimestamp) {
9469
- const result = {
9470
- userMessages: [],
9471
- agentDecisions: [],
9472
- codeChanges: [],
9473
- errors: [],
9474
- bookmarks: []
9475
- };
9476
- if (!existsSync42(transcriptPath)) return result;
9477
- try {
9478
- const stat = statSync7(transcriptPath);
9479
- if (stat.size === 0 || stat.size > MAX_TRANSCRIPT_BYTES2) return result;
9480
- const content = readFileSync28(transcriptPath, "utf-8");
9481
- const lines = content.split("\n").filter((l) => l.trim());
9482
- for (const line of lines) {
9483
- let entry;
9484
- try {
9485
- entry = JSON.parse(line);
9486
- } catch {
9487
- continue;
9488
- }
9489
- if (sinceTimestamp && entry.timestamp && entry.timestamp <= sinceTimestamp) continue;
9490
- if (!entry.message) continue;
9491
- const msg = entry.message;
9492
- if (msg.role === "user") {
9493
- let text = "";
9494
- if (typeof msg.content === "string") {
9495
- text = msg.content;
9496
- } else if (Array.isArray(msg.content)) {
9497
- for (const block of msg.content) {
9498
- if (typeof block === "string") {
9499
- text += block + " ";
9500
- } else if (block && typeof block === "object") {
9501
- if (block.type === "text" && typeof block.text === "string") {
9502
- text += block.text + " ";
9503
- } else if (block.type === "tool_result" && Array.isArray(block.content)) {
9504
- const content2 = block.content;
9505
- for (const contentBlock of content2) {
9506
- if (typeof contentBlock === "string") {
9507
- text += contentBlock + " ";
9508
- } else if (contentBlock && typeof contentBlock === "object" && contentBlock.type === "text") {
9509
- text += (contentBlock.text || "") + " ";
9510
- }
9511
- }
9512
- }
9513
- }
9514
- }
9515
- }
9516
- const trimmed = text.trim();
9517
- if (trimmed && trimmed.length > 0) {
9518
- result.userMessages.push(trimmed);
9519
- }
9520
- continue;
9521
- }
9522
- if (msg.role === "assistant" && Array.isArray(msg.content)) {
9523
- for (const block of msg.content) {
9524
- if (block.type === "text" && block.text) {
9525
- const text = block.text.trim();
9526
- if (text.length > 0) {
9527
- result.agentDecisions.push(text);
9528
- }
9529
- }
9530
- if (block.type === "thinking" && block.thinking) {
9531
- const thinking = (typeof block.thinking === "string" ? block.thinking : "").trim();
9532
- if (thinking.length > 0) {
9533
- result.agentDecisions.push(`[thinking] ${thinking}`);
9534
- }
9535
- }
9536
- if (block.type === "tool_use" && block.name) {
9537
- const toolName = block.name;
9538
- if (toolName === "Bash" && block.input) {
9539
- const cmd = typeof block.input.command === "string" ? block.input.command : "";
9540
- if (cmd.includes("dreamcontext bookmark")) {
9541
- result.bookmarks.push(cmd);
9542
- continue;
9543
- }
9544
- }
9545
- if (CHANGE_TOOLS.has(toolName) && block.input) {
9546
- const filePath = typeof block.input.file_path === "string" ? block.input.file_path : "";
9547
- if (toolName === "Write") {
9548
- const content2 = typeof block.input.content === "string" ? block.input.content : "";
9549
- const lines2 = content2.split("\n").length;
9550
- result.codeChanges.push(`WRITE ${filePath} (${lines2} lines)
9551
- ${content2}`);
9552
- } else if (toolName === "Edit") {
9553
- const oldStr = typeof block.input.old_string === "string" ? block.input.old_string : "";
9554
- const newStr = typeof block.input.new_string === "string" ? block.input.new_string : "";
9555
- result.codeChanges.push(`EDIT ${filePath}
9556
- --- OLD ---
9557
- ${oldStr}
9558
- --- NEW ---
9559
- ${newStr}`);
9560
- } else if (toolName === "NotebookEdit") {
9561
- const nbPath = typeof block.input.notebook_path === "string" ? block.input.notebook_path : "";
9562
- result.codeChanges.push(`NOTEBOOK_EDIT ${nbPath}`);
9563
- }
9564
- continue;
9565
- }
9566
- if (toolName === "Bash" && block.input) {
9567
- const cmd = typeof block.input.command === "string" ? block.input.command : "";
9568
- if (/\b(npm install|npm i |yarn add|pnpm add|pip install|git |mkdir |rm |mv |cp |chmod |chown |sed |awk )/.test(cmd)) {
9569
- result.codeChanges.push(`BASH ${cmd}`);
9570
- }
9571
- continue;
9572
- }
9573
- if (NOISE_TOOLS.has(toolName)) continue;
9574
- if (toolName === "Task" && block.input) {
9575
- const prompt = typeof block.input.prompt === "string" ? block.input.prompt : "";
9576
- if (prompt.length > 20) {
9577
- result.agentDecisions.push(`[subagent-task] ${prompt}`);
9578
- }
9579
- continue;
9580
- }
9581
- }
9582
- }
9583
- continue;
9584
- }
9585
- if (msg.role === "tool" && Array.isArray(msg.content)) {
9586
- for (const block of msg.content) {
9587
- if (block.type === "tool_result") {
9588
- const text = typeof block.text === "string" ? block.text : "";
9589
- if (/error|Error|ERROR|failed|Failed|FAILED|exception|Exception/.test(text)) {
9590
- result.errors.push(text);
9591
- }
9592
- if (text.length > 20 && text.includes("[subagent]")) {
9593
- result.agentDecisions.push(`[subagent-result] ${text}`);
9594
- }
9595
- }
9596
- }
9597
- }
9598
- if (entry.subagent?.result) {
9599
- const subResult = entry.subagent.result.trim();
9600
- if (subResult.length > 20) {
9601
- result.agentDecisions.push(`[subagent] ${subResult}`);
10143
+ init_config();
10144
+ init_packs();
10145
+ init_packs_install();
10146
+ init_version_check2();
10147
+ }
10148
+ });
10149
+
10150
+ // src/cli/commands/dashboard.ts
10151
+ function registerDashboardCommand(program) {
10152
+ program.command("dashboard").description("Open the web dashboard in your browser").option("-p, --port <port>", "Port number", "4173").option("--host <host>", "Interface to bind (default loopback). Use 0.0.0.0 to expose on your network.", "127.0.0.1").option("--no-open", "Do not open browser automatically").option("--vault <path>", "Open a specific vault by registered name or path").action(async (opts) => {
10153
+ let contextRoot;
10154
+ if (opts.vault !== void 0) {
10155
+ try {
10156
+ contextRoot = resolveVaultContextRoot(opts.vault);
10157
+ } catch (err) {
10158
+ if (err instanceof VaultError) {
10159
+ error(err.message);
10160
+ } else {
10161
+ error(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
9602
10162
  }
10163
+ process.exitCode = 1;
10164
+ return;
9603
10165
  }
10166
+ } else {
10167
+ contextRoot = ensureContextRoot();
9604
10168
  }
9605
- } catch {
9606
- }
9607
- return result;
9608
- }
9609
- function formatDistilled(sessionId, distilled, sinceTimestamp) {
9610
- const suffix = sinceTimestamp ? ` (since ${sinceTimestamp})` : "";
9611
- const parts = [`## Session ${sessionId} -- Distilled Transcript${suffix}
9612
- `];
9613
- if (distilled.userMessages.length > 0) {
9614
- parts.push("### User Messages");
9615
- for (const m of distilled.userMessages) {
9616
- parts.push(`- "${m}"`);
10169
+ const port = parseInt(opts.port, 10);
10170
+ if (isNaN(port) || port < 1 || port > 65535) {
10171
+ throw new Error("Invalid port number. Must be between 1 and 65535.");
9617
10172
  }
9618
- parts.push("");
10173
+ await startDashboardServer({ port, contextRoot, open: opts.open, host: opts.host });
10174
+ });
10175
+ }
10176
+ var init_dashboard = __esm({
10177
+ "src/cli/commands/dashboard.ts"() {
10178
+ "use strict";
10179
+ init_context_path();
10180
+ init_vaults();
10181
+ init_server();
10182
+ init_format();
9619
10183
  }
9620
- if (distilled.agentDecisions.length > 0) {
9621
- parts.push("### Agent Decisions & Reasoning");
9622
- for (const d of distilled.agentDecisions) {
9623
- parts.push(`- ${d}`);
10184
+ });
10185
+
10186
+ // src/cli/commands/bookmark.ts
10187
+ import chalk11 from "chalk";
10188
+ function registerBookmarkCommand(program) {
10189
+ const bookmark = program.command("bookmark").description("Tag important moments for consolidation (awake ripples)");
10190
+ bookmark.command("add").argument("<message...>", "What to remember").option("-s, --salience <level>", "Importance level (1=notable, 2=significant, 3=critical)", "2").option("-t, --task <slug>", "Associated task slug").description("Create a bookmark").action((messageParts, opts) => {
10191
+ const salience = parseInt(opts.salience, 10);
10192
+ if (![1, 2, 3].includes(salience)) {
10193
+ error("Salience must be 1, 2, or 3.");
10194
+ return;
9624
10195
  }
9625
- parts.push("");
9626
- }
9627
- if (distilled.codeChanges.length > 0) {
9628
- parts.push("### Code Changes");
9629
- for (const c of distilled.codeChanges) {
9630
- parts.push(`- ${c}`);
10196
+ const message = messageParts.join(" ").trim();
10197
+ if (!message) {
10198
+ error("Message is required.");
10199
+ return;
9631
10200
  }
9632
- parts.push("");
9633
- }
9634
- if (distilled.errors.length > 0) {
9635
- parts.push("### Errors & Issues");
9636
- for (const e of distilled.errors) {
9637
- parts.push(`- ${e}`);
10201
+ const root = ensureContextRoot();
10202
+ const state = readSleepState(root);
10203
+ state.bookmarks.unshift({
10204
+ id: generateId("bm"),
10205
+ message,
10206
+ salience,
10207
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
10208
+ session_id: null,
10209
+ // linked by stop hook later
10210
+ task_slug: opts.task ?? null
10211
+ });
10212
+ writeSleepState(root, state);
10213
+ const taskRef = opts.task ? chalk11.dim(` (task: ${opts.task})`) : "";
10214
+ success(`${SALIENCE_LABELS[salience]} Bookmarked: ${message}${taskRef}`);
10215
+ });
10216
+ bookmark.command("list").description("Show current bookmarks").action(() => {
10217
+ const root = ensureContextRoot();
10218
+ const state = readSleepState(root);
10219
+ if (state.bookmarks.length === 0) {
10220
+ console.log(chalk11.dim("No bookmarks."));
10221
+ return;
9638
10222
  }
9639
- parts.push("");
9640
- }
9641
- if (distilled.bookmarks.length > 0) {
9642
- parts.push("### Bookmarks");
9643
- for (const b of distilled.bookmarks) {
9644
- parts.push(`- ${b}`);
10223
+ console.log(header("Bookmarks"));
10224
+ for (const b of state.bookmarks) {
10225
+ const stars = SALIENCE_LABELS[b.salience] || "\u2605";
10226
+ const time = chalk11.dim(b.created_at.split("T")[0]);
10227
+ const taskRef = b.task_slug ? chalk11.cyan(` [${b.task_slug}]`) : "";
10228
+ console.log(` ${stars} ${time} ${b.message}${taskRef}`);
9645
10229
  }
9646
- parts.push("");
9647
- }
9648
- return parts.join("\n");
9649
- }
9650
- function registerTranscriptCommand(program) {
9651
- const transcript = program.command("transcript").description("Process session transcripts");
9652
- transcript.command("distill").argument("<session_id>", "Session ID to distill").option("--since <timestamp>", "Only include content after this ISO timestamp").option("--full", "Show full transcript (skip auto-filter by last consolidation)").description("Extract high-signal content from a session transcript (pure structural filtering)").action((sessionId, opts) => {
10230
+ });
10231
+ bookmark.command("clear").description("Remove all bookmarks").action(() => {
9653
10232
  const root = ensureContextRoot();
9654
10233
  const state = readSleepState(root);
9655
- const session = state.sessions.find((s) => s.session_id === sessionId);
9656
- if (!session) {
9657
- error(`Session not found: ${sessionId}`);
10234
+ const count = state.bookmarks.length;
10235
+ state.bookmarks = [];
10236
+ writeSleepState(root, state);
10237
+ success(`Cleared ${count} bookmark(s).`);
10238
+ });
10239
+ }
10240
+ var SALIENCE_LABELS;
10241
+ var init_bookmark = __esm({
10242
+ "src/cli/commands/bookmark.ts"() {
10243
+ "use strict";
10244
+ init_context_path();
10245
+ init_sleep();
10246
+ init_id();
10247
+ init_format();
10248
+ SALIENCE_LABELS = {
10249
+ 1: "\u2605",
10250
+ 2: "\u2605\u2605",
10251
+ 3: "\u2605\u2605\u2605"
10252
+ };
10253
+ }
10254
+ });
10255
+
10256
+ // src/cli/commands/trigger.ts
10257
+ import chalk12 from "chalk";
10258
+ function registerTriggerCommand(program) {
10259
+ const trigger = program.command("trigger").description("Manage contextual reminders (prospective memory)");
10260
+ trigger.command("add").argument("<when>", "Context keyword(s) that activate this trigger").argument("<remind...>", "What to remind about").option("-m, --max-fires <count>", "Auto-expire after N triggers", "3").option("-s, --source <source>", "Source reference (e.g. memory.md#section)").description("Create a contextual trigger").action((when, remindParts, opts) => {
10261
+ const maxFires = parseInt(opts.maxFires, 10);
10262
+ if (isNaN(maxFires) || maxFires < 1) {
10263
+ error("max-fires must be a positive integer.");
9658
10264
  return;
9659
10265
  }
9660
- if (!session.transcript_path) {
9661
- error(`No transcript path for session: ${sessionId}`);
10266
+ const remind = remindParts.join(" ").trim();
10267
+ if (!remind) {
10268
+ error("Reminder message is required.");
9662
10269
  return;
9663
10270
  }
9664
- if (!existsSync42(session.transcript_path)) {
9665
- error(`Transcript file not found: ${session.transcript_path}`);
10271
+ const root = ensureContextRoot();
10272
+ const state = readSleepState(root);
10273
+ state.triggers.push({
10274
+ id: generateId("trg"),
10275
+ when: when.trim(),
10276
+ remind,
10277
+ source: opts.source || null,
10278
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
10279
+ fired_count: 0,
10280
+ max_fires: maxFires
10281
+ });
10282
+ writeSleepState(root, state);
10283
+ success(`Trigger created: when "${when}" -> ${remind}`);
10284
+ });
10285
+ trigger.command("list").description("Show active triggers").action(() => {
10286
+ const root = ensureContextRoot();
10287
+ const state = readSleepState(root);
10288
+ const active = state.triggers.filter((t) => t.fired_count < t.max_fires);
10289
+ if (active.length === 0) {
10290
+ info("No active triggers.");
9666
10291
  return;
9667
10292
  }
9668
- let sinceTimestamp;
9669
- if (opts.since) {
9670
- sinceTimestamp = opts.since;
9671
- } else if (!opts.full) {
9672
- const history = readSleepHistory(root);
9673
- const lastConsolidation = history.find(
9674
- (h) => Array.isArray(h.session_ids) && h.session_ids.includes(sessionId)
9675
- );
9676
- if (lastConsolidation?.consolidated_at) {
9677
- sinceTimestamp = lastConsolidation.consolidated_at;
9678
- info(`Auto-filtering: showing content after last consolidation (${lastConsolidation.consolidated_at})`);
10293
+ console.log(header("Active Triggers"));
10294
+ for (const t of active) {
10295
+ const remaining = t.max_fires - t.fired_count;
10296
+ console.log(` ${chalk12.magentaBright(t.id)} ${chalk12.dim(`(${remaining} fires left)`)}`);
10297
+ console.log(` When: ${chalk12.white(t.when)}`);
10298
+ console.log(` Remind: ${t.remind}`);
10299
+ if (t.source) {
10300
+ console.log(` Source: ${chalk12.dim(t.source)}`);
9679
10301
  }
9680
10302
  }
9681
- const distilled = distillTranscript(session.transcript_path, sinceTimestamp);
9682
- console.log(formatDistilled(sessionId, distilled, sinceTimestamp));
10303
+ });
10304
+ trigger.command("remove").argument("<id>", "Trigger ID to remove").description("Remove a trigger").action((id) => {
10305
+ const root = ensureContextRoot();
10306
+ const state = readSleepState(root);
10307
+ const before = state.triggers.length;
10308
+ state.triggers = state.triggers.filter((t) => t.id !== id);
10309
+ if (state.triggers.length === before) {
10310
+ error(`Trigger not found: ${id}`);
10311
+ return;
10312
+ }
10313
+ writeSleepState(root, state);
10314
+ success(`Trigger removed: ${id}`);
9683
10315
  });
9684
10316
  }
9685
- var MAX_TRANSCRIPT_BYTES2, NOISE_TOOLS, CHANGE_TOOLS;
9686
- var init_transcript = __esm({
9687
- "src/cli/commands/transcript.ts"() {
10317
+ var init_trigger = __esm({
10318
+ "src/cli/commands/trigger.ts"() {
9688
10319
  "use strict";
9689
10320
  init_context_path();
9690
10321
  init_sleep();
10322
+ init_id();
9691
10323
  init_format();
9692
- MAX_TRANSCRIPT_BYTES2 = 50 * 1024 * 1024;
9693
- NOISE_TOOLS = /* @__PURE__ */ new Set([
9694
- "Read",
9695
- "Glob",
9696
- "Grep",
9697
- "WebFetch",
9698
- "WebSearch",
9699
- "ListMcpResourcesTool",
9700
- "ReadMcpResourceTool",
9701
- "ToolSearch"
9702
- ]);
9703
- CHANGE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "NotebookEdit"]);
9704
10324
  }
9705
10325
  });
9706
10326
 
9707
10327
  // src/cli/commands/council.ts
9708
- import { existsSync as existsSync43, mkdirSync as mkdirSync12, readFileSync as readFileSync29, writeFileSync as writeFileSync19 } from "fs";
9709
- import { join as join39 } from "path";
10328
+ import { existsSync as existsSync44, mkdirSync as mkdirSync13, readFileSync as readFileSync30, writeFileSync as writeFileSync20 } from "fs";
10329
+ import { join as join40 } from "path";
9710
10330
  import chalk13 from "chalk";
9711
10331
  function roundRunningStatus(n) {
9712
10332
  return `round_${n}_running`;
@@ -9749,12 +10369,12 @@ function registerCreate(council) {
9749
10369
  }
9750
10370
  const id = generateId("council");
9751
10371
  const dir = getDebateDir(id);
9752
- mkdirSync12(dir, { recursive: true });
10372
+ mkdirSync13(dir, { recursive: true });
9753
10373
  const template = loadTemplate("council-debate.md");
9754
10374
  const content = template.replaceAll("{{ID}}", id).replaceAll("{{TOPIC}}", topic.replace(/"/g, '\\"')).replaceAll("{{ROUNDS}}", String(rounds)).replaceAll("{{INTERRUPT}}", opts.interrupt ? "true" : "false").replaceAll("{{DATE}}", today());
9755
- writeFileSync19(join39(dir, "debate.md"), content, "utf-8");
9756
- writeFileSync19(
9757
- join39(dir, "round-log.md"),
10375
+ writeFileSync20(join40(dir, "debate.md"), content, "utf-8");
10376
+ writeFileSync20(
10377
+ join40(dir, "round-log.md"),
9758
10378
  `# Round log \u2014 ${id}
9759
10379
 
9760
10380
  (Main agent appends timeline entries here.)
@@ -9796,8 +10416,8 @@ function registerAgent(council) {
9796
10416
  process.exit(1);
9797
10417
  }
9798
10418
  const personaDir = getPersonaDir(debateId, slug);
9799
- const personaFile = join39(personaDir, "context-and-persona.md");
9800
- if (existsSync43(personaFile) && !opts.force) {
10419
+ const personaFile = join40(personaDir, "context-and-persona.md");
10420
+ if (existsSync44(personaFile) && !opts.force) {
9801
10421
  error(`Persona already exists: ${slug}`, "Use --force to overwrite.");
9802
10422
  process.exit(1);
9803
10423
  }
@@ -9807,14 +10427,14 @@ function registerAgent(council) {
9807
10427
  process.exit(1);
9808
10428
  }
9809
10429
  const aspects = opts.aspects ? opts.aspects.split(",").map((a) => a.trim()).filter(Boolean) : [];
9810
- mkdirSync12(personaDir, { recursive: true });
10430
+ mkdirSync13(personaDir, { recursive: true });
9811
10431
  const template = loadTemplate("council-persona.md");
9812
10432
  const personaContent = template.replaceAll("{{SLUG}}", slug).replaceAll("{{MODEL}}", opts.model).replaceAll("{{ASPECTS}}", JSON.stringify(aspects)).replaceAll("{{PERSONA_BODY}}", body.trim());
9813
- writeFileSync19(personaFile, personaContent, "utf-8");
10433
+ writeFileSync20(personaFile, personaContent, "utf-8");
9814
10434
  const reportTemplate = loadTemplate("council-report.md");
9815
10435
  const reportContent = reportTemplate.replaceAll("{{SLUG}}", slug);
9816
- writeFileSync19(join39(personaDir, "report.md"), reportContent, "utf-8");
9817
- const debateFile = join39(getDebateDir(debateId), "debate.md");
10436
+ writeFileSync20(join40(personaDir, "report.md"), reportContent, "utf-8");
10437
+ const debateFile = join40(getDebateDir(debateId), "debate.md");
9818
10438
  const { data } = readFrontmatter(debateFile);
9819
10439
  const personas = Array.isArray(data.personas) ? data.personas.slice() : [];
9820
10440
  if (!personas.includes(slug)) personas.push(slug);
@@ -9843,7 +10463,7 @@ function registerRound(council) {
9843
10463
  error(`Round ${n} exceeds planned rounds (${data.rounds_planned}).`);
9844
10464
  process.exit(1);
9845
10465
  }
9846
- const debateFile = join39(getDebateDir(debateId), "debate.md");
10466
+ const debateFile = join40(getDebateDir(debateId), "debate.md");
9847
10467
  const personas = data.personas ?? [];
9848
10468
  if (personas.length === 0) {
9849
10469
  error("No personas registered. Run `council agent create` first.");
@@ -9852,9 +10472,9 @@ function registerRound(council) {
9852
10472
  const crossContextHeader = `## Round ${n} \u2014 Cross-context loaded`;
9853
10473
  if (n >= 2) {
9854
10474
  for (const persona of personas) {
9855
- const personaFile = join39(getPersonaDir(debateId, persona), "context-and-persona.md");
9856
- if (!existsSync43(personaFile)) continue;
9857
- const existing = readFileSync29(personaFile, "utf-8");
10475
+ const personaFile = join40(getPersonaDir(debateId, persona), "context-and-persona.md");
10476
+ if (!existsSync44(personaFile)) continue;
10477
+ const existing = readFileSync30(personaFile, "utf-8");
9858
10478
  if (existing.includes(crossContextHeader)) {
9859
10479
  continue;
9860
10480
  }
@@ -9876,7 +10496,7 @@ _(no round ${n - 1} report submitted)_`);
9876
10496
 
9877
10497
  ${peerLines.join("\n\n")}`;
9878
10498
  const updated = existing.trimEnd() + "\n\n" + newSection + "\n";
9879
- writeFileSync19(personaFile, updated, "utf-8");
10499
+ writeFileSync20(personaFile, updated, "utf-8");
9880
10500
  }
9881
10501
  }
9882
10502
  updateFrontmatterFields(debateFile, {
@@ -9884,13 +10504,13 @@ ${peerLines.join("\n\n")}`;
9884
10504
  current_round: n,
9885
10505
  updated_at: today()
9886
10506
  });
9887
- const logFile = join39(getDebateDir(debateId), "round-log.md");
9888
- const logExisting = existsSync43(logFile) ? readFileSync29(logFile, "utf-8") : "";
10507
+ const logFile = join40(getDebateDir(debateId), "round-log.md");
10508
+ const logExisting = existsSync44(logFile) ? readFileSync30(logFile, "utf-8") : "";
9889
10509
  const logEntry = `
9890
10510
  ## Round ${n} started \u2014 ${today()}
9891
10511
  - Personas: ${formatPersonaList(personas)}
9892
10512
  `;
9893
- writeFileSync19(logFile, logExisting.trimEnd() + "\n" + logEntry + "\n", "utf-8");
10513
+ writeFileSync20(logFile, logExisting.trimEnd() + "\n" + logEntry + "\n", "utf-8");
9894
10514
  upsertCouncilIndex({
9895
10515
  id: debateId,
9896
10516
  topic: data.topic,
@@ -9924,18 +10544,18 @@ ${peerLines.join("\n\n")}`;
9924
10544
  error(`Round ${n} incomplete. Missing reports from: ${missing.join(", ")}`);
9925
10545
  process.exit(1);
9926
10546
  }
9927
- const debateFile = join39(getDebateDir(debateId), "debate.md");
10547
+ const debateFile = join40(getDebateDir(debateId), "debate.md");
9928
10548
  updateFrontmatterFields(debateFile, {
9929
10549
  status: roundCompleteStatus(n),
9930
10550
  updated_at: today()
9931
10551
  });
9932
- const logFile = join39(getDebateDir(debateId), "round-log.md");
9933
- const logExisting = existsSync43(logFile) ? readFileSync29(logFile, "utf-8") : "";
10552
+ const logFile = join40(getDebateDir(debateId), "round-log.md");
10553
+ const logExisting = existsSync44(logFile) ? readFileSync30(logFile, "utf-8") : "";
9934
10554
  const logEntry = `
9935
10555
  ## Round ${n} complete \u2014 ${today()}
9936
10556
  - All ${personas.length} personas submitted reports.
9937
10557
  `;
9938
- writeFileSync19(logFile, logExisting.trimEnd() + "\n" + logEntry + "\n", "utf-8");
10558
+ writeFileSync20(logFile, logExisting.trimEnd() + "\n" + logEntry + "\n", "utf-8");
9939
10559
  upsertCouncilIndex({
9940
10560
  id: debateId,
9941
10561
  topic: data.topic,
@@ -9958,8 +10578,8 @@ function registerRoundContext(council) {
9958
10578
  try {
9959
10579
  const data = readDebateFrontmatter(debateId);
9960
10580
  ensurePersonaExists(debateId, personaSlug);
9961
- const personaFile = join39(getPersonaDir(debateId, personaSlug), "context-and-persona.md");
9962
- const personaContent = readFileSync29(personaFile, "utf-8");
10581
+ const personaFile = join40(getPersonaDir(debateId, personaSlug), "context-and-persona.md");
10582
+ const personaContent = readFileSync30(personaFile, "utf-8");
9963
10583
  console.log(`# Council debate: ${data.topic}`);
9964
10584
  console.log(`Debate ID: ${debateId}`);
9965
10585
  console.log(`Current round: ${data.current_round}/${data.rounds_planned}`);
@@ -9968,8 +10588,8 @@ function registerRoundContext(council) {
9968
10588
  console.log("---");
9969
10589
  console.log("");
9970
10590
  console.log(personaContent);
9971
- const debateFile = join39(getDebateDir(debateId), "debate.md");
9972
- const debateRaw = readFileSync29(debateFile, "utf-8");
10591
+ const debateFile = join40(getDebateDir(debateId), "debate.md");
10592
+ const debateRaw = readFileSync30(debateFile, "utf-8");
9973
10593
  const questionMatch = debateRaw.match(/##\s+Question\s*\n([\s\S]*?)(?=\n##\s|\Z)/);
9974
10594
  const constraintsMatch = debateRaw.match(/##\s+Constraints & Known Facts\s*\n([\s\S]*?)(?=\n##\s|\Z)/);
9975
10595
  if (questionMatch) {
@@ -10012,7 +10632,7 @@ function registerReport(council) {
10012
10632
  process.exit(1);
10013
10633
  }
10014
10634
  for (const w of validation.warnings) warn(w);
10015
- const reportFile = join39(getPersonaDir(debateId, personaSlug), "report.md");
10635
+ const reportFile = join40(getPersonaDir(debateId, personaSlug), "report.md");
10016
10636
  const { data: reportData, content } = readFrontmatter(reportFile);
10017
10637
  const existingRounds = parseReportRounds(content);
10018
10638
  if (existingRounds.find((r) => r.round === round)) {
@@ -10034,7 +10654,7 @@ ${otherSerialized}
10034
10654
  persona: personaSlug,
10035
10655
  rounds_completed: Math.max(reportData.rounds_completed ?? 0, round)
10036
10656
  }, "\n" + merged);
10037
- const personaFile = join39(getPersonaDir(debateId, personaSlug), "context-and-persona.md");
10657
+ const personaFile = join40(getPersonaDir(debateId, personaSlug), "context-and-persona.md");
10038
10658
  const { data: pData } = readFrontmatter(personaFile);
10039
10659
  updateFrontmatterFields(personaFile, {
10040
10660
  round_entries: Math.max(pData.round_entries ?? 0, round)
@@ -10077,13 +10697,13 @@ function registerResearch(council) {
10077
10697
  error("Research topic is required.");
10078
10698
  process.exit(1);
10079
10699
  }
10080
- const researchesDir = join39(getPersonaDir(debateId, personaSlug), "researches");
10081
- mkdirSync12(researchesDir, { recursive: true });
10082
- const indexPath2 = join39(researchesDir, "index.json");
10083
- if (!existsSync43(indexPath2)) writeJsonArray(indexPath2, []);
10700
+ const researchesDir = join40(getPersonaDir(debateId, personaSlug), "researches");
10701
+ mkdirSync13(researchesDir, { recursive: true });
10702
+ const indexPath2 = join40(researchesDir, "index.json");
10703
+ if (!existsSync44(indexPath2)) writeJsonArray(indexPath2, []);
10084
10704
  const slug = slugify(topic);
10085
- const file = join39(researchesDir, `${slug}.md`);
10086
- if (existsSync43(file)) {
10705
+ const file = join40(researchesDir, `${slug}.md`);
10706
+ if (existsSync44(file)) {
10087
10707
  error(`Research note already exists: ${slug}.md`);
10088
10708
  process.exit(1);
10089
10709
  }
@@ -10098,7 +10718,7 @@ _Added ${today()} by ${personaSlug}_
10098
10718
 
10099
10719
  ${body.trim()}
10100
10720
  `;
10101
- writeFileSync19(file, noteContent, "utf-8");
10721
+ writeFileSync20(file, noteContent, "utf-8");
10102
10722
  insertToJsonArray(indexPath2, {
10103
10723
  slug,
10104
10724
  topic,
@@ -10113,9 +10733,9 @@ ${body.trim()}
10113
10733
  research.command("list").argument("<debate_id>").argument("<persona_slug>").description("List this persona's prior research notes.").action((debateId, personaSlug) => {
10114
10734
  try {
10115
10735
  ensurePersonaExists(debateId, personaSlug);
10116
- const researchesDir = join39(getPersonaDir(debateId, personaSlug), "researches");
10117
- const indexPath2 = join39(researchesDir, "index.json");
10118
- if (!existsSync43(indexPath2)) {
10736
+ const researchesDir = join40(getPersonaDir(debateId, personaSlug), "researches");
10737
+ const indexPath2 = join40(researchesDir, "index.json");
10738
+ if (!existsSync44(indexPath2)) {
10119
10739
  console.log(chalk13.dim("(no researches)"));
10120
10740
  return;
10121
10741
  }
@@ -10137,7 +10757,7 @@ function registerSynthesize(council) {
10137
10757
  council.command("synthesize").argument("<debate_id>").description("Mark status=synthesizing; print manifest of files synthesizer should read.").action((debateId) => {
10138
10758
  try {
10139
10759
  const data = readDebateFrontmatter(debateId);
10140
- const debateFile = join39(getDebateDir(debateId), "debate.md");
10760
+ const debateFile = join40(getDebateDir(debateId), "debate.md");
10141
10761
  updateFrontmatterFields(debateFile, {
10142
10762
  status: "synthesizing",
10143
10763
  updated_at: today()
@@ -10159,8 +10779,8 @@ function registerSynthesize(council) {
10159
10779
  const personaDir = getPersonaDir(debateId, persona);
10160
10780
  manifest.push(`_dream_context/council/${debateId}/${persona}/context-and-persona.md`);
10161
10781
  manifest.push(`_dream_context/council/${debateId}/${persona}/report.md`);
10162
- const researchesIndex = join39(personaDir, "researches", "index.json");
10163
- if (existsSync43(researchesIndex)) {
10782
+ const researchesIndex = join40(personaDir, "researches", "index.json");
10783
+ if (existsSync44(researchesIndex)) {
10164
10784
  manifest.push(`_dream_context/council/${debateId}/${persona}/researches/`);
10165
10785
  }
10166
10786
  }
@@ -10178,12 +10798,12 @@ function registerComplete(council) {
10178
10798
  council.command("complete").argument("<debate_id>").description("Mark status=complete after final-report.md is written.").action((debateId) => {
10179
10799
  try {
10180
10800
  const data = readDebateFrontmatter(debateId);
10181
- const finalReport = join39(getDebateDir(debateId), "final-report.md");
10182
- if (!existsSync43(finalReport)) {
10801
+ const finalReport = join40(getDebateDir(debateId), "final-report.md");
10802
+ if (!existsSync44(finalReport)) {
10183
10803
  error("final-report.md not found.", "Synthesizer must write it before calling `council complete`.");
10184
10804
  process.exit(1);
10185
10805
  }
10186
- const debateFile = join39(getDebateDir(debateId), "debate.md");
10806
+ const debateFile = join40(getDebateDir(debateId), "debate.md");
10187
10807
  updateFrontmatterFields(debateFile, {
10188
10808
  status: "complete",
10189
10809
  updated_at: today()
@@ -10215,8 +10835,8 @@ function registerPromote(council) {
10215
10835
  error(`Cannot promote: debate status is "${data.status}", expected "complete".`);
10216
10836
  process.exit(1);
10217
10837
  }
10218
- const finalReport = join39(getDebateDir(debateId), "final-report.md");
10219
- if (!existsSync43(finalReport)) {
10838
+ const finalReport = join40(getDebateDir(debateId), "final-report.md");
10839
+ if (!existsSync44(finalReport)) {
10220
10840
  error("final-report.md not found.");
10221
10841
  process.exit(1);
10222
10842
  }
@@ -10229,12 +10849,12 @@ function registerPromote(council) {
10229
10849
 
10230
10850
  ${sec.body.trim()}` : null;
10231
10851
  }).filter(Boolean).join("\n\n");
10232
- const knowledgeDir = join39(root, "knowledge");
10233
- mkdirSync12(knowledgeDir, { recursive: true });
10852
+ const knowledgeDir = join40(root, "knowledge");
10853
+ mkdirSync13(knowledgeDir, { recursive: true });
10234
10854
  const slug = slugify(data.topic).slice(0, 80) || debateId;
10235
10855
  const filename = `decision-${slug}.md`;
10236
- const target = join39(knowledgeDir, filename);
10237
- if (existsSync43(target) && !opts.force) {
10856
+ const target = join40(knowledgeDir, filename);
10857
+ if (existsSync44(target) && !opts.force) {
10238
10858
  error(`Knowledge file exists: ${filename}`, "Use --force to overwrite.");
10239
10859
  process.exit(1);
10240
10860
  }
@@ -10258,7 +10878,7 @@ ${kept}
10258
10878
  _Promoted from council debate \`${debateId}\` on ${today()}. See \`_dream_context/council/${debateId}/final-report.md\` for the full record._
10259
10879
  `;
10260
10880
  writeFrontmatter(target, knowledgeFrontmatter, body);
10261
- updateFrontmatterFields(join39(getDebateDir(debateId), "debate.md"), {
10881
+ updateFrontmatterFields(join40(getDebateDir(debateId), "debate.md"), {
10262
10882
  promoted_to_knowledge: `knowledge/${filename}`,
10263
10883
  updated_at: today()
10264
10884
  });
@@ -10320,10 +10940,10 @@ function registerShow(council) {
10320
10940
  console.log(` promoted: ${data.promoted_to_knowledge ?? chalk13.dim("(no)")}`);
10321
10941
  console.log(` created: ${data.created_at}`);
10322
10942
  console.log(` updated: ${data.updated_at}`);
10323
- const logFile = join39(getDebateDir(debateId), "round-log.md");
10324
- if (existsSync43(logFile)) {
10943
+ const logFile = join40(getDebateDir(debateId), "round-log.md");
10944
+ if (existsSync44(logFile)) {
10325
10945
  console.log("\n" + chalk13.dim("\u2500\u2500\u2500 Round log \u2500\u2500\u2500"));
10326
- console.log(readFileSync29(logFile, "utf-8"));
10946
+ console.log(readFileSync30(logFile, "utf-8"));
10327
10947
  }
10328
10948
  } catch (err) {
10329
10949
  error(err.message);
@@ -10514,12 +11134,12 @@ var init_env_loader = __esm({
10514
11134
  });
10515
11135
 
10516
11136
  // src/lib/marketing/config.ts
10517
- import { existsSync as existsSync44, readFileSync as readFileSync30, writeFileSync as writeFileSync20, mkdirSync as mkdirSync13 } from "fs";
11137
+ import { existsSync as existsSync45, readFileSync as readFileSync31, writeFileSync as writeFileSync21, mkdirSync as mkdirSync14 } from "fs";
10518
11138
  import { dirname as dirname18 } from "path";
10519
11139
  function readEnvFile() {
10520
11140
  const path = MARKETING_PATHS.envFile();
10521
- if (!existsSync44(path)) return {};
10522
- const raw = readFileSync30(path, "utf8");
11141
+ if (!existsSync45(path)) return {};
11142
+ const raw = readFileSync31(path, "utf8");
10523
11143
  const parsed = parseEnv(raw);
10524
11144
  if (parsed.errors.length > 0) {
10525
11145
  for (const e of parsed.errors) {
@@ -10563,9 +11183,9 @@ function requireEnv(keys = REQUIRED_KEYS) {
10563
11183
  }
10564
11184
  function loadConfig() {
10565
11185
  const path = MARKETING_PATHS.configFile();
10566
- if (!existsSync44(path)) return { ...EMPTY_CONFIG, feature_flags: { ...EMPTY_CONFIG.feature_flags } };
11186
+ if (!existsSync45(path)) return { ...EMPTY_CONFIG, feature_flags: { ...EMPTY_CONFIG.feature_flags } };
10567
11187
  try {
10568
- const raw = readFileSync30(path, "utf8");
11188
+ const raw = readFileSync31(path, "utf8");
10569
11189
  const parsed = JSON.parse(raw);
10570
11190
  return {
10571
11191
  default_profile: parsed.default_profile ?? null,
@@ -10580,21 +11200,21 @@ function loadConfig() {
10580
11200
  }
10581
11201
  function writeConfig(cfg) {
10582
11202
  const path = MARKETING_PATHS.configFile();
10583
- mkdirSync13(dirname18(path), { recursive: true });
10584
- writeFileSync20(path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
11203
+ mkdirSync14(dirname18(path), { recursive: true });
11204
+ writeFileSync21(path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
10585
11205
  }
10586
11206
  function ensureConfigFile() {
10587
11207
  const path = MARKETING_PATHS.configFile();
10588
- if (existsSync44(path)) return loadConfig();
11208
+ if (existsSync45(path)) return loadConfig();
10589
11209
  const cfg = { ...EMPTY_CONFIG, feature_flags: { ...EMPTY_CONFIG.feature_flags } };
10590
11210
  writeConfig(cfg);
10591
11211
  return cfg;
10592
11212
  }
10593
11213
  function ensureEnvFile() {
10594
11214
  const path = MARKETING_PATHS.envFile();
10595
- if (existsSync44(path)) return;
10596
- mkdirSync13(marketingRoot(), { recursive: true });
10597
- writeFileSync20(path, ENV_TEMPLATE, { encoding: "utf8", mode: 384 });
11215
+ if (existsSync45(path)) return;
11216
+ mkdirSync14(marketingRoot(), { recursive: true });
11217
+ writeFileSync21(path, ENV_TEMPLATE, { encoding: "utf8", mode: 384 });
10598
11218
  }
10599
11219
  var DEFAULT_API_VERSION, EMPTY_CONFIG, REQUIRED_KEYS, OPTIONAL_KEYS, ENV_TEMPLATE;
10600
11220
  var init_config2 = __esm({
@@ -10635,9 +11255,9 @@ REINFLUENCE_BIN= # optional override; default uses .venv
10635
11255
 
10636
11256
  // src/lib/marketing/bootstrap.ts
10637
11257
  import { spawn, spawnSync } from "child_process";
10638
- import { existsSync as existsSync45, mkdirSync as mkdirSync14, cpSync as cpSync3 } from "fs";
11258
+ import { existsSync as existsSync46, mkdirSync as mkdirSync15, cpSync as cpSync3 } from "fs";
10639
11259
  import { fileURLToPath as fileURLToPath8 } from "url";
10640
- import { dirname as dirname19, resolve as resolve11, join as join40 } from "path";
11260
+ import { dirname as dirname19, resolve as resolve11, join as join41 } from "path";
10641
11261
  function findBundledReinfluence() {
10642
11262
  const candidates = [
10643
11263
  // Installed npm package: dist/<chunk>.js -> ../tools/reinfluence
@@ -10647,7 +11267,7 @@ function findBundledReinfluence() {
10647
11267
  resolve11(__dirname7, "..", "..", "..", "tools", "reinfluence")
10648
11268
  ];
10649
11269
  for (const c of candidates) {
10650
- if (existsSync45(join40(c, "__main__.py"))) return c;
11270
+ if (existsSync46(join41(c, "__main__.py"))) return c;
10651
11271
  }
10652
11272
  throw new Error(
10653
11273
  "Bundled reinfluence not found. Reinstall dreamcontext or set REINFLUENCE_BIN."
@@ -10678,18 +11298,18 @@ async function bootstrapMarketing(opts = {}) {
10678
11298
  if (verbose) process.stderr.write(`[mk init] ${msg}
10679
11299
  `);
10680
11300
  };
10681
- mkdirSync14(marketingRoot(), { recursive: true });
11301
+ mkdirSync15(marketingRoot(), { recursive: true });
10682
11302
  const bundled = findBundledReinfluence();
10683
11303
  const target = MARKETING_PATHS.reinfluenceDir();
10684
11304
  log(`copying tools \u2192 ${target}`);
10685
- mkdirSync14(MARKETING_PATHS.toolsDir(), { recursive: true });
11305
+ mkdirSync15(MARKETING_PATHS.toolsDir(), { recursive: true });
10686
11306
  cpSync3(bundled, target, { recursive: true });
10687
11307
  const py = findPython3();
10688
11308
  if (!py) {
10689
11309
  throw new Error("python3 not found on PATH. Install Python 3.10+ and retry.");
10690
11310
  }
10691
11311
  const venv = MARKETING_PATHS.venvDir();
10692
- if (!existsSync45(MARKETING_PATHS.venvPython())) {
11312
+ if (!existsSync46(MARKETING_PATHS.venvPython())) {
10693
11313
  log(`creating venv \u2192 ${venv}`);
10694
11314
  const r2 = runBlocking(py, ["-m", "venv", venv]);
10695
11315
  if (r2.code !== 0) {
@@ -10702,7 +11322,7 @@ async function bootstrapMarketing(opts = {}) {
10702
11322
  log("upgrading pip");
10703
11323
  let r = runBlocking(venvPy, ["-m", "pip", "install", "--upgrade", "pip", "--quiet"]);
10704
11324
  if (r.code !== 0) log(`pip upgrade warning: ${r.stderr.trim().slice(0, 200)}`);
10705
- const reqs = join40(target, "requirements.txt");
11325
+ const reqs = join41(target, "requirements.txt");
10706
11326
  log("installing python deps (whisper / yt-dlp / instaloader) \u2014 first run can take several minutes");
10707
11327
  r = runBlocking(venvPy, ["-m", "pip", "install", "-r", reqs, "--quiet"]);
10708
11328
  if (r.code !== 0) {
@@ -10712,7 +11332,7 @@ ${r.stderr.trim() || r.stdout.trim()}`);
10712
11332
  let whisperPrimed = false;
10713
11333
  if (whisperModel) {
10714
11334
  log(`pre-pulling whisper model "${whisperModel}" into ${MARKETING_PATHS.whisperCacheDir()}`);
10715
- mkdirSync14(MARKETING_PATHS.whisperCacheDir(), { recursive: true });
11335
+ mkdirSync15(MARKETING_PATHS.whisperCacheDir(), { recursive: true });
10716
11336
  whisperPrimed = await primeWhisper(whisperModel);
10717
11337
  if (!whisperPrimed) log("whisper pre-pull failed (will retry on first ingest)");
10718
11338
  }
@@ -10752,15 +11372,15 @@ function subprocessEnv() {
10752
11372
  return {
10753
11373
  ...process.env,
10754
11374
  XDG_CACHE_HOME: cache,
10755
- HF_HOME: join40(cache, "hf"),
10756
- TRANSFORMERS_CACHE: join40(cache, "hf", "transformers"),
11375
+ HF_HOME: join41(cache, "hf"),
11376
+ TRANSFORMERS_CACHE: join41(cache, "hf", "transformers"),
10757
11377
  WHISPER_CACHE_DIR: MARKETING_PATHS.whisperCacheDir(),
10758
11378
  PYTHONUNBUFFERED: "1",
10759
11379
  PYTHONPATH: MARKETING_PATHS.toolsDir()
10760
11380
  };
10761
11381
  }
10762
11382
  function isBootstrapped() {
10763
- return existsSync45(MARKETING_PATHS.venvPython()) && existsSync45(join40(MARKETING_PATHS.reinfluenceDir(), "__main__.py"));
11383
+ return existsSync46(MARKETING_PATHS.venvPython()) && existsSync46(join41(MARKETING_PATHS.reinfluenceDir(), "__main__.py"));
10764
11384
  }
10765
11385
  var __filename, __dirname7;
10766
11386
  var init_bootstrap = __esm({
@@ -10773,7 +11393,7 @@ var init_bootstrap = __esm({
10773
11393
  });
10774
11394
 
10775
11395
  // src/cli/commands/marketing/init.ts
10776
- import { mkdirSync as mkdirSync15 } from "fs";
11396
+ import { mkdirSync as mkdirSync16 } from "fs";
10777
11397
  import chalk14 from "chalk";
10778
11398
  function registerMarketingInit(parent) {
10779
11399
  parent.command("init").description("Bootstrap _dream_context/marketing/ (folders, .env, venv, reinfluence tools, whisper cache)").option("--skip-bootstrap", "Only create folders + .env template; skip venv + pip install", false).option("--whisper-model <model>", "Whisper model to pre-pull (default: medium)", "medium").option("--no-whisper", "Skip Whisper model pre-pull").action(async (opts) => {
@@ -10785,7 +11405,7 @@ function registerMarketingInit(parent) {
10785
11405
  }
10786
11406
  console.log(header("Marketing init"));
10787
11407
  const root = marketingRoot();
10788
- mkdirSync15(root, { recursive: true });
11408
+ mkdirSync16(root, { recursive: true });
10789
11409
  for (const dir of [
10790
11410
  MARKETING_PATHS.cohortsDir(),
10791
11411
  MARKETING_PATHS.campaignsDir(),
@@ -10798,7 +11418,7 @@ function registerMarketingInit(parent) {
10798
11418
  MARKETING_PATHS.byIdemDir(),
10799
11419
  MARKETING_PATHS.cacheDir()
10800
11420
  ]) {
10801
- mkdirSync15(dir, { recursive: true });
11421
+ mkdirSync16(dir, { recursive: true });
10802
11422
  }
10803
11423
  info(`folders ready under ${chalk14.dim(root)}`);
10804
11424
  ensureEnvFile();
@@ -10837,8 +11457,8 @@ var init_init2 = __esm({
10837
11457
  });
10838
11458
 
10839
11459
  // src/lib/marketing/vision.ts
10840
- import { existsSync as existsSync46, readFileSync as readFileSync31, writeFileSync as writeFileSync21 } from "fs";
10841
- import { dirname as dirname20, join as join41 } from "path";
11460
+ import { existsSync as existsSync47, readFileSync as readFileSync32, writeFileSync as writeFileSync22 } from "fs";
11461
+ import { dirname as dirname20, join as join42 } from "path";
10842
11462
  function pickVisionProvider(env = process.env) {
10843
11463
  const oai = env.OPENAI_VISION_API_KEY;
10844
11464
  if (oai && oai.trim().length > 0) return { provider: "openai", apiKey: oai.trim() };
@@ -10877,11 +11497,11 @@ function parseLabelsFromText(text, allowlist = LABEL_ALLOWLIST) {
10877
11497
  return out;
10878
11498
  }
10879
11499
  async function readImageAsBase64(framePath) {
10880
- const buf = readFileSync31(framePath);
11500
+ const buf = readFileSync32(framePath);
10881
11501
  return buf.toString("base64");
10882
11502
  }
10883
11503
  async function labelHookFrame(framePath, opts) {
10884
- if (!existsSync46(framePath)) return [];
11504
+ if (!existsSync47(framePath)) return [];
10885
11505
  const fetcher = opts.fetcher ?? defaultFetcher;
10886
11506
  const prompt = opts.promptOverride ?? PROMPT;
10887
11507
  const b64 = await readImageAsBase64(framePath);
@@ -10931,7 +11551,7 @@ async function labelHookFrame(framePath, opts) {
10931
11551
  return parseLabelsFromText(text);
10932
11552
  }
10933
11553
  async function runVisionPassOnPost(postJsonPath, opts = {}) {
10934
- const raw = readFileSync31(postJsonPath, "utf-8");
11554
+ const raw = readFileSync32(postJsonPath, "utf-8");
10935
11555
  const post = JSON.parse(raw);
10936
11556
  const shortcode = post.shortcode ?? "?";
10937
11557
  const provider = opts.provider && opts.apiKey ? { provider: opts.provider, apiKey: opts.apiKey } : pickVisionProvider();
@@ -10970,15 +11590,15 @@ async function runVisionPassOnPost(postJsonPath, opts = {}) {
10970
11590
  post.pattern_tags = [...aggregated].sort();
10971
11591
  post.vision_summary = post.pattern_tags.length > 0 ? `Vision-tagged ${framesLabeled}/${hookFrames.length} hook frames; patterns: ${post.pattern_tags.join(", ")}.` : `Vision pass found no allowlisted patterns across ${hookFrames.length} hook frames.`;
10972
11592
  const tmp = postJsonPath + ".tmp";
10973
- writeFileSync21(tmp, JSON.stringify(post, null, 2) + "\n", "utf-8");
11593
+ writeFileSync22(tmp, JSON.stringify(post, null, 2) + "\n", "utf-8");
10974
11594
  const fs = await import("fs");
10975
11595
  fs.renameSync(tmp, postJsonPath);
10976
- const mdPath = join41(dirname20(postJsonPath), `${shortcode}.md`);
10977
- if (existsSync46(mdPath)) {
11596
+ const mdPath = join42(dirname20(postJsonPath), `${shortcode}.md`);
11597
+ if (existsSync47(mdPath)) {
10978
11598
  try {
10979
- const md = readFileSync31(mdPath, "utf-8");
11599
+ const md = readFileSync32(mdPath, "utf-8");
10980
11600
  const refreshed = updatePatternTagsInBridge(md, post.pattern_tags);
10981
- writeFileSync21(mdPath, refreshed, "utf-8");
11601
+ writeFileSync22(mdPath, refreshed, "utf-8");
10982
11602
  } catch {
10983
11603
  }
10984
11604
  }
@@ -11053,8 +11673,8 @@ Example output: ["talking-head","prop-driven"]`;
11053
11673
  // src/lib/marketing/competitors.ts
11054
11674
  import { spawn as spawn2 } from "child_process";
11055
11675
  import { spawnSync as spawnSync2 } from "child_process";
11056
- import { existsSync as existsSync47, mkdirSync as mkdirSync16, statfsSync } from "fs";
11057
- import { dirname as dirname21, join as join42, basename as basename14, extname as extname3 } from "path";
11676
+ import { existsSync as existsSync48, mkdirSync as mkdirSync17, statfsSync } from "fs";
11677
+ import { dirname as dirname21, join as join43, basename as basename16, extname as extname3 } from "path";
11058
11678
  function which(cmd) {
11059
11679
  const r = spawnSync2(process.platform === "win32" ? "where" : "which", [cmd], { encoding: "utf8" });
11060
11680
  if (r.status === 0) return (r.stdout || "").trim().split("\n")[0] || null;
@@ -11131,8 +11751,8 @@ ${h.installHints.join("\n")}`);
11131
11751
  skip_transcripts: !!opts.skipTranscripts,
11132
11752
  skip_frames: !!opts.skipFrames
11133
11753
  });
11134
- const outDir = join42(MARKETING_PATHS.competitorsDir(), "_ingest-tmp", run.id);
11135
- mkdirSync16(outDir, { recursive: true });
11754
+ const outDir = join43(MARKETING_PATHS.competitorsDir(), "_ingest-tmp", run.id);
11755
+ mkdirSync17(outDir, { recursive: true });
11136
11756
  const args = [
11137
11757
  "-m",
11138
11758
  "reinfluence",
@@ -11232,15 +11852,15 @@ stderr: ${stderrBuf.slice(0, 1e3)}`));
11232
11852
  return { runId: run.id, postsIngested, warnings, errors };
11233
11853
  }
11234
11854
  function persistPost(evt) {
11235
- const handleDir = join42(MARKETING_PATHS.competitorsDir(), evt.handle);
11236
- const postsDir = join42(handleDir, "posts");
11237
- const mediaDir = join42(handleDir, "_media", evt.shortcode);
11238
- mkdirSync16(postsDir, { recursive: true });
11239
- mkdirSync16(mediaDir, { recursive: true });
11240
- const finalVideoPath = evt.video_path ? moveAsset(evt.video_path, join42(mediaDir, basename14(evt.video_path))) : null;
11855
+ const handleDir = join43(MARKETING_PATHS.competitorsDir(), evt.handle);
11856
+ const postsDir = join43(handleDir, "posts");
11857
+ const mediaDir = join43(handleDir, "_media", evt.shortcode);
11858
+ mkdirSync17(postsDir, { recursive: true });
11859
+ mkdirSync17(mediaDir, { recursive: true });
11860
+ const finalVideoPath = evt.video_path ? moveAsset(evt.video_path, join43(mediaDir, basename16(evt.video_path))) : null;
11241
11861
  const finalFramePaths = [];
11242
11862
  for (const fr of evt.frames) {
11243
- const finalP = moveAsset(fr.path, join42(mediaDir, "frames", basename14(fr.path)));
11863
+ const finalP = moveAsset(fr.path, join43(mediaDir, "frames", basename16(fr.path)));
11244
11864
  finalFramePaths.push(finalP);
11245
11865
  }
11246
11866
  const id = `${evt.handle}__${evt.shortcode}`;
@@ -11260,14 +11880,14 @@ function persistPost(evt) {
11260
11880
  ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
11261
11881
  asset_paths: { video: finalVideoPath, frames: finalFramePaths }
11262
11882
  };
11263
- const jsonPath = join42(postsDir, `${evt.shortcode}.json`);
11264
- const bridgePath = join42(postsDir, `${evt.shortcode}.md`);
11883
+ const jsonPath = join43(postsDir, `${evt.shortcode}.json`);
11884
+ const bridgePath = join43(postsDir, `${evt.shortcode}.md`);
11265
11885
  writeJsonWithBridge(jsonPath, bridgePath, json, renderPostBridge(json));
11266
- const metaPath = join42(handleDir, "meta.json");
11267
- if (!existsSync47(metaPath)) {
11886
+ const metaPath = join43(handleDir, "meta.json");
11887
+ if (!existsSync48(metaPath)) {
11268
11888
  writeJsonWithBridge(
11269
11889
  metaPath,
11270
- join42(handleDir, "meta.md"),
11890
+ join43(handleDir, "meta.md"),
11271
11891
  { handle: evt.handle, first_seen_at: (/* @__PURE__ */ new Date()).toISOString() },
11272
11892
  `---
11273
11893
  id: competitor_${evt.handle}
@@ -11284,8 +11904,8 @@ First seen: ${(/* @__PURE__ */ new Date()).toISOString()}
11284
11904
  return jsonPath;
11285
11905
  }
11286
11906
  function moveAsset(src, dest) {
11287
- if (!existsSync47(src)) return dest;
11288
- mkdirSync16(dirname21(dest), { recursive: true });
11907
+ if (!existsSync48(src)) return dest;
11908
+ mkdirSync17(dirname21(dest), { recursive: true });
11289
11909
  const fs = __require("fs");
11290
11910
  try {
11291
11911
  fs.renameSync(src, dest);
@@ -11346,8 +11966,8 @@ var init_competitors = __esm({
11346
11966
  });
11347
11967
 
11348
11968
  // src/cli/commands/marketing/competitor.ts
11349
- import { existsSync as existsSync48, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
11350
- import { join as join43 } from "path";
11969
+ import { existsSync as existsSync49, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
11970
+ import { join as join44 } from "path";
11351
11971
  import chalk15 from "chalk";
11352
11972
  function registerMarketingCompetitor(parent) {
11353
11973
  const cmd = parent.command("competitor").description("Ingest competitor IG / YouTube content (Reinfluence pipeline).");
@@ -11398,13 +12018,13 @@ function registerMarketingCompetitor(parent) {
11398
12018
  });
11399
12019
  cmd.command("list").description("List ingested competitor handles.").action(() => {
11400
12020
  const dir = MARKETING_PATHS.competitorsDir();
11401
- if (!existsSync48(dir)) {
12021
+ if (!existsSync49(dir)) {
11402
12022
  info("No competitors ingested yet.");
11403
12023
  return;
11404
12024
  }
11405
- const handles = readdirSync10(dir).filter((name) => {
12025
+ const handles = readdirSync11(dir).filter((name) => {
11406
12026
  if (name.startsWith("_") || name.startsWith(".")) return false;
11407
- const full = join43(dir, name);
12027
+ const full = join44(dir, name);
11408
12028
  try {
11409
12029
  return statSync8(full).isDirectory();
11410
12030
  } catch {
@@ -11417,8 +12037,8 @@ function registerMarketingCompetitor(parent) {
11417
12037
  }
11418
12038
  console.log(header("Competitors"));
11419
12039
  for (const handle of handles) {
11420
- const postsDir = join43(dir, handle, "posts");
11421
- const postCount = existsSync48(postsDir) ? readdirSync10(postsDir).filter((f) => f.endsWith(".json")).length : 0;
12040
+ const postsDir = join44(dir, handle, "posts");
12041
+ const postCount = existsSync49(postsDir) ? readdirSync11(postsDir).filter((f) => f.endsWith(".json")).length : 0;
11422
12042
  console.log(` ${chalk15.cyan("@" + handle)} ${chalk15.dim(`${postCount} post(s)`)}`);
11423
12043
  }
11424
12044
  });
@@ -11431,11 +12051,11 @@ function registerMarketingCompetitor(parent) {
11431
12051
  }
11432
12052
  info(`Provider: ${provider.provider}`);
11433
12053
  const competitorsDir = MARKETING_PATHS.competitorsDir();
11434
- const handles = opts.handle ? [opts.handle] : existsSync48(competitorsDir) ? readdirSync10(competitorsDir).filter((h) => !h.startsWith("_") && statSync8(join43(competitorsDir, h)).isDirectory()) : [];
12054
+ const handles = opts.handle ? [opts.handle] : existsSync49(competitorsDir) ? readdirSync11(competitorsDir).filter((h) => !h.startsWith("_") && statSync8(join44(competitorsDir, h)).isDirectory()) : [];
11435
12055
  const matches = [];
11436
12056
  for (const h of handles) {
11437
- const candidate = join43(competitorsDir, h, "posts", `${shortcode}.json`);
11438
- if (existsSync48(candidate)) matches.push(candidate);
12057
+ const candidate = join44(competitorsDir, h, "posts", `${shortcode}.json`);
12058
+ if (existsSync49(candidate)) matches.push(candidate);
11439
12059
  }
11440
12060
  if (matches.length === 0) {
11441
12061
  error(`No post JSON found for shortcode "${shortcode}".`);
@@ -11470,18 +12090,18 @@ function registerMarketingCompetitor(parent) {
11470
12090
  process.exit(1);
11471
12091
  }
11472
12092
  const competitorsDir = MARKETING_PATHS.competitorsDir();
11473
- if (!existsSync48(competitorsDir)) {
12093
+ if (!existsSync49(competitorsDir)) {
11474
12094
  warn("No competitors directory yet \u2014 nothing to relabel.");
11475
12095
  return;
11476
12096
  }
11477
- const handles = readdirSync10(competitorsDir).filter((h) => !h.startsWith("_") && statSync8(join43(competitorsDir, h)).isDirectory());
12097
+ const handles = readdirSync11(competitorsDir).filter((h) => !h.startsWith("_") && statSync8(join44(competitorsDir, h)).isDirectory());
11478
12098
  let labeled = 0;
11479
12099
  let skipped = 0;
11480
12100
  for (const h of handles) {
11481
- const postsDir = join43(competitorsDir, h, "posts");
11482
- if (!existsSync48(postsDir)) continue;
11483
- for (const f of readdirSync10(postsDir).filter((n) => n.endsWith(".json"))) {
11484
- const path = join43(postsDir, f);
12101
+ const postsDir = join44(competitorsDir, h, "posts");
12102
+ if (!existsSync49(postsDir)) continue;
12103
+ for (const f of readdirSync11(postsDir).filter((n) => n.endsWith(".json"))) {
12104
+ const path = join44(postsDir, f);
11485
12105
  if (opts.dryRun) {
11486
12106
  info(`would relabel: ${path}`);
11487
12107
  continue;
@@ -11527,8 +12147,8 @@ var init_competitor = __esm({
11527
12147
  });
11528
12148
 
11529
12149
  // src/lib/marketing/meta-fetch.ts
11530
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync32, writeFileSync as writeFileSync22, statSync as statSync9, openSync as openSync2, readSync, closeSync as closeSync2 } from "fs";
11531
- import { dirname as dirname22, basename as basename15 } from "path";
12150
+ import { existsSync as existsSync50, mkdirSync as mkdirSync18, readFileSync as readFileSync33, writeFileSync as writeFileSync23, statSync as statSync9, openSync as openSync2, readSync, closeSync as closeSync2 } from "fs";
12151
+ import { dirname as dirname22, basename as basename17 } from "path";
11532
12152
  import { randomUUID } from "crypto";
11533
12153
  function getQueue(accountId) {
11534
12154
  let q = writeQueues.get(accountId);
@@ -11578,17 +12198,17 @@ function idemCachePath(key) {
11578
12198
  }
11579
12199
  function readIdem(key) {
11580
12200
  const p = idemCachePath(key);
11581
- if (!existsSync49(p)) return null;
12201
+ if (!existsSync50(p)) return null;
11582
12202
  try {
11583
- return JSON.parse(readFileSync32(p, "utf8"));
12203
+ return JSON.parse(readFileSync33(p, "utf8"));
11584
12204
  } catch {
11585
12205
  return null;
11586
12206
  }
11587
12207
  }
11588
12208
  function writeIdem(rec) {
11589
12209
  const p = idemCachePath(rec.key);
11590
- mkdirSync17(dirname22(p), { recursive: true });
11591
- writeFileSync22(p, JSON.stringify(rec, null, 2) + "\n", "utf8");
12210
+ mkdirSync18(dirname22(p), { recursive: true });
12211
+ writeFileSync23(p, JSON.stringify(rec, null, 2) + "\n", "utf8");
11592
12212
  }
11593
12213
  function backoffMs(attempt, hintSec) {
11594
12214
  if (hintSec != null && Number.isFinite(hintSec) && hintSec > 0) {
@@ -11821,12 +12441,12 @@ async function uploadVideoFile(ctx, filepath, fields = {}) {
11821
12441
  form.set("upload_session_id", sessionId);
11822
12442
  form.set("start_offset", String(startOffset));
11823
12443
  const blob = new Blob([new Uint8Array(buf)], { type: "application/octet-stream" });
11824
- form.set("video_file_chunk", blob, basename15(filepath));
12444
+ form.set("video_file_chunk", blob, basename17(filepath));
11825
12445
  const transfer = await uploadFormDataChunked(ctx, path, form);
11826
12446
  const next = transfer;
11827
12447
  startOffset = Number(next.start_offset);
11828
12448
  endOffset = Number(next.end_offset);
11829
- logLine(ctx, `[upload] ${basename15(filepath)} progress: ${startOffset}/${fileSize}`);
12449
+ logLine(ctx, `[upload] ${basename17(filepath)} progress: ${startOffset}/${fileSize}`);
11830
12450
  }
11831
12451
  } finally {
11832
12452
  closeSync2(fd);
@@ -11856,9 +12476,9 @@ async function singleShotVideoUpload(ctx, filepath, fields) {
11856
12476
  if (fields.name) form.set("name", fields.name);
11857
12477
  if (fields.title) form.set("title", fields.title);
11858
12478
  if (fields.description) form.set("description", fields.description);
11859
- const buffer = readFileSync32(filepath);
12479
+ const buffer = readFileSync33(filepath);
11860
12480
  const blob = new Blob([new Uint8Array(buffer)], { type: "video/mp4" });
11861
- form.set("source", blob, basename15(filepath));
12481
+ form.set("source", blob, basename17(filepath));
11862
12482
  const resp = await fetch(url, {
11863
12483
  method: "POST",
11864
12484
  headers: { Authorization: `Bearer ${ctx.accessToken}` },
@@ -11905,9 +12525,9 @@ async function uploadImageFile(ctx, filepath) {
11905
12525
  return withWriteSlot(ctx.adAccountId, async () => {
11906
12526
  const url = buildUrl(ctx, { method: "POST", path: `${ctx.adAccountId}/adimages` });
11907
12527
  const form = new FormData();
11908
- const buffer = readFileSync32(filepath);
12528
+ const buffer = readFileSync33(filepath);
11909
12529
  const blob = new Blob([new Uint8Array(buffer)], { type: "image/png" });
11910
- form.set("filename", blob, basename15(filepath));
12530
+ form.set("filename", blob, basename17(filepath));
11911
12531
  const resp = await fetch(url, {
11912
12532
  method: "POST",
11913
12533
  headers: { Authorization: `Bearer ${ctx.accessToken}` },
@@ -12324,7 +12944,7 @@ var init_account = __esm({
12324
12944
  });
12325
12945
 
12326
12946
  // src/lib/marketing/hypothesis.ts
12327
- import { existsSync as existsSync50, readFileSync as readFileSync33 } from "fs";
12947
+ import { existsSync as existsSync51, readFileSync as readFileSync34 } from "fs";
12328
12948
  function validateHypothesis(raw, options = {}) {
12329
12949
  const errors = [];
12330
12950
  const fieldErrors = {};
@@ -12391,12 +13011,12 @@ function validateHypothesis(raw, options = {}) {
12391
13011
  };
12392
13012
  }
12393
13013
  function loadHypothesisFile(path, options = {}) {
12394
- if (!existsSync50(path)) {
13014
+ if (!existsSync51(path)) {
12395
13015
  return { ok: false, errors: [`Hypothesis file not found: ${path}`] };
12396
13016
  }
12397
13017
  let parsed;
12398
13018
  try {
12399
- parsed = JSON.parse(readFileSync33(path, "utf8"));
13019
+ parsed = JSON.parse(readFileSync34(path, "utf8"));
12400
13020
  } catch (e) {
12401
13021
  return { ok: false, errors: [`Failed to parse JSON at ${path}: ${e.message}`] };
12402
13022
  }
@@ -12584,8 +13204,8 @@ var init_cohort2 = __esm({
12584
13204
  });
12585
13205
 
12586
13206
  // src/lib/marketing/insights-cache.ts
12587
- import { existsSync as existsSync51, readFileSync as readFileSync34, mkdirSync as mkdirSync18, writeFileSync as writeFileSync23, readdirSync as readdirSync11, statSync as statSync10 } from "fs";
12588
- import { join as join44 } from "path";
13207
+ import { existsSync as existsSync52, readFileSync as readFileSync35, mkdirSync as mkdirSync19, writeFileSync as writeFileSync24, readdirSync as readdirSync12, statSync as statSync10 } from "fs";
13208
+ import { join as join45 } from "path";
12589
13209
  import { randomBytes as randomBytes3 } from "crypto";
12590
13210
  function snapshotFilename(entityId, pulledAt) {
12591
13211
  const yyyy = pulledAt.getUTCFullYear();
@@ -12596,30 +13216,30 @@ function snapshotFilename(entityId, pulledAt) {
12596
13216
  return `${safe}__${yyyy}-${mm}-${dd}-${hh}.json`;
12597
13217
  }
12598
13218
  function indexPath() {
12599
- return join44(MARKETING_PATHS.insightsDir(), "_index.json");
13219
+ return join45(MARKETING_PATHS.insightsDir(), "_index.json");
12600
13220
  }
12601
13221
  function readIndex() {
12602
13222
  const p = indexPath();
12603
- if (!existsSync51(p)) return { latest: {}, updated_at: "1970-01-01T00:00:00Z" };
13223
+ if (!existsSync52(p)) return { latest: {}, updated_at: "1970-01-01T00:00:00Z" };
12604
13224
  try {
12605
- return JSON.parse(readFileSync34(p, "utf8"));
13225
+ return JSON.parse(readFileSync35(p, "utf8"));
12606
13226
  } catch {
12607
13227
  return { latest: {}, updated_at: "1970-01-01T00:00:00Z" };
12608
13228
  }
12609
13229
  }
12610
13230
  function writeIndex(idx) {
12611
13231
  const p = indexPath();
12612
- mkdirSync18(MARKETING_PATHS.insightsDir(), { recursive: true });
13232
+ mkdirSync19(MARKETING_PATHS.insightsDir(), { recursive: true });
12613
13233
  const tmp = `${p}.tmp.${process.pid}.${randomBytes3(4).toString("hex")}`;
12614
- writeFileSync23(tmp, JSON.stringify(idx, null, 2) + "\n", "utf8");
13234
+ writeFileSync24(tmp, JSON.stringify(idx, null, 2) + "\n", "utf8");
12615
13235
  __require("fs").renameSync(tmp, p);
12616
13236
  }
12617
13237
  function saveInsightsSnapshot(snap) {
12618
- mkdirSync18(MARKETING_PATHS.insightsDir(), { recursive: true });
13238
+ mkdirSync19(MARKETING_PATHS.insightsDir(), { recursive: true });
12619
13239
  const filename = snapshotFilename(snap.entity_id, new Date(snap.pulled_at));
12620
- const fullPath = join44(MARKETING_PATHS.insightsDir(), filename);
13240
+ const fullPath = join45(MARKETING_PATHS.insightsDir(), filename);
12621
13241
  const tmp = `${fullPath}.tmp.${process.pid}.${randomBytes3(4).toString("hex")}`;
12622
- writeFileSync23(tmp, JSON.stringify(snap, null, 2) + "\n", "utf8");
13242
+ writeFileSync24(tmp, JSON.stringify(snap, null, 2) + "\n", "utf8");
12623
13243
  __require("fs").renameSync(tmp, fullPath);
12624
13244
  const idx = readIndex();
12625
13245
  idx.latest[snap.entity_id] = filename;
@@ -12631,10 +13251,10 @@ function getCachedInsights(entityId) {
12631
13251
  const idx = readIndex();
12632
13252
  const filename = idx.latest[entityId];
12633
13253
  if (!filename) return null;
12634
- const fullPath = join44(MARKETING_PATHS.insightsDir(), filename);
12635
- if (!existsSync51(fullPath)) return null;
13254
+ const fullPath = join45(MARKETING_PATHS.insightsDir(), filename);
13255
+ if (!existsSync52(fullPath)) return null;
12636
13256
  try {
12637
- const snap = JSON.parse(readFileSync34(fullPath, "utf8"));
13257
+ const snap = JSON.parse(readFileSync35(fullPath, "utf8"));
12638
13258
  const ageMs = Date.now() - new Date(snap.pulled_at).getTime();
12639
13259
  if (ageMs > INSIGHTS_TTL_MS) return null;
12640
13260
  return snap;
@@ -12646,20 +13266,20 @@ function getLatestSnapshot(entityId) {
12646
13266
  const idx = readIndex();
12647
13267
  const filename = idx.latest[entityId];
12648
13268
  if (!filename) return null;
12649
- const fullPath = join44(MARKETING_PATHS.insightsDir(), filename);
12650
- if (!existsSync51(fullPath)) return null;
13269
+ const fullPath = join45(MARKETING_PATHS.insightsDir(), filename);
13270
+ if (!existsSync52(fullPath)) return null;
12651
13271
  try {
12652
- return JSON.parse(readFileSync34(fullPath, "utf8"));
13272
+ return JSON.parse(readFileSync35(fullPath, "utf8"));
12653
13273
  } catch {
12654
13274
  return null;
12655
13275
  }
12656
13276
  }
12657
13277
  function getPriorSnapshot(entityId, beforeMs) {
12658
13278
  const dir = MARKETING_PATHS.insightsDir();
12659
- if (!existsSync51(dir)) return null;
13279
+ if (!existsSync52(dir)) return null;
12660
13280
  const safe = entityId.replace(/[^A-Za-z0-9_-]/g, "_");
12661
- const candidates = readdirSync11(dir).filter((f) => f.startsWith(`${safe}__`) && f.endsWith(".json")).map((f) => {
12662
- const p = join44(dir, f);
13281
+ const candidates = readdirSync12(dir).filter((f) => f.startsWith(`${safe}__`) && f.endsWith(".json")).map((f) => {
13282
+ const p = join45(dir, f);
12663
13283
  const stat = statSync10(p);
12664
13284
  return { f, p, mtimeMs: stat.mtimeMs };
12665
13285
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -12667,7 +13287,7 @@ function getPriorSnapshot(entityId, beforeMs) {
12667
13287
  for (const c of candidates) {
12668
13288
  if (c.mtimeMs <= cutoff) {
12669
13289
  try {
12670
- return JSON.parse(readFileSync34(c.p, "utf8"));
13290
+ return JSON.parse(readFileSync35(c.p, "utf8"));
12671
13291
  } catch {
12672
13292
  continue;
12673
13293
  }
@@ -13303,8 +13923,8 @@ var init_kill = __esm({
13303
13923
  });
13304
13924
 
13305
13925
  // src/cli/commands/marketing/doctor.ts
13306
- import { readFileSync as readFileSync35, readdirSync as readdirSync12, statSync as statSync11 } from "fs";
13307
- import { join as join45 } from "path";
13926
+ import { readFileSync as readFileSync36, readdirSync as readdirSync13, statSync as statSync11 } from "fs";
13927
+ import { join as join46 } from "path";
13308
13928
  import chalk25 from "chalk";
13309
13929
  function registerMarketingDoctor(parent) {
13310
13930
  parent.command("doctor").description("Run diagnostics: env, FK integrity, retroactive secret scan, Reinfluence health.").option("--scan", "Run retroactive secret scan over the entire _dream_context/ tree", false).action(async (opts) => {
@@ -13386,7 +14006,7 @@ function scanForSecrets(root) {
13386
14006
  for (const file of walkText(root)) {
13387
14007
  let txt;
13388
14008
  try {
13389
- txt = readFileSync35(file, "utf8");
14009
+ txt = readFileSync36(file, "utf8");
13390
14010
  } catch {
13391
14011
  continue;
13392
14012
  }
@@ -13407,13 +14027,13 @@ function scanForSecrets(root) {
13407
14027
  function* walkText(dir) {
13408
14028
  let entries;
13409
14029
  try {
13410
- entries = readdirSync12(dir);
14030
+ entries = readdirSync13(dir);
13411
14031
  } catch {
13412
14032
  return;
13413
14033
  }
13414
14034
  for (const e of entries) {
13415
14035
  if (e.startsWith(".") && (e === ".env" || e === ".env.local")) continue;
13416
- const full = join45(dir, e);
14036
+ const full = join46(dir, e);
13417
14037
  let stat;
13418
14038
  try {
13419
14039
  stat = statSync11(full);
@@ -13450,8 +14070,8 @@ var init_doctor2 = __esm({
13450
14070
  });
13451
14071
 
13452
14072
  // src/lib/marketing/entity-store.ts
13453
- import { existsSync as existsSync53, readFileSync as readFileSync36, readdirSync as readdirSync13 } from "fs";
13454
- import { join as join46 } from "path";
14073
+ import { existsSync as existsSync54, readFileSync as readFileSync37, readdirSync as readdirSync14 } from "fs";
14074
+ import { join as join47 } from "path";
13455
14075
  import { customAlphabet as customAlphabet3 } from "nanoid";
13456
14076
  function dirFor(kind) {
13457
14077
  switch (kind) {
@@ -13471,24 +14091,24 @@ function newEntityId(kind) {
13471
14091
  }
13472
14092
  function entityPaths(kind, id) {
13473
14093
  const d = dirFor(kind);
13474
- return { json: join46(d, `${id}.json`), md: join46(d, `${id}.md`) };
14094
+ return { json: join47(d, `${id}.json`), md: join47(d, `${id}.md`) };
13475
14095
  }
13476
14096
  function loadEntity(kind, id) {
13477
14097
  const { json } = entityPaths(kind, id);
13478
- if (!existsSync53(json)) return null;
14098
+ if (!existsSync54(json)) return null;
13479
14099
  try {
13480
- return JSON.parse(readFileSync36(json, "utf8"));
14100
+ return JSON.parse(readFileSync37(json, "utf8"));
13481
14101
  } catch {
13482
14102
  return null;
13483
14103
  }
13484
14104
  }
13485
14105
  function listEntities(kind) {
13486
14106
  const d = dirFor(kind);
13487
- if (!existsSync53(d)) return [];
14107
+ if (!existsSync54(d)) return [];
13488
14108
  const out = [];
13489
- for (const f of readdirSync13(d).filter((x) => x.endsWith(".json"))) {
14109
+ for (const f of readdirSync14(d).filter((x) => x.endsWith(".json"))) {
13490
14110
  try {
13491
- out.push(JSON.parse(readFileSync36(join46(d, f), "utf8")));
14111
+ out.push(JSON.parse(readFileSync37(join47(d, f), "utf8")));
13492
14112
  } catch {
13493
14113
  }
13494
14114
  }
@@ -13681,7 +14301,7 @@ var init_campaign = __esm({
13681
14301
  });
13682
14302
 
13683
14303
  // src/cli/commands/marketing/adset.ts
13684
- import { existsSync as existsSync54, readFileSync as readFileSync37 } from "fs";
14304
+ import { existsSync as existsSync55, readFileSync as readFileSync38 } from "fs";
13685
14305
  import chalk27 from "chalk";
13686
14306
  function registerMarketingAdSet(parent) {
13687
14307
  const cmd = parent.command("adset").description("Manage adsets.");
@@ -13702,13 +14322,13 @@ function registerMarketingAdSet(parent) {
13702
14322
  }
13703
14323
  throw e;
13704
14324
  }
13705
- if (!existsSync54(opts.targeting)) {
14325
+ if (!existsSync55(opts.targeting)) {
13706
14326
  error(`Targeting file not found: ${opts.targeting}`);
13707
14327
  process.exit(1);
13708
14328
  }
13709
14329
  let targeting;
13710
14330
  try {
13711
- targeting = JSON.parse(readFileSync37(opts.targeting, "utf8"));
14331
+ targeting = JSON.parse(readFileSync38(opts.targeting, "utf8"));
13712
14332
  } catch (e) {
13713
14333
  error(`Failed to parse targeting JSON: ${e.message}`);
13714
14334
  process.exit(1);
@@ -13964,13 +14584,13 @@ var init_creative = __esm({
13964
14584
  });
13965
14585
 
13966
14586
  // src/cli/commands/marketing/asset.ts
13967
- import { existsSync as existsSync55, statSync as statSync12 } from "fs";
14587
+ import { existsSync as existsSync56, statSync as statSync12 } from "fs";
13968
14588
  import { extname as extname4 } from "path";
13969
14589
  import chalk29 from "chalk";
13970
14590
  function registerMarketingAsset(parent) {
13971
14591
  const cmd = parent.command("asset").description("Upload creative assets to Meta.");
13972
14592
  cmd.command("upload <path>").description("Upload a video (>50MB \u2192 chunked) or image to Meta. Returns id/hash.").option("--type <type>", "override auto-detection (video|image)").option("--name <name>", "asset name (videos)").option("--no-dry-run", "Actually upload to Meta").action(async (path, opts) => {
13973
- if (!existsSync55(path)) {
14593
+ if (!existsSync56(path)) {
13974
14594
  error(`File not found: ${path}`);
13975
14595
  process.exit(1);
13976
14596
  }
@@ -14137,8 +14757,8 @@ var init_ad = __esm({
14137
14757
  });
14138
14758
 
14139
14759
  // src/lib/marketing/launch.ts
14140
- import { existsSync as existsSync56, readFileSync as readFileSync38, writeFileSync as writeFileSync24, mkdirSync as mkdirSync19, renameSync as renameSync3, readdirSync as readdirSync14 } from "fs";
14141
- import { dirname as dirname23, join as join47, basename as basename16 } from "path";
14760
+ import { existsSync as existsSync57, readFileSync as readFileSync39, writeFileSync as writeFileSync25, mkdirSync as mkdirSync20, renameSync as renameSync3, readdirSync as readdirSync15 } from "fs";
14761
+ import { dirname as dirname23, join as join48, basename as basename18 } from "path";
14142
14762
  import { randomBytes as randomBytes4 } from "crypto";
14143
14763
  function buildLaunchSummary(cohortId) {
14144
14764
  const cohort = loadCohort(cohortId);
@@ -14182,15 +14802,15 @@ function isoTs2() {
14182
14802
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
14183
14803
  }
14184
14804
  function atomicWriteFile3(path, data) {
14185
- mkdirSync19(dirname23(path), { recursive: true });
14805
+ mkdirSync20(dirname23(path), { recursive: true });
14186
14806
  const tmp = `${path}.tmp.${process.pid}.${randomBytes4(4).toString("hex")}`;
14187
- writeFileSync24(tmp, data, "utf8");
14807
+ writeFileSync25(tmp, data, "utf8");
14188
14808
  renameSync3(tmp, path);
14189
14809
  }
14190
14810
  function readWal(path) {
14191
- if (!existsSync56(path)) return null;
14811
+ if (!existsSync57(path)) return null;
14192
14812
  try {
14193
- return JSON.parse(readFileSync38(path, "utf8"));
14813
+ return JSON.parse(readFileSync39(path, "utf8"));
14194
14814
  } catch {
14195
14815
  return null;
14196
14816
  }
@@ -14200,13 +14820,13 @@ function writeWal(path, wal) {
14200
14820
  }
14201
14821
  function findWalByRunId(runId) {
14202
14822
  const dir = MARKETING_PATHS.runsDir();
14203
- if (!existsSync56(dir)) return null;
14204
- for (const f of readdirSync14(dir)) {
14823
+ if (!existsSync57(dir)) return null;
14824
+ for (const f of readdirSync15(dir)) {
14205
14825
  if (!f.endsWith(".json")) continue;
14206
14826
  if (f.startsWith("by-idem")) continue;
14207
14827
  const stem = f.replace(/\.json$/, "");
14208
- if (stem === runId || f === runId || basename16(f, ".json") === runId) {
14209
- return join47(dir, f);
14828
+ if (stem === runId || f === runId || basename18(f, ".json") === runId) {
14829
+ return join48(dir, f);
14210
14830
  }
14211
14831
  }
14212
14832
  return null;
@@ -14246,7 +14866,7 @@ function createLaunchWal(args) {
14246
14866
  flipped_count: 0,
14247
14867
  error: null
14248
14868
  };
14249
- const walPath = join47(MARKETING_PATHS.runsDir(), `${id}.json`);
14869
+ const walPath = join48(MARKETING_PATHS.runsDir(), `${id}.json`);
14250
14870
  writeWal(walPath, wal);
14251
14871
  return { walPath, wal };
14252
14872
  }
@@ -14478,7 +15098,7 @@ var init_launch2 = __esm({
14478
15098
 
14479
15099
  // src/cli/commands/marketing/learnings.ts
14480
15100
  import chalk32 from "chalk";
14481
- import { existsSync as existsSync57, readFileSync as readFileSync39 } from "fs";
15101
+ import { existsSync as existsSync58, readFileSync as readFileSync40 } from "fs";
14482
15102
  function registerMarketingLearnings(parent) {
14483
15103
  const cmd = parent.command("learnings").description("Performance Monitor learnings ledger (per-day .md + index).");
14484
15104
  cmd.command("show").description("Show today's (or specified day's) learnings file.").option("--date <YYYY-MM-DD>", "Day to show (default: today UTC)").option("--id <id>", "Show a single entry by id").action((opts) => {
@@ -14621,8 +15241,8 @@ async function resolveBody(body, bodyFile) {
14621
15241
  if (body) return body;
14622
15242
  if (bodyFile === "-") return readStdin3();
14623
15243
  if (bodyFile) {
14624
- if (!existsSync57(bodyFile)) throw new Error(`Body file not found: ${bodyFile}`);
14625
- return readFileSync39(bodyFile, "utf8");
15244
+ if (!existsSync58(bodyFile)) throw new Error(`Body file not found: ${bodyFile}`);
15245
+ return readFileSync40(bodyFile, "utf8");
14626
15246
  }
14627
15247
  return null;
14628
15248
  }
@@ -14651,29 +15271,29 @@ var init_learnings2 = __esm({
14651
15271
 
14652
15272
  // src/lib/marketing/rem-sleep.ts
14653
15273
  import {
14654
- existsSync as existsSync58,
14655
- mkdirSync as mkdirSync20,
14656
- readFileSync as readFileSync40,
14657
- writeFileSync as writeFileSync25,
14658
- readdirSync as readdirSync15,
15274
+ existsSync as existsSync59,
15275
+ mkdirSync as mkdirSync21,
15276
+ readFileSync as readFileSync41,
15277
+ writeFileSync as writeFileSync26,
15278
+ readdirSync as readdirSync16,
14659
15279
  statSync as statSync13,
14660
15280
  unlinkSync as unlinkSync2,
14661
15281
  renameSync as renameSync4
14662
15282
  } from "fs";
14663
- import { dirname as dirname24, join as join48 } from "path";
15283
+ import { dirname as dirname24, join as join49 } from "path";
14664
15284
  import { randomBytes as randomBytes5 } from "crypto";
14665
15285
  function atomicWriteFile4(path, data) {
14666
- mkdirSync20(dirname24(path), { recursive: true });
15286
+ mkdirSync21(dirname24(path), { recursive: true });
14667
15287
  const tmp = `${path}.tmp.${process.pid}.${randomBytes5(4).toString("hex")}`;
14668
- writeFileSync25(tmp, data, "utf8");
15288
+ writeFileSync26(tmp, data, "utf8");
14669
15289
  renameSync4(tmp, path);
14670
15290
  }
14671
15291
  function pruneRuns(opts = {}) {
14672
15292
  const keepLast = opts.keepLast ?? 100;
14673
15293
  const dir = MARKETING_PATHS.runsDir();
14674
- if (!existsSync58(dir)) return { scanned: 0, kept: 0, deleted: 0, deletedFiles: [] };
14675
- const files = readdirSync15(dir).filter((f) => f.endsWith(".json")).map((f) => {
14676
- const p = join48(dir, f);
15294
+ if (!existsSync59(dir)) return { scanned: 0, kept: 0, deleted: 0, deletedFiles: [] };
15295
+ const files = readdirSync16(dir).filter((f) => f.endsWith(".json")).map((f) => {
15296
+ const p = join49(dir, f);
14677
15297
  try {
14678
15298
  return { f, p, mtimeMs: statSync13(p).mtimeMs };
14679
15299
  } catch {
@@ -14707,13 +15327,13 @@ function compactInsights(opts = {}) {
14707
15327
  const weeklyAfterDays = opts.weeklyAfterDays ?? 14;
14708
15328
  const now = opts.now ?? /* @__PURE__ */ new Date();
14709
15329
  const dir = MARKETING_PATHS.insightsDir();
14710
- if (!existsSync58(dir)) return { scanned: 0, kept: 0, deleted: 0, deletedFiles: [] };
15330
+ if (!existsSync59(dir)) return { scanned: 0, kept: 0, deleted: 0, deletedFiles: [] };
14711
15331
  const files = [];
14712
- for (const f of readdirSync15(dir)) {
15332
+ for (const f of readdirSync16(dir)) {
14713
15333
  if (!f.endsWith(".json") || f.startsWith("_")) continue;
14714
15334
  const m = f.match(SNAP_RE);
14715
15335
  if (!m) continue;
14716
- const p = join48(dir, f);
15336
+ const p = join49(dir, f);
14717
15337
  try {
14718
15338
  const stat = statSync13(p);
14719
15339
  files.push({ f, p, mtimeMs: stat.mtimeMs, entityId: m[1], bucket: m[2] });
@@ -14779,18 +15399,18 @@ function mergeDailyLearnings(opts = {}) {
14779
15399
  const retainDays = opts.retainDays ?? 7;
14780
15400
  const now = opts.now ?? /* @__PURE__ */ new Date();
14781
15401
  const dir = MARKETING_PATHS.learningsDir();
14782
- if (!existsSync58(dir)) {
15402
+ if (!existsSync59(dir)) {
14783
15403
  return { scanned: 0, merged: 0, archivePath: null, droppedRejected: 0, mergedFiles: [] };
14784
15404
  }
14785
15405
  const dayMs = 24 * 60 * 60 * 1e3;
14786
15406
  const cutoff = now.getTime() - retainDays * dayMs;
14787
15407
  const candidates = [];
14788
- for (const f of readdirSync15(dir)) {
15408
+ for (const f of readdirSync16(dir)) {
14789
15409
  const m = f.match(/^(\d{4}-\d{2}-\d{2})\.md$/);
14790
15410
  if (!m) continue;
14791
15411
  const date = m[1];
14792
15412
  const dateMs = (/* @__PURE__ */ new Date(`${date}T00:00:00Z`)).getTime();
14793
- if (dateMs < cutoff) candidates.push({ f, p: join48(dir, f), date });
15413
+ if (dateMs < cutoff) candidates.push({ f, p: join49(dir, f), date });
14794
15414
  }
14795
15415
  if (candidates.length === 0) {
14796
15416
  return { scanned: 0, merged: 0, archivePath: null, droppedRejected: 0, mergedFiles: [] };
@@ -14804,10 +15424,10 @@ function mergeDailyLearnings(opts = {}) {
14804
15424
  const mergedFiles = [];
14805
15425
  let lastArchivePath = null;
14806
15426
  for (const [q, list] of byQuarter) {
14807
- const archivePath = join48(dir, `_archive-${q}.md`);
15427
+ const archivePath = join49(dir, `_archive-${q}.md`);
14808
15428
  list.sort((a, b) => a.date.localeCompare(b.date));
14809
15429
  for (const c of list) {
14810
- const existingArchive = existsSync58(archivePath) ? readFileSync40(archivePath, "utf8") : `# Marketing learnings archive \u2014 ${q}
15430
+ const existingArchive = existsSync59(archivePath) ? readFileSync41(archivePath, "utf8") : `# Marketing learnings archive \u2014 ${q}
14811
15431
  `;
14812
15432
  const dateMarker = `# Marketing learnings \u2014 ${c.date}`;
14813
15433
  const alreadyMerged = existingArchive.includes(dateMarker);
@@ -14816,7 +15436,7 @@ function mergeDailyLearnings(opts = {}) {
14816
15436
  continue;
14817
15437
  }
14818
15438
  if (!alreadyMerged) {
14819
- const body = readFileSync40(c.p, "utf8").trimEnd();
15439
+ const body = readFileSync41(c.p, "utf8").trimEnd();
14820
15440
  const next2 = existingArchive.trimEnd() + "\n\n" + body + "\n";
14821
15441
  atomicWriteFile4(archivePath, next2);
14822
15442
  }
@@ -14859,14 +15479,14 @@ function quarterKey(yyyymmdd) {
14859
15479
  }
14860
15480
  function redactRunsSweep(opts = {}) {
14861
15481
  const dir = MARKETING_PATHS.runsDir();
14862
- if (!existsSync58(dir)) return { scanned: 0, rewritten: 0, rewrittenFiles: [] };
14863
- const files = readdirSync15(dir).filter((f) => f.endsWith(".json"));
15482
+ if (!existsSync59(dir)) return { scanned: 0, rewritten: 0, rewrittenFiles: [] };
15483
+ const files = readdirSync16(dir).filter((f) => f.endsWith(".json"));
14864
15484
  const rewrittenFiles = [];
14865
15485
  for (const f of files) {
14866
- const p = join48(dir, f);
15486
+ const p = join49(dir, f);
14867
15487
  let raw;
14868
15488
  try {
14869
- raw = readFileSync40(p, "utf8");
15489
+ raw = readFileSync41(p, "utf8");
14870
15490
  } catch {
14871
15491
  continue;
14872
15492
  }
@@ -14888,7 +15508,7 @@ function redactRunsSweep(opts = {}) {
14888
15508
  function runRemSleep(opts = {}) {
14889
15509
  const ranAt = (opts.now ?? /* @__PURE__ */ new Date()).toISOString();
14890
15510
  const root = marketingRootIfExists();
14891
- if (!root || !existsSync58(root)) {
15511
+ if (!root || !existsSync59(root)) {
14892
15512
  return {
14893
15513
  ranAt,
14894
15514
  marketingPresent: false,
@@ -14910,7 +15530,7 @@ function runRemSleep(opts = {}) {
14910
15530
  now: opts.now
14911
15531
  });
14912
15532
  const redaction = redactRunsSweep({ dryRun: opts.dryRun });
14913
- void mkdirSync20;
15533
+ void mkdirSync21;
14914
15534
  return { ranAt, marketingPresent: true, runs, insights, learnings, redaction };
14915
15535
  }
14916
15536
  var SNAP_RE;
@@ -15007,9 +15627,9 @@ var init_git_guard = __esm({
15007
15627
  });
15008
15628
 
15009
15629
  // src/cli/commands/marketing/hooks.ts
15010
- import { existsSync as existsSync59, readFileSync as readFileSync41, writeFileSync as writeFileSync26, chmodSync, mkdirSync as mkdirSync21, statSync as statSync14 } from "fs";
15630
+ import { existsSync as existsSync60, readFileSync as readFileSync42, writeFileSync as writeFileSync27, chmodSync, mkdirSync as mkdirSync22, statSync as statSync14 } from "fs";
15011
15631
  import { execFileSync as execFileSync5 } from "child_process";
15012
- import { join as join49 } from "path";
15632
+ import { join as join50 } from "path";
15013
15633
  import chalk34 from "chalk";
15014
15634
  function findGitDir(cwd = process.cwd()) {
15015
15635
  try {
@@ -15019,7 +15639,7 @@ function findGitDir(cwd = process.cwd()) {
15019
15639
  stdio: ["ignore", "pipe", "ignore"]
15020
15640
  }).trim();
15021
15641
  if (!out) return null;
15022
- return out.startsWith("/") ? out : join49(cwd, out);
15642
+ return out.startsWith("/") ? out : join50(cwd, out);
15023
15643
  } catch {
15024
15644
  return null;
15025
15645
  }
@@ -15050,11 +15670,11 @@ function registerMarketingHooks(parent) {
15050
15670
  error("Not inside a git repository (or `git` is not on PATH).");
15051
15671
  process.exit(1);
15052
15672
  }
15053
- const hooksDir = join49(gitDir, "hooks");
15054
- mkdirSync21(hooksDir, { recursive: true });
15055
- const target = join49(hooksDir, "pre-commit");
15056
- if (existsSync59(target)) {
15057
- const existing = readFileSync41(target, "utf-8");
15673
+ const hooksDir = join50(gitDir, "hooks");
15674
+ mkdirSync22(hooksDir, { recursive: true });
15675
+ const target = join50(hooksDir, "pre-commit");
15676
+ if (existsSync60(target)) {
15677
+ const existing = readFileSync42(target, "utf-8");
15058
15678
  if (existing.includes(MANAGED_MARKER)) {
15059
15679
  info("pre-commit hook already managed by dreamcontext \u2014 refreshing.");
15060
15680
  } else if (!opts.force) {
@@ -15066,7 +15686,7 @@ function registerMarketingHooks(parent) {
15066
15686
  warn("Overwriting existing pre-commit hook (--force).");
15067
15687
  }
15068
15688
  }
15069
- writeFileSync26(target, HOOK_LAUNCHER, "utf-8");
15689
+ writeFileSync27(target, HOOK_LAUNCHER, "utf-8");
15070
15690
  try {
15071
15691
  chmodSync(target, 493);
15072
15692
  } catch {
@@ -15104,27 +15724,27 @@ exec dreamcontext mk hooks check-staged
15104
15724
  });
15105
15725
 
15106
15726
  // src/lib/marketing/council-personas.ts
15107
- import { existsSync as existsSync60, readFileSync as readFileSync42, readdirSync as readdirSync16 } from "fs";
15727
+ import { existsSync as existsSync61, readFileSync as readFileSync43, readdirSync as readdirSync17 } from "fs";
15108
15728
  import { fileURLToPath as fileURLToPath9 } from "url";
15109
- import { join as join50, dirname as dirname25 } from "path";
15729
+ import { join as join51, dirname as dirname25 } from "path";
15110
15730
  import matter4 from "gray-matter";
15111
15731
  function findPersonasDir() {
15112
15732
  const candidates = [
15113
15733
  // dev: src/lib/marketing/council-personas.ts → ../../../skill-packs/...
15114
- join50(__dirname_local, "..", "..", "..", PERSONA_SUBPATH),
15734
+ join51(__dirname_local, "..", "..", "..", PERSONA_SUBPATH),
15115
15735
  // built: dist/index.js → ./skill-packs/...
15116
- join50(__dirname_local, PERSONA_SUBPATH),
15736
+ join51(__dirname_local, PERSONA_SUBPATH),
15117
15737
  // tsup-bundled (deeper nesting if ever): ../skill-packs/...
15118
- join50(__dirname_local, "..", PERSONA_SUBPATH),
15119
- join50(__dirname_local, "..", "..", PERSONA_SUBPATH)
15738
+ join51(__dirname_local, "..", PERSONA_SUBPATH),
15739
+ join51(__dirname_local, "..", "..", PERSONA_SUBPATH)
15120
15740
  ];
15121
15741
  for (const candidate of candidates) {
15122
- if (existsSync60(candidate)) return candidate;
15742
+ if (existsSync61(candidate)) return candidate;
15123
15743
  }
15124
15744
  return null;
15125
15745
  }
15126
15746
  function parsePersonaFile(filePath) {
15127
- const raw = readFileSync42(filePath, "utf-8");
15747
+ const raw = readFileSync43(filePath, "utf-8");
15128
15748
  const parsed = matter4(raw);
15129
15749
  const data = parsed.data;
15130
15750
  const slug = typeof data.slug === "string" ? data.slug.trim() : "";
@@ -15144,9 +15764,9 @@ function parsePersonaFile(filePath) {
15144
15764
  }
15145
15765
  function loadAllPersonas(personasDir) {
15146
15766
  const dir = personasDir ?? findPersonasDir();
15147
- if (!dir || !existsSync60(dir)) return [];
15148
- const files = readdirSync16(dir).filter((n) => n.endsWith(".md")).sort();
15149
- return files.map((name) => parsePersonaFile(join50(dir, name)));
15767
+ if (!dir || !existsSync61(dir)) return [];
15768
+ const files = readdirSync17(dir).filter((n) => n.endsWith(".md")).sort();
15769
+ return files.map((name) => parsePersonaFile(join51(dir, name)));
15150
15770
  }
15151
15771
  function selectPersonas(all, requested) {
15152
15772
  if (requested.length === 0) return [...all];
@@ -15170,18 +15790,18 @@ var init_council_personas = __esm({
15170
15790
  }
15171
15791
  })();
15172
15792
  VALID_MODELS2 = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku"]);
15173
- PERSONA_SUBPATH = join50("skill-packs", "meta-marketing", "council-personas");
15793
+ PERSONA_SUBPATH = join51("skill-packs", "meta-marketing", "council-personas");
15174
15794
  }
15175
15795
  });
15176
15796
 
15177
15797
  // src/cli/commands/marketing/council.ts
15178
15798
  import { spawnSync as spawnSync3 } from "child_process";
15179
- import { existsSync as existsSync61 } from "fs";
15799
+ import { existsSync as existsSync62 } from "fs";
15180
15800
  import chalk35 from "chalk";
15181
15801
  function resolveDreamcontextEntry() {
15182
15802
  const node = process.argv[0];
15183
15803
  const script = process.argv[1];
15184
- if (script && existsSync61(script)) {
15804
+ if (script && existsSync62(script)) {
15185
15805
  return { node, script };
15186
15806
  }
15187
15807
  return { node, script: null };
@@ -15352,8 +15972,8 @@ var init_marketing = __esm({
15352
15972
  });
15353
15973
 
15354
15974
  // src/cli/commands/memory.ts
15355
- import { existsSync as existsSync62, unlinkSync as unlinkSync3 } from "fs";
15356
- import { join as join51 } from "path";
15975
+ import { existsSync as existsSync63, unlinkSync as unlinkSync3 } from "fs";
15976
+ import { join as join52 } from "path";
15357
15977
  import chalk36 from "chalk";
15358
15978
  import { confirm as confirm5 } from "@inquirer/prompts";
15359
15979
  function parseTypes(value) {
@@ -15433,7 +16053,7 @@ function registerMemoryCommand(program) {
15433
16053
  }
15434
16054
  const summary = opts.summary ?? (text.length > 200 ? text.slice(0, 197) + "..." : text);
15435
16055
  const references = opts.references ? opts.references.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
15436
- const changelogPath = join51(root, "core", "CHANGELOG.json");
16056
+ const changelogPath = join52(root, "core", "CHANGELOG.json");
15437
16057
  const entry = {
15438
16058
  date: today(),
15439
16059
  type: opts.type ?? "note",
@@ -15450,8 +16070,8 @@ function registerMemoryCommand(program) {
15450
16070
  memory.command("update").argument("<slug>", "Knowledge file slug (without .md)").option("-d, --description <desc>", "New description (replaces existing)").option("-t, --tags <tags>", "New tags (comma-separated, replaces existing)").option("-c, --content <content>", "New body content (replaces existing)").option("--append <text>", "Append text to body (preserves existing content)").option("--pin", "Set pinned: true").option("--unpin", "Set pinned: false").description("Update a knowledge file (frontmatter and/or body)").action(
15451
16071
  async (slug, opts) => {
15452
16072
  const root = ensureContextRoot();
15453
- const filePath = join51(root, "knowledge", `${slug}.md`);
15454
- if (!existsSync62(filePath)) {
16073
+ const filePath = join52(root, "knowledge", `${slug}.md`);
16074
+ if (!existsSync63(filePath)) {
15455
16075
  error(`Knowledge file not found: ${slug}.md`);
15456
16076
  return;
15457
16077
  }
@@ -15498,8 +16118,8 @@ ${opts.append}
15498
16118
  );
15499
16119
  memory.command("delete").argument("<slug>", "Knowledge file slug to delete (without .md)").option("-f, --force", "Skip confirmation prompt").description("Delete a knowledge file (irreversible; use git to recover)").action(async (slug, opts) => {
15500
16120
  const root = ensureContextRoot();
15501
- const filePath = join51(root, "knowledge", `${slug}.md`);
15502
- if (!existsSync62(filePath)) {
16121
+ const filePath = join52(root, "knowledge", `${slug}.md`);
16122
+ if (!existsSync63(filePath)) {
15503
16123
  error(`Knowledge file not found: ${slug}.md`);
15504
16124
  return;
15505
16125
  }
@@ -15608,18 +16228,43 @@ import chalk37 from "chalk";
15608
16228
  function defaultInstaller(args) {
15609
16229
  execFileSync7("npm", args, { stdio: "inherit" });
15610
16230
  }
15611
- function defaultLatestVersion() {
16231
+ function projectRootForCache() {
15612
16232
  const contextDir = resolveContextRoot();
15613
- const projectRoot = contextDir ? dirname26(contextDir) : process.cwd();
15614
- const cache = readVersionCache(projectRoot);
15615
- return cache?.latestCli ?? null;
16233
+ return contextDir ? dirname26(contextDir) : process.cwd();
16234
+ }
16235
+ function defaultLiveLatest() {
16236
+ let latest = null;
16237
+ try {
16238
+ const raw = execFileSync7("npm", ["view", "dreamcontext", "version"], {
16239
+ timeout: 5e3,
16240
+ encoding: "utf-8"
16241
+ });
16242
+ const trimmed = (typeof raw === "string" ? raw : "").trim();
16243
+ if (/^\d+\.\d+/.test(trimmed)) latest = trimmed;
16244
+ } catch {
16245
+ return null;
16246
+ }
16247
+ if (latest !== null) {
16248
+ try {
16249
+ const root = projectRootForCache();
16250
+ const existing = readVersionCache(root);
16251
+ writeVersionCache(root, {
16252
+ checkedAt: Date.now(),
16253
+ latestCli: latest,
16254
+ availablePacks: existing?.availablePacks ?? [],
16255
+ ttlHours: existing?.ttlHours ?? 24
16256
+ });
16257
+ } catch {
16258
+ }
16259
+ }
16260
+ return latest;
15616
16261
  }
15617
16262
  function runUpgrade(check, opts) {
15618
16263
  const installer = opts?.installer ?? defaultInstaller;
15619
- const getLatest = opts?.latestVersion ?? defaultLatestVersion;
15620
16264
  if (check) {
15621
16265
  const current = dreamcontextVersion();
15622
- const latest = getLatest();
16266
+ const source = opts?.liveLatest ?? opts?.latestVersion ?? defaultLiveLatest;
16267
+ const latest = source();
15623
16268
  if (latest === null) {
15624
16269
  console.log(`dreamcontext ${current} (latest unknown \u2014 run without --check to refresh)`);
15625
16270
  } else {