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/agents/dreamcontext-explore.md +1 -1
- package/dist/agents/dreamcontext-explore.md +1 -1
- package/dist/index.js +1684 -1039
- package/package.json +1 -1
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
|
|
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() :
|
|
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` :
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
2309
|
-
name: String(data.name ??
|
|
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:
|
|
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
|
|
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) =>
|
|
2900
|
+
const exactGlob = files.find((f) => basename5(f, ".md") === slug);
|
|
2874
2901
|
if (exactGlob) return exactGlob;
|
|
2875
|
-
const prefixMatches = files.filter((f) =>
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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 ${
|
|
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
|
|
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:
|
|
3031
|
-
name: String(data.name ??
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
3622
|
+
const exactGlob = files.find((f) => basename7(f, ".md") === slug);
|
|
3594
3623
|
if (exactGlob) return exactGlob;
|
|
3595
|
-
const prefixMatches = files.filter((f) =>
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
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 ${
|
|
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: ${
|
|
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 ${
|
|
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 ${
|
|
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: ${
|
|
3992
|
+
console.log(header(name ? `Workflow check: ${basename7(files[0], ".md")}` : "Workflow check"));
|
|
3964
3993
|
for (const file of files) {
|
|
3965
|
-
const slug =
|
|
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
|
|
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
|
|
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: [],
|
|
4076
|
-
|
|
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(
|
|
4079
|
-
console.log(chalk9.dim("
|
|
4080
|
-
|
|
4081
|
-
|
|
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 (${
|
|
4085
|
-
for (const p of
|
|
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: [],
|
|
4135
|
+
return { removed: [], keep: candidates };
|
|
4091
4136
|
}
|
|
4092
4137
|
}
|
|
4093
4138
|
const removed = [];
|
|
4094
|
-
for (const rel of
|
|
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,
|
|
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
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
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/
|
|
5681
|
-
import { existsSync as existsSync27,
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
if (
|
|
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
|
-
|
|
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
|
|
5702
|
-
|
|
5703
|
-
const
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
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 (
|
|
5709
|
-
|
|
5710
|
-
const
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
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
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
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
|
-
|
|
5724
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
5788
|
-
const
|
|
5789
|
-
|
|
5790
|
-
|
|
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
|
|
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 =
|
|
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
|
|
5820
|
-
const
|
|
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
|
|
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 :
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5909
|
-
const
|
|
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 * (
|
|
5917
|
-
|
|
5918
|
-
}
|
|
5919
|
-
|
|
5920
|
-
|
|
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.
|
|
5924
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 (!
|
|
6992
|
+
if (!existsSync31(transcriptPath)) return ZERO_ANALYSIS;
|
|
6172
6993
|
try {
|
|
6173
|
-
const stat =
|
|
6174
|
-
if (stat.size === 0 || stat.size >
|
|
6175
|
-
const content =
|
|
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 (
|
|
6217
|
-
formatter = { type: "biome", configPath:
|
|
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 (
|
|
6224
|
-
formatter = { type: "prettier", configPath:
|
|
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 &&
|
|
6231
|
-
tsconfig =
|
|
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 =
|
|
6242
|
-
return
|
|
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 ${
|
|
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 !== "
|
|
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
|
|
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 =
|
|
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 ${
|
|
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
|
|
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
|
-
|
|
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
|
|
6739
|
-
import { join as
|
|
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 =
|
|
6743
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
6772
|
-
if (!
|
|
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 =
|
|
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 =
|
|
6785
|
-
if (!
|
|
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 =
|
|
7654
|
+
const dirAbs = join29(root, dirRel);
|
|
6794
7655
|
const legacyRel = "core/5.data_structures.sql";
|
|
6795
|
-
const legacyAbs =
|
|
6796
|
-
const legacyExists =
|
|
6797
|
-
if (!
|
|
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 =
|
|
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
|
-
...
|
|
6873
|
-
...
|
|
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 =
|
|
6876
|
-
if (
|
|
7736
|
+
const sleepPath = join29(root, "state", ".sleep.json");
|
|
7737
|
+
if (existsSync32(sleepPath)) {
|
|
6877
7738
|
try {
|
|
6878
|
-
const parsed = JSON.parse(
|
|
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
|
|
6916
|
-
import { dirname as dirname13, join as
|
|
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
|
|
7780
|
+
return join30(home, ".dreamcontext", "vaults.json");
|
|
6920
7781
|
}
|
|
6921
7782
|
function listVaults(home) {
|
|
6922
7783
|
const filePath = vaultsFilePath(home);
|
|
6923
|
-
if (!
|
|
7784
|
+
if (!existsSync33(filePath)) return [];
|
|
6924
7785
|
try {
|
|
6925
|
-
const raw =
|
|
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
|
-
|
|
6940
|
-
|
|
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 (!
|
|
7805
|
+
if (!existsSync33(resolved)) {
|
|
6945
7806
|
throw new VaultError(`Path does not exist: ${resolved}`);
|
|
6946
7807
|
}
|
|
6947
|
-
if (!
|
|
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 (!
|
|
7830
|
+
if (!existsSync33(resolved)) {
|
|
6970
7831
|
throw new VaultError(`Vault path does not exist: ${resolved}`);
|
|
6971
7832
|
}
|
|
6972
|
-
if (!
|
|
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
|
|
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
|
|
7127
|
-
import { join as
|
|
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 =
|
|
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 (
|
|
7137
|
-
filePath =
|
|
7997
|
+
if (existsSync34(filePath) && statSync7(filePath).isDirectory()) {
|
|
7998
|
+
filePath = join31(filePath, "index.html");
|
|
7138
7999
|
}
|
|
7139
|
-
if (
|
|
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 =
|
|
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 =
|
|
7152
|
-
if (
|
|
7153
|
-
const content =
|
|
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
|
|
7293
|
-
import { join as
|
|
7294
|
-
import
|
|
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 =
|
|
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
|
|
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 (!
|
|
7348
|
-
return
|
|
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 =
|
|
7394
|
-
if (
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
7758
|
-
import { join as
|
|
8618
|
+
import { existsSync as existsSync36, readdirSync as readdirSync10 } from "fs";
|
|
8619
|
+
import { join as join33 } from "path";
|
|
7759
8620
|
function getCoreDir(contextRoot) {
|
|
7760
|
-
return
|
|
8621
|
+
return join33(contextRoot, "core");
|
|
7761
8622
|
}
|
|
7762
8623
|
async function handleCoreList(_req, res, _params, contextRoot) {
|
|
7763
8624
|
const coreDir = getCoreDir(contextRoot);
|
|
7764
|
-
if (!
|
|
8625
|
+
if (!existsSync36(coreDir)) {
|
|
7765
8626
|
sendJson(res, 200, { files: [] });
|
|
7766
8627
|
return;
|
|
7767
8628
|
}
|
|
7768
|
-
const entries =
|
|
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 =
|
|
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 (!
|
|
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:
|
|
7800
|
-
const raw2 =
|
|
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:
|
|
7835
|
-
const raw =
|
|
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 (!
|
|
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:
|
|
7886
|
-
|
|
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
|
|
7910
|
-
import { join as
|
|
8770
|
+
import { existsSync as existsSync37 } from "fs";
|
|
8771
|
+
import { join as join34 } from "path";
|
|
7911
8772
|
function getKnowledgeDir2(contextRoot) {
|
|
7912
|
-
return
|
|
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 (!
|
|
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 (!
|
|
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
|
|
8007
|
-
import { join as
|
|
8008
|
-
import
|
|
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
|
|
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 (!
|
|
8875
|
+
if (!existsSync38(featuresDir)) {
|
|
8015
8876
|
sendJson(res, 200, { features: [] });
|
|
8016
8877
|
return;
|
|
8017
8878
|
}
|
|
8018
|
-
const files =
|
|
8879
|
+
const files = fg11.sync("*.md", { cwd: featuresDir, absolute: true });
|
|
8019
8880
|
const features = files.map((file) => {
|
|
8020
|
-
const slug =
|
|
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 (!
|
|
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
|
|
8081
|
-
import { join as
|
|
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 =
|
|
8084
|
-
if (!
|
|
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 =
|
|
8097
|
-
if (!
|
|
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 =
|
|
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 =
|
|
8171
|
-
if (!
|
|
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
|
|
8226
|
-
import { join as
|
|
8227
|
-
import
|
|
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 =
|
|
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 (!
|
|
8249
|
-
const matches =
|
|
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 =
|
|
9114
|
+
const filename = basename15(file);
|
|
8254
9115
|
addNode({
|
|
8255
9116
|
id: `core/${filename}`,
|
|
8256
|
-
label:
|
|
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 (
|
|
8269
|
-
const others =
|
|
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 =
|
|
9132
|
+
const filename = basename15(file);
|
|
8272
9133
|
try {
|
|
8273
9134
|
const { data } = readFrontmatter(file);
|
|
8274
9135
|
addNode({
|
|
8275
9136
|
id: `core/${filename}`,
|
|
8276
|
-
label:
|
|
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 =
|
|
8289
|
-
if (
|
|
8290
|
-
const files =
|
|
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 =
|
|
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 =
|
|
8320
|
-
if (
|
|
8321
|
-
const files =
|
|
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 =
|
|
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 =
|
|
8372
|
-
if (
|
|
8373
|
-
const files =
|
|
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 =
|
|
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 =
|
|
9254
|
+
const releasesPath = join37(coreDir, "RELEASES.json");
|
|
8394
9255
|
let releases = [];
|
|
8395
|
-
if (
|
|
9256
|
+
if (existsSync40(releasesPath)) {
|
|
8396
9257
|
try {
|
|
8397
|
-
const parsed = JSON.parse(
|
|
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
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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
|
|
8567
|
-
import { join as
|
|
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
|
|
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 =
|
|
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 =
|
|
9460
|
+
const target = join38(getDebateDir(debateId), personaSlug);
|
|
8600
9461
|
return assertWithinCouncil(target);
|
|
8601
9462
|
}
|
|
8602
9463
|
function getCouncilIndexPath() {
|
|
8603
|
-
return
|
|
9464
|
+
return join38(getCouncilDir(), "index.json");
|
|
8604
9465
|
}
|
|
8605
9466
|
function ensureCouncilDir() {
|
|
8606
9467
|
const dir = getCouncilDir();
|
|
8607
|
-
|
|
9468
|
+
mkdirSync12(dir, { recursive: true });
|
|
8608
9469
|
const indexPath2 = getCouncilIndexPath();
|
|
8609
|
-
if (!
|
|
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 (!
|
|
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 (!
|
|
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(
|
|
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 (!
|
|
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
|
-
|
|
9515
|
+
join38(__dirname6, "templates", filename),
|
|
8655
9516
|
// bundled: dist/templates/
|
|
8656
|
-
|
|
9517
|
+
join38(__dirname6, "..", "templates", filename),
|
|
8657
9518
|
// alt layout
|
|
8658
|
-
|
|
9519
|
+
join38(__dirname6, "..", "src", "templates", filename),
|
|
8659
9520
|
// dev fallback
|
|
8660
|
-
|
|
9521
|
+
join38(__dirname6, "..", "..", "src", "templates", filename)
|
|
8661
9522
|
];
|
|
8662
9523
|
for (const path of candidates) {
|
|
8663
|
-
if (
|
|
8664
|
-
return
|
|
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 =
|
|
8739
|
-
if (!
|
|
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
|
|
8774
|
-
import { join as
|
|
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
|
|
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 =
|
|
8830
|
-
if (!
|
|
8831
|
-
const personaFile =
|
|
8832
|
-
const reportFile =
|
|
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 (
|
|
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 (
|
|
9710
|
+
if (existsSync43(reportFile)) {
|
|
8850
9711
|
const { content } = readFrontmatter(reportFile);
|
|
8851
9712
|
rounds = parseReportRounds(content).map(toParsedRound);
|
|
8852
9713
|
}
|
|
8853
|
-
const researchIndex =
|
|
9714
|
+
const researchIndex = join39(personaDir, "researches", "index.json");
|
|
8854
9715
|
let researches = [];
|
|
8855
|
-
if (
|
|
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 =
|
|
8866
|
-
if (!
|
|
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 =
|
|
9734
|
+
const debateFile = join39(getCouncilDir2(contextRoot), d.id, "debate.md");
|
|
8874
9735
|
let personaSlugs = [];
|
|
8875
|
-
if (
|
|
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 =
|
|
9759
|
+
const debateDirUnchecked = join39(councilDir, debateId);
|
|
8899
9760
|
const debateDir = assertWithin(councilDir, debateDirUnchecked);
|
|
8900
|
-
if (!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 =
|
|
8905
|
-
if (!
|
|
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 =
|
|
8923
|
-
const roundLog =
|
|
8924
|
-
const finalReportFile =
|
|
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 (
|
|
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
|
-
|
|
9815
|
+
join39(councilDir, debateId, personaSlug, "researches", `${researchSlug}.md`)
|
|
8955
9816
|
);
|
|
8956
|
-
if (!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
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
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
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
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
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
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
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
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
|
-
|
|
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
|
|
9656
|
-
|
|
9657
|
-
|
|
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
|
-
|
|
9661
|
-
|
|
10266
|
+
const remind = remindParts.join(" ").trim();
|
|
10267
|
+
if (!remind) {
|
|
10268
|
+
error("Reminder message is required.");
|
|
9662
10269
|
return;
|
|
9663
10270
|
}
|
|
9664
|
-
|
|
9665
|
-
|
|
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
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
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
|
-
|
|
9682
|
-
|
|
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
|
|
9686
|
-
|
|
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
|
|
9709
|
-
import { join as
|
|
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
|
-
|
|
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
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
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 =
|
|
9800
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
10433
|
+
writeFileSync20(personaFile, personaContent, "utf-8");
|
|
9814
10434
|
const reportTemplate = loadTemplate("council-report.md");
|
|
9815
10435
|
const reportContent = reportTemplate.replaceAll("{{SLUG}}", slug);
|
|
9816
|
-
|
|
9817
|
-
const debateFile =
|
|
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 =
|
|
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 =
|
|
9856
|
-
if (!
|
|
9857
|
-
const existing =
|
|
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
|
-
|
|
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 =
|
|
9888
|
-
const logExisting =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
9933
|
-
const logExisting =
|
|
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
|
-
|
|
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 =
|
|
9962
|
-
const personaContent =
|
|
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 =
|
|
9972
|
-
const debateRaw =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
10081
|
-
|
|
10082
|
-
const indexPath2 =
|
|
10083
|
-
if (!
|
|
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 =
|
|
10086
|
-
if (
|
|
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
|
-
|
|
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 =
|
|
10117
|
-
const indexPath2 =
|
|
10118
|
-
if (!
|
|
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 =
|
|
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 =
|
|
10163
|
-
if (
|
|
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 =
|
|
10182
|
-
if (!
|
|
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 =
|
|
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 =
|
|
10219
|
-
if (!
|
|
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 =
|
|
10233
|
-
|
|
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 =
|
|
10237
|
-
if (
|
|
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(
|
|
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 =
|
|
10324
|
-
if (
|
|
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(
|
|
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
|
|
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 (!
|
|
10522
|
-
const raw =
|
|
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 (!
|
|
11186
|
+
if (!existsSync45(path)) return { ...EMPTY_CONFIG, feature_flags: { ...EMPTY_CONFIG.feature_flags } };
|
|
10567
11187
|
try {
|
|
10568
|
-
const raw =
|
|
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
|
-
|
|
10584
|
-
|
|
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 (
|
|
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 (
|
|
10596
|
-
|
|
10597
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
10756
|
-
TRANSFORMERS_CACHE:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
10841
|
-
import { dirname as dirname20, join as
|
|
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 =
|
|
11500
|
+
const buf = readFileSync32(framePath);
|
|
10881
11501
|
return buf.toString("base64");
|
|
10882
11502
|
}
|
|
10883
11503
|
async function labelHookFrame(framePath, opts) {
|
|
10884
|
-
if (!
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
10977
|
-
if (
|
|
11596
|
+
const mdPath = join42(dirname20(postJsonPath), `${shortcode}.md`);
|
|
11597
|
+
if (existsSync47(mdPath)) {
|
|
10978
11598
|
try {
|
|
10979
|
-
const md =
|
|
11599
|
+
const md = readFileSync32(mdPath, "utf-8");
|
|
10980
11600
|
const refreshed = updatePatternTagsInBridge(md, post.pattern_tags);
|
|
10981
|
-
|
|
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
|
|
11057
|
-
import { dirname as dirname21, join as
|
|
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 =
|
|
11135
|
-
|
|
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 =
|
|
11236
|
-
const postsDir =
|
|
11237
|
-
const mediaDir =
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
const finalVideoPath = evt.video_path ? moveAsset(evt.video_path,
|
|
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,
|
|
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 =
|
|
11264
|
-
const bridgePath =
|
|
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 =
|
|
11267
|
-
if (!
|
|
11886
|
+
const metaPath = join43(handleDir, "meta.json");
|
|
11887
|
+
if (!existsSync48(metaPath)) {
|
|
11268
11888
|
writeJsonWithBridge(
|
|
11269
11889
|
metaPath,
|
|
11270
|
-
|
|
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 (!
|
|
11288
|
-
|
|
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
|
|
11350
|
-
import { join as
|
|
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 (!
|
|
12021
|
+
if (!existsSync49(dir)) {
|
|
11402
12022
|
info("No competitors ingested yet.");
|
|
11403
12023
|
return;
|
|
11404
12024
|
}
|
|
11405
|
-
const handles =
|
|
12025
|
+
const handles = readdirSync11(dir).filter((name) => {
|
|
11406
12026
|
if (name.startsWith("_") || name.startsWith(".")) return false;
|
|
11407
|
-
const full =
|
|
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 =
|
|
11421
|
-
const postCount =
|
|
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] :
|
|
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 =
|
|
11438
|
-
if (
|
|
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 (!
|
|
12093
|
+
if (!existsSync49(competitorsDir)) {
|
|
11474
12094
|
warn("No competitors directory yet \u2014 nothing to relabel.");
|
|
11475
12095
|
return;
|
|
11476
12096
|
}
|
|
11477
|
-
const handles =
|
|
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 =
|
|
11482
|
-
if (!
|
|
11483
|
-
for (const f of
|
|
11484
|
-
const path =
|
|
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
|
|
11531
|
-
import { dirname as dirname22, basename as
|
|
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 (!
|
|
12201
|
+
if (!existsSync50(p)) return null;
|
|
11582
12202
|
try {
|
|
11583
|
-
return JSON.parse(
|
|
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
|
-
|
|
11591
|
-
|
|
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,
|
|
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] ${
|
|
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 =
|
|
12479
|
+
const buffer = readFileSync33(filepath);
|
|
11860
12480
|
const blob = new Blob([new Uint8Array(buffer)], { type: "video/mp4" });
|
|
11861
|
-
form.set("source", blob,
|
|
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 =
|
|
12528
|
+
const buffer = readFileSync33(filepath);
|
|
11909
12529
|
const blob = new Blob([new Uint8Array(buffer)], { type: "image/png" });
|
|
11910
|
-
form.set("filename", blob,
|
|
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
|
|
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 (!
|
|
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(
|
|
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
|
|
12588
|
-
import { join as
|
|
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
|
|
13219
|
+
return join45(MARKETING_PATHS.insightsDir(), "_index.json");
|
|
12600
13220
|
}
|
|
12601
13221
|
function readIndex() {
|
|
12602
13222
|
const p = indexPath();
|
|
12603
|
-
if (!
|
|
13223
|
+
if (!existsSync52(p)) return { latest: {}, updated_at: "1970-01-01T00:00:00Z" };
|
|
12604
13224
|
try {
|
|
12605
|
-
return JSON.parse(
|
|
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
|
-
|
|
13232
|
+
mkdirSync19(MARKETING_PATHS.insightsDir(), { recursive: true });
|
|
12613
13233
|
const tmp = `${p}.tmp.${process.pid}.${randomBytes3(4).toString("hex")}`;
|
|
12614
|
-
|
|
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
|
-
|
|
13238
|
+
mkdirSync19(MARKETING_PATHS.insightsDir(), { recursive: true });
|
|
12619
13239
|
const filename = snapshotFilename(snap.entity_id, new Date(snap.pulled_at));
|
|
12620
|
-
const fullPath =
|
|
13240
|
+
const fullPath = join45(MARKETING_PATHS.insightsDir(), filename);
|
|
12621
13241
|
const tmp = `${fullPath}.tmp.${process.pid}.${randomBytes3(4).toString("hex")}`;
|
|
12622
|
-
|
|
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 =
|
|
12635
|
-
if (!
|
|
13254
|
+
const fullPath = join45(MARKETING_PATHS.insightsDir(), filename);
|
|
13255
|
+
if (!existsSync52(fullPath)) return null;
|
|
12636
13256
|
try {
|
|
12637
|
-
const snap = JSON.parse(
|
|
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 =
|
|
12650
|
-
if (!
|
|
13269
|
+
const fullPath = join45(MARKETING_PATHS.insightsDir(), filename);
|
|
13270
|
+
if (!existsSync52(fullPath)) return null;
|
|
12651
13271
|
try {
|
|
12652
|
-
return JSON.parse(
|
|
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 (!
|
|
13279
|
+
if (!existsSync52(dir)) return null;
|
|
12660
13280
|
const safe = entityId.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
12661
|
-
const candidates =
|
|
12662
|
-
const p =
|
|
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(
|
|
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
|
|
13307
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
13454
|
-
import { join as
|
|
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:
|
|
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 (!
|
|
14098
|
+
if (!existsSync54(json)) return null;
|
|
13479
14099
|
try {
|
|
13480
|
-
return JSON.parse(
|
|
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 (!
|
|
14107
|
+
if (!existsSync54(d)) return [];
|
|
13488
14108
|
const out = [];
|
|
13489
|
-
for (const f of
|
|
14109
|
+
for (const f of readdirSync14(d).filter((x) => x.endsWith(".json"))) {
|
|
13490
14110
|
try {
|
|
13491
|
-
out.push(JSON.parse(
|
|
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
|
|
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 (!
|
|
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(
|
|
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
|
|
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 (!
|
|
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
|
|
14141
|
-
import { dirname as dirname23, join as
|
|
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
|
-
|
|
14805
|
+
mkdirSync20(dirname23(path), { recursive: true });
|
|
14186
14806
|
const tmp = `${path}.tmp.${process.pid}.${randomBytes4(4).toString("hex")}`;
|
|
14187
|
-
|
|
14807
|
+
writeFileSync25(tmp, data, "utf8");
|
|
14188
14808
|
renameSync3(tmp, path);
|
|
14189
14809
|
}
|
|
14190
14810
|
function readWal(path) {
|
|
14191
|
-
if (!
|
|
14811
|
+
if (!existsSync57(path)) return null;
|
|
14192
14812
|
try {
|
|
14193
|
-
return JSON.parse(
|
|
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 (!
|
|
14204
|
-
for (const f of
|
|
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 ||
|
|
14209
|
-
return
|
|
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 =
|
|
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
|
|
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 (!
|
|
14625
|
-
return
|
|
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
|
|
14655
|
-
mkdirSync as
|
|
14656
|
-
readFileSync as
|
|
14657
|
-
writeFileSync as
|
|
14658
|
-
readdirSync as
|
|
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
|
|
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
|
-
|
|
15286
|
+
mkdirSync21(dirname24(path), { recursive: true });
|
|
14667
15287
|
const tmp = `${path}.tmp.${process.pid}.${randomBytes5(4).toString("hex")}`;
|
|
14668
|
-
|
|
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 (!
|
|
14675
|
-
const files =
|
|
14676
|
-
const p =
|
|
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 (!
|
|
15330
|
+
if (!existsSync59(dir)) return { scanned: 0, kept: 0, deleted: 0, deletedFiles: [] };
|
|
14711
15331
|
const files = [];
|
|
14712
|
-
for (const f of
|
|
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 =
|
|
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 (!
|
|
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
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
14863
|
-
const files =
|
|
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 =
|
|
15486
|
+
const p = join49(dir, f);
|
|
14867
15487
|
let raw;
|
|
14868
15488
|
try {
|
|
14869
|
-
raw =
|
|
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 || !
|
|
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
|
|
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
|
|
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
|
|
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 :
|
|
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 =
|
|
15054
|
-
|
|
15055
|
-
const target =
|
|
15056
|
-
if (
|
|
15057
|
-
const existing =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
15734
|
+
join51(__dirname_local, "..", "..", "..", PERSONA_SUBPATH),
|
|
15115
15735
|
// built: dist/index.js → ./skill-packs/...
|
|
15116
|
-
|
|
15736
|
+
join51(__dirname_local, PERSONA_SUBPATH),
|
|
15117
15737
|
// tsup-bundled (deeper nesting if ever): ../skill-packs/...
|
|
15118
|
-
|
|
15119
|
-
|
|
15738
|
+
join51(__dirname_local, "..", PERSONA_SUBPATH),
|
|
15739
|
+
join51(__dirname_local, "..", "..", PERSONA_SUBPATH)
|
|
15120
15740
|
];
|
|
15121
15741
|
for (const candidate of candidates) {
|
|
15122
|
-
if (
|
|
15742
|
+
if (existsSync61(candidate)) return candidate;
|
|
15123
15743
|
}
|
|
15124
15744
|
return null;
|
|
15125
15745
|
}
|
|
15126
15746
|
function parsePersonaFile(filePath) {
|
|
15127
|
-
const raw =
|
|
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 || !
|
|
15148
|
-
const files =
|
|
15149
|
-
return files.map((name) => parsePersonaFile(
|
|
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 =
|
|
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
|
|
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 &&
|
|
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
|
|
15356
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
15454
|
-
if (!
|
|
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 =
|
|
15502
|
-
if (!
|
|
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
|
|
16231
|
+
function projectRootForCache() {
|
|
15612
16232
|
const contextDir = resolveContextRoot();
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
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
|
|
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 {
|