dreamcontext 0.8.7 → 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22775,6 +22775,19 @@ async function installCoreForPlatform(platform2, projectRoot, manifest) {
22775
22775
  const coreSkillRel = `${skillRootRel}/dreamcontext/SKILL.md`;
22776
22776
  recordIfManifest2(manifest, coreSkillRel, "core");
22777
22777
  installed.push(platformPrefixed(platform2, coreSkillRel));
22778
+ const refsSourceDir = join9(dirname6(skillSource), "references");
22779
+ if (existsSync9(refsSourceDir)) {
22780
+ const refsDestDir = join9(skillDestDir, "references");
22781
+ cpSync2(refsSourceDir, refsDestDir, { recursive: true });
22782
+ const refFiles = readdirSync4(refsSourceDir).filter((f) => f.endsWith(".md"));
22783
+ for (const rf of refFiles) {
22784
+ const refRel = `${skillRootRel}/dreamcontext/references/${rf}`;
22785
+ recordIfManifest2(manifest, refRel, "core");
22786
+ }
22787
+ if (refFiles.length > 0) {
22788
+ installed.push(platformPrefixed(platform2, `${skillRootRel}/dreamcontext/references/ ${source_default.dim(`(${refFiles.length} reference${refFiles.length === 1 ? "" : "s"})`)}`));
22789
+ }
22790
+ }
22778
22791
  const agentsSourceDir = findPackageDir("agents");
22779
22792
  if (agentsSourceDir) {
22780
22793
  const agentFiles = readdirSync4(agentsSourceDir).filter((f) => f.endsWith(".md"));
@@ -29289,6 +29302,548 @@ var init_tech_stack = __esm({
29289
29302
  }
29290
29303
  });
29291
29304
 
29305
+ // src/lib/version-check.ts
29306
+ import { existsSync as existsSync13, readFileSync as readFileSync14, mkdirSync as mkdirSync8, writeFileSync as writeFileSync12 } from "fs";
29307
+ import { dirname as dirname8, join as join13 } from "path";
29308
+ import { execFileSync, spawn as spawn2 } from "child_process";
29309
+ function cachePath(root) {
29310
+ return join13(root, CACHE_REL_PATH);
29311
+ }
29312
+ function readVersionCache(root) {
29313
+ const path2 = cachePath(root);
29314
+ if (!existsSync13(path2)) return null;
29315
+ try {
29316
+ const raw = readFileSync14(path2, "utf-8");
29317
+ const parsed = JSON.parse(raw);
29318
+ if (!parsed || typeof parsed !== "object") return null;
29319
+ if (typeof parsed.checkedAt !== "number") return null;
29320
+ return {
29321
+ checkedAt: parsed.checkedAt,
29322
+ latestCli: typeof parsed.latestCli === "string" ? parsed.latestCli : null,
29323
+ availablePacks: Array.isArray(parsed.availablePacks) ? parsed.availablePacks.filter((p) => typeof p === "string") : [],
29324
+ ttlHours: typeof parsed.ttlHours === "number" ? parsed.ttlHours : DEFAULT_TTL_HOURS
29325
+ };
29326
+ } catch {
29327
+ return null;
29328
+ }
29329
+ }
29330
+ function writeVersionCache(root, cache) {
29331
+ const path2 = cachePath(root);
29332
+ try {
29333
+ mkdirSync8(dirname8(path2), { recursive: true });
29334
+ writeFileSync12(path2, JSON.stringify(cache, null, 2) + "\n", "utf-8");
29335
+ } catch {
29336
+ }
29337
+ }
29338
+ function isCacheFresh(cache, nowMs) {
29339
+ if (!cache) return false;
29340
+ try {
29341
+ if (typeof cache.checkedAt !== "number" || !isFinite(cache.checkedAt)) return false;
29342
+ const ttlHours = typeof cache.ttlHours === "number" && isFinite(cache.ttlHours) && cache.ttlHours > 0 ? cache.ttlHours : DEFAULT_TTL_HOURS;
29343
+ const ttlMs = ttlHours * 60 * 60 * 1e3;
29344
+ const now = nowMs ?? Date.now();
29345
+ return now - cache.checkedAt < ttlMs;
29346
+ } catch {
29347
+ return false;
29348
+ }
29349
+ }
29350
+ function compareVersions(a, b) {
29351
+ const clean = (v) => v.replace(/[-+].*$/, "").trim();
29352
+ const parseSegments = (v) => clean(v).split(".").map((s) => {
29353
+ const n = parseInt(s, 10);
29354
+ return isFinite(n) ? n : 0;
29355
+ });
29356
+ const segA = parseSegments(a);
29357
+ const segB = parseSegments(b);
29358
+ const len = Math.max(segA.length, segB.length);
29359
+ for (let i = 0; i < len; i++) {
29360
+ const sa = segA[i] ?? 0;
29361
+ const sb = segB[i] ?? 0;
29362
+ if (sa < sb) return -1;
29363
+ if (sa > sb) return 1;
29364
+ }
29365
+ return 0;
29366
+ }
29367
+ function buildNudge(installedCli, cache, installedPacks, catalogPackNames, opts = {}) {
29368
+ if (!cache || cache.latestCli === null) return null;
29369
+ const cliOutdated = compareVersions(installedCli, cache.latestCli) < 0;
29370
+ const showCliNudge = cliOutdated && !opts.suppressCliNudge;
29371
+ const newPacks = catalogPackNames.filter((p) => !installedPacks.includes(p));
29372
+ const hasNewPacks = newPacks.length > 0;
29373
+ if (!showCliNudge && !hasNewPacks) return null;
29374
+ const lines = ["## Update Available\n"];
29375
+ if (showCliNudge) {
29376
+ lines.push(`- **New version available: v${installedCli} \u2192 v${cache.latestCli}.** A new release can add, update, or remove hooks, prompts, sub-agents, and skills. Run \`dreamcontext upgrade\` to get it, then \`dreamcontext update\` to apply those changes to this project.`);
29377
+ }
29378
+ if (hasNewPacks) {
29379
+ lines.push(`- New skill pack${newPacks.length > 1 ? "s" : ""} available: ${newPacks.join(", ")} \u2014 run \`dreamcontext install-skill --packs\``);
29380
+ }
29381
+ return lines.join("\n");
29382
+ }
29383
+ function refreshVersionCache(root, opts) {
29384
+ const runner = opts?.runner ?? ((args) => execFileSync("npm", args, {
29385
+ timeout: NPM_TIMEOUT_MS,
29386
+ encoding: "utf-8",
29387
+ stdio: ["pipe", "pipe", "pipe"]
29388
+ }));
29389
+ const catalogPackNames = opts?.catalogPackNames ?? [];
29390
+ let latestCli = null;
29391
+ try {
29392
+ const raw = runner(["view", "dreamcontext", "version"]);
29393
+ const trimmed = (typeof raw === "string" ? raw : "").trim();
29394
+ if (/^\d+\.\d+/.test(trimmed)) {
29395
+ latestCli = trimmed;
29396
+ }
29397
+ } catch {
29398
+ latestCli = null;
29399
+ }
29400
+ const cache = {
29401
+ checkedAt: Date.now(),
29402
+ latestCli,
29403
+ availablePacks: catalogPackNames,
29404
+ ttlHours: DEFAULT_TTL_HOURS
29405
+ };
29406
+ writeVersionCache(root, cache);
29407
+ }
29408
+ function markerPath(root) {
29409
+ return join13(root, AUTO_UPGRADE_MARKER_REL_PATH);
29410
+ }
29411
+ function readAutoUpgradeMarker(root) {
29412
+ const path2 = markerPath(root);
29413
+ if (!existsSync13(path2)) return null;
29414
+ try {
29415
+ const parsed = JSON.parse(readFileSync14(path2, "utf-8"));
29416
+ if (!parsed || typeof parsed !== "object") return null;
29417
+ if (typeof parsed.attemptedFor !== "string" || typeof parsed.at !== "number") return null;
29418
+ return { attemptedFor: parsed.attemptedFor, at: parsed.at };
29419
+ } catch {
29420
+ return null;
29421
+ }
29422
+ }
29423
+ function writeAutoUpgradeMarker(root, marker) {
29424
+ const path2 = markerPath(root);
29425
+ try {
29426
+ mkdirSync8(dirname8(path2), { recursive: true });
29427
+ writeFileSync12(path2, JSON.stringify(marker, null, 2) + "\n", "utf-8");
29428
+ } catch {
29429
+ }
29430
+ }
29431
+ function autoUpgradeEnabled(env2 = process.env) {
29432
+ if (env2.DREAMCONTEXT_VERSION_CHECK === "0") return false;
29433
+ return env2.DREAMCONTEXT_AUTO_UPGRADE !== "0";
29434
+ }
29435
+ function shouldAutoUpgrade(installedCli, cache, env2 = process.env) {
29436
+ if (!autoUpgradeEnabled(env2)) return null;
29437
+ if (!cache || cache.latestCli === null) return null;
29438
+ if (compareVersions(installedCli, cache.latestCli) >= 0) return null;
29439
+ return cache.latestCli;
29440
+ }
29441
+ function shouldSuppressCliNudge(target, marker, env2 = process.env, now = Date.now()) {
29442
+ if (!autoUpgradeEnabled(env2)) return false;
29443
+ if (!target || !marker) return false;
29444
+ if (marker.attemptedFor !== target) return false;
29445
+ return now - marker.at < AUTO_UPGRADE_INFLIGHT_MS;
29446
+ }
29447
+ function defaultSpawner(cmd, args) {
29448
+ const child = spawn2(cmd, args, { detached: true, stdio: "ignore" });
29449
+ child.unref();
29450
+ }
29451
+ function maybeAutoUpgrade(root, installedCli, cache, env2 = process.env, deps = {}) {
29452
+ try {
29453
+ const target = shouldAutoUpgrade(installedCli, cache, env2);
29454
+ if (!target) return null;
29455
+ const now = deps.now ?? Date.now();
29456
+ const readMarker = deps.readMarker ?? readAutoUpgradeMarker;
29457
+ const writeMarker = deps.writeMarker ?? writeAutoUpgradeMarker;
29458
+ const marker = readMarker(root);
29459
+ const recentlyAttempted = marker !== null && marker.attemptedFor === target && now - marker.at < AUTO_UPGRADE_RETRY_MS;
29460
+ if (recentlyAttempted) return null;
29461
+ writeMarker(root, { attemptedFor: target, at: now });
29462
+ const spawner = deps.spawner ?? defaultSpawner;
29463
+ spawner("npm", ["install", "-g", "dreamcontext@latest"]);
29464
+ return `\u2B06 Auto-upgrading dreamcontext v${installedCli} \u2192 v${target} in the background (takes effect next session).`;
29465
+ } catch {
29466
+ return null;
29467
+ }
29468
+ }
29469
+ var CACHE_REL_PATH, AUTO_UPGRADE_MARKER_REL_PATH, DEFAULT_TTL_HOURS, NPM_TIMEOUT_MS, AUTO_UPGRADE_RETRY_MS, AUTO_UPGRADE_INFLIGHT_MS;
29470
+ var init_version_check = __esm({
29471
+ "src/lib/version-check.ts"() {
29472
+ "use strict";
29473
+ CACHE_REL_PATH = "_dream_context/state/.version-check.json";
29474
+ AUTO_UPGRADE_MARKER_REL_PATH = "_dream_context/state/.auto-upgrade.json";
29475
+ DEFAULT_TTL_HOURS = 24;
29476
+ NPM_TIMEOUT_MS = 5e3;
29477
+ AUTO_UPGRADE_RETRY_MS = 24 * 60 * 60 * 1e3;
29478
+ AUTO_UPGRADE_INFLIGHT_MS = 60 * 60 * 1e3;
29479
+ }
29480
+ });
29481
+
29482
+ // src/cli/commands/app.ts
29483
+ import { execFileSync as execFileSync2, spawn as spawnProcess } from "child_process";
29484
+ import {
29485
+ existsSync as existsSync14,
29486
+ mkdirSync as mkdirSync9,
29487
+ mkdtempSync,
29488
+ readFileSync as readFileSync15,
29489
+ writeFileSync as writeFileSync13,
29490
+ rmSync as rmSync2,
29491
+ renameSync,
29492
+ readdirSync as readdirSync5
29493
+ } from "fs";
29494
+ import { homedir, tmpdir } from "os";
29495
+ import { join as join14, resolve as resolve3, dirname as dirname9 } from "path";
29496
+ import { createHash } from "crypto";
29497
+ function detectPlatform(platform2 = process.platform, arch2 = process.arch) {
29498
+ const os3 = platform2 === "darwin" ? "darwin" : platform2 === "win32" ? "win32" : platform2 === "linux" ? "linux" : "other";
29499
+ const a = arch2 === "arm64" ? "aarch64" : arch2 === "x64" ? "x86_64" : "other";
29500
+ return { os: os3, arch: a };
29501
+ }
29502
+ function pickAssetForArch(assetNames, arch2) {
29503
+ const appAssets = assetNames.filter((n) => n.endsWith(".app.tar.gz"));
29504
+ const exact = appAssets.find((n) => n.includes(`_${arch2}.`));
29505
+ return exact ?? null;
29506
+ }
29507
+ function defaultInstallDir(home = homedir()) {
29508
+ return join14(home, "Applications");
29509
+ }
29510
+ function appManifestPath(home = homedir()) {
29511
+ return join14(home, ".dreamcontext", "app.json");
29512
+ }
29513
+ function readAppManifest(home = homedir()) {
29514
+ const p = appManifestPath(home);
29515
+ if (!existsSync14(p)) return null;
29516
+ try {
29517
+ const parsed = JSON.parse(readFileSync15(p, "utf-8"));
29518
+ if (!parsed || typeof parsed.version !== "string" || typeof parsed.path !== "string") return null;
29519
+ return {
29520
+ version: parsed.version,
29521
+ path: parsed.path,
29522
+ installedAt: typeof parsed.installedAt === "number" ? parsed.installedAt : 0,
29523
+ source: typeof parsed.source === "string" ? parsed.source : "unknown"
29524
+ };
29525
+ } catch {
29526
+ return null;
29527
+ }
29528
+ }
29529
+ function writeAppManifest(manifest, home = homedir()) {
29530
+ const p = appManifestPath(home);
29531
+ mkdirSync9(dirname9(p), { recursive: true });
29532
+ writeFileSync13(p, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
29533
+ }
29534
+ function run(cmd, args) {
29535
+ execFileSync2(cmd, args, { stdio: ["ignore", "ignore", "pipe"] });
29536
+ }
29537
+ function runCapture(cmd, args) {
29538
+ return execFileSync2(cmd, args, { encoding: "utf-8" }).toString().trim();
29539
+ }
29540
+ function validateAppBundle(appDir) {
29541
+ if (!appDir.endsWith(".app") || !existsSync14(appDir)) {
29542
+ throw new Error(`Not a .app bundle: ${appDir}`);
29543
+ }
29544
+ const macos = join14(appDir, "Contents", "MacOS");
29545
+ const plist = join14(appDir, "Contents", "Info.plist");
29546
+ if (!existsSync14(macos) || !existsSync14(plist)) {
29547
+ throw new Error(`Malformed .app (missing Contents/MacOS or Info.plist): ${appDir}`);
29548
+ }
29549
+ }
29550
+ function readBundleVersion(appDir) {
29551
+ const plist = join14(appDir, "Contents", "Info.plist");
29552
+ try {
29553
+ return runCapture("plutil", ["-extract", "CFBundleShortVersionString", "raw", "-o", "-", plist]) || null;
29554
+ } catch {
29555
+ return null;
29556
+ }
29557
+ }
29558
+ function findAppInDir(dir) {
29559
+ const entries = readdirSync5(dir).filter((e) => e.endsWith(".app"));
29560
+ return entries.length > 0 ? join14(dir, entries[0]) : null;
29561
+ }
29562
+ function materializeAppBundle(source, workDir) {
29563
+ const src = resolve3(source);
29564
+ if (!existsSync14(src)) throw new Error(`Source not found: ${src}`);
29565
+ if (src.endsWith(".app")) {
29566
+ return src;
29567
+ }
29568
+ if (src.endsWith(".tar.gz") || src.endsWith(".tgz")) {
29569
+ run("tar", ["-xzf", src, "-C", workDir]);
29570
+ } else if (src.endsWith(".zip")) {
29571
+ run("ditto", ["-x", "-k", src, workDir]);
29572
+ } else {
29573
+ throw new Error(`Unsupported source (expect .app, .tar.gz, or .zip): ${src}`);
29574
+ }
29575
+ const app = findAppInDir(workDir);
29576
+ if (!app) throw new Error(`No .app found inside archive: ${src}`);
29577
+ return app;
29578
+ }
29579
+ function verifyCodesign(appDir) {
29580
+ try {
29581
+ execFileSync2("codesign", ["--verify", "--deep", appDir], { stdio: ["ignore", "ignore", "ignore"] });
29582
+ return true;
29583
+ } catch {
29584
+ return false;
29585
+ }
29586
+ }
29587
+ function isAppRunning() {
29588
+ try {
29589
+ const out = execFileSync2("pgrep", ["-f", `${APP_BUNDLE_NAME}/Contents/MacOS/`], { encoding: "utf-8" }).toString().trim();
29590
+ return out.length > 0;
29591
+ } catch {
29592
+ return false;
29593
+ }
29594
+ }
29595
+ function installAppBundle(source, opts = {}) {
29596
+ if (detectPlatform().os !== "darwin") {
29597
+ throw new Error("Desktop app install is currently macOS-only.");
29598
+ }
29599
+ const home = opts.home ?? homedir();
29600
+ const installDir = opts.installDir ?? defaultInstallDir(home);
29601
+ mkdirSync9(installDir, { recursive: true });
29602
+ const workDir = mkdtempSync(join14(tmpdir(), "dc-app-"));
29603
+ try {
29604
+ const appDir = materializeAppBundle(source, workDir);
29605
+ validateAppBundle(appDir);
29606
+ const target = join14(installDir, APP_BUNDLE_NAME);
29607
+ const staging = join14(installDir, `.${APP_BUNDLE_NAME}.new-${process.pid}`);
29608
+ const backup = join14(installDir, `.${APP_BUNDLE_NAME}.old-${process.pid}`);
29609
+ rmSync2(staging, { recursive: true, force: true });
29610
+ rmSync2(backup, { recursive: true, force: true });
29611
+ let wasRunning;
29612
+ let replaced;
29613
+ try {
29614
+ run("ditto", [appDir, staging]);
29615
+ try {
29616
+ run("xattr", ["-dr", "com.apple.quarantine", staging]);
29617
+ } catch {
29618
+ }
29619
+ wasRunning = isAppRunning();
29620
+ replaced = existsSync14(target);
29621
+ if (replaced) renameSync(target, backup);
29622
+ renameSync(staging, target);
29623
+ } catch (e) {
29624
+ if (existsSync14(target) === false && existsSync14(backup)) {
29625
+ try {
29626
+ renameSync(backup, target);
29627
+ } catch {
29628
+ }
29629
+ }
29630
+ rmSync2(staging, { recursive: true, force: true });
29631
+ throw e;
29632
+ }
29633
+ rmSync2(backup, { recursive: true, force: true });
29634
+ const version = readBundleVersion(target);
29635
+ writeAppManifest(
29636
+ {
29637
+ version: version ?? "unknown",
29638
+ path: target,
29639
+ installedAt: Date.now(),
29640
+ source: opts.sourceLabel ?? "local"
29641
+ },
29642
+ home
29643
+ );
29644
+ const signatureValid = verifyCodesign(target);
29645
+ return { version, path: target, replaced, wasRunning, signatureValid };
29646
+ } finally {
29647
+ rmSync2(workDir, { recursive: true, force: true });
29648
+ }
29649
+ }
29650
+ function versionFromTag(tag) {
29651
+ return tag.replace(/^v/, "");
29652
+ }
29653
+ async function fetchLatestRelease(repo = APP_RELEASE_REPO, fetchImpl = fetch) {
29654
+ try {
29655
+ const res = await fetchImpl(`https://api.github.com/repos/${repo}/releases/latest`, {
29656
+ headers: { Accept: "application/vnd.github+json", "User-Agent": "dreamcontext-cli" }
29657
+ });
29658
+ if (!res.ok) return null;
29659
+ const json = await res.json();
29660
+ if (!json || typeof json.tag_name !== "string" || !Array.isArray(json.assets)) return null;
29661
+ return json;
29662
+ } catch {
29663
+ return null;
29664
+ }
29665
+ }
29666
+ async function downloadTo(url, destFile, fetchImpl = fetch) {
29667
+ const res = await fetchImpl(url, { headers: { "User-Agent": "dreamcontext-cli" } });
29668
+ if (!res.ok) throw new Error(`Download failed (${res.status}): ${url}`);
29669
+ const buf = Buffer.from(await res.arrayBuffer());
29670
+ writeFileSync13(destFile, buf);
29671
+ }
29672
+ function sha256File(file) {
29673
+ return createHash("sha256").update(readFileSync15(file)).digest("hex");
29674
+ }
29675
+ async function downloadLatestArtifact(repo = APP_RELEASE_REPO, fetchImpl = fetch) {
29676
+ const plat = detectPlatform();
29677
+ if (plat.os !== "darwin") return null;
29678
+ const release2 = await fetchLatestRelease(repo, fetchImpl);
29679
+ if (!release2) return null;
29680
+ const assetName = pickAssetForArch(release2.assets.map((a) => a.name), plat.arch);
29681
+ if (!assetName) return null;
29682
+ const asset = release2.assets.find((a) => a.name === assetName);
29683
+ const sumAsset = release2.assets.find((a) => a.name === `${assetName}.sha256`);
29684
+ if (!sumAsset) {
29685
+ throw new Error(
29686
+ `Release ${release2.tag_name} is missing ${assetName}.sha256 \u2014 refusing to install an unverified binary.`
29687
+ );
29688
+ }
29689
+ const workDir = mkdtempSync(join14(tmpdir(), "dc-app-dl-"));
29690
+ try {
29691
+ const archivePath = join14(workDir, assetName);
29692
+ await downloadTo(asset.browser_download_url, archivePath, fetchImpl);
29693
+ const sumFile = join14(workDir, `${assetName}.sha256`);
29694
+ await downloadTo(sumAsset.browser_download_url, sumFile, fetchImpl);
29695
+ const expected = readFileSync15(sumFile, "utf-8").trim().split(/\s+/)[0].toLowerCase();
29696
+ const actual = sha256File(archivePath).toLowerCase();
29697
+ if (!expected || expected !== actual) {
29698
+ throw new Error(`Checksum mismatch for ${assetName}: expected ${expected || "(empty)"}, got ${actual}`);
29699
+ }
29700
+ return { archivePath, version: versionFromTag(release2.tag_name) };
29701
+ } catch (e) {
29702
+ rmSync2(workDir, { recursive: true, force: true });
29703
+ throw e;
29704
+ }
29705
+ }
29706
+ function appAutoUpdateEnabled(env2 = process.env) {
29707
+ if (env2.DREAMCONTEXT_VERSION_CHECK === "0") return false;
29708
+ return env2.DREAMCONTEXT_APP_AUTO_UPDATE !== "0";
29709
+ }
29710
+ function defaultAppUpdateSpawner() {
29711
+ const child = spawnProcess(process.execPath, [process.argv[1], "app", "update"], {
29712
+ detached: true,
29713
+ stdio: "ignore"
29714
+ });
29715
+ child.unref();
29716
+ }
29717
+ function maybeTriggerAppUpdate(env2 = process.env, deps = {}) {
29718
+ try {
29719
+ if (detectPlatform().os !== "darwin") return false;
29720
+ if (!appAutoUpdateEnabled(env2)) return false;
29721
+ const installed = deps.manifest !== void 0 ? deps.manifest : readAppManifest();
29722
+ if (!installed) return false;
29723
+ (deps.spawner ?? defaultAppUpdateSpawner)();
29724
+ return true;
29725
+ } catch {
29726
+ return false;
29727
+ }
29728
+ }
29729
+ async function doInstall(from, dir) {
29730
+ const plat = detectPlatform();
29731
+ if (plat.os !== "darwin") {
29732
+ console.log(source_default.yellow("The desktop app is currently macOS-only. Windows/Linux support is planned."));
29733
+ return;
29734
+ }
29735
+ let tempToClean = null;
29736
+ let source = from;
29737
+ let sourceLabel = "local";
29738
+ if (!source) {
29739
+ console.log(source_default.cyan("Fetching the latest desktop app from GitHub Releases\u2026"));
29740
+ let dl = null;
29741
+ try {
29742
+ dl = await downloadLatestArtifact();
29743
+ } catch (e) {
29744
+ console.log(source_default.red(`Install aborted: ${e.message}`));
29745
+ return;
29746
+ }
29747
+ if (!dl) {
29748
+ console.log(
29749
+ source_default.yellow(
29750
+ "No published desktop release found yet.\nBuild one locally and install it with: dreamcontext app install --from <path-to.app|.tar.gz>"
29751
+ )
29752
+ );
29753
+ return;
29754
+ }
29755
+ source = dl.archivePath;
29756
+ sourceLabel = "github";
29757
+ tempToClean = dirname9(dl.archivePath);
29758
+ }
29759
+ try {
29760
+ const res = installAppBundle(source, { installDir: dir, sourceLabel });
29761
+ console.log(source_default.green(`\u2713 Installed dreamcontext-beta ${res.version ?? ""}`.trim()) + source_default.dim(` \u2192 ${res.path}`));
29762
+ if (res.signatureValid === false) {
29763
+ console.log(
29764
+ source_default.yellow(
29765
+ "Warning: the installed app failed code-signature verification. It may still launch (ad-hoc),\nbut the published artifact should be properly ad-hoc deep-signed at build time."
29766
+ )
29767
+ );
29768
+ }
29769
+ if (res.wasRunning) {
29770
+ console.log(source_default.yellow("The app is currently running \u2014 restart it to apply the update."));
29771
+ } else {
29772
+ console.log(source_default.dim(`Launch it: open "${res.path}"`));
29773
+ }
29774
+ } finally {
29775
+ if (tempToClean) rmSync2(tempToClean, { recursive: true, force: true });
29776
+ }
29777
+ }
29778
+ async function doUpdate(from, dir) {
29779
+ const installed = readAppManifest();
29780
+ if (!installed) {
29781
+ console.log(source_default.dim("No installed app recorded \u2014 running a fresh install."));
29782
+ await doInstall(from, dir);
29783
+ return;
29784
+ }
29785
+ if (from) {
29786
+ await doInstall(from, dir);
29787
+ return;
29788
+ }
29789
+ let dl = null;
29790
+ try {
29791
+ dl = await downloadLatestArtifact();
29792
+ } catch (e) {
29793
+ console.log(source_default.red(`Update aborted: ${e.message}`));
29794
+ return;
29795
+ }
29796
+ if (!dl) {
29797
+ console.log(source_default.yellow("No published desktop release available to update to."));
29798
+ return;
29799
+ }
29800
+ try {
29801
+ if (compareVersions(installed.version, dl.version) >= 0) {
29802
+ console.log(source_default.green(`Desktop app is up to date (${installed.version}).`));
29803
+ return;
29804
+ }
29805
+ const res = installAppBundle(dl.archivePath, { installDir: dir, sourceLabel: "github" });
29806
+ console.log(source_default.green(`\u2713 Updated dreamcontext-beta ${installed.version} \u2192 ${res.version ?? dl.version}`));
29807
+ if (res.wasRunning) console.log(source_default.yellow("Restart the app to apply the update."));
29808
+ } finally {
29809
+ rmSync2(dirname9(dl.archivePath), { recursive: true, force: true });
29810
+ }
29811
+ }
29812
+ function doStatus() {
29813
+ const installed = readAppManifest();
29814
+ if (!installed) {
29815
+ console.log("Desktop app: not installed (run `dreamcontext app install`).");
29816
+ return;
29817
+ }
29818
+ const onDisk = existsSync14(installed.path);
29819
+ console.log(`Desktop app: ${installed.version}`);
29820
+ console.log(` path: ${installed.path}${onDisk ? "" : source_default.red(" (missing!)")}`);
29821
+ console.log(` source: ${installed.source}`);
29822
+ console.log(` running: ${isAppRunning() ? "yes" : "no"}`);
29823
+ }
29824
+ function registerAppCommand(program2) {
29825
+ const app = program2.command("app").description("Manage the dreamcontext desktop app (install / update / status)");
29826
+ app.command("install").description("Install the desktop app to ~/Applications (no quarantine, no admin)").option("--from <path>", "Install from a local .app, .tar.gz, or .zip instead of GitHub Releases").option("--dir <dir>", "Install directory (default: ~/Applications)").action(async (opts) => {
29827
+ await doInstall(opts.from, opts.dir);
29828
+ });
29829
+ app.command("update").description("Update the installed desktop app to the latest version").option("--from <path>", "Update from a local artifact instead of GitHub Releases").option("--dir <dir>", "Install directory (default: ~/Applications)").action(async (opts) => {
29830
+ await doUpdate(opts.from, opts.dir);
29831
+ });
29832
+ app.command("status").description("Show the installed desktop app version and state").action(() => {
29833
+ doStatus();
29834
+ });
29835
+ }
29836
+ var APP_BUNDLE_NAME, APP_RELEASE_REPO;
29837
+ var init_app = __esm({
29838
+ "src/cli/commands/app.ts"() {
29839
+ "use strict";
29840
+ init_source();
29841
+ init_version_check();
29842
+ APP_BUNDLE_NAME = "dreamcontext-beta.app";
29843
+ APP_RELEASE_REPO = "meanllbrl/dreamcontext";
29844
+ }
29845
+ });
29846
+
29292
29847
  // src/cli/commands/setup.ts
29293
29848
  var setup_exports = {};
29294
29849
  __export(setup_exports, {
@@ -29296,7 +29851,8 @@ __export(setup_exports, {
29296
29851
  registerSetupCommand: () => registerSetupCommand,
29297
29852
  runSetup: () => runSetup
29298
29853
  });
29299
- import { existsSync as existsSync13 } from "fs";
29854
+ import { existsSync as existsSync15 } from "fs";
29855
+ import { execFileSync as execFileSync3 } from "child_process";
29300
29856
  function parsePlatformsOption3(raw) {
29301
29857
  if (!raw) return [];
29302
29858
  const parsed = parsePlatformList(raw);
@@ -29355,6 +29911,38 @@ async function installPlatformIntegration(projectRoot, opts) {
29355
29911
  });
29356
29912
  return { installed, notes, fileCount: Object.keys(manifest.files).length };
29357
29913
  }
29914
+ async function maybeInstallApp(opts) {
29915
+ if (opts.skipApp) return;
29916
+ if (process.env.DREAMCONTEXT_INSTALL_NO_APP === "1") return;
29917
+ try {
29918
+ if (detectPlatform().os !== "darwin") return;
29919
+ } catch {
29920
+ return;
29921
+ }
29922
+ const installed = (opts.appInstalledCheck ?? (() => readAppManifest() !== null))();
29923
+ if (installed) {
29924
+ info(source_default.dim("Desktop app already installed \u2014 skipping."));
29925
+ return;
29926
+ }
29927
+ const interactive = process.stdin.isTTY === true && !opts.defaults;
29928
+ let proceed = opts.installApp === true || !!opts.yes && interactive;
29929
+ if (!proceed && interactive) {
29930
+ proceed = await esm_default4({
29931
+ message: "Install the macOS desktop app too? (manage all your vaults in one window)",
29932
+ default: true
29933
+ });
29934
+ }
29935
+ if (!proceed) {
29936
+ info(source_default.dim("Desktop app not installed \u2014 add it anytime with `dreamcontext app install`."));
29937
+ return;
29938
+ }
29939
+ info("Installing the desktop app...");
29940
+ try {
29941
+ (opts.appInstaller ?? (() => execFileSync3("npx", ["dreamcontext", "app", "install"], { stdio: "inherit" })))();
29942
+ } catch (e) {
29943
+ info(source_default.yellow(`Desktop app install skipped: ${e.message}`));
29944
+ }
29945
+ }
29358
29946
  async function runSetup(opts) {
29359
29947
  const projectRoot = process.cwd();
29360
29948
  const useDefaults = !!opts.defaults;
@@ -29418,7 +30006,7 @@ async function runSetup(opts) {
29418
30006
  }
29419
30007
  }
29420
30008
  const contextDir = getInitPath();
29421
- if (!existsSync13(contextDir)) {
30009
+ if (!existsSync15(contextDir)) {
29422
30010
  info("Running init...");
29423
30011
  const { registerInitCommand: registerInitCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
29424
30012
  const tempProgram = new Command();
@@ -29438,6 +30026,7 @@ async function runSetup(opts) {
29438
30026
  multiProduct,
29439
30027
  disableNativeMemory
29440
30028
  });
30029
+ await maybeInstallApp(opts);
29441
30030
  const manifestPath2 = "_dream_context/state/.install-manifest.json";
29442
30031
  console.log();
29443
30032
  console.log(miniBox([
@@ -29464,7 +30053,7 @@ async function runSetup(opts) {
29464
30053
  }
29465
30054
  }
29466
30055
  function registerSetupCommand(program2) {
29467
- program2.command("setup").description("One-shot setup: init + install-skill + install-instructions").option("--defaults", "Skip prompts; use claude platform, no packs, single-product").option("-y, --yes", "Accept all confirmation prompts").option("--platforms <list>", `Comma-separated platforms: ${formatSupportedPlatforms()}`).option("--packs <list>", "Comma-separated pack names to install").option("--multi-product <list>", "Comma-separated product names for multi-product setup").option("--keep-native-memory", "Keep Claude Code's native auto-memory (default: disabled so dreamcontext owns memory)").action(async (opts) => {
30056
+ program2.command("setup").description("One-shot setup: init + install-skill + install-instructions").option("--defaults", "Skip prompts; use claude platform, no packs, single-product").option("-y, --yes", "Accept all confirmation prompts").option("--platforms <list>", `Comma-separated platforms: ${formatSupportedPlatforms()}`).option("--packs <list>", "Comma-separated pack names to install").option("--multi-product <list>", "Comma-separated product names for multi-product setup").option("--keep-native-memory", "Keep Claude Code's native auto-memory (default: disabled so dreamcontext owns memory)").option("--install-app", "Also install the macOS desktop app (non-interactive)").option("--skip-app", "Do not install the desktop app").action(async (opts) => {
29468
30057
  try {
29469
30058
  await runSetup(opts);
29470
30059
  } catch (err) {
@@ -29485,6 +30074,7 @@ var init_setup = __esm({
29485
30074
  init_source();
29486
30075
  init_esm16();
29487
30076
  init_format();
30077
+ init_app();
29488
30078
  init_context_path();
29489
30079
  init_platforms();
29490
30080
  init_install_skill();
@@ -29501,27 +30091,27 @@ var init_exports = {};
29501
30091
  __export(init_exports, {
29502
30092
  registerInitCommand: () => registerInitCommand
29503
30093
  });
29504
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync12, copyFileSync as copyFileSync2, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
29505
- import { join as join13 } from "path";
30094
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync14, copyFileSync as copyFileSync2, readdirSync as readdirSync6, statSync as statSync2 } from "fs";
30095
+ import { join as join15 } from "path";
29506
30096
  import { fileURLToPath as fileURLToPath5 } from "url";
29507
30097
  function getTemplateDir(subdir = "init") {
29508
30098
  const candidates = [
29509
- join13(__dirname5, "..", "..", "templates", subdir),
29510
- join13(__dirname5, "..", "templates", subdir),
29511
- join13(__dirname5, "templates", subdir)
30099
+ join15(__dirname5, "..", "..", "templates", subdir),
30100
+ join15(__dirname5, "..", "templates", subdir),
30101
+ join15(__dirname5, "templates", subdir)
29512
30102
  ];
29513
30103
  for (const dir of candidates) {
29514
- if (existsSync14(dir)) return dir;
30104
+ if (existsSync16(dir)) return dir;
29515
30105
  }
29516
- return join13(__dirname5, "..", "..", "templates", subdir);
30106
+ return join15(__dirname5, "..", "..", "templates", subdir);
29517
30107
  }
29518
30108
  function copyObsidianConfig(destDir) {
29519
30109
  const src = getTemplateDir("obsidian");
29520
- if (!existsSync14(src)) return false;
29521
- mkdirSync8(destDir, { recursive: true });
29522
- for (const entry of readdirSync5(src)) {
29523
- const from = join13(src, entry);
29524
- const to = join13(destDir, entry);
30110
+ if (!existsSync16(src)) return false;
30111
+ mkdirSync10(destDir, { recursive: true });
30112
+ for (const entry of readdirSync6(src)) {
30113
+ const from = join15(src, entry);
30114
+ const to = join15(destDir, entry);
29525
30115
  if (statSync2(from).isFile()) {
29526
30116
  copyFileSync2(from, to);
29527
30117
  }
@@ -29538,7 +30128,7 @@ function replaceTokens(content, tokens) {
29538
30128
  function registerInitCommand(program2) {
29539
30129
  program2.command("init").description("Initialize _dream_context/ in the current directory").option("-y, --yes", "Skip prompts and use defaults").option("--name <name>", "Project name").option("--description <desc>", "Project description").option("--user <user>", "Target user").option("--stack <stack>", "Tech stack").option("--priority <priority>", "Current priority").option("--platforms <list>", `Comma-separated platforms: ${formatSupportedPlatforms()}`).option("--multi-product <list>", "Comma-separated product names for monorepos (lowercase kebab-case). Skips the interactive prompt.").action(async (opts) => {
29540
30130
  const contextDir = getInitPath();
29541
- if (existsSync14(contextDir)) {
30131
+ if (existsSync16(contextDir)) {
29542
30132
  error("_dream_context/ already exists in this directory.");
29543
30133
  return;
29544
30134
  }
@@ -29643,13 +30233,13 @@ function registerInitCommand(program2) {
29643
30233
  PRIORITY: priority,
29644
30234
  DATE: dateStr
29645
30235
  };
29646
- mkdirSync8(join13(contextDir, "core", "features"), { recursive: true });
29647
- mkdirSync8(join13(contextDir, "knowledge"), { recursive: true });
29648
- mkdirSync8(join13(contextDir, "knowledge", "data-structures"), { recursive: true });
29649
- mkdirSync8(join13(contextDir, "knowledge", "products"), { recursive: true });
29650
- mkdirSync8(join13(contextDir, "state"), { recursive: true });
29651
- mkdirSync8(join13(contextDir, "inbox"), { recursive: true });
29652
- const obsidianInstalled = copyObsidianConfig(join13(contextDir, ".obsidian"));
30236
+ mkdirSync10(join15(contextDir, "core", "features"), { recursive: true });
30237
+ mkdirSync10(join15(contextDir, "knowledge"), { recursive: true });
30238
+ mkdirSync10(join15(contextDir, "knowledge", "data-structures"), { recursive: true });
30239
+ mkdirSync10(join15(contextDir, "knowledge", "products"), { recursive: true });
30240
+ mkdirSync10(join15(contextDir, "state"), { recursive: true });
30241
+ mkdirSync10(join15(contextDir, "inbox"), { recursive: true });
30242
+ const obsidianInstalled = copyObsidianConfig(join15(contextDir, ".obsidian"));
29653
30243
  const templateDir = getTemplateDir();
29654
30244
  const templateFiles = [
29655
30245
  "0.soul.md",
@@ -29659,26 +30249,26 @@ function registerInitCommand(program2) {
29659
30249
  "4.tech_stack.md"
29660
30250
  ];
29661
30251
  for (const file of templateFiles) {
29662
- const templatePath = join13(templateDir, file);
29663
- const destPath = join13(contextDir, "core", file);
29664
- if (existsSync14(templatePath)) {
29665
- const content = readFileSync14(templatePath, "utf-8");
29666
- writeFileSync12(destPath, replaceTokens(content, tokens), "utf-8");
30252
+ const templatePath = join15(templateDir, file);
30253
+ const destPath = join15(contextDir, "core", file);
30254
+ if (existsSync16(templatePath)) {
30255
+ const content = readFileSync16(templatePath, "utf-8");
30256
+ writeFileSync14(destPath, replaceTokens(content, tokens), "utf-8");
29667
30257
  } else {
29668
- writeFileSync12(destPath, `# ${file}
30258
+ writeFileSync14(destPath, `# ${file}
29669
30259
 
29670
30260
  Created: ${dateStr}
29671
30261
  `, "utf-8");
29672
30262
  }
29673
30263
  }
29674
- const dataStructuresTemplate = join13(templateDir, "data-structures", "default.md");
30264
+ const dataStructuresTemplate = join15(templateDir, "data-structures", "default.md");
29675
30265
  const dataStructuresFallback = "---\nname: {{PRODUCT_NAME}}\ndescription: Data structures for {{PRODUCT_NAME}}\ntype: data-structures\nproduct: {{PRODUCT_NAME}}\ntags:\n - data-structures\n - database\n - schema\nupdated: {{DATE}}\n---\n\n# Data Structures \u2014 {{PRODUCT_NAME}}\n\nDocument schemas, models, and API contracts here.\n";
29676
- const dsTemplateContent = existsSync14(dataStructuresTemplate) ? readFileSync14(dataStructuresTemplate, "utf-8") : dataStructuresFallback;
30266
+ const dsTemplateContent = existsSync16(dataStructuresTemplate) ? readFileSync16(dataStructuresTemplate, "utf-8") : dataStructuresFallback;
29677
30267
  const productList = multiProduct === false ? ["default"] : multiProduct;
29678
30268
  for (const product of productList) {
29679
30269
  const productTokens = { ...tokens, PRODUCT_NAME: product };
29680
- const destPath = join13(contextDir, "knowledge", "data-structures", `${product}.md`);
29681
- writeFileSync12(destPath, replaceTokens(dsTemplateContent, productTokens), "utf-8");
30270
+ const destPath = join15(contextDir, "knowledge", "data-structures", `${product}.md`);
30271
+ writeFileSync14(destPath, replaceTokens(dsTemplateContent, productTokens), "utf-8");
29682
30272
  }
29683
30273
  if (multiProduct !== false) {
29684
30274
  for (const product of multiProduct) {
@@ -29695,12 +30285,12 @@ tags:
29695
30285
 
29696
30286
  Product-scoped knowledge. Cross-cutting findings still go to top-level \`knowledge/\`.
29697
30287
  `;
29698
- writeFileSync12(join13(contextDir, "knowledge", "products", `${product}.md`), knowledgeStub, "utf-8");
30288
+ writeFileSync14(join15(contextDir, "knowledge", "products", `${product}.md`), knowledgeStub, "utf-8");
29699
30289
  }
29700
30290
  }
29701
30291
  const jsonFiles = ["CHANGELOG.json", "RELEASES.json"];
29702
30292
  for (const file of jsonFiles) {
29703
- writeFileSync12(join13(contextDir, "core", file), "[]\n", "utf-8");
30293
+ writeFileSync14(join15(contextDir, "core", file), "[]\n", "utf-8");
29704
30294
  }
29705
30295
  ensureTaxonomyFile(contextDir);
29706
30296
  updateSetupConfig(process.cwd(), {
@@ -29709,7 +30299,7 @@ Product-scoped knowledge. Cross-cutting findings still go to top-level \`knowled
29709
30299
  setupVersion: dreamcontextVersion()
29710
30300
  });
29711
30301
  writeProjectPlatformDefaults(process.cwd(), selectedPlatforms);
29712
- insertToJsonArray(join13(contextDir, "core", "CHANGELOG.json"), {
30302
+ insertToJsonArray(join15(contextDir, "core", "CHANGELOG.json"), {
29713
30303
  date: dateStr,
29714
30304
  type: "chore",
29715
30305
  scope: "project",
@@ -29760,7 +30350,7 @@ Product-scoped knowledge. Cross-cutting findings still go to top-level \`knowled
29760
30350
  }
29761
30351
  const viaSetup = process.env[SETUP_INTERNAL_ENV] === "1";
29762
30352
  const integrationPresent = selectedPlatforms.every(
29763
- (p) => existsSync14(join13(platformSkillRoot(process.cwd(), p), "dreamcontext", "SKILL.md"))
30353
+ (p) => existsSync16(join15(platformSkillRoot(process.cwd(), p), "dreamcontext", "SKILL.md"))
29764
30354
  );
29765
30355
  let integrationInstalled = false;
29766
30356
  if (!viaSetup && !integrationPresent && !useDefaults && process.stdin.isTTY) {
@@ -29841,14 +30431,14 @@ var init_init = __esm({
29841
30431
  });
29842
30432
 
29843
30433
  // src/lib/release-discovery.ts
29844
- import { join as join14, basename as basename4 } from "path";
29845
- import { existsSync as existsSync15 } from "fs";
30434
+ import { join as join16, basename as basename4 } from "path";
30435
+ import { existsSync as existsSync17 } from "fs";
29846
30436
  function changelogFingerprint(entry) {
29847
30437
  return `${entry.date}|${entry.type}|${entry.scope}|${entry.description}`;
29848
30438
  }
29849
30439
  function getExistingReleases(root) {
29850
- const releasesPath = join14(root, "core", "RELEASES.json");
29851
- if (!existsSync15(releasesPath)) return [];
30440
+ const releasesPath = join16(root, "core", "RELEASES.json");
30441
+ if (!existsSync17(releasesPath)) return [];
29852
30442
  try {
29853
30443
  const entries = readJsonArray(releasesPath);
29854
30444
  for (const entry of entries) {
@@ -29862,8 +30452,8 @@ function getExistingReleases(root) {
29862
30452
  function findUnreleasedTasks(root) {
29863
30453
  const releases = getExistingReleases(root);
29864
30454
  const releasedTaskIds = new Set(releases.flatMap((r) => r.tasks ?? []));
29865
- const stateDir = join14(root, "state");
29866
- if (!existsSync15(stateDir)) return [];
30455
+ const stateDir = join16(root, "state");
30456
+ if (!existsSync17(stateDir)) return [];
29867
30457
  const files = import_fast_glob2.default.sync("*.md", { cwd: stateDir, absolute: true });
29868
30458
  const result = [];
29869
30459
  for (const file of files) {
@@ -29884,8 +30474,8 @@ function findUnreleasedTasks(root) {
29884
30474
  return result;
29885
30475
  }
29886
30476
  function findUnreleasedFeatures(root) {
29887
- const featuresDir = join14(root, "core", "features");
29888
- if (!existsSync15(featuresDir)) return [];
30477
+ const featuresDir = join16(root, "core", "features");
30478
+ if (!existsSync17(featuresDir)) return [];
29889
30479
  const files = import_fast_glob2.default.sync("*.md", { cwd: featuresDir, absolute: true });
29890
30480
  const result = [];
29891
30481
  for (const file of files) {
@@ -29910,8 +30500,8 @@ function findUnreleasedChangelog(root) {
29910
30500
  releasedFingerprints.add(changelogFingerprint(entry));
29911
30501
  }
29912
30502
  }
29913
- const changelogPath = join14(root, "core", "CHANGELOG.json");
29914
- if (!existsSync15(changelogPath)) return [];
30503
+ const changelogPath = join16(root, "core", "CHANGELOG.json");
30504
+ if (!existsSync17(changelogPath)) return [];
29915
30505
  try {
29916
30506
  const entries = readJsonArray(changelogPath);
29917
30507
  const result = [];
@@ -29937,11 +30527,11 @@ var init_release_discovery = __esm({
29937
30527
  });
29938
30528
 
29939
30529
  // src/lib/release-backpopulate.ts
29940
- import { join as join15 } from "path";
29941
- import { existsSync as existsSync16 } from "fs";
30530
+ import { join as join17 } from "path";
30531
+ import { existsSync as existsSync18 } from "fs";
29942
30532
  function backPopulateFeatures(root, featureIds, version) {
29943
- const featuresDir = join15(root, "core", "features");
29944
- if (!existsSync16(featuresDir)) return;
30533
+ const featuresDir = join17(root, "core", "features");
30534
+ if (!existsSync18(featuresDir)) return;
29945
30535
  const files = import_fast_glob3.default.sync("*.md", { cwd: featuresDir, absolute: true });
29946
30536
  const idSet = new Set(featureIds);
29947
30537
  for (const file of files) {
@@ -29969,14 +30559,14 @@ var init_release_backpopulate = __esm({
29969
30559
  });
29970
30560
 
29971
30561
  // src/lib/active-version.ts
29972
- import { existsSync as existsSync17 } from "fs";
29973
- import { join as join16 } from "path";
30562
+ import { existsSync as existsSync19 } from "fs";
30563
+ import { join as join18 } from "path";
29974
30564
  function statePath() {
29975
- return join16(ensureContextRoot(), "state", ".active-version.json");
30565
+ return join18(ensureContextRoot(), "state", ".active-version.json");
29976
30566
  }
29977
30567
  function getActivePlanningVersion() {
29978
30568
  const path2 = statePath();
29979
- if (!existsSync17(path2)) return null;
30569
+ if (!existsSync19(path2)) return null;
29980
30570
  let stored;
29981
30571
  try {
29982
30572
  const data = readJsonObject(path2);
@@ -30018,13 +30608,13 @@ var init_active_version = __esm({
30018
30608
  });
30019
30609
 
30020
30610
  // src/cli/commands/core.ts
30021
- import { join as join17 } from "path";
30611
+ import { join as join19 } from "path";
30022
30612
  function registerCoreCommand(program2) {
30023
30613
  const core = program2.command("core").description("Add changelog and release entries");
30024
30614
  const changelog = core.command("changelog").description("Manage CHANGELOG.json");
30025
30615
  changelog.command("add").description("Add a changelog entry").option("--type <type>", "Type (feat|fix|refactor|chore|docs|perf|test|change)").option("--scope <scope>", "Scope (e.g., auth, ui, api)").option("--description <desc>", "Description (long-form, full body)").option("--summary <summary>", "Optional \u2264200-char one-liner for snapshot display").option("--references <refs>", "Optional comma-separated references (commit:<sha>, file:<path>, knowledge:<slug>, feature:<slug>, task:<slug>, url:<href>)").option("--authors <list>", 'Optional comma-separated people involved (e.g. "mehmet,ada")').option("--supersedes <key>", 'Optional pointer to prior entry this supersedes (e.g., "2026-05-09|sleep")').option("--breaking", "Mark as a breaking change", false).action(async (opts) => {
30026
30616
  const root = ensureContextRoot();
30027
- const filePath = join17(root, "core", "CHANGELOG.json");
30617
+ const filePath = join19(root, "core", "CHANGELOG.json");
30028
30618
  const type = opts.type ?? await esm_default11({
30029
30619
  message: "Type:",
30030
30620
  choices: [
@@ -30078,7 +30668,7 @@ function registerCoreCommand(program2) {
30078
30668
  const releases = core.command("releases").description("Manage RELEASES.json");
30079
30669
  releases.command("add").description("Create a release with auto-discovered tasks, features, and changelog entries").option("-V, --ver <version>", "Version string (e.g., 1.2.0)").option("-s, --summary <summary>", "Release summary").option("--status <status>", "Release status: planning or released (default: released)").option("-y, --yes", "Skip interactive selection, include all discovered items").action(async (opts) => {
30080
30670
  const root = ensureContextRoot();
30081
- const releasesPath = join17(root, "core", "RELEASES.json");
30671
+ const releasesPath = join19(root, "core", "RELEASES.json");
30082
30672
  const auto = !!opts.yes;
30083
30673
  const releaseStatus = opts.status === "planning" ? "planning" : "released";
30084
30674
  const version = opts.ver ?? (auto ? "" : await esm_default5({ message: "Version (e.g., 1.2.0):" }));
@@ -30283,7 +30873,7 @@ var init_core = __esm({
30283
30873
  });
30284
30874
 
30285
30875
  // src/lib/markdown.ts
30286
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
30876
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "fs";
30287
30877
  function parseSections(content) {
30288
30878
  const lines = content.split("\n");
30289
30879
  const sections = [];
@@ -30311,13 +30901,13 @@ function findSection(sections, sectionName) {
30311
30901
  return sections.find((s) => normalizeName(s.name) === normalized) ?? null;
30312
30902
  }
30313
30903
  function listSections(filePath) {
30314
- const raw = readFileSync15(filePath, "utf-8");
30904
+ const raw = readFileSync17(filePath, "utf-8");
30315
30905
  const parsed = (0, import_gray_matter3.default)(raw);
30316
30906
  const sections = parseSections(parsed.content);
30317
30907
  return sections.map((s) => s.name);
30318
30908
  }
30319
30909
  function readSection(filePath, sectionName) {
30320
- const raw = readFileSync15(filePath, "utf-8");
30910
+ const raw = readFileSync17(filePath, "utf-8");
30321
30911
  const parsed = (0, import_gray_matter3.default)(raw);
30322
30912
  const sections = parseSections(parsed.content);
30323
30913
  const section2 = findSection(sections, sectionName);
@@ -30373,7 +30963,7 @@ function stripPlaceholderBody(lines, section2) {
30373
30963
  return originalCount - kept.length;
30374
30964
  }
30375
30965
  function insertToSection(filePath, sectionName, newContent, position = "top", createIfMissing = false, replacePlaceholders = false) {
30376
- const raw = readFileSync15(filePath, "utf-8");
30966
+ const raw = readFileSync17(filePath, "utf-8");
30377
30967
  const parsed = (0, import_gray_matter3.default)(raw);
30378
30968
  let lines = parsed.content.split("\n");
30379
30969
  let section2 = findSection(parseSections(lines.join("\n")), sectionName);
@@ -30405,7 +30995,7 @@ function insertToSection(filePath, sectionName, newContent, position = "top", cr
30405
30995
  lines.splice(insertAt, 0, ...block);
30406
30996
  }
30407
30997
  const output = import_gray_matter3.default.stringify(lines.join("\n"), parsed.data);
30408
- writeFileSync13(filePath, output, "utf-8");
30998
+ writeFileSync15(filePath, output, "utf-8");
30409
30999
  }
30410
31000
  function extractMermaidNodes(content) {
30411
31001
  const fence = content.match(/```mermaid\s+([\s\S]*?)```/);
@@ -30609,20 +31199,20 @@ var init_feature_freshness = __esm({
30609
31199
  });
30610
31200
 
30611
31201
  // src/cli/commands/features.ts
30612
- import { existsSync as existsSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
30613
- import { join as join18, basename as basename5 } from "path";
31202
+ import { existsSync as existsSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
31203
+ import { join as join20, basename as basename5 } from "path";
30614
31204
  function parseCsv(value) {
30615
31205
  return value.split(",").map((s) => s.trim()).filter(Boolean);
30616
31206
  }
30617
31207
  function getFeaturesDir() {
30618
31208
  const root = ensureContextRoot();
30619
- return join18(root, "core", "features");
31209
+ return join20(root, "core", "features");
30620
31210
  }
30621
31211
  function findFeatureFile(name) {
30622
31212
  const dir = getFeaturesDir();
30623
31213
  const slug = slugify(name);
30624
- const exact = join18(dir, `${slug}.md`);
30625
- if (existsSync18(exact)) return exact;
31214
+ const exact = join20(dir, `${slug}.md`);
31215
+ if (existsSync20(exact)) return exact;
30626
31216
  const files = import_fast_glob4.default.sync("*.md", { cwd: dir, absolute: true });
30627
31217
  const exactGlob = files.find((f) => basename5(f, ".md") === slug);
30628
31218
  if (exactGlob) return exactGlob;
@@ -30642,12 +31232,12 @@ function findFeatureFile(name) {
30642
31232
  }
30643
31233
  function getFeatureTemplate() {
30644
31234
  const candidates = [
30645
- join18(new URL(".", import.meta.url).pathname, "..", "..", "templates", "feature.md"),
30646
- join18(new URL(".", import.meta.url).pathname, "..", "templates", "feature.md")
31235
+ join20(new URL(".", import.meta.url).pathname, "..", "..", "templates", "feature.md"),
31236
+ join20(new URL(".", import.meta.url).pathname, "..", "templates", "feature.md")
30647
31237
  ];
30648
31238
  for (const path2 of candidates) {
30649
- if (existsSync18(path2)) {
30650
- return readFileSync16(path2, "utf-8");
31239
+ if (existsSync20(path2)) {
31240
+ return readFileSync18(path2, "utf-8");
30651
31241
  }
30652
31242
  }
30653
31243
  return `---
@@ -30695,8 +31285,8 @@ function registerFeaturesCommand(program2) {
30695
31285
  features.command("create").argument("<name>").option("-w, --why <why>", "Why are we building this?").option("-t, --tags <tags>", "Comma-separated tags").option("-s, --status <status>", `Status (${VALID_FEATURE_STATUSES.join(", ")})`).option("--related-tasks <slugs>", "Comma-separated related task slugs").description("Create a new feature document").action(async (name, opts) => {
30696
31286
  const dir = getFeaturesDir();
30697
31287
  const slug = slugify(name);
30698
- const filePath = join18(dir, `${slug}.md`);
30699
- if (existsSync18(filePath)) {
31288
+ const filePath = join20(dir, `${slug}.md`);
31289
+ if (existsSync20(filePath)) {
30700
31290
  error(`Feature already exists: ${slug}.md`);
30701
31291
  return;
30702
31292
  }
@@ -30707,7 +31297,7 @@ function registerFeaturesCommand(program2) {
30707
31297
  const why = opts.why || await promptInput({ message: "Why are we building this?" });
30708
31298
  const template = getFeatureTemplate();
30709
31299
  const content = template.replaceAll("{{ID}}", generateId("feat")).replaceAll("{{DATE}}", today()).replaceAll("{{WHY}}", why || "(To be defined)");
30710
- writeFileSync14(filePath, content, "utf-8");
31300
+ writeFileSync16(filePath, content, "utf-8");
30711
31301
  const fields = {};
30712
31302
  if (opts.tags) fields.tags = parseCsv(opts.tags);
30713
31303
  if (opts.status) fields.status = opts.status;
@@ -30773,8 +31363,8 @@ function registerFeaturesCommand(program2) {
30773
31363
  features.command("doctor").description("Check feature PRDs for staleness, orphans, and dangling task references (read-only)").action(() => {
30774
31364
  const root = ensureContextRoot();
30775
31365
  const featuresDir = getFeaturesDir();
30776
- const stateDir = join18(root, "state");
30777
- const featureFiles = existsSync18(featuresDir) ? import_fast_glob4.default.sync("*.md", { cwd: featuresDir, absolute: true }) : [];
31366
+ const stateDir = join20(root, "state");
31367
+ const featureFiles = existsSync20(featuresDir) ? import_fast_glob4.default.sync("*.md", { cwd: featuresDir, absolute: true }) : [];
30778
31368
  const featureRefs = [];
30779
31369
  for (const file of featureFiles) {
30780
31370
  try {
@@ -30789,7 +31379,7 @@ function registerFeaturesCommand(program2) {
30789
31379
  } catch {
30790
31380
  }
30791
31381
  }
30792
- const taskFiles = existsSync18(stateDir) ? import_fast_glob4.default.sync("*.md", { cwd: stateDir, absolute: true }) : [];
31382
+ const taskFiles = existsSync20(stateDir) ? import_fast_glob4.default.sync("*.md", { cwd: stateDir, absolute: true }) : [];
30793
31383
  const taskRefs = [];
30794
31384
  for (const file of taskFiles) {
30795
31385
  const taskSlug = basename5(file, ".md");
@@ -31067,13 +31657,13 @@ var init_types = __esm({
31067
31657
  });
31068
31658
 
31069
31659
  // src/lib/task-backend/local.ts
31070
- import { existsSync as existsSync19, readFileSync as readFileSync17, rmSync as rmSync2, writeFileSync as writeFileSync15 } from "fs";
31071
- import { basename as basename6, join as join19, resolve as resolve3, sep as sep2 } from "path";
31660
+ import { existsSync as existsSync21, readFileSync as readFileSync19, rmSync as rmSync3, writeFileSync as writeFileSync17 } from "fs";
31661
+ import { basename as basename6, join as join21, resolve as resolve4, sep as sep2 } from "path";
31072
31662
  function isSafeTaskSlug(slug) {
31073
31663
  if (!slug || slug.includes("\0")) return false;
31074
- const base = resolve3(sep2, "dc-slug-check");
31075
- const target = resolve3(base, `${slug}.md`);
31076
- return target === join19(base, `${slug}.md`) && target.startsWith(base + sep2);
31664
+ const base = resolve4(sep2, "dc-slug-check");
31665
+ const target = resolve4(base, `${slug}.md`);
31666
+ return target === join21(base, `${slug}.md`) && target.startsWith(base + sep2);
31077
31667
  }
31078
31668
  function hasBacklogTag(tags) {
31079
31669
  return Array.isArray(tags) && tags.some((t) => String(t).toLowerCase() === BACKLOG_TAG);
@@ -31094,12 +31684,12 @@ function normalizeBacklogFields(prev, patch) {
31094
31684
  }
31095
31685
  function getTaskTemplate() {
31096
31686
  const candidates = [
31097
- join19(new URL(".", import.meta.url).pathname, "..", "..", "templates", "task.md"),
31098
- join19(new URL(".", import.meta.url).pathname, "..", "templates", "task.md")
31687
+ join21(new URL(".", import.meta.url).pathname, "..", "..", "templates", "task.md"),
31688
+ join21(new URL(".", import.meta.url).pathname, "..", "templates", "task.md")
31099
31689
  ];
31100
31690
  for (const path2 of candidates) {
31101
- if (existsSync19(path2)) {
31102
- return readFileSync17(path2, "utf-8");
31691
+ if (existsSync21(path2)) {
31692
+ return readFileSync19(path2, "utf-8");
31103
31693
  }
31104
31694
  }
31105
31695
  return `---
@@ -31233,20 +31823,20 @@ var init_local = __esm({
31233
31823
  }
31234
31824
  name = "local";
31235
31825
  taskPath(slug) {
31236
- return join19(this.stateDir, `${slug}.md`);
31826
+ return join21(this.stateDir, `${slug}.md`);
31237
31827
  }
31238
31828
  requirePath(slug) {
31239
31829
  if (!isSafeTaskSlug(slug)) {
31240
31830
  throw new TaskBackendError("invalid_input", `Invalid task slug: ${slug}`);
31241
31831
  }
31242
31832
  const path2 = this.taskPath(slug);
31243
- if (!existsSync19(path2)) {
31833
+ if (!existsSync21(path2)) {
31244
31834
  throw new TaskBackendError("not_found", `Task not found: ${slug}`);
31245
31835
  }
31246
31836
  return path2;
31247
31837
  }
31248
31838
  taskFiles() {
31249
- if (!existsSync19(this.stateDir)) return [];
31839
+ if (!existsSync21(this.stateDir)) return [];
31250
31840
  return import_fast_glob5.default.sync("*.md", { cwd: this.stateDir, absolute: true });
31251
31841
  }
31252
31842
  async list(filter) {
@@ -31263,7 +31853,7 @@ var init_local = __esm({
31263
31853
  async get(slug) {
31264
31854
  if (!isSafeTaskSlug(slug)) return null;
31265
31855
  const path2 = this.taskPath(slug);
31266
- if (!existsSync19(path2)) return null;
31856
+ if (!existsSync21(path2)) return null;
31267
31857
  return readTaskFile(path2);
31268
31858
  }
31269
31859
  async create(input2) {
@@ -31275,13 +31865,13 @@ var init_local = __esm({
31275
31865
  throw new TaskBackendError("invalid_input", `Invalid task name: ${input2.name}`);
31276
31866
  }
31277
31867
  const filePath = this.taskPath(slug);
31278
- if (existsSync19(filePath)) {
31868
+ if (existsSync21(filePath)) {
31279
31869
  throw new TaskBackendError("already_exists", `Task already exists: ${slug}`);
31280
31870
  }
31281
31871
  if (input2.variant === "cli") {
31282
31872
  const template = getTaskTemplate();
31283
31873
  const content = template.replaceAll("{{ID}}", generateId("task")).replaceAll("{{NAME}}", input2.name).replaceAll("{{DESCRIPTION}}", input2.description ?? input2.name).replaceAll("{{PRIORITY}}", input2.priority ?? "medium").replaceAll("{{URGENCY}}", input2.urgency ?? "medium").replaceAll("{{STATUS}}", input2.status ?? "todo").replaceAll("{{TAGS}}", JSON.stringify(input2.tags ?? [])).replaceAll("{{DATE}}", today()).replaceAll("{{WHY}}", input2.why || "(To be defined)").replaceAll("{{VERSION}}", input2.version ? `"${input2.version}"` : "null");
31284
- writeFileSync15(filePath, content, "utf-8");
31874
+ writeFileSync17(filePath, content, "utf-8");
31285
31875
  if (input2.rice) {
31286
31876
  updateFrontmatterFields(filePath, { rice: input2.rice });
31287
31877
  }
@@ -31345,7 +31935,7 @@ ${input2.why || "(To be defined)"}
31345
31935
  ### ${dateStr} - Created
31346
31936
  - Task created.
31347
31937
  `;
31348
- writeFileSync15(filePath, content, "utf-8");
31938
+ writeFileSync17(filePath, content, "utf-8");
31349
31939
  if (input2.due_date) {
31350
31940
  updateFrontmatterFields(filePath, { due_date: input2.due_date });
31351
31941
  }
@@ -31376,8 +31966,8 @@ ${input2.why || "(To be defined)"}
31376
31966
  insertToSection(path2, "Changelog", entry, "top");
31377
31967
  } catch (err) {
31378
31968
  if (!opts?.fallbackAppend) throw err;
31379
- const existing = readFileSync17(path2, "utf-8");
31380
- writeFileSync15(path2, existing.trimEnd() + "\n\n" + entry + "\n", "utf-8");
31969
+ const existing = readFileSync19(path2, "utf-8");
31970
+ writeFileSync17(path2, existing.trimEnd() + "\n\n" + entry + "\n", "utf-8");
31381
31971
  }
31382
31972
  }
31383
31973
  async complete(slug, summary) {
@@ -31391,11 +31981,11 @@ ${input2.why || "(To be defined)"}
31391
31981
  }
31392
31982
  async delete(slug) {
31393
31983
  const path2 = this.requirePath(slug);
31394
- rmSync2(path2);
31984
+ rmSync3(path2);
31395
31985
  }
31396
31986
  async resolveSlug(name) {
31397
31987
  const slug = slugify(name);
31398
- if (existsSync19(this.taskPath(slug))) return { kind: "match", slug };
31988
+ if (existsSync21(this.taskPath(slug))) return { kind: "match", slug };
31399
31989
  const slugs = this.taskFiles().map((f) => basename6(f, ".md"));
31400
31990
  const exact = slugs.find((s) => s === slug);
31401
31991
  if (exact) return { kind: "match", slug: exact };
@@ -31902,14 +32492,14 @@ var init_change_tracker = __esm({
31902
32492
  });
31903
32493
 
31904
32494
  // src/lib/gitignore.ts
31905
- import { existsSync as existsSync20, lstatSync, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
31906
- import { join as join20 } from "path";
32495
+ import { existsSync as existsSync22, lstatSync, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "fs";
32496
+ import { join as join22 } from "path";
31907
32497
  function ensureGitignoreEntries(projectRoot, entries, opts) {
31908
- const path2 = join20(projectRoot, ".gitignore");
31909
- if (existsSync20(path2) && !lstatSync(path2).isFile()) {
32498
+ const path2 = join22(projectRoot, ".gitignore");
32499
+ if (existsSync22(path2) && !lstatSync(path2).isFile()) {
31910
32500
  throw new Error(`.gitignore at ${path2} is not a regular file`);
31911
32501
  }
31912
- const current = existsSync20(path2) ? readFileSync18(path2, "utf-8") : "";
32502
+ const current = existsSync22(path2) ? readFileSync20(path2, "utf-8") : "";
31913
32503
  const have = new Set(
31914
32504
  current.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"))
31915
32505
  );
@@ -31919,7 +32509,7 @@ function ensureGitignoreEntries(projectRoot, entries, opts) {
31919
32509
  if (current.length > 0 && !current.endsWith("\n")) lines.push("");
31920
32510
  if (opts?.comment) lines.push("", `# ${opts.comment}`);
31921
32511
  lines.push(...missing);
31922
- writeFileSync16(path2, current + lines.join("\n") + "\n", "utf-8");
32512
+ writeFileSync18(path2, current + lines.join("\n") + "\n", "utf-8");
31923
32513
  return missing;
31924
32514
  }
31925
32515
  var init_gitignore = __esm({
@@ -31929,16 +32519,16 @@ var init_gitignore = __esm({
31929
32519
  });
31930
32520
 
31931
32521
  // src/lib/task-backend/secrets.ts
31932
- import { chmodSync, existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "fs";
31933
- import { dirname as dirname8, join as join21 } from "path";
32522
+ import { chmodSync, existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "fs";
32523
+ import { dirname as dirname10, join as join23 } from "path";
31934
32524
  function secretsPath(projectRoot) {
31935
- return join21(projectRoot, SECRETS_REL_PATH);
32525
+ return join23(projectRoot, SECRETS_REL_PATH);
31936
32526
  }
31937
32527
  function readSecretsFile(projectRoot) {
31938
32528
  const path2 = secretsPath(projectRoot);
31939
- if (!existsSync21(path2)) return {};
32529
+ if (!existsSync23(path2)) return {};
31940
32530
  try {
31941
- const parsed = JSON.parse(readFileSync19(path2, "utf-8"));
32531
+ const parsed = JSON.parse(readFileSync21(path2, "utf-8"));
31942
32532
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
31943
32533
  return parsed;
31944
32534
  } catch {
@@ -31959,7 +32549,7 @@ function writeClickUpToken(projectRoot, token, user) {
31959
32549
  );
31960
32550
  }
31961
32551
  const path2 = secretsPath(projectRoot);
31962
- mkdirSync9(dirname8(path2), { recursive: true });
32552
+ mkdirSync11(dirname10(path2), { recursive: true });
31963
32553
  const secrets = readSecretsFile(projectRoot);
31964
32554
  secrets.clickup = secrets.clickup ?? {};
31965
32555
  if (user && user.trim()) {
@@ -31967,7 +32557,7 @@ function writeClickUpToken(projectRoot, token, user) {
31967
32557
  } else {
31968
32558
  secrets.clickup.token = token.trim();
31969
32559
  }
31970
- writeFileSync17(path2, JSON.stringify(secrets, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
32560
+ writeFileSync19(path2, JSON.stringify(secrets, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
31971
32561
  try {
31972
32562
  chmodSync(path2, 384);
31973
32563
  } catch {
@@ -31992,7 +32582,7 @@ function resolveClickUpToken(projectRoot, opts) {
31992
32582
  return null;
31993
32583
  }
31994
32584
  function hasSecretsFile(projectRoot) {
31995
- return existsSync21(secretsPath(projectRoot));
32585
+ return existsSync23(secretsPath(projectRoot));
31996
32586
  }
31997
32587
  function maskToken(token) {
31998
32588
  const t = token.trim();
@@ -32010,14 +32600,14 @@ var init_secrets = __esm({
32010
32600
  });
32011
32601
 
32012
32602
  // src/lib/task-backend/identity.ts
32013
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "fs";
32014
- import { join as join22 } from "path";
32603
+ import { existsSync as existsSync24, readFileSync as readFileSync22 } from "fs";
32604
+ import { join as join24 } from "path";
32015
32605
  function seedRolesFromTeamOwners(contextRoot) {
32016
- const path2 = join22(contextRoot, "knowledge", "team_owners.md");
32017
- if (!existsSync22(path2)) return {};
32606
+ const path2 = join24(contextRoot, "knowledge", "team_owners.md");
32607
+ if (!existsSync24(path2)) return {};
32018
32608
  const roles = {};
32019
32609
  try {
32020
- const lines = readFileSync20(path2, "utf-8").split("\n");
32610
+ const lines = readFileSync22(path2, "utf-8").split("\n");
32021
32611
  for (const line of lines) {
32022
32612
  const bullet = line.match(/^\s*[-*]\s*([^:|]+):\s*(.+)\s*$/);
32023
32613
  if (bullet) {
@@ -32235,23 +32825,23 @@ var init_paths = __esm({
32235
32825
  });
32236
32826
 
32237
32827
  // src/lib/task-backend/sync-state.ts
32238
- import { createHash } from "crypto";
32239
- import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync21, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync18 } from "fs";
32240
- import { dirname as dirname9, join as join23 } from "path";
32828
+ import { createHash as createHash2 } from "crypto";
32829
+ import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync23, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync20 } from "fs";
32830
+ import { dirname as dirname11, join as join25 } from "path";
32241
32831
  function readJson(path2, fallback) {
32242
- if (!existsSync23(path2)) return fallback;
32832
+ if (!existsSync25(path2)) return fallback;
32243
32833
  try {
32244
- return JSON.parse(readFileSync21(path2, "utf-8"));
32834
+ return JSON.parse(readFileSync23(path2, "utf-8"));
32245
32835
  } catch {
32246
32836
  return fallback;
32247
32837
  }
32248
32838
  }
32249
32839
  function writeJson(path2, data) {
32250
- mkdirSync10(dirname9(path2), { recursive: true });
32251
- writeFileSync18(path2, JSON.stringify(data, null, 2) + "\n", "utf-8");
32840
+ mkdirSync12(dirname11(path2), { recursive: true });
32841
+ writeFileSync20(path2, JSON.stringify(data, null, 2) + "\n", "utf-8");
32252
32842
  }
32253
32843
  function hashContent(content) {
32254
- return createHash("sha256").update(content, "utf-8").digest("hex");
32844
+ return createHash2("sha256").update(content, "utf-8").digest("hex");
32255
32845
  }
32256
32846
  var SyncLedger;
32257
32847
  var init_sync_state = __esm({
@@ -32263,16 +32853,16 @@ var init_sync_state = __esm({
32263
32853
  this.contextRoot = contextRoot;
32264
32854
  }
32265
32855
  get mapPath() {
32266
- return join23(this.contextRoot, TASKS_MAP_REL);
32856
+ return join25(this.contextRoot, TASKS_MAP_REL);
32267
32857
  }
32268
32858
  get syncPath() {
32269
- return join23(this.contextRoot, TASKS_SYNC_REL);
32859
+ return join25(this.contextRoot, TASKS_SYNC_REL);
32270
32860
  }
32271
32861
  get queuePath() {
32272
- return join23(this.contextRoot, TASKS_QUEUE_REL);
32862
+ return join25(this.contextRoot, TASKS_QUEUE_REL);
32273
32863
  }
32274
32864
  conflictsDir() {
32275
- return join23(this.contextRoot, CONFLICTS_DIR_REL);
32865
+ return join25(this.contextRoot, CONFLICTS_DIR_REL);
32276
32866
  }
32277
32867
  // ── id-map (committed) ──
32278
32868
  readMap() {
@@ -32384,17 +32974,17 @@ var init_sync_state = __esm({
32384
32974
  */
32385
32975
  reset() {
32386
32976
  let backupPath2 = null;
32387
- if (existsSync23(this.mapPath)) {
32977
+ if (existsSync25(this.mapPath)) {
32388
32978
  backupPath2 = this.mapPath.replace(/\.json$/, `.backup-${Date.now()}.json`);
32389
- writeFileSync18(backupPath2, readFileSync21(this.mapPath));
32390
- rmSync3(this.mapPath);
32979
+ writeFileSync20(backupPath2, readFileSync23(this.mapPath));
32980
+ rmSync4(this.mapPath);
32391
32981
  }
32392
- if (existsSync23(this.syncPath)) rmSync3(this.syncPath);
32393
- if (existsSync23(this.queuePath)) rmSync3(this.queuePath);
32982
+ if (existsSync25(this.syncPath)) rmSync4(this.syncPath);
32983
+ if (existsSync25(this.queuePath)) rmSync4(this.queuePath);
32394
32984
  return { backupPath: backupPath2 };
32395
32985
  }
32396
32986
  get lockPath() {
32397
- return join23(this.contextRoot, TASKS_LOCK_REL);
32987
+ return join25(this.contextRoot, TASKS_LOCK_REL);
32398
32988
  }
32399
32989
  /**
32400
32990
  * SYNC LOCK — at most one sync engine per project at a time. Sync fires
@@ -32406,10 +32996,10 @@ var init_sync_state = __esm({
32406
32996
  * (JSON timestamp first, file mtime as the fallback for garbage content).
32407
32997
  */
32408
32998
  acquireSyncLock(nowMs, staleMs) {
32409
- mkdirSync10(dirname9(this.lockPath), { recursive: true });
32999
+ mkdirSync12(dirname11(this.lockPath), { recursive: true });
32410
33000
  const tryCreate = () => {
32411
33001
  try {
32412
- writeFileSync18(this.lockPath, JSON.stringify({ pid: process.pid, at: nowMs }) + "\n", { flag: "wx" });
33002
+ writeFileSync20(this.lockPath, JSON.stringify({ pid: process.pid, at: nowMs }) + "\n", { flag: "wx" });
32413
33003
  return true;
32414
33004
  } catch {
32415
33005
  return false;
@@ -32418,7 +33008,7 @@ var init_sync_state = __esm({
32418
33008
  if (tryCreate()) return true;
32419
33009
  let heldSince = null;
32420
33010
  try {
32421
- const info2 = JSON.parse(readFileSync21(this.lockPath, "utf-8"));
33011
+ const info2 = JSON.parse(readFileSync23(this.lockPath, "utf-8"));
32422
33012
  if (typeof info2.at === "number") heldSince = info2.at;
32423
33013
  } catch {
32424
33014
  }
@@ -32431,14 +33021,14 @@ var init_sync_state = __esm({
32431
33021
  }
32432
33022
  if (nowMs - heldSince <= staleMs) return false;
32433
33023
  try {
32434
- rmSync3(this.lockPath, { force: true });
33024
+ rmSync4(this.lockPath, { force: true });
32435
33025
  } catch {
32436
33026
  }
32437
33027
  return tryCreate();
32438
33028
  }
32439
33029
  releaseSyncLock() {
32440
33030
  try {
32441
- rmSync3(this.lockPath, { force: true });
33031
+ rmSync4(this.lockPath, { force: true });
32442
33032
  } catch {
32443
33033
  }
32444
33034
  }
@@ -32450,8 +33040,8 @@ var init_sync_state = __esm({
32450
33040
  });
32451
33041
 
32452
33042
  // src/lib/task-backend/clickup.ts
32453
- import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
32454
- import { basename as basename7, dirname as dirname10, join as join24 } from "path";
33043
+ import { existsSync as existsSync26, mkdirSync as mkdirSync13, readFileSync as readFileSync24, writeFileSync as writeFileSync21 } from "fs";
33044
+ import { basename as basename7, dirname as dirname12, join as join26 } from "path";
32455
33045
  function stripPersonTags(tags) {
32456
33046
  return tags.filter((t) => !t.startsWith(PERSON_TAG_PREFIX));
32457
33047
  }
@@ -32556,7 +33146,7 @@ var init_clickup = __esm({
32556
33146
  CLICKUP_MAX_RETRIES = 5;
32557
33147
  ClickUpTaskBackend = class _ClickUpTaskBackend extends LocalTaskBackend {
32558
33148
  constructor(contextRoot, config, deps = {}) {
32559
- super(join24(contextRoot, "state"));
33149
+ super(join26(contextRoot, "state"));
32560
33150
  this.contextRoot = contextRoot;
32561
33151
  this.config = config;
32562
33152
  this.deps = deps;
@@ -32571,7 +33161,7 @@ var init_clickup = __esm({
32571
33161
  return (this.deps.now ?? Date.now)();
32572
33162
  }
32573
33163
  get projectRoot() {
32574
- return dirname10(this.contextRoot);
33164
+ return dirname12(this.contextRoot);
32575
33165
  }
32576
33166
  getAdapter() {
32577
33167
  if (this.deps.adapter) return this.deps.adapter;
@@ -32905,7 +33495,7 @@ var init_clickup = __esm({
32905
33495
  for (const file of this.taskFiles()) {
32906
33496
  const slug = basename7(file, ".md");
32907
33497
  const entry = state.tasks[slug];
32908
- const hash = hashContent(readFileSync22(file, "utf-8"));
33498
+ const hash = hashContent(readFileSync24(file, "utf-8"));
32909
33499
  if (!entry?.localHash || entry.localHash !== hash) candidates.add(slug);
32910
33500
  }
32911
33501
  for (const slug of [...candidates].sort()) {
@@ -32919,7 +33509,7 @@ var init_clickup = __esm({
32919
33509
  }
32920
33510
  async pushTask(slug, adapter, listId, report) {
32921
33511
  const path2 = this.taskPath(slug);
32922
- if (!existsSync24(path2)) return;
33512
+ if (!existsSync26(path2)) return;
32923
33513
  const task = await super.get(slug);
32924
33514
  const enqueueCutoff = this.nowMs();
32925
33515
  const entry = this.ledger.taskSync(slug);
@@ -33024,7 +33614,7 @@ var init_clickup = __esm({
33024
33614
  serverTime = serverTimeMs(comment.date) ?? serverTime;
33025
33615
  report.commentsAdded++;
33026
33616
  }
33027
- const raw = readFileSync22(path2, "utf-8");
33617
+ const raw = readFileSync24(path2, "utf-8");
33028
33618
  this.ledger.updateTaskSync(slug, {
33029
33619
  last_synced_at: serverTime ?? entry?.last_synced_at ?? 0,
33030
33620
  base_snapshot: { hash: hashContent(raw), body: raw },
@@ -33112,8 +33702,8 @@ var init_clickup = __esm({
33112
33702
  if (pendingDeletes.has(entry.remoteId)) continue;
33113
33703
  const path2 = this.taskPath(entry.slug);
33114
33704
  try {
33115
- if (existsSync24(path2)) {
33116
- const raw = readFileSync22(path2, "utf-8");
33705
+ if (existsSync26(path2)) {
33706
+ const raw = readFileSync24(path2, "utf-8");
33117
33707
  const syncEntry = this.ledger.taskSync(entry.slug);
33118
33708
  const localChanged = !syncEntry?.localHash || hashContent(raw) !== syncEntry.localHash;
33119
33709
  if (localChanged) {
@@ -33165,7 +33755,7 @@ var init_clickup = __esm({
33165
33755
  )
33166
33756
  ].sort();
33167
33757
  let slug = this.ledger.slugForRemoteId(remote.id);
33168
- if (!slug || !existsSync24(this.taskPath(slug))) {
33758
+ if (!slug || !existsSync26(this.taskPath(slug))) {
33169
33759
  slug = slug ?? this.uniqueSlugFor(remote.name);
33170
33760
  const fm2 = {
33171
33761
  id: generateId("task"),
@@ -33208,7 +33798,7 @@ var init_clickup = __esm({
33208
33798
  }
33209
33799
  const path2 = this.taskPath(slug);
33210
33800
  const entry = this.ledger.taskSync(slug);
33211
- const localRaw = readFileSync22(path2, "utf-8");
33801
+ const localRaw = readFileSync24(path2, "utf-8");
33212
33802
  const local = await this.getLocal(slug);
33213
33803
  const localChanged = entry?.localHash ? hashContent(localRaw) !== entry.localHash : true;
33214
33804
  const localChangedAt = entry?.lastLocalChangeAt ?? null;
@@ -33420,7 +34010,7 @@ var init_clickup = __esm({
33420
34010
  const baseSlug = slugify(name) || "task";
33421
34011
  let candidate = baseSlug;
33422
34012
  let n = 1;
33423
- while (existsSync24(this.taskPath(candidate))) {
34013
+ while (existsSync26(this.taskPath(candidate))) {
33424
34014
  n++;
33425
34015
  candidate = `${baseSlug}-${n}`;
33426
34016
  }
@@ -33442,7 +34032,7 @@ ${changelog}` : ""}`;
33442
34032
  const content = this.renderMirror(fm, desc, changelogEntries);
33443
34033
  this.applyingRemote = true;
33444
34034
  try {
33445
- writeFileSync19(this.taskPath(slug), content, "utf-8");
34035
+ writeFileSync21(this.taskPath(slug), content, "utf-8");
33446
34036
  } finally {
33447
34037
  this.applyingRemote = false;
33448
34038
  }
@@ -33451,15 +34041,15 @@ ${changelog}` : ""}`;
33451
34041
  /** Preserve a losing local copy under state/.conflicts/ — never silent loss. */
33452
34042
  saveConflictCopy(slug, localRaw, remoteTime) {
33453
34043
  const dir = this.ledger.conflictsDir();
33454
- mkdirSync11(dir, { recursive: true });
34044
+ mkdirSync13(dir, { recursive: true });
33455
34045
  const stamp = remoteTime ?? this.nowMs();
33456
- let path2 = join24(dir, `${slug}-${stamp}.md`);
34046
+ let path2 = join26(dir, `${slug}-${stamp}.md`);
33457
34047
  let n = 1;
33458
- while (existsSync24(path2)) {
34048
+ while (existsSync26(path2)) {
33459
34049
  n++;
33460
- path2 = join24(dir, `${slug}-${stamp}-${n}.md`);
34050
+ path2 = join26(dir, `${slug}-${stamp}-${n}.md`);
33461
34051
  }
33462
- writeFileSync19(path2, localRaw, "utf-8");
34052
+ writeFileSync21(path2, localRaw, "utf-8");
33463
34053
  return path2;
33464
34054
  }
33465
34055
  };
@@ -33467,8 +34057,8 @@ ${changelog}` : ""}`;
33467
34057
  });
33468
34058
 
33469
34059
  // src/lib/task-backend/git-hooks.ts
33470
- import { chmodSync as chmodSync2, existsSync as existsSync25, readFileSync as readFileSync23, rmSync as rmSync4, writeFileSync as writeFileSync20 } from "fs";
33471
- import { join as join25 } from "path";
34060
+ import { chmodSync as chmodSync2, existsSync as existsSync27, readFileSync as readFileSync25, rmSync as rmSync5, writeFileSync as writeFileSync22 } from "fs";
34061
+ import { join as join27 } from "path";
33472
34062
  function taskSyncHookScript(cliInvocation) {
33473
34063
  return `#!/bin/sh
33474
34064
  ${MARKER}
@@ -33489,23 +34079,23 @@ function defaultCliInvocation() {
33489
34079
  return `${quote(process.execPath)} ${quote(entry)}`;
33490
34080
  }
33491
34081
  function installTaskSyncHooks(projectRoot, opts = {}) {
33492
- const hooksDir = join25(projectRoot, ".git", "hooks");
33493
- if (!existsSync25(join25(projectRoot, ".git"))) {
34082
+ const hooksDir = join27(projectRoot, ".git", "hooks");
34083
+ if (!existsSync27(join27(projectRoot, ".git"))) {
33494
34084
  return { installed: [], skipped: [], noGit: true };
33495
34085
  }
33496
34086
  const script = taskSyncHookScript(opts.cliInvocation ?? defaultCliInvocation());
33497
34087
  const installed = [];
33498
34088
  const skipped = [];
33499
34089
  for (const hook of TASK_SYNC_HOOKS) {
33500
- const path2 = join25(hooksDir, hook);
33501
- if (existsSync25(path2)) {
33502
- const existing = readFileSync23(path2, "utf-8");
34090
+ const path2 = join27(hooksDir, hook);
34091
+ if (existsSync27(path2)) {
34092
+ const existing = readFileSync25(path2, "utf-8");
33503
34093
  if (!existing.includes(MARKER)) {
33504
34094
  skipped.push(hook);
33505
34095
  continue;
33506
34096
  }
33507
34097
  }
33508
- writeFileSync20(path2, script, "utf-8");
34098
+ writeFileSync22(path2, script, "utf-8");
33509
34099
  try {
33510
34100
  chmodSync2(path2, 493);
33511
34101
  } catch {
@@ -33515,12 +34105,12 @@ function installTaskSyncHooks(projectRoot, opts = {}) {
33515
34105
  return { installed, skipped, noGit: false };
33516
34106
  }
33517
34107
  function uninstallTaskSyncHooks(projectRoot) {
33518
- const hooksDir = join25(projectRoot, ".git", "hooks");
34108
+ const hooksDir = join27(projectRoot, ".git", "hooks");
33519
34109
  const removed = [];
33520
34110
  for (const hook of TASK_SYNC_HOOKS) {
33521
- const path2 = join25(hooksDir, hook);
33522
- if (existsSync25(path2) && readFileSync23(path2, "utf-8").includes(MARKER)) {
33523
- rmSync4(path2);
34111
+ const path2 = join27(hooksDir, hook);
34112
+ if (existsSync27(path2) && readFileSync25(path2, "utf-8").includes(MARKER)) {
34113
+ rmSync5(path2);
33524
34114
  removed.push(hook);
33525
34115
  }
33526
34116
  }
@@ -33530,8 +34120,8 @@ function hasManagedTaskSyncHooks(projectRoot) {
33530
34120
  return TASK_SYNC_HOOKS.some((hook) => isManagedTaskSyncHook(projectRoot, hook));
33531
34121
  }
33532
34122
  function isManagedTaskSyncHook(projectRoot, hook) {
33533
- const path2 = join25(projectRoot, ".git", "hooks", hook);
33534
- return existsSync25(path2) && readFileSync23(path2, "utf-8").includes(MARKER);
34123
+ const path2 = join27(projectRoot, ".git", "hooks", hook);
34124
+ return existsSync27(path2) && readFileSync25(path2, "utf-8").includes(MARKER);
33535
34125
  }
33536
34126
  var MARKER, TASK_SYNC_HOOKS;
33537
34127
  var init_git_hooks = __esm({
@@ -33543,18 +34133,18 @@ var init_git_hooks = __esm({
33543
34133
  });
33544
34134
 
33545
34135
  // src/lib/task-backend/index.ts
33546
- import { existsSync as existsSync26, readdirSync as readdirSync6 } from "fs";
33547
- import { dirname as dirname11, join as join26 } from "path";
34136
+ import { existsSync as existsSync28, readdirSync as readdirSync7 } from "fs";
34137
+ import { dirname as dirname13, join as join28 } from "path";
33548
34138
  function getTaskBackend(contextRoot, config, deps) {
33549
34139
  const root = contextRoot ?? ensureContextRoot();
33550
- const cfg = config !== void 0 ? config : readSetupConfig(dirname11(root));
34140
+ const cfg = config !== void 0 ? config : readSetupConfig(dirname13(root));
33551
34141
  if (cfg?.taskBackend === "clickup") {
33552
34142
  return createClickUpBackend(root, cfg ?? null, deps);
33553
34143
  }
33554
- return new LocalTaskBackend(join26(root, "state"));
34144
+ return new LocalTaskBackend(join28(root, "state"));
33555
34145
  }
33556
34146
  function getTaskSyncStatus(contextRoot, config) {
33557
- const cfg = config !== void 0 ? config : readSetupConfig(dirname11(contextRoot));
34147
+ const cfg = config !== void 0 ? config : readSetupConfig(dirname13(contextRoot));
33558
34148
  const ledger = new SyncLedger(contextRoot);
33559
34149
  const state = ledger.readSyncState();
33560
34150
  const conflictsDir = ledger.conflictsDir();
@@ -33562,7 +34152,7 @@ function getTaskSyncStatus(contextRoot, config) {
33562
34152
  backend: cfg?.taskBackend ?? "local",
33563
34153
  pendingPush: Object.values(state.tasks).filter((t) => t.pendingPush).length,
33564
34154
  queuedOps: ledger.readQueue().length,
33565
- conflicts: existsSync26(conflictsDir) ? readdirSync6(conflictsDir).filter((f) => f.endsWith(".md")).length : 0,
34155
+ conflicts: existsSync28(conflictsDir) ? readdirSync7(conflictsDir).filter((f) => f.endsWith(".md")).length : 0,
33566
34156
  watermark: state.watermark
33567
34157
  };
33568
34158
  }
@@ -33583,186 +34173,9 @@ var init_task_backend = __esm({
33583
34173
  }
33584
34174
  });
33585
34175
 
33586
- // src/lib/version-check.ts
33587
- import { existsSync as existsSync27, readFileSync as readFileSync24, mkdirSync as mkdirSync12, writeFileSync as writeFileSync21 } from "fs";
33588
- import { dirname as dirname12, join as join27 } from "path";
33589
- import { execFileSync, spawn as spawn2 } from "child_process";
33590
- function cachePath(root) {
33591
- return join27(root, CACHE_REL_PATH);
33592
- }
33593
- function readVersionCache(root) {
33594
- const path2 = cachePath(root);
33595
- if (!existsSync27(path2)) return null;
33596
- try {
33597
- const raw = readFileSync24(path2, "utf-8");
33598
- const parsed = JSON.parse(raw);
33599
- if (!parsed || typeof parsed !== "object") return null;
33600
- if (typeof parsed.checkedAt !== "number") return null;
33601
- return {
33602
- checkedAt: parsed.checkedAt,
33603
- latestCli: typeof parsed.latestCli === "string" ? parsed.latestCli : null,
33604
- availablePacks: Array.isArray(parsed.availablePacks) ? parsed.availablePacks.filter((p) => typeof p === "string") : [],
33605
- ttlHours: typeof parsed.ttlHours === "number" ? parsed.ttlHours : DEFAULT_TTL_HOURS
33606
- };
33607
- } catch {
33608
- return null;
33609
- }
33610
- }
33611
- function writeVersionCache(root, cache) {
33612
- const path2 = cachePath(root);
33613
- try {
33614
- mkdirSync12(dirname12(path2), { recursive: true });
33615
- writeFileSync21(path2, JSON.stringify(cache, null, 2) + "\n", "utf-8");
33616
- } catch {
33617
- }
33618
- }
33619
- function isCacheFresh(cache, nowMs) {
33620
- if (!cache) return false;
33621
- try {
33622
- if (typeof cache.checkedAt !== "number" || !isFinite(cache.checkedAt)) return false;
33623
- const ttlHours = typeof cache.ttlHours === "number" && isFinite(cache.ttlHours) && cache.ttlHours > 0 ? cache.ttlHours : DEFAULT_TTL_HOURS;
33624
- const ttlMs = ttlHours * 60 * 60 * 1e3;
33625
- const now = nowMs ?? Date.now();
33626
- return now - cache.checkedAt < ttlMs;
33627
- } catch {
33628
- return false;
33629
- }
33630
- }
33631
- function compareVersions(a, b) {
33632
- const clean = (v) => v.replace(/[-+].*$/, "").trim();
33633
- const parseSegments = (v) => clean(v).split(".").map((s) => {
33634
- const n = parseInt(s, 10);
33635
- return isFinite(n) ? n : 0;
33636
- });
33637
- const segA = parseSegments(a);
33638
- const segB = parseSegments(b);
33639
- const len = Math.max(segA.length, segB.length);
33640
- for (let i = 0; i < len; i++) {
33641
- const sa = segA[i] ?? 0;
33642
- const sb = segB[i] ?? 0;
33643
- if (sa < sb) return -1;
33644
- if (sa > sb) return 1;
33645
- }
33646
- return 0;
33647
- }
33648
- function buildNudge(installedCli, cache, installedPacks, catalogPackNames, opts = {}) {
33649
- if (!cache || cache.latestCli === null) return null;
33650
- const cliOutdated = compareVersions(installedCli, cache.latestCli) < 0;
33651
- const showCliNudge = cliOutdated && !opts.suppressCliNudge;
33652
- const newPacks = catalogPackNames.filter((p) => !installedPacks.includes(p));
33653
- const hasNewPacks = newPacks.length > 0;
33654
- if (!showCliNudge && !hasNewPacks) return null;
33655
- const lines = ["## Update Available\n"];
33656
- if (showCliNudge) {
33657
- lines.push(`- **New version available: v${installedCli} \u2192 v${cache.latestCli}.** A new release can add, update, or remove hooks, prompts, sub-agents, and skills. Run \`dreamcontext upgrade\` to get it, then \`dreamcontext update\` to apply those changes to this project.`);
33658
- }
33659
- if (hasNewPacks) {
33660
- lines.push(`- New skill pack${newPacks.length > 1 ? "s" : ""} available: ${newPacks.join(", ")} \u2014 run \`dreamcontext install-skill --packs\``);
33661
- }
33662
- return lines.join("\n");
33663
- }
33664
- function refreshVersionCache(root, opts) {
33665
- const runner = opts?.runner ?? ((args) => execFileSync("npm", args, {
33666
- timeout: NPM_TIMEOUT_MS,
33667
- encoding: "utf-8",
33668
- stdio: ["pipe", "pipe", "pipe"]
33669
- }));
33670
- const catalogPackNames = opts?.catalogPackNames ?? [];
33671
- let latestCli = null;
33672
- try {
33673
- const raw = runner(["view", "dreamcontext", "version"]);
33674
- const trimmed = (typeof raw === "string" ? raw : "").trim();
33675
- if (/^\d+\.\d+/.test(trimmed)) {
33676
- latestCli = trimmed;
33677
- }
33678
- } catch {
33679
- latestCli = null;
33680
- }
33681
- const cache = {
33682
- checkedAt: Date.now(),
33683
- latestCli,
33684
- availablePacks: catalogPackNames,
33685
- ttlHours: DEFAULT_TTL_HOURS
33686
- };
33687
- writeVersionCache(root, cache);
33688
- }
33689
- function markerPath(root) {
33690
- return join27(root, AUTO_UPGRADE_MARKER_REL_PATH);
33691
- }
33692
- function readAutoUpgradeMarker(root) {
33693
- const path2 = markerPath(root);
33694
- if (!existsSync27(path2)) return null;
33695
- try {
33696
- const parsed = JSON.parse(readFileSync24(path2, "utf-8"));
33697
- if (!parsed || typeof parsed !== "object") return null;
33698
- if (typeof parsed.attemptedFor !== "string" || typeof parsed.at !== "number") return null;
33699
- return { attemptedFor: parsed.attemptedFor, at: parsed.at };
33700
- } catch {
33701
- return null;
33702
- }
33703
- }
33704
- function writeAutoUpgradeMarker(root, marker) {
33705
- const path2 = markerPath(root);
33706
- try {
33707
- mkdirSync12(dirname12(path2), { recursive: true });
33708
- writeFileSync21(path2, JSON.stringify(marker, null, 2) + "\n", "utf-8");
33709
- } catch {
33710
- }
33711
- }
33712
- function autoUpgradeEnabled(env2 = process.env) {
33713
- if (env2.DREAMCONTEXT_VERSION_CHECK === "0") return false;
33714
- return env2.DREAMCONTEXT_AUTO_UPGRADE !== "0";
33715
- }
33716
- function shouldAutoUpgrade(installedCli, cache, env2 = process.env) {
33717
- if (!autoUpgradeEnabled(env2)) return null;
33718
- if (!cache || cache.latestCli === null) return null;
33719
- if (compareVersions(installedCli, cache.latestCli) >= 0) return null;
33720
- return cache.latestCli;
33721
- }
33722
- function shouldSuppressCliNudge(target, marker, env2 = process.env, now = Date.now()) {
33723
- if (!autoUpgradeEnabled(env2)) return false;
33724
- if (!target || !marker) return false;
33725
- if (marker.attemptedFor !== target) return false;
33726
- return now - marker.at < AUTO_UPGRADE_INFLIGHT_MS;
33727
- }
33728
- function defaultSpawner(cmd, args) {
33729
- const child = spawn2(cmd, args, { detached: true, stdio: "ignore" });
33730
- child.unref();
33731
- }
33732
- function maybeAutoUpgrade(root, installedCli, cache, env2 = process.env, deps = {}) {
33733
- try {
33734
- const target = shouldAutoUpgrade(installedCli, cache, env2);
33735
- if (!target) return null;
33736
- const now = deps.now ?? Date.now();
33737
- const readMarker = deps.readMarker ?? readAutoUpgradeMarker;
33738
- const writeMarker = deps.writeMarker ?? writeAutoUpgradeMarker;
33739
- const marker = readMarker(root);
33740
- const recentlyAttempted = marker !== null && marker.attemptedFor === target && now - marker.at < AUTO_UPGRADE_RETRY_MS;
33741
- if (recentlyAttempted) return null;
33742
- writeMarker(root, { attemptedFor: target, at: now });
33743
- const spawner = deps.spawner ?? defaultSpawner;
33744
- spawner("npm", ["install", "-g", "dreamcontext@latest"]);
33745
- return `\u2B06 Auto-upgrading dreamcontext v${installedCli} \u2192 v${target} in the background (takes effect next session).`;
33746
- } catch {
33747
- return null;
33748
- }
33749
- }
33750
- var CACHE_REL_PATH, AUTO_UPGRADE_MARKER_REL_PATH, DEFAULT_TTL_HOURS, NPM_TIMEOUT_MS, AUTO_UPGRADE_RETRY_MS, AUTO_UPGRADE_INFLIGHT_MS;
33751
- var init_version_check = __esm({
33752
- "src/lib/version-check.ts"() {
33753
- "use strict";
33754
- CACHE_REL_PATH = "_dream_context/state/.version-check.json";
33755
- AUTO_UPGRADE_MARKER_REL_PATH = "_dream_context/state/.auto-upgrade.json";
33756
- DEFAULT_TTL_HOURS = 24;
33757
- NPM_TIMEOUT_MS = 5e3;
33758
- AUTO_UPGRADE_RETRY_MS = 24 * 60 * 60 * 1e3;
33759
- AUTO_UPGRADE_INFLIGHT_MS = 60 * 60 * 1e3;
33760
- }
33761
- });
33762
-
33763
34176
  // src/lib/data-structures-migration.ts
33764
- import { existsSync as existsSync28, mkdirSync as mkdirSync13 } from "fs";
33765
- import { join as join28, basename as basename8 } from "path";
34177
+ import { existsSync as existsSync29, mkdirSync as mkdirSync14 } from "fs";
34178
+ import { join as join29, basename as basename8 } from "path";
33766
34179
  function enrichDataStructuresFrontmatter(data, product) {
33767
34180
  const existingTags = Array.isArray(data.tags) ? data.tags.map(String) : [];
33768
34181
  const tags = [...existingTags];
@@ -33784,16 +34197,16 @@ function ensureSqlFence(body) {
33784
34197
  }
33785
34198
  function migrateDataStructures(contextRoot) {
33786
34199
  const result = { migrated: [], skipped: [] };
33787
- const oldDir = join28(contextRoot, OLD_DATA_STRUCTURES_DIR);
33788
- if (!existsSync28(oldDir)) return result;
34200
+ const oldDir = join29(contextRoot, OLD_DATA_STRUCTURES_DIR);
34201
+ if (!existsSync29(oldDir)) return result;
33789
34202
  const files = import_fast_glob6.default.sync("*.md", { cwd: oldDir, absolute: true });
33790
34203
  if (files.length === 0) return result;
33791
- const newDir = join28(contextRoot, NEW_DATA_STRUCTURES_DIR);
33792
- mkdirSync13(newDir, { recursive: true });
34204
+ const newDir = join29(contextRoot, NEW_DATA_STRUCTURES_DIR);
34205
+ mkdirSync14(newDir, { recursive: true });
33793
34206
  for (const oldFile of files) {
33794
34207
  const product = basename8(oldFile, ".md");
33795
- const newFile = join28(newDir, `${product}.md`);
33796
- if (existsSync28(newFile)) {
34208
+ const newFile = join29(newDir, `${product}.md`);
34209
+ if (existsSync29(newFile)) {
33797
34210
  result.skipped.push(product);
33798
34211
  continue;
33799
34212
  }
@@ -33804,8 +34217,8 @@ function migrateDataStructures(contextRoot) {
33804
34217
  return result;
33805
34218
  }
33806
34219
  function listUnfencedDataStructures(contextRoot) {
33807
- const dir = join28(contextRoot, NEW_DATA_STRUCTURES_DIR);
33808
- if (!existsSync28(dir)) return [];
34220
+ const dir = join29(contextRoot, NEW_DATA_STRUCTURES_DIR);
34221
+ if (!existsSync29(dir)) return [];
33809
34222
  const out = [];
33810
34223
  for (const file of import_fast_glob6.default.sync("*.md", { cwd: dir, absolute: true })) {
33811
34224
  try {
@@ -33817,8 +34230,8 @@ function listUnfencedDataStructures(contextRoot) {
33817
34230
  return out.sort();
33818
34231
  }
33819
34232
  function fenceExistingDataStructures(contextRoot) {
33820
- const dir = join28(contextRoot, NEW_DATA_STRUCTURES_DIR);
33821
- if (!existsSync28(dir)) return [];
34233
+ const dir = join29(contextRoot, NEW_DATA_STRUCTURES_DIR);
34234
+ if (!existsSync29(dir)) return [];
33822
34235
  const fenced = [];
33823
34236
  for (const file of import_fast_glob6.default.sync("*.md", { cwd: dir, absolute: true })) {
33824
34237
  try {
@@ -33839,8 +34252,8 @@ var init_data_structures_migration = __esm({
33839
34252
  "use strict";
33840
34253
  import_fast_glob6 = __toESM(require_out4(), 1);
33841
34254
  init_frontmatter();
33842
- OLD_DATA_STRUCTURES_DIR = join28("core", "data-structures");
33843
- NEW_DATA_STRUCTURES_DIR = join28("knowledge", "data-structures");
34255
+ OLD_DATA_STRUCTURES_DIR = join29("core", "data-structures");
34256
+ NEW_DATA_STRUCTURES_DIR = join29("knowledge", "data-structures");
33844
34257
  DATA_STRUCTURES_TAGS = ["data-structures", "database", "schema"];
33845
34258
  }
33846
34259
  });
@@ -33892,13 +34305,13 @@ var init__ = __esm({
33892
34305
 
33893
34306
  // src/lib/wikilink-rewrite.ts
33894
34307
  import {
33895
- existsSync as existsSync29,
33896
- readFileSync as readFileSync25,
33897
- writeFileSync as writeFileSync22,
33898
- renameSync,
33899
- mkdirSync as mkdirSync14
34308
+ existsSync as existsSync30,
34309
+ readFileSync as readFileSync26,
34310
+ writeFileSync as writeFileSync23,
34311
+ renameSync as renameSync2,
34312
+ mkdirSync as mkdirSync15
33900
34313
  } from "fs";
33901
- import { dirname as dirname13 } from "path";
34314
+ import { dirname as dirname14 } from "path";
33902
34315
  function rewriteWikilinks(contextRoot, remaps) {
33903
34316
  if (remaps.length === 0) return [];
33904
34317
  const remapMap = new Map(remaps.map((r) => [r.from, r.to]));
@@ -33911,7 +34324,7 @@ function rewriteWikilinks(contextRoot, remaps) {
33911
34324
  for (const file of files) {
33912
34325
  let content;
33913
34326
  try {
33914
- content = readFileSync25(file, "utf-8");
34327
+ content = readFileSync26(file, "utf-8");
33915
34328
  } catch {
33916
34329
  continue;
33917
34330
  }
@@ -33919,14 +34332,14 @@ function rewriteWikilinks(contextRoot, remaps) {
33919
34332
  if (rewritten === content) continue;
33920
34333
  const tmp = file + ".wl-tmp";
33921
34334
  try {
33922
- mkdirSync14(dirname13(tmp), { recursive: true });
33923
- writeFileSync22(tmp, rewritten, "utf-8");
33924
- renameSync(tmp, file);
34335
+ mkdirSync15(dirname14(tmp), { recursive: true });
34336
+ writeFileSync23(tmp, rewritten, "utf-8");
34337
+ renameSync2(tmp, file);
33925
34338
  changed.push(file);
33926
34339
  } catch {
33927
34340
  try {
33928
- if (existsSync29(tmp)) {
33929
- writeFileSync22(tmp, "");
34341
+ if (existsSync30(tmp)) {
34342
+ writeFileSync23(tmp, "");
33930
34343
  }
33931
34344
  } catch {
33932
34345
  }
@@ -34002,13 +34415,13 @@ var init_wikilink_rewrite = __esm({
34002
34415
 
34003
34416
  // src/lib/diagrams-migration.ts
34004
34417
  import {
34005
- existsSync as existsSync30,
34006
- mkdirSync as mkdirSync15,
34007
- renameSync as renameSync2,
34008
- readdirSync as readdirSync7,
34418
+ existsSync as existsSync31,
34419
+ mkdirSync as mkdirSync16,
34420
+ renameSync as renameSync3,
34421
+ readdirSync as readdirSync8,
34009
34422
  statSync as statSync4
34010
34423
  } from "fs";
34011
- import { join as join30, resolve as resolve4, sep as sep3 } from "path";
34424
+ import { join as join31, resolve as resolve5, sep as sep3 } from "path";
34012
34425
  function isSameBasenameSibling(filename, boardBase) {
34013
34426
  for (const ext of GENERATOR_EXTS) {
34014
34427
  if (filename === `${boardBase}${ext}`) return true;
@@ -34016,9 +34429,9 @@ function isSameBasenameSibling(filename, boardBase) {
34016
34429
  return false;
34017
34430
  }
34018
34431
  function migrateDiagramsToFolders(contextRoot) {
34019
- const diagramsDir = join30(contextRoot, "knowledge", "diagrams");
34432
+ const diagramsDir = join31(contextRoot, "knowledge", "diagrams");
34020
34433
  const result = { moved: [], skipped: [], ambiguous: [] };
34021
- if (!existsSync30(diagramsDir)) return result;
34434
+ if (!existsSync31(diagramsDir)) return result;
34022
34435
  const flatFiles = import_fast_glob8.default.sync("*.excalidraw.md", {
34023
34436
  cwd: diagramsDir,
34024
34437
  absolute: false
@@ -34030,13 +34443,13 @@ function migrateDiagramsToFolders(contextRoot) {
34030
34443
  for (const nested of nestedFiles) {
34031
34444
  result.skipped.push(nested.replace(EXCALIDRAW_SUFFIX, ""));
34032
34445
  }
34033
- const diagramsDirResolved = resolve4(diagramsDir);
34446
+ const diagramsDirResolved = resolve5(diagramsDir);
34034
34447
  for (const filename of flatFiles) {
34035
34448
  const boardBase = filename.slice(0, -EXCALIDRAW_SUFFIX.length);
34036
- const srcBoard = join30(diagramsDir, filename);
34037
- const destDir = join30(diagramsDir, boardBase);
34038
- const destBoard = join30(destDir, filename);
34039
- if (boardBase === "" || boardBase === "." || boardBase === ".." || boardBase.includes("/") || boardBase.includes("\\") || resolve4(destDir) !== join30(diagramsDirResolved, boardBase) || !resolve4(destDir).startsWith(diagramsDirResolved + sep3)) {
34449
+ const srcBoard = join31(diagramsDir, filename);
34450
+ const destDir = join31(diagramsDir, boardBase);
34451
+ const destBoard = join31(destDir, filename);
34452
+ if (boardBase === "" || boardBase === "." || boardBase === ".." || boardBase.includes("/") || boardBase.includes("\\") || resolve5(destDir) !== join31(diagramsDirResolved, boardBase) || !resolve5(destDir).startsWith(diagramsDirResolved + sep3)) {
34040
34453
  result.ambiguous.push(boardBase);
34041
34454
  continue;
34042
34455
  }
@@ -34044,8 +34457,8 @@ function migrateDiagramsToFolders(contextRoot) {
34044
34457
  const newSlug = `diagrams/${boardBase}/${boardBase}.excalidraw`;
34045
34458
  let siblings = [];
34046
34459
  try {
34047
- const allInDir = readdirSync7(diagramsDir).filter(
34048
- (f) => statSync4(join30(diagramsDir, f)).isFile()
34460
+ const allInDir = readdirSync8(diagramsDir).filter(
34461
+ (f) => statSync4(join31(diagramsDir, f)).isFile()
34049
34462
  );
34050
34463
  siblings = allInDir.filter(
34051
34464
  (f) => f !== filename && isSameBasenameSibling(f, boardBase)
@@ -34055,7 +34468,7 @@ function migrateDiagramsToFolders(contextRoot) {
34055
34468
  continue;
34056
34469
  }
34057
34470
  try {
34058
- mkdirSync15(destDir, { recursive: true });
34471
+ mkdirSync16(destDir, { recursive: true });
34059
34472
  } catch {
34060
34473
  result.ambiguous.push(boardBase);
34061
34474
  continue;
@@ -34063,14 +34476,14 @@ function migrateDiagramsToFolders(contextRoot) {
34063
34476
  const remaps = [{ from: oldSlug, to: newSlug }];
34064
34477
  rewriteWikilinks(contextRoot, remaps);
34065
34478
  try {
34066
- renameSync2(srcBoard, destBoard);
34479
+ renameSync3(srcBoard, destBoard);
34067
34480
  } catch {
34068
34481
  result.ambiguous.push(boardBase);
34069
34482
  continue;
34070
34483
  }
34071
34484
  for (const sib of siblings) {
34072
34485
  try {
34073
- renameSync2(join30(diagramsDir, sib), join30(destDir, sib));
34486
+ renameSync3(join31(diagramsDir, sib), join31(destDir, sib));
34074
34487
  } catch {
34075
34488
  result.ambiguous.push(`${boardBase} (sibling: ${sib})`);
34076
34489
  }
@@ -34080,8 +34493,8 @@ function migrateDiagramsToFolders(contextRoot) {
34080
34493
  return result;
34081
34494
  }
34082
34495
  function detectFlatDiagramBoards(contextRoot) {
34083
- const diagramsDir = join30(contextRoot, "knowledge", "diagrams");
34084
- if (!existsSync30(diagramsDir)) return [];
34496
+ const diagramsDir = join31(contextRoot, "knowledge", "diagrams");
34497
+ if (!existsSync31(diagramsDir)) return [];
34085
34498
  const flatFiles = import_fast_glob8.default.sync("*.excalidraw.md", {
34086
34499
  cwd: diagramsDir,
34087
34500
  absolute: false
@@ -34131,17 +34544,17 @@ var init__2 = __esm({
34131
34544
  });
34132
34545
 
34133
34546
  // src/lib/vaults.ts
34134
- import { existsSync as existsSync31, mkdirSync as mkdirSync16, readFileSync as readFileSync26, writeFileSync as writeFileSync23 } from "fs";
34135
- import { dirname as dirname14, join as join31, resolve as resolve5 } from "path";
34136
- import { homedir } from "os";
34137
- function vaultsFilePath(home = homedir()) {
34138
- return join31(home, ".dreamcontext", "vaults.json");
34547
+ import { existsSync as existsSync32, mkdirSync as mkdirSync17, readFileSync as readFileSync27, writeFileSync as writeFileSync24 } from "fs";
34548
+ import { dirname as dirname15, join as join32, resolve as resolve6 } from "path";
34549
+ import { homedir as homedir2 } from "os";
34550
+ function vaultsFilePath(home = homedir2()) {
34551
+ return join32(home, ".dreamcontext", "vaults.json");
34139
34552
  }
34140
34553
  function listVaults(home) {
34141
34554
  const filePath = vaultsFilePath(home);
34142
- if (!existsSync31(filePath)) return [];
34555
+ if (!existsSync32(filePath)) return [];
34143
34556
  try {
34144
- const raw = readFileSync26(filePath, "utf-8");
34557
+ const raw = readFileSync27(filePath, "utf-8");
34145
34558
  const parsed = JSON.parse(raw);
34146
34559
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.vaults)) {
34147
34560
  return [];
@@ -34155,15 +34568,15 @@ function listVaults(home) {
34155
34568
  }
34156
34569
  }
34157
34570
  function writeRegistry(filePath, registry) {
34158
- mkdirSync16(dirname14(filePath), { recursive: true });
34159
- writeFileSync23(filePath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
34571
+ mkdirSync17(dirname15(filePath), { recursive: true });
34572
+ writeFileSync24(filePath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
34160
34573
  }
34161
34574
  function addVault(name, dirPath, home) {
34162
- const resolved = resolve5(dirPath);
34163
- if (!existsSync31(resolved)) {
34575
+ const resolved = resolve6(dirPath);
34576
+ if (!existsSync32(resolved)) {
34164
34577
  throw new VaultError(`Path does not exist: ${resolved}`);
34165
34578
  }
34166
- if (!existsSync31(join31(resolved, "_dream_context"))) {
34579
+ if (!existsSync32(join32(resolved, "_dream_context"))) {
34167
34580
  throw new VaultError(
34168
34581
  `Path is not a dreamcontext project (no _dream_context/ directory): ${resolved}`
34169
34582
  );
@@ -34173,7 +34586,7 @@ function addVault(name, dirPath, home) {
34173
34586
  if (existing.some((v) => v.name === name)) {
34174
34587
  throw new VaultError(`A vault named "${name}" is already registered.`);
34175
34588
  }
34176
- if (existing.some((v) => resolve5(v.path) === resolved)) {
34589
+ if (existing.some((v) => resolve6(v.path) === resolved)) {
34177
34590
  throw new VaultError(`Path is already registered: ${resolved}`);
34178
34591
  }
34179
34592
  const vault = { name, path: resolved };
@@ -34181,19 +34594,19 @@ function addVault(name, dirPath, home) {
34181
34594
  writeRegistry(filePath, registry);
34182
34595
  return vault;
34183
34596
  }
34184
- function resolveVaultContextRoot(arg, home = homedir()) {
34597
+ function resolveVaultContextRoot(arg, home = homedir2()) {
34185
34598
  const vaults = listVaults(home);
34186
34599
  const named = vaults.find((v) => v.name === arg);
34187
- const resolved = named ? named.path : resolve5(arg);
34188
- if (!existsSync31(resolved)) {
34600
+ const resolved = named ? named.path : resolve6(arg);
34601
+ if (!existsSync32(resolved)) {
34189
34602
  throw new VaultError(`Vault path does not exist: ${resolved}`);
34190
34603
  }
34191
- if (!existsSync31(join31(resolved, "_dream_context"))) {
34604
+ if (!existsSync32(join32(resolved, "_dream_context"))) {
34192
34605
  throw new VaultError(
34193
34606
  `Path is not a dreamcontext project (no _dream_context/ directory): ${resolved}`
34194
34607
  );
34195
34608
  }
34196
- return join31(resolved, "_dream_context");
34609
+ return join32(resolved, "_dream_context");
34197
34610
  }
34198
34611
  function removeVault(name, home) {
34199
34612
  const existing = listVaults(home);
@@ -34218,11 +34631,11 @@ var init_vaults = __esm({
34218
34631
  });
34219
34632
 
34220
34633
  // src/lib/connections.ts
34221
- import { existsSync as existsSync32, mkdirSync as mkdirSync17, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync24 } from "fs";
34634
+ import { existsSync as existsSync33, mkdirSync as mkdirSync18, readFileSync as readFileSync28, renameSync as renameSync4, writeFileSync as writeFileSync25 } from "fs";
34222
34635
  import { randomBytes } from "crypto";
34223
- import { dirname as dirname15, join as join32, resolve as resolve6 } from "path";
34636
+ import { dirname as dirname16, join as join33, resolve as resolve7 } from "path";
34224
34637
  function connectionsPath(contextRoot) {
34225
- return join32(contextRoot, CONNECTIONS_REL_PATH);
34638
+ return join33(contextRoot, CONNECTIONS_REL_PATH);
34226
34639
  }
34227
34640
  function emptyFile() {
34228
34641
  return { version: 1, connections: [] };
@@ -34242,9 +34655,9 @@ function sanitizeConnection(raw) {
34242
34655
  }
34243
34656
  function readConnections(contextRoot) {
34244
34657
  const path2 = connectionsPath(contextRoot);
34245
- if (!existsSync32(path2)) return emptyFile();
34658
+ if (!existsSync33(path2)) return emptyFile();
34246
34659
  try {
34247
- const parsed = JSON.parse(readFileSync27(path2, "utf-8"));
34660
+ const parsed = JSON.parse(readFileSync28(path2, "utf-8"));
34248
34661
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.connections)) {
34249
34662
  return emptyFile();
34250
34663
  }
@@ -34257,10 +34670,10 @@ function readConnections(contextRoot) {
34257
34670
  }
34258
34671
  function writeConnections(contextRoot, file) {
34259
34672
  const path2 = connectionsPath(contextRoot);
34260
- mkdirSync17(dirname15(path2), { recursive: true });
34673
+ mkdirSync18(dirname16(path2), { recursive: true });
34261
34674
  const tmp = `${path2}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
34262
- writeFileSync24(tmp, JSON.stringify(file, null, 2) + "\n", "utf-8");
34263
- renameSync3(tmp, path2);
34675
+ writeFileSync25(tmp, JSON.stringify(file, null, 2) + "\n", "utf-8");
34676
+ renameSync4(tmp, path2);
34264
34677
  }
34265
34678
  function listConnections(contextRoot) {
34266
34679
  return readConnections(contextRoot).connections;
@@ -34280,7 +34693,7 @@ function addConnection(contextRoot, currentVaultName3, peer, direction, topics,
34280
34693
  throw new VaultError(`Unknown vault "${peer}" \u2014 register it first with \`dreamcontext vaults add\`.`);
34281
34694
  }
34282
34695
  const peerRoot = resolveVaultContextRoot(peer, home);
34283
- if (resolve6(peerRoot) === resolve6(contextRoot)) {
34696
+ if (resolve7(peerRoot) === resolve7(contextRoot)) {
34284
34697
  throw new VaultError("A vault cannot connect to itself.");
34285
34698
  }
34286
34699
  const file = readConnections(contextRoot);
@@ -34320,11 +34733,11 @@ var init_connections = __esm({
34320
34733
  });
34321
34734
 
34322
34735
  // src/server/safe-path.ts
34323
- import { resolve as resolve7, sep as sep4 } from "path";
34736
+ import { resolve as resolve8, sep as sep4 } from "path";
34324
34737
  function safeChildPath2(baseDir, child) {
34325
34738
  if (!child || child.includes("\0")) return null;
34326
- const base = resolve7(baseDir);
34327
- const target = resolve7(base, child);
34739
+ const base = resolve8(baseDir);
34740
+ const target = resolve8(base, child);
34328
34741
  if (target !== base && !target.startsWith(base + sep4)) return null;
34329
34742
  return target;
34330
34743
  }
@@ -34336,22 +34749,22 @@ var init_safe_path = __esm({
34336
34749
 
34337
34750
  // src/lib/federation-inbox.ts
34338
34751
  import {
34339
- existsSync as existsSync33,
34340
- mkdirSync as mkdirSync18,
34341
- readFileSync as readFileSync28,
34342
- readdirSync as readdirSync8,
34343
- renameSync as renameSync4,
34344
- writeFileSync as writeFileSync25
34752
+ existsSync as existsSync34,
34753
+ mkdirSync as mkdirSync19,
34754
+ readFileSync as readFileSync29,
34755
+ readdirSync as readdirSync9,
34756
+ renameSync as renameSync5,
34757
+ writeFileSync as writeFileSync26
34345
34758
  } from "fs";
34346
- import { join as join33 } from "path";
34759
+ import { join as join34 } from "path";
34347
34760
  function inboxDir(contextRoot) {
34348
- return join33(contextRoot, INBOX_REL);
34761
+ return join34(contextRoot, INBOX_REL);
34349
34762
  }
34350
34763
  function consumedDir(contextRoot) {
34351
- return join33(inboxDir(contextRoot), CONSUMED_DIRNAME);
34764
+ return join34(inboxDir(contextRoot), CONSUMED_DIRNAME);
34352
34765
  }
34353
34766
  function ensureInbox(contextRoot) {
34354
- mkdirSync18(consumedDir(contextRoot), { recursive: true });
34767
+ mkdirSync19(consumedDir(contextRoot), { recursive: true });
34355
34768
  }
34356
34769
  function majorOf(version) {
34357
34770
  return Math.floor(Number.isFinite(version) ? version : 0);
@@ -34364,19 +34777,19 @@ function isDigestEntry(raw) {
34364
34777
  function drainInbox(contextRoot) {
34365
34778
  const dir = inboxDir(contextRoot);
34366
34779
  const result = { entries: [], quarantined: [] };
34367
- if (!existsSync33(dir)) return result;
34780
+ if (!existsSync34(dir)) return result;
34368
34781
  let files;
34369
34782
  try {
34370
- files = readdirSync8(dir).filter((f) => f.endsWith(".json"));
34783
+ files = readdirSync9(dir).filter((f) => f.endsWith(".json"));
34371
34784
  } catch (err) {
34372
34785
  console.error(`[dreamcontext] federation: cannot read inbox at ${dir}: ${String(err)}`);
34373
34786
  return result;
34374
34787
  }
34375
34788
  for (const file of files) {
34376
- const path2 = join33(dir, file);
34789
+ const path2 = join34(dir, file);
34377
34790
  let entry;
34378
34791
  try {
34379
- const parsed = JSON.parse(readFileSync28(path2, "utf-8"));
34792
+ const parsed = JSON.parse(readFileSync29(path2, "utf-8"));
34380
34793
  if (!isDigestEntry(parsed)) {
34381
34794
  console.error(`[dreamcontext] federation: skipping malformed inbox entry "${file}".`);
34382
34795
  continue;
@@ -34401,9 +34814,9 @@ function drainInbox(contextRoot) {
34401
34814
  }
34402
34815
  function pendingInboxCount(contextRoot) {
34403
34816
  const dir = inboxDir(contextRoot);
34404
- if (!existsSync33(dir)) return 0;
34817
+ if (!existsSync34(dir)) return 0;
34405
34818
  try {
34406
- return readdirSync8(dir, { withFileTypes: true }).filter(
34819
+ return readdirSync9(dir, { withFileTypes: true }).filter(
34407
34820
  (d) => d.isFile() && d.name.endsWith(".json")
34408
34821
  ).length;
34409
34822
  } catch {
@@ -34412,17 +34825,17 @@ function pendingInboxCount(contextRoot) {
34412
34825
  }
34413
34826
  function listConsumedEntries(contextRoot) {
34414
34827
  const dir = consumedDir(contextRoot);
34415
- if (!existsSync33(dir)) return [];
34828
+ if (!existsSync34(dir)) return [];
34416
34829
  let files;
34417
34830
  try {
34418
- files = readdirSync8(dir).filter((f) => f.endsWith(".json"));
34831
+ files = readdirSync9(dir).filter((f) => f.endsWith(".json"));
34419
34832
  } catch {
34420
34833
  return [];
34421
34834
  }
34422
34835
  const out = [];
34423
34836
  for (const file of files) {
34424
34837
  try {
34425
- const parsed = JSON.parse(readFileSync28(join33(dir, file), "utf-8"));
34838
+ const parsed = JSON.parse(readFileSync29(join34(dir, file), "utf-8"));
34426
34839
  if (isDigestEntry(parsed)) out.push(parsed);
34427
34840
  } catch {
34428
34841
  }
@@ -34435,14 +34848,14 @@ var init_federation_inbox = __esm({
34435
34848
  "use strict";
34436
34849
  init_safe_path();
34437
34850
  DIGEST_SCHEMA_VERSION = 1;
34438
- INBOX_REL = join33("state", ".federation-inbox");
34851
+ INBOX_REL = join34("state", ".federation-inbox");
34439
34852
  CONSUMED_DIRNAME = "consumed";
34440
34853
  }
34441
34854
  });
34442
34855
 
34443
34856
  // src/migrations/0.8.0.ts
34444
- import { existsSync as existsSync34 } from "fs";
34445
- import { dirname as dirname16, join as join34 } from "path";
34857
+ import { existsSync as existsSync35 } from "fs";
34858
+ import { dirname as dirname17, join as join35 } from "path";
34446
34859
  var migration080;
34447
34860
  var init__3 = __esm({
34448
34861
  "src/migrations/0.8.0.ts"() {
@@ -34454,16 +34867,16 @@ var init__3 = __esm({
34454
34867
  version: "0.8.0",
34455
34868
  steps: [
34456
34869
  (root) => {
34457
- const projectRoot = dirname16(root);
34870
+ const projectRoot = dirname17(root);
34458
34871
  const filesTouched = [];
34459
34872
  const connPath = connectionsPath(root);
34460
- if (!existsSync34(connPath)) {
34873
+ if (!existsSync35(connPath)) {
34461
34874
  writeConnections(root, { version: 1, connections: [] });
34462
34875
  filesTouched.push("state/.connections.json");
34463
34876
  } else {
34464
34877
  readConnections(root);
34465
34878
  }
34466
- const inboxExisted = existsSync34(consumedDir(root));
34879
+ const inboxExisted = existsSync35(consumedDir(root));
34467
34880
  ensureInbox(root);
34468
34881
  if (!inboxExisted) {
34469
34882
  filesTouched.push("state/.federation-inbox/", "state/.federation-inbox/consumed/");
@@ -34479,7 +34892,7 @@ var init__3 = __esm({
34479
34892
  const summary = detected ? "Federation already scaffolded (.connections.json + .federation-inbox/ present, shareable set) \u2014 nothing to do." : `Scaffolded federation: ${filesTouched.join(", ")}` + (shareableTouched ? "" : cfg ? " (shareable already set \u2014 preserved)" : "");
34480
34893
  return {
34481
34894
  step: "scaffold-federation",
34482
- filesTouched: filesTouched.map((f) => join34(root, f.split(" ")[0])),
34895
+ filesTouched: filesTouched.map((f) => join35(root, f.split(" ")[0])),
34483
34896
  summary,
34484
34897
  detected
34485
34898
  };
@@ -34509,21 +34922,21 @@ var init_migrations = __esm({
34509
34922
 
34510
34923
  // src/lib/migration-ledger.ts
34511
34924
  import {
34512
- existsSync as existsSync35,
34513
- mkdirSync as mkdirSync19,
34514
- readFileSync as readFileSync29,
34515
- writeFileSync as writeFileSync26,
34516
- renameSync as renameSync5
34925
+ existsSync as existsSync36,
34926
+ mkdirSync as mkdirSync20,
34927
+ readFileSync as readFileSync30,
34928
+ writeFileSync as writeFileSync27,
34929
+ renameSync as renameSync6
34517
34930
  } from "fs";
34518
- import { dirname as dirname17, join as join35 } from "path";
34931
+ import { dirname as dirname18, join as join36 } from "path";
34519
34932
  function ledgerPath(contextRoot) {
34520
- return join35(contextRoot, LEDGER_REL_PATH);
34933
+ return join36(contextRoot, LEDGER_REL_PATH);
34521
34934
  }
34522
34935
  function readLedger(contextRoot) {
34523
34936
  const path2 = ledgerPath(contextRoot);
34524
- if (!existsSync35(path2)) return [];
34937
+ if (!existsSync36(path2)) return [];
34525
34938
  try {
34526
- const raw = readFileSync29(path2, "utf-8");
34939
+ const raw = readFileSync30(path2, "utf-8");
34527
34940
  const parsed = JSON.parse(raw);
34528
34941
  if (!Array.isArray(parsed)) return [];
34529
34942
  return parsed;
@@ -34534,9 +34947,9 @@ function readLedger(contextRoot) {
34534
34947
  function writeLedger(contextRoot, entries) {
34535
34948
  const path2 = ledgerPath(contextRoot);
34536
34949
  const tmp = path2 + ".tmp";
34537
- mkdirSync19(dirname17(path2), { recursive: true });
34538
- writeFileSync26(tmp, JSON.stringify(entries, null, 2) + "\n", "utf-8");
34539
- renameSync5(tmp, path2);
34950
+ mkdirSync20(dirname18(path2), { recursive: true });
34951
+ writeFileSync27(tmp, JSON.stringify(entries, null, 2) + "\n", "utf-8");
34952
+ renameSync6(tmp, path2);
34540
34953
  }
34541
34954
  function appendLedger(contextRoot, entry) {
34542
34955
  const entries = readLedger(contextRoot);
@@ -34556,8 +34969,8 @@ var init_migration_ledger = __esm({
34556
34969
  });
34557
34970
 
34558
34971
  // src/lib/migration-runner.ts
34559
- import { existsSync as existsSync36 } from "fs";
34560
- import { join as join36 } from "path";
34972
+ import { existsSync as existsSync37 } from "fs";
34973
+ import { join as join37 } from "path";
34561
34974
  function runMigrations(contextRoot, fromVersion, toVersion) {
34562
34975
  if (compareVersions(toVersion, fromVersion) < 0) {
34563
34976
  return { applied: [], pendingAgentTasks: [] };
@@ -34588,8 +35001,8 @@ function runMigrations(contextRoot, fromVersion, toVersion) {
34588
35001
  applied.push(entry);
34589
35002
  }
34590
35003
  }
34591
- const changelogPath = join36(contextRoot, "core", "CHANGELOG.json");
34592
- if (existsSync36(changelogPath)) {
35004
+ const changelogPath = join37(contextRoot, "core", "CHANGELOG.json");
35005
+ if (existsSync37(changelogPath)) {
34593
35006
  const codeByVersion = /* @__PURE__ */ new Map();
34594
35007
  for (const entry of applied) {
34595
35008
  if (entry.executor === "code") {
@@ -34808,13 +35221,13 @@ var init_sleep_consolidation = __esm({
34808
35221
  });
34809
35222
 
34810
35223
  // src/cli/commands/sleep.ts
34811
- import { existsSync as existsSync37 } from "fs";
34812
- import { join as join37, dirname as dirname18 } from "path";
35224
+ import { existsSync as existsSync38 } from "fs";
35225
+ import { join as join38, dirname as dirname19 } from "path";
34813
35226
  function getSleepPath(root) {
34814
- return join37(root, "state", ".sleep.json");
35227
+ return join38(root, "state", ".sleep.json");
34815
35228
  }
34816
35229
  function getSleepHistoryPath(root) {
34817
- return join37(root, "state", ".sleep-history.json");
35230
+ return join38(root, "state", ".sleep-history.json");
34818
35231
  }
34819
35232
  function freshDefaults() {
34820
35233
  return {
@@ -34836,7 +35249,7 @@ function freshDefaults() {
34836
35249
  }
34837
35250
  function readSleepState(root) {
34838
35251
  const filePath = getSleepPath(root);
34839
- if (!existsSync37(filePath)) {
35252
+ if (!existsSync38(filePath)) {
34840
35253
  return freshDefaults();
34841
35254
  }
34842
35255
  try {
@@ -34845,7 +35258,7 @@ function readSleepState(root) {
34845
35258
  const historyPath = getSleepHistoryPath(root);
34846
35259
  let existing = [];
34847
35260
  try {
34848
- if (existsSync37(historyPath)) {
35261
+ if (existsSync38(historyPath)) {
34849
35262
  existing = readJsonArray(historyPath);
34850
35263
  }
34851
35264
  } catch {
@@ -34872,7 +35285,7 @@ function readSleepState(root) {
34872
35285
  }
34873
35286
  function readSleepHistory(root) {
34874
35287
  const filePath = getSleepHistoryPath(root);
34875
- if (!existsSync37(filePath)) return [];
35288
+ if (!existsSync38(filePath)) return [];
34876
35289
  try {
34877
35290
  return readJsonArray(filePath);
34878
35291
  } catch {
@@ -34965,7 +35378,7 @@ function registerSleepCommand(program2) {
34965
35378
  const decision = consolidationDepth(state.debt, { userRequestedDeep: !!opts.deep });
34966
35379
  state.consolidation_depth = decision.depth;
34967
35380
  state.pendingMigrationNotices = [];
34968
- const projectRoot = dirname18(root);
35381
+ const projectRoot = dirname19(root);
34969
35382
  const config = readSetupConfig(projectRoot);
34970
35383
  const fromVersion = config?.setupVersion ?? "0.0.0";
34971
35384
  const migResult = runMigrations(root, fromVersion, dreamcontextVersion());
@@ -35119,19 +35532,19 @@ var init_sleep = __esm({
35119
35532
  });
35120
35533
 
35121
35534
  // src/cli/commands/knowledge.ts
35122
- import { existsSync as existsSync38 } from "fs";
35123
- import { join as join38 } from "path";
35535
+ import { existsSync as existsSync39 } from "fs";
35536
+ import { join as join39 } from "path";
35124
35537
  function getKnowledgeDir() {
35125
35538
  const root = ensureContextRoot();
35126
- return join38(root, "knowledge");
35539
+ return join39(root, "knowledge");
35127
35540
  }
35128
35541
  function registerKnowledgeCommand(program2) {
35129
35542
  const knowledge = program2.command("knowledge").description("Create and index knowledge files");
35130
35543
  knowledge.command("create").argument("<name>").option("-d, --description <desc>", "Description").option("-t, --tags <tags>", "Tags (comma-separated)").option("-c, --content <content>", "Content body").description("Create a new knowledge file").action(async (name, opts) => {
35131
35544
  const dir = getKnowledgeDir();
35132
35545
  const slug = slugify(name);
35133
- const filePath = join38(dir, `${slug}.md`);
35134
- if (existsSync38(filePath)) {
35546
+ const filePath = join39(dir, `${slug}.md`);
35547
+ if (existsSync39(filePath)) {
35135
35548
  error(`Knowledge file already exists: ${slug}.md`);
35136
35549
  return;
35137
35550
  }
@@ -35206,8 +35619,8 @@ ${content || "(Content to be added)"}
35206
35619
  });
35207
35620
  knowledge.command("touch").argument("<slug>", "Knowledge file slug").description("Record access to a knowledge file (for decay tracking)").action((slug) => {
35208
35621
  const root = ensureContextRoot();
35209
- const knowledgePath2 = join38(root, "knowledge", `${slug}.md`);
35210
- if (!existsSync38(knowledgePath2)) {
35622
+ const knowledgePath2 = join39(root, "knowledge", `${slug}.md`);
35623
+ if (!existsSync39(knowledgePath2)) {
35211
35624
  error(`Knowledge file not found: ${slug}.md`);
35212
35625
  return;
35213
35626
  }
@@ -35232,10 +35645,10 @@ var init_knowledge = __esm({
35232
35645
  });
35233
35646
 
35234
35647
  // src/cli/commands/tasks.ts
35235
- import { join as join39, basename as basename10, dirname as dirname19 } from "path";
35648
+ import { join as join40, basename as basename10, dirname as dirname20 } from "path";
35236
35649
  function getStateDir() {
35237
35650
  const root = ensureContextRoot();
35238
- return join39(root, "state");
35651
+ return join40(root, "state");
35239
35652
  }
35240
35653
  function collectOption(value, previous) {
35241
35654
  return previous.concat(value);
@@ -35374,7 +35787,7 @@ function registerTasksCommand(program2) {
35374
35787
  const description = opts.description || name;
35375
35788
  const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
35376
35789
  if (opts.person && opts.person.trim()) {
35377
- const projectRoot = dirname19(ensureContextRoot());
35790
+ const projectRoot = dirname20(ensureContextRoot());
35378
35791
  if (isMultiPerson(readSetupConfig(projectRoot))) {
35379
35792
  const personTag = `person:${slugify(opts.person)}`;
35380
35793
  if (!tags.includes(personTag)) tags.push(personTag);
@@ -35726,7 +36139,7 @@ function registerTasksCommand(program2) {
35726
36139
  }
35727
36140
  });
35728
36141
  tasks.command("sync-hooks").argument("<action>", "install | uninstall").description("Install/uninstall best-effort git sync triggers (post-commit, pre-push)").action((action) => {
35729
- const projectRoot = dirname19(ensureContextRoot());
36142
+ const projectRoot = dirname20(ensureContextRoot());
35730
36143
  if (action === "install") {
35731
36144
  const res = installTaskSyncHooks(projectRoot);
35732
36145
  if (res.noGit) {
@@ -35754,7 +36167,7 @@ function registerTasksCommand(program2) {
35754
36167
  const backend = getTaskBackend();
35755
36168
  const slug = await resolveTaskSlug(backend, name);
35756
36169
  if (!slug) return;
35757
- files = [join39(dir, `${slug}.md`)];
36170
+ files = [join40(dir, `${slug}.md`)];
35758
36171
  } else {
35759
36172
  files = import_fast_glob9.default.sync("*.md", { cwd: dir, absolute: true });
35760
36173
  }
@@ -35842,8 +36255,8 @@ var init_tasks = __esm({
35842
36255
  });
35843
36256
 
35844
36257
  // src/cli/commands/update.ts
35845
- import { existsSync as existsSync39, readdirSync as readdirSync9, rmSync as rmSync5 } from "fs";
35846
- import { join as join40 } from "path";
36258
+ import { existsSync as existsSync40, readdirSync as readdirSync10, rmSync as rmSync6 } from "fs";
36259
+ import { join as join41 } from "path";
35847
36260
  function buildUpdateSummary(input2) {
35848
36261
  const { platforms, installedCount, packs, removed, setupVersion } = input2;
35849
36262
  const lines = ["## Update Summary\n"];
@@ -35866,7 +36279,7 @@ function buildUpdateSummary(input2) {
35866
36279
  }
35867
36280
  function detectInstalledPlatforms(projectRoot) {
35868
36281
  return SUPPORTED_PLATFORMS.filter(
35869
- (p) => existsSync39(join40(platformSkillRoot(projectRoot, p), "dreamcontext", "SKILL.md"))
36282
+ (p) => existsSync40(join41(platformSkillRoot(projectRoot, p), "dreamcontext", "SKILL.md"))
35870
36283
  );
35871
36284
  }
35872
36285
  function detectInstalledPacks(projectRoot, platforms) {
@@ -35879,11 +36292,11 @@ function detectInstalledPacks(projectRoot, platforms) {
35879
36292
  const found = /* @__PURE__ */ new Set();
35880
36293
  for (const platform2 of platforms) {
35881
36294
  const root = platformSkillRoot(projectRoot, platform2);
35882
- if (!existsSync39(root)) continue;
35883
- for (const name of readdirSync9(root)) {
36295
+ if (!existsSync40(root)) continue;
36296
+ for (const name of readdirSync10(root)) {
35884
36297
  if (name === "dreamcontext") continue;
35885
36298
  if (!knownNames.has(name)) continue;
35886
- if (existsSync39(join40(root, name, "SKILL.md"))) found.add(name);
36299
+ if (existsSync40(join41(root, name, "SKILL.md"))) found.add(name);
35887
36300
  }
35888
36301
  }
35889
36302
  return [...found].sort();
@@ -35933,9 +36346,9 @@ async function pruneStaleFiles(projectRoot, oldManifest, newManifest, isFirstRun
35933
36346
  }
35934
36347
  const removed = [];
35935
36348
  for (const rel of owned) {
35936
- const abs = join40(projectRoot, rel);
36349
+ const abs = join41(projectRoot, rel);
35937
36350
  try {
35938
- rmSync5(abs, { force: true });
36351
+ rmSync6(abs, { force: true });
35939
36352
  removed.push(rel);
35940
36353
  } catch (e) {
35941
36354
  const msg = e instanceof Error ? e.message : String(e);
@@ -36026,7 +36439,7 @@ function registerUpdateCommand(program2) {
36026
36439
  const ver = dreamcontextVersion();
36027
36440
  updateSetupConfig(projectRoot, { setupVersion: ver });
36028
36441
  newSetupVersion = ver;
36029
- const ctxRoot = join40(projectRoot, "_dream_context");
36442
+ const ctxRoot = join41(projectRoot, "_dream_context");
36030
36443
  const migResult = runMigrations(ctxRoot, fromVersion, ver);
36031
36444
  if (migResult.applied.length > 0) {
36032
36445
  const codeApplied = migResult.applied.filter(
@@ -36085,11 +36498,11 @@ var init_update = __esm({
36085
36498
  });
36086
36499
 
36087
36500
  // src/lib/core-index.ts
36088
- import { existsSync as existsSync40 } from "fs";
36089
- import { join as join41, basename as basename11 } from "path";
36501
+ import { existsSync as existsSync41 } from "fs";
36502
+ import { join as join42, basename as basename11 } from "path";
36090
36503
  function buildCoreIndex(contextRoot) {
36091
- const coreDir = join41(contextRoot, "core");
36092
- if (!existsSync40(coreDir)) return [];
36504
+ const coreDir = join42(contextRoot, "core");
36505
+ if (!existsSync41(coreDir)) return [];
36093
36506
  const files = import_fast_glob10.default.sync("[3-9]*", { cwd: coreDir, absolute: true });
36094
36507
  const entries = [];
36095
36508
  for (const file of files) {
@@ -36137,19 +36550,19 @@ var init_core_index = __esm({
36137
36550
  });
36138
36551
 
36139
36552
  // src/lib/marketing/paths.ts
36140
- import { join as join42 } from "path";
36553
+ import { join as join43 } from "path";
36141
36554
  function marketingRoot(from) {
36142
- return join42(ensureContextRoot(from), MARKETING_DIR);
36555
+ return join43(ensureContextRoot(from), MARKETING_DIR);
36143
36556
  }
36144
36557
  function marketingRootIfExists(from) {
36145
36558
  const ctx = resolveContextRoot(from);
36146
- return ctx ? join42(ctx, MARKETING_DIR) : null;
36559
+ return ctx ? join43(ctx, MARKETING_DIR) : null;
36147
36560
  }
36148
36561
  function marketingPath(...segments) {
36149
- return join42(marketingRoot(), ...segments);
36562
+ return join43(marketingRoot(), ...segments);
36150
36563
  }
36151
36564
  function knowledgePath(...segments) {
36152
- return join42(ensureContextRoot(), KNOWLEDGE_DIR, ...segments);
36565
+ return join43(ensureContextRoot(), KNOWLEDGE_DIR, ...segments);
36153
36566
  }
36154
36567
  var MARKETING_DIR, KNOWLEDGE_DIR, LEARNINGS_SUBDIR, MARKETING_PATHS;
36155
36568
  var init_paths2 = __esm({
@@ -36231,33 +36644,33 @@ var init_secrets2 = __esm({
36231
36644
 
36232
36645
  // src/lib/marketing/store.ts
36233
36646
  import {
36234
- existsSync as existsSync41,
36235
- mkdirSync as mkdirSync20,
36647
+ existsSync as existsSync42,
36648
+ mkdirSync as mkdirSync21,
36236
36649
  openSync,
36237
36650
  closeSync,
36238
- readFileSync as readFileSync30,
36239
- writeFileSync as writeFileSync27,
36240
- renameSync as renameSync6,
36651
+ readFileSync as readFileSync31,
36652
+ writeFileSync as writeFileSync28,
36653
+ renameSync as renameSync7,
36241
36654
  unlinkSync as unlinkSync2,
36242
- readdirSync as readdirSync10
36655
+ readdirSync as readdirSync11
36243
36656
  } from "fs";
36244
- import { dirname as dirname20, join as join43 } from "path";
36657
+ import { dirname as dirname21, join as join44 } from "path";
36245
36658
  import { randomBytes as randomBytes2 } from "crypto";
36246
36659
  function atomicWriteFile(path2, data) {
36247
- mkdirSync20(dirname20(path2), { recursive: true });
36660
+ mkdirSync21(dirname21(path2), { recursive: true });
36248
36661
  const tmp = `${path2}.tmp.${process.pid}.${randomBytes2(4).toString("hex")}`;
36249
- writeFileSync27(tmp, data, "utf8");
36250
- renameSync6(tmp, path2);
36662
+ writeFileSync28(tmp, data, "utf8");
36663
+ renameSync7(tmp, path2);
36251
36664
  }
36252
36665
  function writeJsonWithBridge(jsonPath, bridgePath, jsonValue, bridgeMarkdown) {
36253
- mkdirSync20(dirname20(jsonPath), { recursive: true });
36254
- mkdirSync20(dirname20(bridgePath), { recursive: true });
36666
+ mkdirSync21(dirname21(jsonPath), { recursive: true });
36667
+ mkdirSync21(dirname21(bridgePath), { recursive: true });
36255
36668
  const jsonStr = JSON.stringify(jsonValue, null, 2) + "\n";
36256
36669
  const jsonTmp = `${jsonPath}.tmp.${process.pid}.${randomBytes2(4).toString("hex")}`;
36257
36670
  const bridgeTmp = `${bridgePath}.tmp.${process.pid}.${randomBytes2(4).toString("hex")}`;
36258
- writeFileSync27(jsonTmp, jsonStr, "utf8");
36671
+ writeFileSync28(jsonTmp, jsonStr, "utf8");
36259
36672
  try {
36260
- writeFileSync27(bridgeTmp, bridgeMarkdown, "utf8");
36673
+ writeFileSync28(bridgeTmp, bridgeMarkdown, "utf8");
36261
36674
  } catch (e) {
36262
36675
  try {
36263
36676
  unlinkSync2(jsonTmp);
@@ -36265,9 +36678,9 @@ function writeJsonWithBridge(jsonPath, bridgePath, jsonValue, bridgeMarkdown) {
36265
36678
  }
36266
36679
  throw e;
36267
36680
  }
36268
- renameSync6(jsonTmp, jsonPath);
36681
+ renameSync7(jsonTmp, jsonPath);
36269
36682
  try {
36270
- renameSync6(bridgeTmp, bridgePath);
36683
+ renameSync7(bridgeTmp, bridgePath);
36271
36684
  } catch (e) {
36272
36685
  try {
36273
36686
  unlinkSync2(bridgeTmp);
@@ -36288,9 +36701,9 @@ function pidAlive(pid) {
36288
36701
  }
36289
36702
  function acquireLock() {
36290
36703
  const lockPath = MARKETING_PATHS.lockFile();
36291
- mkdirSync20(marketingRoot(), { recursive: true });
36292
- if (existsSync41(lockPath)) {
36293
- const heldRaw = readFileSync30(lockPath, "utf8").trim();
36704
+ mkdirSync21(marketingRoot(), { recursive: true });
36705
+ if (existsSync42(lockPath)) {
36706
+ const heldRaw = readFileSync31(lockPath, "utf8").trim();
36294
36707
  const heldPid = Number.parseInt(heldRaw, 10);
36295
36708
  if (pidAlive(heldPid)) {
36296
36709
  throw new LockBusyError(heldPid);
@@ -36306,19 +36719,19 @@ function acquireLock() {
36306
36719
  } catch (e) {
36307
36720
  const code = e.code;
36308
36721
  if (code === "EEXIST") {
36309
- const heldRaw = existsSync41(lockPath) ? readFileSync30(lockPath, "utf8").trim() : "?";
36722
+ const heldRaw = existsSync42(lockPath) ? readFileSync31(lockPath, "utf8").trim() : "?";
36310
36723
  throw new LockBusyError(Number.parseInt(heldRaw, 10) || -1);
36311
36724
  }
36312
36725
  throw e;
36313
36726
  }
36314
36727
  try {
36315
- writeFileSync27(fd, String(process.pid), "utf8");
36728
+ writeFileSync28(fd, String(process.pid), "utf8");
36316
36729
  } finally {
36317
36730
  closeSync(fd);
36318
36731
  }
36319
36732
  return function release2() {
36320
36733
  try {
36321
- const heldRaw = readFileSync30(lockPath, "utf8").trim();
36734
+ const heldRaw = readFileSync31(lockPath, "utf8").trim();
36322
36735
  if (Number.parseInt(heldRaw, 10) === process.pid) {
36323
36736
  unlinkSync2(lockPath);
36324
36737
  }
@@ -36337,7 +36750,7 @@ function newRunId(verb) {
36337
36750
  return `${isoTs()}__${verb}`;
36338
36751
  }
36339
36752
  function runPath(runId) {
36340
- return join43(MARKETING_PATHS.runsDir(), `${runId}.json`);
36753
+ return join44(MARKETING_PATHS.runsDir(), `${runId}.json`);
36341
36754
  }
36342
36755
  function beginRun(verb, inputs) {
36343
36756
  const id = newRunId(verb);
@@ -36380,24 +36793,24 @@ function beginRun(verb, inputs) {
36380
36793
  }
36381
36794
  function appendIndex(runId, verb, status) {
36382
36795
  const path2 = MARKETING_PATHS.runsIndex();
36383
- mkdirSync20(dirname20(path2), { recursive: true });
36796
+ mkdirSync21(dirname21(path2), { recursive: true });
36384
36797
  const ts = (/* @__PURE__ */ new Date()).toISOString();
36385
36798
  const line = `- \`${ts}\` **${verb}** (${status}) \u2014 [\`${runId}\`](./${runId}.json)
36386
36799
  `;
36387
36800
  let header2 = "";
36388
36801
  let body = "";
36389
- if (existsSync41(path2)) {
36390
- body = readFileSync30(path2, "utf8");
36802
+ if (existsSync42(path2)) {
36803
+ body = readFileSync31(path2, "utf8");
36391
36804
  } else {
36392
36805
  header2 = "# Marketing runs (LIFO)\n\n";
36393
36806
  }
36394
36807
  if (header2) {
36395
- writeFileSync27(path2, header2 + line + body, "utf8");
36808
+ writeFileSync28(path2, header2 + line + body, "utf8");
36396
36809
  } else if (body.startsWith("# Marketing runs (LIFO)\n\n")) {
36397
36810
  const HDR = "# Marketing runs (LIFO)\n\n";
36398
- writeFileSync27(path2, HDR + line + body.slice(HDR.length), "utf8");
36811
+ writeFileSync28(path2, HDR + line + body.slice(HDR.length), "utf8");
36399
36812
  } else {
36400
- writeFileSync27(path2, "# Marketing runs (LIFO)\n\n" + line + body, "utf8");
36813
+ writeFileSync28(path2, "# Marketing runs (LIFO)\n\n" + line + body, "utf8");
36401
36814
  }
36402
36815
  }
36403
36816
  var LockBusyError;
@@ -36417,35 +36830,35 @@ var init_store = __esm({
36417
36830
  });
36418
36831
 
36419
36832
  // src/lib/marketing/cohort.ts
36420
- import { existsSync as existsSync42, readFileSync as readFileSync31, readdirSync as readdirSync11 } from "fs";
36421
- import { join as join44 } from "path";
36833
+ import { existsSync as existsSync43, readFileSync as readFileSync32, readdirSync as readdirSync12 } from "fs";
36834
+ import { join as join45 } from "path";
36422
36835
  function newCohortId() {
36423
36836
  return `coh_${idgen()}`;
36424
36837
  }
36425
36838
  function cohortPaths(id) {
36426
36839
  const dir = MARKETING_PATHS.cohortsDir();
36427
36840
  return {
36428
- json: join44(dir, `${id}.json`),
36429
- md: join44(dir, `${id}.md`)
36841
+ json: join45(dir, `${id}.json`),
36842
+ md: join45(dir, `${id}.md`)
36430
36843
  };
36431
36844
  }
36432
36845
  function loadCohort(id) {
36433
36846
  const { json } = cohortPaths(id);
36434
- if (!existsSync42(json)) return null;
36847
+ if (!existsSync43(json)) return null;
36435
36848
  try {
36436
- return JSON.parse(readFileSync31(json, "utf8"));
36849
+ return JSON.parse(readFileSync32(json, "utf8"));
36437
36850
  } catch {
36438
36851
  return null;
36439
36852
  }
36440
36853
  }
36441
36854
  function listCohorts() {
36442
36855
  const dir = MARKETING_PATHS.cohortsDir();
36443
- if (!existsSync42(dir)) return [];
36444
- const files = readdirSync11(dir).filter((f) => f.endsWith(".json"));
36856
+ if (!existsSync43(dir)) return [];
36857
+ const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
36445
36858
  const cohorts = [];
36446
36859
  for (const f of files) {
36447
36860
  try {
36448
- const c = JSON.parse(readFileSync31(join44(dir, f), "utf8"));
36861
+ const c = JSON.parse(readFileSync32(join45(dir, f), "utf8"));
36449
36862
  cohorts.push(c);
36450
36863
  } catch {
36451
36864
  }
@@ -36498,19 +36911,19 @@ var init_cohort = __esm({
36498
36911
 
36499
36912
  // src/lib/marketing/learnings.ts
36500
36913
  import {
36501
- existsSync as existsSync43,
36502
- mkdirSync as mkdirSync21,
36503
- readFileSync as readFileSync32,
36504
- writeFileSync as writeFileSync28,
36505
- renameSync as renameSync7
36914
+ existsSync as existsSync44,
36915
+ mkdirSync as mkdirSync22,
36916
+ readFileSync as readFileSync33,
36917
+ writeFileSync as writeFileSync29,
36918
+ renameSync as renameSync8
36506
36919
  } from "fs";
36507
- import { dirname as dirname21 } from "path";
36920
+ import { dirname as dirname22 } from "path";
36508
36921
  import { randomBytes as randomBytes3 } from "crypto";
36509
36922
  function atomicWriteFile2(path2, data) {
36510
- mkdirSync21(dirname21(path2), { recursive: true });
36923
+ mkdirSync22(dirname22(path2), { recursive: true });
36511
36924
  const tmp = `${path2}.tmp.${process.pid}.${randomBytes3(4).toString("hex")}`;
36512
- writeFileSync28(tmp, data, "utf8");
36513
- renameSync7(tmp, path2);
36925
+ writeFileSync29(tmp, data, "utf8");
36926
+ renameSync8(tmp, path2);
36514
36927
  }
36515
36928
  function newLearningId(type) {
36516
36929
  return type === "ledger" ? `led_${idgen2()}` : `rec_${idgen2()}`;
@@ -36520,11 +36933,11 @@ function todayDateString(now = /* @__PURE__ */ new Date()) {
36520
36933
  }
36521
36934
  function loadIndex() {
36522
36935
  const path2 = MARKETING_PATHS.learningsIndex();
36523
- if (!existsSync43(path2)) {
36936
+ if (!existsSync44(path2)) {
36524
36937
  return { version: INDEX_VERSION, entries: [] };
36525
36938
  }
36526
36939
  try {
36527
- const parsed = JSON.parse(readFileSync32(path2, "utf8"));
36940
+ const parsed = JSON.parse(readFileSync33(path2, "utf8"));
36528
36941
  if (parsed.version !== INDEX_VERSION || !Array.isArray(parsed.entries)) {
36529
36942
  return { version: INDEX_VERSION, entries: [] };
36530
36943
  }
@@ -36538,8 +36951,8 @@ function saveIndex(index) {
36538
36951
  }
36539
36952
  function loadByDate(date) {
36540
36953
  const path2 = MARKETING_PATHS.learningsFile(date);
36541
- if (!existsSync43(path2)) return null;
36542
- return readFileSync32(path2, "utf8");
36954
+ if (!existsSync44(path2)) return null;
36955
+ return readFileSync33(path2, "utf8");
36543
36956
  }
36544
36957
  function renderEntryBlock(entry, body) {
36545
36958
  const time = entry.created_at.slice(11, 16);
@@ -36574,7 +36987,7 @@ function ensureDailyHeader(date, existing) {
36574
36987
  }
36575
36988
  function appendToDayFile(entry, body) {
36576
36989
  const path2 = MARKETING_PATHS.learningsFile(entry.date_file);
36577
- const existing = existsSync43(path2) ? readFileSync32(path2, "utf8") : null;
36990
+ const existing = existsSync44(path2) ? readFileSync33(path2, "utf8") : null;
36578
36991
  const base = ensureDailyHeader(entry.date_file, existing);
36579
36992
  const block = renderEntryBlock(entry, body);
36580
36993
  const next = existing && existing.startsWith("# Marketing learnings") ? existing.endsWith("\n") ? existing + block : existing + "\n" + block : base + block;
@@ -36582,8 +36995,8 @@ function appendToDayFile(entry, body) {
36582
36995
  }
36583
36996
  function rewriteDayFileForStatusUpdate(oldEntry, newEntry) {
36584
36997
  const path2 = MARKETING_PATHS.learningsFile(oldEntry.date_file);
36585
- if (!existsSync43(path2)) return;
36586
- const text = readFileSync32(path2, "utf8");
36998
+ if (!existsSync44(path2)) return;
36999
+ const text = readFileSync33(path2, "utf8");
36587
37000
  const re = new RegExp(
36588
37001
  `<!-- entry ([^>]*?id=${escapeRegExp(oldEntry.id)}[^>]*) -->`
36589
37002
  );
@@ -36691,10 +37104,10 @@ var init_learnings = __esm({
36691
37104
  });
36692
37105
 
36693
37106
  // src/lib/marketing/snapshot.ts
36694
- import { existsSync as existsSync44, readFileSync as readFileSync33 } from "fs";
37107
+ import { existsSync as existsSync45, readFileSync as readFileSync34 } from "fs";
36695
37108
  function buildMarketingSnapshot(opts = {}) {
36696
37109
  const root = marketingRootIfExists();
36697
- if (!root || !existsSync44(root)) return null;
37110
+ if (!root || !existsSync45(root)) return null;
36698
37111
  const now = opts.now ?? /* @__PURE__ */ new Date();
36699
37112
  const lines = ["## Marketing\n"];
36700
37113
  const cohorts = listCohorts();
@@ -36751,9 +37164,9 @@ function cohortHypothesisLine(c) {
36751
37164
  }
36752
37165
  function readLastInsightsPull() {
36753
37166
  const idxPath = MARKETING_PATHS.insightsDir() + "/_index.json";
36754
- if (!existsSync44(idxPath)) return null;
37167
+ if (!existsSync45(idxPath)) return null;
36755
37168
  try {
36756
- const raw = JSON.parse(readFileSync33(idxPath, "utf8"));
37169
+ const raw = JSON.parse(readFileSync34(idxPath, "utf8"));
36757
37170
  return raw.updated_at && raw.updated_at !== "1970-01-01T00:00:00Z" ? raw.updated_at : null;
36758
37171
  } catch {
36759
37172
  return null;
@@ -36851,16 +37264,16 @@ var init_setup_drift = __esm({
36851
37264
  });
36852
37265
 
36853
37266
  // src/lib/asset-drift-cache.ts
36854
- import { existsSync as existsSync45, readFileSync as readFileSync34, writeFileSync as writeFileSync29, mkdirSync as mkdirSync22 } from "fs";
36855
- import { join as join45, dirname as dirname22 } from "path";
37267
+ import { existsSync as existsSync46, readFileSync as readFileSync35, writeFileSync as writeFileSync30, mkdirSync as mkdirSync23 } from "fs";
37268
+ import { join as join46, dirname as dirname23 } from "path";
36856
37269
  function assetDriftCachePath(contextRoot) {
36857
- return join45(contextRoot, "state", ".asset-drift.json");
37270
+ return join46(contextRoot, "state", ".asset-drift.json");
36858
37271
  }
36859
37272
  function readAssetDriftCache(contextRoot) {
36860
37273
  try {
36861
37274
  const p = assetDriftCachePath(contextRoot);
36862
- if (!existsSync45(p)) return null;
36863
- const parsed = JSON.parse(readFileSync34(p, "utf-8"));
37275
+ if (!existsSync46(p)) return null;
37276
+ const parsed = JSON.parse(readFileSync35(p, "utf-8"));
36864
37277
  if (!parsed || typeof parsed.cliVersion !== "string" || typeof parsed.setupVersion !== "string" || typeof parsed.usedAssetsChanged !== "boolean") {
36865
37278
  return null;
36866
37279
  }
@@ -36876,8 +37289,8 @@ function readAssetDriftCache(contextRoot) {
36876
37289
  }
36877
37290
  function writeAssetDriftCache(contextRoot, cache) {
36878
37291
  const p = assetDriftCachePath(contextRoot);
36879
- mkdirSync22(dirname22(p), { recursive: true });
36880
- writeFileSync29(p, JSON.stringify(cache, null, 2) + "\n", "utf-8");
37292
+ mkdirSync23(dirname23(p), { recursive: true });
37293
+ writeFileSync30(p, JSON.stringify(cache, null, 2) + "\n", "utf-8");
36881
37294
  }
36882
37295
  function cacheConfidentlyClean(cache, cliVersion, setupVersion) {
36883
37296
  return cache !== null && cache.cliVersion === cliVersion && cache.setupVersion === setupVersion && cache.usedAssetsChanged === false;
@@ -37022,8 +37435,8 @@ var init_recall_synonyms = __esm({
37022
37435
  });
37023
37436
 
37024
37437
  // src/lib/session-digest.ts
37025
- import { existsSync as existsSync46, mkdirSync as mkdirSync23, writeFileSync as writeFileSync30 } from "fs";
37026
- import { join as join46 } from "path";
37438
+ import { existsSync as existsSync47, mkdirSync as mkdirSync24, writeFileSync as writeFileSync31 } from "fs";
37439
+ import { join as join47 } from "path";
37027
37440
  function coerceCreatedAt(value) {
37028
37441
  if (typeof value === "string" && value.trim()) return value.trim();
37029
37442
  if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();
@@ -37081,18 +37494,18 @@ function enforceByteCap(lines, maxBytes) {
37081
37494
  return out.trimEnd() + "\n";
37082
37495
  }
37083
37496
  function digestsDir(root) {
37084
- return join46(root, "state", DIGESTS_DIRNAME);
37497
+ return join47(root, "state", DIGESTS_DIRNAME);
37085
37498
  }
37086
37499
  function digestPath(root, sessionId) {
37087
37500
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
37088
- return join46(digestsDir(root), `${safe}.md`);
37501
+ return join47(digestsDir(root), `${safe}.md`);
37089
37502
  }
37090
37503
  function digestExists(root, sessionId) {
37091
- return existsSync46(digestPath(root, sessionId));
37504
+ return existsSync47(digestPath(root, sessionId));
37092
37505
  }
37093
37506
  function digestIsPartial(root, sessionId) {
37094
37507
  const file = digestPath(root, sessionId);
37095
- if (!existsSync46(file)) return false;
37508
+ if (!existsSync47(file)) return false;
37096
37509
  try {
37097
37510
  const { data } = readFrontmatter(file);
37098
37511
  return data.partial === true;
@@ -37102,7 +37515,7 @@ function digestIsPartial(root, sessionId) {
37102
37515
  }
37103
37516
  function writeDigest(root, sessionId, md, opts = {}) {
37104
37517
  const dir = digestsDir(root);
37105
- mkdirSync23(dir, { recursive: true });
37518
+ mkdirSync24(dir, { recursive: true });
37106
37519
  const file = digestPath(root, sessionId);
37107
37520
  const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
37108
37521
  const frontmatter = [
@@ -37114,12 +37527,12 @@ function writeDigest(root, sessionId, md, opts = {}) {
37114
37527
  "---",
37115
37528
  ""
37116
37529
  ].join("\n");
37117
- writeFileSync30(file, frontmatter + md, "utf-8");
37530
+ writeFileSync31(file, frontmatter + md, "utf-8");
37118
37531
  return file;
37119
37532
  }
37120
37533
  function loadDigestDocs(root) {
37121
37534
  const dir = digestsDir(root);
37122
- if (!existsSync46(dir)) return [];
37535
+ if (!existsSync47(dir)) return [];
37123
37536
  const files = import_fast_glob11.default.sync("*.md", { cwd: dir, absolute: true });
37124
37537
  const parsed = [];
37125
37538
  for (const file of files) {
@@ -37130,7 +37543,7 @@ function loadDigestDocs(root) {
37130
37543
  const title = `Session digest ${sessionId}`;
37131
37544
  const body = content.trim();
37132
37545
  if (!body) continue;
37133
- const relPath = join46("state", DIGESTS_DIRNAME, `${sessionId}.md`);
37546
+ const relPath = join47("state", DIGESTS_DIRNAME, `${sessionId}.md`);
37134
37547
  const createdAt = coerceCreatedAt(data.created_at);
37135
37548
  const fields = buildFields({ slug, title, description: "", tags: [], body });
37136
37549
  const ms = createdAt ? Date.parse(createdAt) : NaN;
@@ -37182,8 +37595,8 @@ var init_session_digest = __esm({
37182
37595
  });
37183
37596
 
37184
37597
  // src/lib/recall.ts
37185
- import { existsSync as existsSync47, readFileSync as readFileSync36 } from "fs";
37186
- import { join as join47, basename as basename12, dirname as dirname23, relative as relative3 } from "path";
37598
+ import { existsSync as existsSync48, readFileSync as readFileSync37 } from "fs";
37599
+ import { join as join48, basename as basename12, dirname as dirname24, relative as relative3 } from "path";
37187
37600
  function isFederated(doc) {
37188
37601
  return doc.federated === true;
37189
37602
  }
@@ -37272,7 +37685,7 @@ function productFromRelPath(relPath) {
37272
37685
  return m ? m[1] : void 0;
37273
37686
  }
37274
37687
  function loadMarkdownDocs(dir, type, contextRoot) {
37275
- if (!existsSync47(dir)) return [];
37688
+ if (!existsSync48(dir)) return [];
37276
37689
  const files = import_fast_glob12.default.sync("**/*.md", { cwd: dir, absolute: true });
37277
37690
  const boardDirs = diagramFolderDirs(files);
37278
37691
  const out = [];
@@ -37316,11 +37729,11 @@ function loadMarkdownDocs(dir, type, contextRoot) {
37316
37729
  return out;
37317
37730
  }
37318
37731
  function loadChangelogEntries(contextRoot) {
37319
- const path2 = join47(contextRoot, "core", "CHANGELOG.json");
37320
- if (!existsSync47(path2)) return [];
37732
+ const path2 = join48(contextRoot, "core", "CHANGELOG.json");
37733
+ if (!existsSync48(path2)) return [];
37321
37734
  let entries = [];
37322
37735
  try {
37323
- const raw = readFileSync36(path2, "utf-8");
37736
+ const raw = readFileSync37(path2, "utf-8");
37324
37737
  const parsed = JSON.parse(raw);
37325
37738
  if (Array.isArray(parsed)) entries = parsed;
37326
37739
  } catch {
@@ -37368,9 +37781,9 @@ function loadChangelogEntries(contextRoot) {
37368
37781
  return out;
37369
37782
  }
37370
37783
  function loadMemoryFile(contextRoot) {
37371
- const path2 = join47(contextRoot, "core", "2.memory.md");
37372
- if (!existsSync47(path2)) return [];
37373
- const raw = readFileSync36(path2, "utf-8");
37784
+ const path2 = join48(contextRoot, "core", "2.memory.md");
37785
+ if (!existsSync48(path2)) return [];
37786
+ const raw = readFileSync37(path2, "utf-8");
37374
37787
  const sections = raw.split(/^##\s+/m).slice(1);
37375
37788
  const out = [];
37376
37789
  for (let i = 0; i < sections.length; i++) {
@@ -37402,11 +37815,11 @@ function loadMemoryFile(contextRoot) {
37402
37815
  return out;
37403
37816
  }
37404
37817
  function loadBookmarkDocs(contextRoot) {
37405
- const path2 = join47(contextRoot, "state", ".sleep.json");
37406
- if (!existsSync47(path2)) return [];
37818
+ const path2 = join48(contextRoot, "state", ".sleep.json");
37819
+ if (!existsSync48(path2)) return [];
37407
37820
  let bookmarks = [];
37408
37821
  try {
37409
- const parsed = JSON.parse(readFileSync36(path2, "utf-8"));
37822
+ const parsed = JSON.parse(readFileSync37(path2, "utf-8"));
37410
37823
  if (parsed && typeof parsed === "object" && Array.isArray(parsed.bookmarks)) {
37411
37824
  bookmarks = parsed.bookmarks;
37412
37825
  }
@@ -37448,14 +37861,14 @@ function loadBookmarkDocs(contextRoot) {
37448
37861
  return out;
37449
37862
  }
37450
37863
  function loadSkillDocs(skillsRoot) {
37451
- if (!existsSync47(skillsRoot)) return [];
37864
+ if (!existsSync48(skillsRoot)) return [];
37452
37865
  const files = import_fast_glob12.default.sync("*/SKILL.md", { cwd: skillsRoot, absolute: true });
37453
37866
  const out = [];
37454
37867
  for (const file of files) {
37455
37868
  try {
37456
37869
  const { data, content } = readFrontmatter(file);
37457
37870
  if (data.alwaysApply === true) continue;
37458
- const slug = typeof data.name === "string" && data.name ? data.name : basename12(dirname23(file));
37871
+ const slug = typeof data.name === "string" && data.name ? data.name : basename12(dirname24(file));
37459
37872
  const title = slug;
37460
37873
  const description = typeof data.description === "string" ? data.description : "";
37461
37874
  const tags = Array.isArray(data.tags) ? data.tags.map(String) : [];
@@ -37487,13 +37900,13 @@ function buildCorpus(contextRoot, opts = {}) {
37487
37900
  const types = new Set(opts.types ?? ["knowledge", "feature", "task", "memory", "changelog"]);
37488
37901
  const docs = [];
37489
37902
  if (types.has("knowledge")) {
37490
- docs.push(...loadMarkdownDocs(join47(contextRoot, "knowledge"), "knowledge", contextRoot));
37903
+ docs.push(...loadMarkdownDocs(join48(contextRoot, "knowledge"), "knowledge", contextRoot));
37491
37904
  }
37492
37905
  if (types.has("feature")) {
37493
- docs.push(...loadMarkdownDocs(join47(contextRoot, "core", "features"), "feature", contextRoot));
37906
+ docs.push(...loadMarkdownDocs(join48(contextRoot, "core", "features"), "feature", contextRoot));
37494
37907
  }
37495
37908
  if (types.has("task")) {
37496
- docs.push(...loadMarkdownDocs(join47(contextRoot, "state"), "task", contextRoot));
37909
+ docs.push(...loadMarkdownDocs(join48(contextRoot, "state"), "task", contextRoot));
37497
37910
  docs.push(...loadDigestDocs(contextRoot));
37498
37911
  }
37499
37912
  if (types.has("memory")) {
@@ -37855,7 +38268,7 @@ var init_federation_config = __esm({
37855
38268
  });
37856
38269
 
37857
38270
  // src/lib/federation-recall.ts
37858
- import { basename as basename13, dirname as dirname24 } from "path";
38271
+ import { basename as basename13, dirname as dirname25 } from "path";
37859
38272
  function namespacedKey(vault, type, slug) {
37860
38273
  return `${vault}::${type}/${slug}`;
37861
38274
  }
@@ -37880,7 +38293,7 @@ function crossVaultRecall(query, opts) {
37880
38293
  }
37881
38294
  const isCurrent = target.current === true;
37882
38295
  if (!isCurrent) {
37883
- const projectRoot = dirname24(contextRoot);
38296
+ const projectRoot = dirname25(contextRoot);
37884
38297
  if (!isShareable(projectRoot)) continue;
37885
38298
  }
37886
38299
  const corpus = buildCorpus(contextRoot, opts.types ? { types: opts.types } : {}).filter((doc) => !isFederated(doc));
@@ -37953,16 +38366,16 @@ var init_federation_recall = __esm({
37953
38366
  });
37954
38367
 
37955
38368
  // src/lib/federation-peer-summary.ts
37956
- import { existsSync as existsSync48, mkdirSync as mkdirSync24, readFileSync as readFileSync37, writeFileSync as writeFileSync31 } from "fs";
37957
- import { dirname as dirname25, join as join48 } from "path";
38369
+ import { existsSync as existsSync49, mkdirSync as mkdirSync25, readFileSync as readFileSync38, writeFileSync as writeFileSync32 } from "fs";
38370
+ import { dirname as dirname26, join as join49 } from "path";
37958
38371
  function peerSummaryCachePath(contextRoot) {
37959
- return join48(contextRoot, CACHE_REL_PATH2);
38372
+ return join49(contextRoot, CACHE_REL_PATH2);
37960
38373
  }
37961
38374
  function readPeerSummaryCache(contextRoot) {
37962
38375
  const path2 = peerSummaryCachePath(contextRoot);
37963
- if (!existsSync48(path2)) return null;
38376
+ if (!existsSync49(path2)) return null;
37964
38377
  try {
37965
- const parsed = JSON.parse(readFileSync37(path2, "utf-8"));
38378
+ const parsed = JSON.parse(readFileSync38(path2, "utf-8"));
37966
38379
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.peers)) return null;
37967
38380
  const peers = parsed.peers.filter((p) => p !== null && typeof p === "object").map(sanitizePeer);
37968
38381
  return {
@@ -37993,8 +38406,8 @@ function buildPeerSummary(peerRoot, peerName) {
37993
38406
  };
37994
38407
  }
37995
38408
  function readWhatItIs(peerRoot) {
37996
- const soulPath = join48(peerRoot, "core", "0.soul.md");
37997
- if (!existsSync48(soulPath)) return "";
38409
+ const soulPath = join49(peerRoot, "core", "0.soul.md");
38410
+ if (!existsSync49(soulPath)) return "";
37998
38411
  try {
37999
38412
  const { data, content } = readFrontmatter(soulPath);
38000
38413
  const name = typeof data.name === "string" ? data.name : "";
@@ -38011,8 +38424,8 @@ function readWhatItIs(peerRoot) {
38011
38424
  }
38012
38425
  }
38013
38426
  function readLastActivity(peerRoot) {
38014
- const path2 = join48(peerRoot, "core", "CHANGELOG.json");
38015
- if (!existsSync48(path2)) return [];
38427
+ const path2 = join49(peerRoot, "core", "CHANGELOG.json");
38428
+ if (!existsSync49(path2)) return [];
38016
38429
  try {
38017
38430
  const entries = readJsonArray(path2);
38018
38431
  const out = [];
@@ -38030,8 +38443,8 @@ function readLastActivity(peerRoot) {
38030
38443
  }
38031
38444
  }
38032
38445
  function readActiveTask(peerRoot) {
38033
- const stateDir = join48(peerRoot, "state");
38034
- if (!existsSync48(stateDir)) return "";
38446
+ const stateDir = join49(peerRoot, "state");
38447
+ if (!existsSync49(stateDir)) return "";
38035
38448
  let files;
38036
38449
  try {
38037
38450
  files = import_fast_glob13.default.sync("*.md", { cwd: stateDir, absolute: true });
@@ -38059,9 +38472,9 @@ function readActiveTask(peerRoot) {
38059
38472
  }
38060
38473
  function readTopTags(peerRoot) {
38061
38474
  const counts = /* @__PURE__ */ new Map();
38062
- const dirs = [join48(peerRoot, "knowledge"), join48(peerRoot, "core", "features")];
38475
+ const dirs = [join49(peerRoot, "knowledge"), join49(peerRoot, "core", "features")];
38063
38476
  for (const dir of dirs) {
38064
- if (!existsSync48(dir)) continue;
38477
+ if (!existsSync49(dir)) continue;
38065
38478
  let files;
38066
38479
  try {
38067
38480
  files = import_fast_glob13.default.sync("*.md", { cwd: dir, absolute: true });
@@ -38083,7 +38496,7 @@ function readTopTags(peerRoot) {
38083
38496
  return Array.from(counts.entries()).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_TAGS).map(([t]) => t);
38084
38497
  }
38085
38498
  function refreshPeerSummaries(contextRoot, home) {
38086
- const { name, target } = currentVaultTarget(dirname25(contextRoot), home);
38499
+ const { name, target } = currentVaultTarget(dirname26(contextRoot), home);
38087
38500
  const targets = resolveConnectedVaults(target, contextRoot, home).filter(
38088
38501
  (t) => t.current !== true && t.name !== name
38089
38502
  );
@@ -38103,8 +38516,8 @@ function refreshPeerSummaries(contextRoot, home) {
38103
38516
  }
38104
38517
  function writePeerSummaryCache(contextRoot, cache) {
38105
38518
  const path2 = peerSummaryCachePath(contextRoot);
38106
- mkdirSync24(dirname25(path2), { recursive: true });
38107
- writeFileSync31(path2, JSON.stringify(cache, null, 2) + "\n", "utf-8");
38519
+ mkdirSync25(dirname26(path2), { recursive: true });
38520
+ writeFileSync32(path2, JSON.stringify(cache, null, 2) + "\n", "utf-8");
38108
38521
  }
38109
38522
  var import_fast_glob13, CACHE_REL_PATH2, MAX_ACTIVITY, MAX_TAGS, WHAT_IT_IS_CHARS;
38110
38523
  var init_federation_peer_summary = __esm({
@@ -38227,8 +38640,8 @@ var init_snapshot_budget = __esm({
38227
38640
  });
38228
38641
 
38229
38642
  // src/cli/commands/snapshot.ts
38230
- import { readFileSync as readFileSync38, existsSync as existsSync49, statSync as statSync5 } from "fs";
38231
- import { join as join49, basename as basename14, dirname as dirname26 } from "path";
38643
+ import { readFileSync as readFileSync39, existsSync as existsSync50, statSync as statSync5 } from "fs";
38644
+ import { join as join50, basename as basename14, dirname as dirname27 } from "path";
38232
38645
  function extractFirstParagraph(content) {
38233
38646
  const lines = content.split("\n");
38234
38647
  const paragraphLines = [];
@@ -38262,8 +38675,8 @@ function sortTaskEntriesByActivity(entries) {
38262
38675
  return [...entries].sort((a, b) => statusRank(a.status) - statusRank(b.status) || prioRank(a.priority) - prioRank(b.priority) || b.updated.localeCompare(a.updated));
38263
38676
  }
38264
38677
  function getActiveTaskEntries(root) {
38265
- const stateDir = join49(root, "state");
38266
- if (!existsSync49(stateDir)) return [];
38678
+ const stateDir = join50(root, "state");
38679
+ if (!existsSync50(stateDir)) return [];
38267
38680
  const taskFiles = import_fast_glob14.default.sync("*.md", { cwd: stateDir, absolute: true });
38268
38681
  const entries = [];
38269
38682
  for (const file of taskFiles) {
@@ -38295,15 +38708,15 @@ function getActiveTaskEntries(root) {
38295
38708
  return entries;
38296
38709
  }
38297
38710
  function resolveActiveTaskPath(root) {
38298
- const stateDir = join49(root, "state");
38299
- if (!existsSync49(stateDir)) return null;
38300
- const overridePath = join49(stateDir, ".active-task");
38301
- if (existsSync49(overridePath)) {
38711
+ const stateDir = join50(root, "state");
38712
+ if (!existsSync50(stateDir)) return null;
38713
+ const overridePath = join50(stateDir, ".active-task");
38714
+ if (existsSync50(overridePath)) {
38302
38715
  try {
38303
- const slug = readFileSync38(overridePath, "utf-8").trim();
38716
+ const slug = readFileSync39(overridePath, "utf-8").trim();
38304
38717
  if (slug) {
38305
- const taskPath = join49(stateDir, `${slug}.md`);
38306
- if (existsSync49(taskPath)) return taskPath;
38718
+ const taskPath = join50(stateDir, `${slug}.md`);
38719
+ if (existsSync50(taskPath)) return taskPath;
38307
38720
  }
38308
38721
  } catch {
38309
38722
  }
@@ -38329,7 +38742,7 @@ function resolveActiveTaskPath(root) {
38329
38742
  return candidates[0].path;
38330
38743
  }
38331
38744
  function getActiveProductKnowledge(root) {
38332
- const config = readSetupConfig(dirname26(root));
38745
+ const config = readSetupConfig(dirname27(root));
38333
38746
  if (!config || !Array.isArray(config.multiProduct) || config.multiProduct.length === 0) {
38334
38747
  return [];
38335
38748
  }
@@ -38346,11 +38759,11 @@ function getActiveProductKnowledge(root) {
38346
38759
  }
38347
38760
  if (!product) return [];
38348
38761
  if (!config.multiProduct.includes(product)) return [];
38349
- const productKnowledgePath = join49(root, "knowledge", "products", `${product}.md`);
38350
- if (!existsSync49(productKnowledgePath)) return [];
38762
+ const productKnowledgePath = join50(root, "knowledge", "products", `${product}.md`);
38763
+ if (!existsSync50(productKnowledgePath)) return [];
38351
38764
  let raw;
38352
38765
  try {
38353
- raw = readFileSync38(productKnowledgePath, "utf-8");
38766
+ raw = readFileSync39(productKnowledgePath, "utf-8");
38354
38767
  } catch {
38355
38768
  return [];
38356
38769
  }
@@ -38385,7 +38798,7 @@ function getActiveProductKnowledge(root) {
38385
38798
  }
38386
38799
  function getVersionNudge(root) {
38387
38800
  try {
38388
- const projectRoot = dirname26(root);
38801
+ const projectRoot = dirname27(root);
38389
38802
  const cache = readVersionCache(projectRoot);
38390
38803
  if (!cache || !isCacheFresh(cache)) return "";
38391
38804
  const installedCli = dreamcontextVersion();
@@ -38401,9 +38814,9 @@ function getVersionNudge(root) {
38401
38814
  }
38402
38815
  function getMigrationNote(root) {
38403
38816
  try {
38404
- const sleepPath = join49(root, "state", ".sleep.json");
38405
- if (!existsSync49(sleepPath)) return "";
38406
- const raw = readFileSync38(sleepPath, "utf-8");
38817
+ const sleepPath = join50(root, "state", ".sleep.json");
38818
+ if (!existsSync50(sleepPath)) return "";
38819
+ const raw = readFileSync39(sleepPath, "utf-8");
38407
38820
  const parsed = JSON.parse(raw);
38408
38821
  if (!parsed || !Array.isArray(parsed.pendingMigrationNotices) || parsed.pendingMigrationNotices.length === 0) {
38409
38822
  return "";
@@ -38421,7 +38834,7 @@ Migrations applied since last session: ${notices.join("; ")}`;
38421
38834
  }
38422
38835
  function getDriftDirective(root) {
38423
38836
  try {
38424
- const projectRoot = dirname26(root);
38837
+ const projectRoot = dirname27(root);
38425
38838
  const config = readSetupConfig(projectRoot);
38426
38839
  if (!config) return "";
38427
38840
  const cliVersion = dreamcontextVersion();
@@ -38451,24 +38864,24 @@ function generateSnapshot(rootOverride) {
38451
38864
  parts = [];
38452
38865
  }
38453
38866
  };
38454
- const soulPath = join49(root, "core", "0.soul.md");
38455
- if (existsSync49(soulPath)) {
38456
- const content = readFileSync38(soulPath, "utf-8").trim();
38867
+ const soulPath = join50(root, "core", "0.soul.md");
38868
+ if (existsSync50(soulPath)) {
38869
+ const content = readFileSync39(soulPath, "utf-8").trim();
38457
38870
  parts.push("## Soul (Agent Identity, Principles, Rules)\n");
38458
38871
  parts.push(content);
38459
38872
  parts.push("");
38460
38873
  }
38461
- const userPath = join49(root, "core", "1.user.md");
38462
- if (existsSync49(userPath)) {
38463
- const content = readFileSync38(userPath, "utf-8").trim();
38874
+ const userPath = join50(root, "core", "1.user.md");
38875
+ if (existsSync50(userPath)) {
38876
+ const content = readFileSync39(userPath, "utf-8").trim();
38464
38877
  parts.push("## User (Preferences, Project Details, Rules)\n");
38465
38878
  parts.push(content);
38466
38879
  parts.push("");
38467
38880
  }
38468
38881
  flush("identity", { neverEvict: true });
38469
- const memoryPath = join49(root, "core", "2.memory.md");
38470
- if (existsSync49(memoryPath)) {
38471
- const content = readFileSync38(memoryPath, "utf-8").trim();
38882
+ const memoryPath = join50(root, "core", "2.memory.md");
38883
+ if (existsSync50(memoryPath)) {
38884
+ const content = readFileSync39(memoryPath, "utf-8").trim();
38472
38885
  if (content) {
38473
38886
  parts.push("## Memory (Technical Decisions, Known Issues, Session Log)\n");
38474
38887
  parts.push(content);
@@ -38533,8 +38946,8 @@ function generateSnapshot(rootOverride) {
38533
38946
  if (sleepState.triggers.length > 0) {
38534
38947
  const taskNames = [];
38535
38948
  const taskTags = [];
38536
- const stateDir = join49(root, "state");
38537
- if (existsSync49(stateDir)) {
38949
+ const stateDir = join50(root, "state");
38950
+ if (existsSync50(stateDir)) {
38538
38951
  const taskFiles = import_fast_glob14.default.sync("*.md", { cwd: stateDir, absolute: true });
38539
38952
  for (const file of taskFiles) {
38540
38953
  try {
@@ -38570,14 +38983,14 @@ function generateSnapshot(rootOverride) {
38570
38983
  const CHANGELOG_TIER2 = 10;
38571
38984
  const TIER1_BODY_CHARS = 300;
38572
38985
  const TIER2_LINE_CHARS = 140;
38573
- const multiPerson = isMultiPerson(readSetupConfig(dirname26(root)));
38986
+ const multiPerson = isMultiPerson(readSetupConfig(dirname27(root)));
38574
38987
  const authorSuffix = (e) => {
38575
38988
  if (!multiPerson) return "";
38576
38989
  const authors = Array.isArray(e.authors) ? e.authors.filter((a) => typeof a === "string") : [];
38577
38990
  return authors.length > 0 ? ` \u2014 by ${authors.join(", ")}` : "";
38578
38991
  };
38579
- const changelogPath = join49(root, "core", "CHANGELOG.json");
38580
- if (existsSync49(changelogPath)) {
38992
+ const changelogPath = join50(root, "core", "CHANGELOG.json");
38993
+ if (existsSync50(changelogPath)) {
38581
38994
  try {
38582
38995
  const entries = readJsonArray(changelogPath);
38583
38996
  const tier1 = entries.slice(0, CHANGELOG_TIER1);
@@ -38633,8 +39046,8 @@ function generateSnapshot(rootOverride) {
38633
39046
  } catch {
38634
39047
  }
38635
39048
  }
38636
- const releasesPath = join49(root, "core", "RELEASES.json");
38637
- if (existsSync49(releasesPath)) {
39049
+ const releasesPath = join50(root, "core", "RELEASES.json");
39050
+ if (existsSync50(releasesPath)) {
38638
39051
  try {
38639
39052
  const releases = readJsonArray(releasesPath);
38640
39053
  if (releases.length > 0) {
@@ -38670,8 +39083,8 @@ function generateSnapshot(rootOverride) {
38670
39083
  }
38671
39084
  }
38672
39085
  flush("releases", { neverEvict: true });
38673
- const featuresDir = join49(root, "core", "features");
38674
- if (existsSync49(featuresDir)) {
39086
+ const featuresDir = join50(root, "core", "features");
39087
+ if (existsSync50(featuresDir)) {
38675
39088
  const featureFiles = import_fast_glob14.default.sync("*.md", { cwd: featuresDir, absolute: true });
38676
39089
  const features = [];
38677
39090
  const featureMeta = [];
@@ -38759,8 +39172,8 @@ function generateSnapshot(rootOverride) {
38759
39172
  const pinnedEntries = [];
38760
39173
  const warmEntries = [];
38761
39174
  const activeTaskTags = [];
38762
- const stDir = join49(root, "state");
38763
- if (existsSync49(stDir)) {
39175
+ const stDir = join50(root, "state");
39176
+ if (existsSync50(stDir)) {
38764
39177
  const tFiles = import_fast_glob14.default.sync("*.md", { cwd: stDir, absolute: true });
38765
39178
  for (const file of tFiles) {
38766
39179
  try {
@@ -38916,8 +39329,8 @@ function generateSubagentBriefing() {
38916
39329
  parts.push('`dreamcontext memory recall "<keywords>"` (BM25 over knowledge/features/tasks/');
38917
39330
  parts.push("memory/changelog, <100ms, zero token overhead) BEFORE Glob/Grep \u2014 it frequently");
38918
39331
  parts.push("beats blind exploration outright.\n");
38919
- const soulPath = join49(root, "core", "0.soul.md");
38920
- if (existsSync49(soulPath)) {
39332
+ const soulPath = join50(root, "core", "0.soul.md");
39333
+ if (existsSync50(soulPath)) {
38921
39334
  const { data, content } = readFrontmatter(soulPath);
38922
39335
  const projectName = typeof data.name === "string" ? data.name : "";
38923
39336
  const lines = content.split("\n");
@@ -38936,8 +39349,8 @@ function generateSubagentBriefing() {
38936
39349
  `);
38937
39350
  }
38938
39351
  }
38939
- const featuresDir = join49(root, "core", "features");
38940
- if (existsSync49(featuresDir)) {
39352
+ const featuresDir = join50(root, "core", "features");
39353
+ if (existsSync50(featuresDir)) {
38941
39354
  const featureFiles = import_fast_glob14.default.sync("*.md", { cwd: featuresDir, absolute: true });
38942
39355
  const features = [];
38943
39356
  for (const file of featureFiles) {
@@ -39004,8 +39417,8 @@ function generateSubagentBriefing() {
39004
39417
  }
39005
39418
  parts.push("");
39006
39419
  }
39007
- const coreDir = join49(root, "core");
39008
- if (existsSync49(coreDir)) {
39420
+ const coreDir = join50(root, "core");
39421
+ if (existsSync50(coreDir)) {
39009
39422
  const allCoreFiles = import_fast_glob14.default.sync(["[0-9]*"], { cwd: coreDir, absolute: true });
39010
39423
  allCoreFiles.sort((a, b) => basename14(a).localeCompare(basename14(b), void 0, { numeric: true }));
39011
39424
  if (allCoreFiles.length > 0) {
@@ -39149,7 +39562,7 @@ var init_salience = __esm({
39149
39562
  });
39150
39563
 
39151
39564
  // src/cli/commands/transcript.ts
39152
- import { readFileSync as readFileSync39, existsSync as existsSync50, statSync as statSync6 } from "fs";
39565
+ import { readFileSync as readFileSync40, existsSync as existsSync51, statSync as statSync6 } from "fs";
39153
39566
  function distillTranscript(transcriptPath, sinceTimestamp) {
39154
39567
  const result = {
39155
39568
  userMessages: [],
@@ -39158,11 +39571,11 @@ function distillTranscript(transcriptPath, sinceTimestamp) {
39158
39571
  errors: [],
39159
39572
  bookmarks: []
39160
39573
  };
39161
- if (!existsSync50(transcriptPath)) return result;
39574
+ if (!existsSync51(transcriptPath)) return result;
39162
39575
  try {
39163
39576
  const stat = statSync6(transcriptPath);
39164
39577
  if (stat.size === 0 || stat.size > MAX_TRANSCRIPT_BYTES) return result;
39165
- const content = readFileSync39(transcriptPath, "utf-8");
39578
+ const content = readFileSync40(transcriptPath, "utf-8");
39166
39579
  const lines = content.split("\n").filter((l) => l.trim());
39167
39580
  for (const line of lines) {
39168
39581
  let entry;
@@ -39346,7 +39759,7 @@ function registerTranscriptCommand(program2) {
39346
39759
  error(`No transcript path for session: ${sessionId}`);
39347
39760
  return;
39348
39761
  }
39349
- if (!existsSync50(session.transcript_path)) {
39762
+ if (!existsSync51(session.transcript_path)) {
39350
39763
  error(`Transcript file not found: ${session.transcript_path}`);
39351
39764
  return;
39352
39765
  }
@@ -39390,22 +39803,22 @@ var init_transcript = __esm({
39390
39803
  });
39391
39804
 
39392
39805
  // src/lib/marketing/path-guards.ts
39393
- import { existsSync as existsSync51, realpathSync } from "fs";
39394
- import { basename as basename15, dirname as dirname27, resolve as resolve8, sep as sep5 } from "path";
39806
+ import { existsSync as existsSync52, realpathSync } from "fs";
39807
+ import { basename as basename15, dirname as dirname28, resolve as resolve9, sep as sep5 } from "path";
39395
39808
  function isMarketingEnvPath(filePath, from) {
39396
39809
  if (!filePath) return false;
39397
39810
  const root = resolveContextRoot(from);
39398
39811
  if (!root) return false;
39399
39812
  const realRoot = realpathOrSelf(root);
39400
- const target = resolve8(realRoot, "marketing", ".env");
39401
- const realCandidate = realpathOfNearestAncestor(resolve8(filePath));
39813
+ const target = resolve9(realRoot, "marketing", ".env");
39814
+ const realCandidate = realpathOfNearestAncestor(resolve9(filePath));
39402
39815
  if (realCandidate === target) return true;
39403
39816
  if (!realCandidate.startsWith(realRoot + sep5)) return false;
39404
39817
  return realCandidate.endsWith(`${sep5}marketing${sep5}.env`);
39405
39818
  }
39406
39819
  function realpathOrSelf(p) {
39407
39820
  try {
39408
- return existsSync51(p) ? realpathSync(p) : p;
39821
+ return existsSync52(p) ? realpathSync(p) : p;
39409
39822
  } catch {
39410
39823
  return p;
39411
39824
  }
@@ -39413,9 +39826,9 @@ function realpathOrSelf(p) {
39413
39826
  function realpathOfNearestAncestor(p) {
39414
39827
  let current = p;
39415
39828
  const tail = [];
39416
- while (current && current !== sep5 && !existsSync51(current)) {
39829
+ while (current && current !== sep5 && !existsSync52(current)) {
39417
39830
  tail.unshift(basename15(current));
39418
- current = dirname27(current);
39831
+ current = dirname28(current);
39419
39832
  }
39420
39833
  if (!current || current === sep5) return p;
39421
39834
  try {
@@ -39433,7 +39846,7 @@ var init_path_guards = __esm({
39433
39846
  });
39434
39847
 
39435
39848
  // src/lib/recall-query-extractor.ts
39436
- import { execFileSync as execFileSync2 } from "child_process";
39849
+ import { execFileSync as execFileSync4 } from "child_process";
39437
39850
  function buildCorpusIndex(corpus) {
39438
39851
  return corpus.map((d) => {
39439
39852
  const desc = d.description ? ` \u2014 ${d.description}` : "";
@@ -39532,7 +39945,7 @@ If nothing is relevant: {"docs":[],"skip":false}
39532
39945
  If pure greeting: {"skip":true}`;
39533
39946
  DEFAULT_TIMEOUT_MS2 = 12e4;
39534
39947
  defaultExecutor = (prompt, systemPrompt) => {
39535
- return execFileSync2("claude", [
39948
+ return execFileSync4("claude", [
39536
39949
  "--model",
39537
39950
  "haiku",
39538
39951
  "-p",
@@ -39554,371 +39967,6 @@ If pure greeting: {"skip":true}`;
39554
39967
  }
39555
39968
  });
39556
39969
 
39557
- // src/cli/commands/app.ts
39558
- import { execFileSync as execFileSync3, spawn as spawnProcess } from "child_process";
39559
- import {
39560
- existsSync as existsSync52,
39561
- mkdirSync as mkdirSync25,
39562
- mkdtempSync,
39563
- readFileSync as readFileSync40,
39564
- writeFileSync as writeFileSync32,
39565
- rmSync as rmSync6,
39566
- renameSync as renameSync8,
39567
- readdirSync as readdirSync12
39568
- } from "fs";
39569
- import { homedir as homedir2, tmpdir } from "os";
39570
- import { join as join50, resolve as resolve9, dirname as dirname28 } from "path";
39571
- import { createHash as createHash2 } from "crypto";
39572
- function detectPlatform(platform2 = process.platform, arch2 = process.arch) {
39573
- const os3 = platform2 === "darwin" ? "darwin" : platform2 === "win32" ? "win32" : platform2 === "linux" ? "linux" : "other";
39574
- const a = arch2 === "arm64" ? "aarch64" : arch2 === "x64" ? "x86_64" : "other";
39575
- return { os: os3, arch: a };
39576
- }
39577
- function pickAssetForArch(assetNames, arch2) {
39578
- const appAssets = assetNames.filter((n) => n.endsWith(".app.tar.gz"));
39579
- const exact = appAssets.find((n) => n.includes(`_${arch2}.`));
39580
- return exact ?? null;
39581
- }
39582
- function defaultInstallDir(home = homedir2()) {
39583
- return join50(home, "Applications");
39584
- }
39585
- function appManifestPath(home = homedir2()) {
39586
- return join50(home, ".dreamcontext", "app.json");
39587
- }
39588
- function readAppManifest(home = homedir2()) {
39589
- const p = appManifestPath(home);
39590
- if (!existsSync52(p)) return null;
39591
- try {
39592
- const parsed = JSON.parse(readFileSync40(p, "utf-8"));
39593
- if (!parsed || typeof parsed.version !== "string" || typeof parsed.path !== "string") return null;
39594
- return {
39595
- version: parsed.version,
39596
- path: parsed.path,
39597
- installedAt: typeof parsed.installedAt === "number" ? parsed.installedAt : 0,
39598
- source: typeof parsed.source === "string" ? parsed.source : "unknown"
39599
- };
39600
- } catch {
39601
- return null;
39602
- }
39603
- }
39604
- function writeAppManifest(manifest, home = homedir2()) {
39605
- const p = appManifestPath(home);
39606
- mkdirSync25(dirname28(p), { recursive: true });
39607
- writeFileSync32(p, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
39608
- }
39609
- function run(cmd, args) {
39610
- execFileSync3(cmd, args, { stdio: ["ignore", "ignore", "pipe"] });
39611
- }
39612
- function runCapture(cmd, args) {
39613
- return execFileSync3(cmd, args, { encoding: "utf-8" }).toString().trim();
39614
- }
39615
- function validateAppBundle(appDir) {
39616
- if (!appDir.endsWith(".app") || !existsSync52(appDir)) {
39617
- throw new Error(`Not a .app bundle: ${appDir}`);
39618
- }
39619
- const macos = join50(appDir, "Contents", "MacOS");
39620
- const plist = join50(appDir, "Contents", "Info.plist");
39621
- if (!existsSync52(macos) || !existsSync52(plist)) {
39622
- throw new Error(`Malformed .app (missing Contents/MacOS or Info.plist): ${appDir}`);
39623
- }
39624
- }
39625
- function readBundleVersion(appDir) {
39626
- const plist = join50(appDir, "Contents", "Info.plist");
39627
- try {
39628
- return runCapture("plutil", ["-extract", "CFBundleShortVersionString", "raw", "-o", "-", plist]) || null;
39629
- } catch {
39630
- return null;
39631
- }
39632
- }
39633
- function findAppInDir(dir) {
39634
- const entries = readdirSync12(dir).filter((e) => e.endsWith(".app"));
39635
- return entries.length > 0 ? join50(dir, entries[0]) : null;
39636
- }
39637
- function materializeAppBundle(source, workDir) {
39638
- const src = resolve9(source);
39639
- if (!existsSync52(src)) throw new Error(`Source not found: ${src}`);
39640
- if (src.endsWith(".app")) {
39641
- return src;
39642
- }
39643
- if (src.endsWith(".tar.gz") || src.endsWith(".tgz")) {
39644
- run("tar", ["-xzf", src, "-C", workDir]);
39645
- } else if (src.endsWith(".zip")) {
39646
- run("ditto", ["-x", "-k", src, workDir]);
39647
- } else {
39648
- throw new Error(`Unsupported source (expect .app, .tar.gz, or .zip): ${src}`);
39649
- }
39650
- const app = findAppInDir(workDir);
39651
- if (!app) throw new Error(`No .app found inside archive: ${src}`);
39652
- return app;
39653
- }
39654
- function verifyCodesign(appDir) {
39655
- try {
39656
- execFileSync3("codesign", ["--verify", "--deep", appDir], { stdio: ["ignore", "ignore", "ignore"] });
39657
- return true;
39658
- } catch {
39659
- return false;
39660
- }
39661
- }
39662
- function isAppRunning() {
39663
- try {
39664
- const out = execFileSync3("pgrep", ["-f", `${APP_BUNDLE_NAME}/Contents/MacOS/`], { encoding: "utf-8" }).toString().trim();
39665
- return out.length > 0;
39666
- } catch {
39667
- return false;
39668
- }
39669
- }
39670
- function installAppBundle(source, opts = {}) {
39671
- if (detectPlatform().os !== "darwin") {
39672
- throw new Error("Desktop app install is currently macOS-only.");
39673
- }
39674
- const home = opts.home ?? homedir2();
39675
- const installDir = opts.installDir ?? defaultInstallDir(home);
39676
- mkdirSync25(installDir, { recursive: true });
39677
- const workDir = mkdtempSync(join50(tmpdir(), "dc-app-"));
39678
- try {
39679
- const appDir = materializeAppBundle(source, workDir);
39680
- validateAppBundle(appDir);
39681
- const target = join50(installDir, APP_BUNDLE_NAME);
39682
- const staging = join50(installDir, `.${APP_BUNDLE_NAME}.new-${process.pid}`);
39683
- const backup = join50(installDir, `.${APP_BUNDLE_NAME}.old-${process.pid}`);
39684
- rmSync6(staging, { recursive: true, force: true });
39685
- rmSync6(backup, { recursive: true, force: true });
39686
- let wasRunning;
39687
- let replaced;
39688
- try {
39689
- run("ditto", [appDir, staging]);
39690
- try {
39691
- run("xattr", ["-dr", "com.apple.quarantine", staging]);
39692
- } catch {
39693
- }
39694
- wasRunning = isAppRunning();
39695
- replaced = existsSync52(target);
39696
- if (replaced) renameSync8(target, backup);
39697
- renameSync8(staging, target);
39698
- } catch (e) {
39699
- if (existsSync52(target) === false && existsSync52(backup)) {
39700
- try {
39701
- renameSync8(backup, target);
39702
- } catch {
39703
- }
39704
- }
39705
- rmSync6(staging, { recursive: true, force: true });
39706
- throw e;
39707
- }
39708
- rmSync6(backup, { recursive: true, force: true });
39709
- const version = readBundleVersion(target);
39710
- writeAppManifest(
39711
- {
39712
- version: version ?? "unknown",
39713
- path: target,
39714
- installedAt: Date.now(),
39715
- source: opts.sourceLabel ?? "local"
39716
- },
39717
- home
39718
- );
39719
- const signatureValid = verifyCodesign(target);
39720
- return { version, path: target, replaced, wasRunning, signatureValid };
39721
- } finally {
39722
- rmSync6(workDir, { recursive: true, force: true });
39723
- }
39724
- }
39725
- function versionFromTag(tag) {
39726
- return tag.replace(/^v/, "");
39727
- }
39728
- async function fetchLatestRelease(repo = APP_RELEASE_REPO, fetchImpl = fetch) {
39729
- try {
39730
- const res = await fetchImpl(`https://api.github.com/repos/${repo}/releases/latest`, {
39731
- headers: { Accept: "application/vnd.github+json", "User-Agent": "dreamcontext-cli" }
39732
- });
39733
- if (!res.ok) return null;
39734
- const json = await res.json();
39735
- if (!json || typeof json.tag_name !== "string" || !Array.isArray(json.assets)) return null;
39736
- return json;
39737
- } catch {
39738
- return null;
39739
- }
39740
- }
39741
- async function downloadTo(url, destFile, fetchImpl = fetch) {
39742
- const res = await fetchImpl(url, { headers: { "User-Agent": "dreamcontext-cli" } });
39743
- if (!res.ok) throw new Error(`Download failed (${res.status}): ${url}`);
39744
- const buf = Buffer.from(await res.arrayBuffer());
39745
- writeFileSync32(destFile, buf);
39746
- }
39747
- function sha256File(file) {
39748
- return createHash2("sha256").update(readFileSync40(file)).digest("hex");
39749
- }
39750
- async function downloadLatestArtifact(repo = APP_RELEASE_REPO, fetchImpl = fetch) {
39751
- const plat = detectPlatform();
39752
- if (plat.os !== "darwin") return null;
39753
- const release2 = await fetchLatestRelease(repo, fetchImpl);
39754
- if (!release2) return null;
39755
- const assetName = pickAssetForArch(release2.assets.map((a) => a.name), plat.arch);
39756
- if (!assetName) return null;
39757
- const asset = release2.assets.find((a) => a.name === assetName);
39758
- const sumAsset = release2.assets.find((a) => a.name === `${assetName}.sha256`);
39759
- if (!sumAsset) {
39760
- throw new Error(
39761
- `Release ${release2.tag_name} is missing ${assetName}.sha256 \u2014 refusing to install an unverified binary.`
39762
- );
39763
- }
39764
- const workDir = mkdtempSync(join50(tmpdir(), "dc-app-dl-"));
39765
- try {
39766
- const archivePath = join50(workDir, assetName);
39767
- await downloadTo(asset.browser_download_url, archivePath, fetchImpl);
39768
- const sumFile = join50(workDir, `${assetName}.sha256`);
39769
- await downloadTo(sumAsset.browser_download_url, sumFile, fetchImpl);
39770
- const expected = readFileSync40(sumFile, "utf-8").trim().split(/\s+/)[0].toLowerCase();
39771
- const actual = sha256File(archivePath).toLowerCase();
39772
- if (!expected || expected !== actual) {
39773
- throw new Error(`Checksum mismatch for ${assetName}: expected ${expected || "(empty)"}, got ${actual}`);
39774
- }
39775
- return { archivePath, version: versionFromTag(release2.tag_name) };
39776
- } catch (e) {
39777
- rmSync6(workDir, { recursive: true, force: true });
39778
- throw e;
39779
- }
39780
- }
39781
- function appAutoUpdateEnabled(env2 = process.env) {
39782
- if (env2.DREAMCONTEXT_VERSION_CHECK === "0") return false;
39783
- return env2.DREAMCONTEXT_APP_AUTO_UPDATE !== "0";
39784
- }
39785
- function defaultAppUpdateSpawner() {
39786
- const child = spawnProcess(process.execPath, [process.argv[1], "app", "update"], {
39787
- detached: true,
39788
- stdio: "ignore"
39789
- });
39790
- child.unref();
39791
- }
39792
- function maybeTriggerAppUpdate(env2 = process.env, deps = {}) {
39793
- try {
39794
- if (detectPlatform().os !== "darwin") return false;
39795
- if (!appAutoUpdateEnabled(env2)) return false;
39796
- const installed = deps.manifest !== void 0 ? deps.manifest : readAppManifest();
39797
- if (!installed) return false;
39798
- (deps.spawner ?? defaultAppUpdateSpawner)();
39799
- return true;
39800
- } catch {
39801
- return false;
39802
- }
39803
- }
39804
- async function doInstall(from, dir) {
39805
- const plat = detectPlatform();
39806
- if (plat.os !== "darwin") {
39807
- console.log(source_default.yellow("The desktop app is currently macOS-only. Windows/Linux support is planned."));
39808
- return;
39809
- }
39810
- let tempToClean = null;
39811
- let source = from;
39812
- let sourceLabel = "local";
39813
- if (!source) {
39814
- console.log(source_default.cyan("Fetching the latest desktop app from GitHub Releases\u2026"));
39815
- let dl = null;
39816
- try {
39817
- dl = await downloadLatestArtifact();
39818
- } catch (e) {
39819
- console.log(source_default.red(`Install aborted: ${e.message}`));
39820
- return;
39821
- }
39822
- if (!dl) {
39823
- console.log(
39824
- source_default.yellow(
39825
- "No published desktop release found yet.\nBuild one locally and install it with: dreamcontext app install --from <path-to.app|.tar.gz>"
39826
- )
39827
- );
39828
- return;
39829
- }
39830
- source = dl.archivePath;
39831
- sourceLabel = "github";
39832
- tempToClean = dirname28(dl.archivePath);
39833
- }
39834
- try {
39835
- const res = installAppBundle(source, { installDir: dir, sourceLabel });
39836
- console.log(source_default.green(`\u2713 Installed dreamcontext-beta ${res.version ?? ""}`.trim()) + source_default.dim(` \u2192 ${res.path}`));
39837
- if (res.signatureValid === false) {
39838
- console.log(
39839
- source_default.yellow(
39840
- "Warning: the installed app failed code-signature verification. It may still launch (ad-hoc),\nbut the published artifact should be properly ad-hoc deep-signed at build time."
39841
- )
39842
- );
39843
- }
39844
- if (res.wasRunning) {
39845
- console.log(source_default.yellow("The app is currently running \u2014 restart it to apply the update."));
39846
- } else {
39847
- console.log(source_default.dim(`Launch it: open "${res.path}"`));
39848
- }
39849
- } finally {
39850
- if (tempToClean) rmSync6(tempToClean, { recursive: true, force: true });
39851
- }
39852
- }
39853
- async function doUpdate(from, dir) {
39854
- const installed = readAppManifest();
39855
- if (!installed) {
39856
- console.log(source_default.dim("No installed app recorded \u2014 running a fresh install."));
39857
- await doInstall(from, dir);
39858
- return;
39859
- }
39860
- if (from) {
39861
- await doInstall(from, dir);
39862
- return;
39863
- }
39864
- let dl = null;
39865
- try {
39866
- dl = await downloadLatestArtifact();
39867
- } catch (e) {
39868
- console.log(source_default.red(`Update aborted: ${e.message}`));
39869
- return;
39870
- }
39871
- if (!dl) {
39872
- console.log(source_default.yellow("No published desktop release available to update to."));
39873
- return;
39874
- }
39875
- try {
39876
- if (compareVersions(installed.version, dl.version) >= 0) {
39877
- console.log(source_default.green(`Desktop app is up to date (${installed.version}).`));
39878
- return;
39879
- }
39880
- const res = installAppBundle(dl.archivePath, { installDir: dir, sourceLabel: "github" });
39881
- console.log(source_default.green(`\u2713 Updated dreamcontext-beta ${installed.version} \u2192 ${res.version ?? dl.version}`));
39882
- if (res.wasRunning) console.log(source_default.yellow("Restart the app to apply the update."));
39883
- } finally {
39884
- rmSync6(dirname28(dl.archivePath), { recursive: true, force: true });
39885
- }
39886
- }
39887
- function doStatus() {
39888
- const installed = readAppManifest();
39889
- if (!installed) {
39890
- console.log("Desktop app: not installed (run `dreamcontext app install`).");
39891
- return;
39892
- }
39893
- const onDisk = existsSync52(installed.path);
39894
- console.log(`Desktop app: ${installed.version}`);
39895
- console.log(` path: ${installed.path}${onDisk ? "" : source_default.red(" (missing!)")}`);
39896
- console.log(` source: ${installed.source}`);
39897
- console.log(` running: ${isAppRunning() ? "yes" : "no"}`);
39898
- }
39899
- function registerAppCommand(program2) {
39900
- const app = program2.command("app").description("Manage the dreamcontext desktop app (install / update / status)");
39901
- app.command("install").description("Install the desktop app to ~/Applications (no quarantine, no admin)").option("--from <path>", "Install from a local .app, .tar.gz, or .zip instead of GitHub Releases").option("--dir <dir>", "Install directory (default: ~/Applications)").action(async (opts) => {
39902
- await doInstall(opts.from, opts.dir);
39903
- });
39904
- app.command("update").description("Update the installed desktop app to the latest version").option("--from <path>", "Update from a local artifact instead of GitHub Releases").option("--dir <dir>", "Install directory (default: ~/Applications)").action(async (opts) => {
39905
- await doUpdate(opts.from, opts.dir);
39906
- });
39907
- app.command("status").description("Show the installed desktop app version and state").action(() => {
39908
- doStatus();
39909
- });
39910
- }
39911
- var APP_BUNDLE_NAME, APP_RELEASE_REPO;
39912
- var init_app = __esm({
39913
- "src/cli/commands/app.ts"() {
39914
- "use strict";
39915
- init_source();
39916
- init_version_check();
39917
- APP_BUNDLE_NAME = "dreamcontext-beta.app";
39918
- APP_RELEASE_REPO = "meanllbrl/dreamcontext";
39919
- }
39920
- });
39921
-
39922
39970
  // src/cli/commands/asset-drift.ts
39923
39971
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync7, existsSync as existsSync53, readFileSync as readFileSync41, readdirSync as readdirSync13 } from "fs";
39924
39972
  import { tmpdir as tmpdir2 } from "os";
@@ -39988,7 +40036,7 @@ var init_asset_drift = __esm({
39988
40036
 
39989
40037
  // src/cli/commands/hook.ts
39990
40038
  import { readFileSync as readFileSync42, existsSync as existsSync54, statSync as statSync7 } from "fs";
39991
- import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
40039
+ import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
39992
40040
  import { get as httpGet } from "http";
39993
40041
  import { dirname as dirname30, resolve as resolve10, join as join52, extname as extname2, basename as basename16, relative as relative4 } from "path";
39994
40042
  function readStdin() {
@@ -40143,13 +40191,13 @@ function runFormatter(detection, filePath) {
40143
40191
  if (detection.type === "biome") {
40144
40192
  const localBin = resolveLocalBin("biome", detection.projectRoot);
40145
40193
  if (localBin) {
40146
- execFileSync4(localBin, ["format", "--write", filePath], {
40194
+ execFileSync5(localBin, ["format", "--write", filePath], {
40147
40195
  cwd: detection.projectRoot,
40148
40196
  timeout: 15e3,
40149
40197
  stdio: ["pipe", "pipe", "pipe"]
40150
40198
  });
40151
40199
  } else {
40152
- execFileSync4("npx", ["@biomejs/biome", "format", "--write", filePath], {
40200
+ execFileSync5("npx", ["@biomejs/biome", "format", "--write", filePath], {
40153
40201
  cwd: detection.projectRoot,
40154
40202
  timeout: 15e3,
40155
40203
  stdio: ["pipe", "pipe", "pipe"]
@@ -40158,13 +40206,13 @@ function runFormatter(detection, filePath) {
40158
40206
  } else {
40159
40207
  const localBin = resolveLocalBin("prettier", detection.projectRoot);
40160
40208
  if (localBin) {
40161
- execFileSync4(localBin, ["--write", filePath], {
40209
+ execFileSync5(localBin, ["--write", filePath], {
40162
40210
  cwd: detection.projectRoot,
40163
40211
  timeout: 15e3,
40164
40212
  stdio: ["pipe", "pipe", "pipe"]
40165
40213
  });
40166
40214
  } else {
40167
- execFileSync4("npx", ["prettier", "--write", filePath], {
40215
+ execFileSync5("npx", ["prettier", "--write", filePath], {
40168
40216
  cwd: detection.projectRoot,
40169
40217
  timeout: 15e3,
40170
40218
  stdio: ["pipe", "pipe", "pipe"]
@@ -40184,14 +40232,14 @@ function runTscCheckWithConfig(filePath, tsconfigPath) {
40184
40232
  const localBin = resolveLocalBin("tsc", projectRoot);
40185
40233
  const args = ["--noEmit", "--pretty", "false", "--incremental"];
40186
40234
  if (localBin) {
40187
- execFileSync4(localBin, args, {
40235
+ execFileSync5(localBin, args, {
40188
40236
  cwd: projectRoot,
40189
40237
  timeout: 3e4,
40190
40238
  encoding: "utf-8",
40191
40239
  stdio: ["pipe", "pipe", "pipe"]
40192
40240
  });
40193
40241
  } else {
40194
- execFileSync4("npx", ["tsc", ...args], {
40242
+ execFileSync5("npx", ["tsc", ...args], {
40195
40243
  cwd: projectRoot,
40196
40244
  timeout: 3e4,
40197
40245
  encoding: "utf-8",
@@ -57139,7 +57187,7 @@ var init_rem_sleep2 = __esm({
57139
57187
  });
57140
57188
 
57141
57189
  // src/lib/marketing/git-guard.ts
57142
- import { execFileSync as execFileSync5 } from "child_process";
57190
+ import { execFileSync as execFileSync6 } from "child_process";
57143
57191
  function isBlockedMarketingPath(repoRelPath) {
57144
57192
  if (typeof repoRelPath !== "string" || repoRelPath.length === 0) return false;
57145
57193
  const norm2 = repoRelPath.replace(/\\/g, "/");
@@ -57153,7 +57201,7 @@ function isBlockedMarketingPath(repoRelPath) {
57153
57201
  function getStagedFiles(cwd) {
57154
57202
  let raw;
57155
57203
  try {
57156
- raw = execFileSync5("git", ["diff", "--cached", "--name-only", "-z"], {
57204
+ raw = execFileSync6("git", ["diff", "--cached", "--name-only", "-z"], {
57157
57205
  cwd,
57158
57206
  encoding: "utf-8",
57159
57207
  stdio: ["ignore", "pipe", "ignore"]
@@ -57178,11 +57226,11 @@ var init_git_guard = __esm({
57178
57226
 
57179
57227
  // src/cli/commands/marketing/hooks.ts
57180
57228
  import { existsSync as existsSync87, readFileSync as readFileSync66, writeFileSync as writeFileSync44, chmodSync as chmodSync3, mkdirSync as mkdirSync39, statSync as statSync18 } from "fs";
57181
- import { execFileSync as execFileSync6 } from "child_process";
57229
+ import { execFileSync as execFileSync7 } from "child_process";
57182
57230
  import { join as join78 } from "path";
57183
57231
  function findGitDir(cwd = process.cwd()) {
57184
57232
  try {
57185
- const out = execFileSync6("git", ["rev-parse", "--git-dir"], {
57233
+ const out = execFileSync7("git", ["rev-parse", "--git-dir"], {
57186
57234
  cwd,
57187
57235
  encoding: "utf-8",
57188
57236
  stdio: ["ignore", "pipe", "ignore"]
@@ -57869,10 +57917,39 @@ var init_memory = __esm({
57869
57917
  });
57870
57918
 
57871
57919
  // src/cli/commands/upgrade.ts
57872
- import { execFileSync as execFileSync8 } from "child_process";
57920
+ import { execFileSync as execFileSync9 } from "child_process";
57873
57921
  import { dirname as dirname52 } from "path";
57922
+ import { existsSync as existsSync91 } from "fs";
57874
57923
  function defaultInstaller(args) {
57875
- execFileSync8("npm", args, { stdio: "inherit" });
57924
+ execFileSync9("npm", args, { stdio: "inherit" });
57925
+ }
57926
+ function defaultProjectUpdater(vault) {
57927
+ if (!existsSync91(vault.path)) {
57928
+ return { vault, ok: false, error: "project folder no longer exists" };
57929
+ }
57930
+ try {
57931
+ execFileSync9("npx", ["dreamcontext", "update", "--yes"], {
57932
+ cwd: vault.path,
57933
+ stdio: "inherit"
57934
+ });
57935
+ return { vault, ok: true };
57936
+ } catch (e) {
57937
+ return { vault, ok: false, error: e.message };
57938
+ }
57939
+ }
57940
+ async function defaultConfirmAll(count) {
57941
+ return esm_default4({
57942
+ message: `Also refresh ${count} registered dreamcontext project(s) to the new version?`,
57943
+ default: true
57944
+ });
57945
+ }
57946
+ function defaultAppUpdater() {
57947
+ try {
57948
+ execFileSync9("npx", ["dreamcontext", "app", "update"], { stdio: "inherit" });
57949
+ return { ok: true };
57950
+ } catch (e) {
57951
+ return { ok: false, error: e.message };
57952
+ }
57876
57953
  }
57877
57954
  function projectRootForCache() {
57878
57955
  const contextDir = resolveContextRoot();
@@ -57881,7 +57958,7 @@ function projectRootForCache() {
57881
57958
  function defaultLiveLatest() {
57882
57959
  let latest = null;
57883
57960
  try {
57884
- const raw = execFileSync8("npm", ["view", "dreamcontext", "version"], {
57961
+ const raw = execFileSync9("npm", ["view", "dreamcontext", "version"], {
57885
57962
  timeout: 5e3,
57886
57963
  encoding: "utf-8"
57887
57964
  });
@@ -57905,7 +57982,62 @@ function defaultLiveLatest() {
57905
57982
  }
57906
57983
  return latest;
57907
57984
  }
57908
- function runUpgrade(check2, opts) {
57985
+ async function maybeUpdateApp(opts) {
57986
+ const installed = (opts?.appInstalledCheck ?? (() => readAppManifest() !== null))();
57987
+ if (!installed) return;
57988
+ const auto = opts?.yes === true;
57989
+ const interactive = process.stdin.isTTY === true;
57990
+ if (!auto && !interactive) {
57991
+ console.log(
57992
+ source_default.dim(
57993
+ "Tip: the desktop app is installed \u2014 run `dreamcontext upgrade --yes` (or `dreamcontext app update`) to update it too."
57994
+ )
57995
+ );
57996
+ return;
57997
+ }
57998
+ console.log(source_default.cyan("\n\u21BB Updating the desktop app\u2026"));
57999
+ const result = (opts?.appUpdater ?? defaultAppUpdater)();
58000
+ if (result.ok) console.log(source_default.green("\u2713 Desktop app updated."));
58001
+ else console.log(source_default.yellow(` \u26A0 Desktop app update skipped: ${result.error ?? "failed"}`));
58002
+ }
58003
+ async function maybeUpdateAllProjects(opts) {
58004
+ const vaults = (opts?.vaultLister ?? (() => listVaults()))();
58005
+ if (vaults.length === 0) return;
58006
+ const auto = opts?.yes === true;
58007
+ const canPrompt = process.stdin.isTTY === true || opts?.confirmAll !== void 0;
58008
+ if (!auto && !canPrompt) {
58009
+ console.log(
58010
+ source_default.dim(
58011
+ `Tip: ${vaults.length} registered project(s) were not refreshed. Run \`dreamcontext upgrade --yes\` to refresh them all, or \`dreamcontext update\` in each.`
58012
+ )
58013
+ );
58014
+ return;
58015
+ }
58016
+ let proceed = auto;
58017
+ if (!proceed) {
58018
+ const confirmFn = opts?.confirmAll ?? defaultConfirmAll;
58019
+ proceed = await confirmFn(vaults.length);
58020
+ }
58021
+ if (!proceed) {
58022
+ console.log(source_default.dim("Skipped refreshing other projects. Run `dreamcontext update` in any project later."));
58023
+ return;
58024
+ }
58025
+ const updater = opts?.projectUpdater ?? defaultProjectUpdater;
58026
+ const results = [];
58027
+ for (const vault of vaults) {
58028
+ console.log(source_default.cyan(`
58029
+ \u21BB Refreshing ${source_default.bold(vault.name)} (${vault.path})...`));
58030
+ results.push(updater(vault));
58031
+ }
58032
+ const ok = results.filter((r) => r.ok);
58033
+ const failed = results.filter((r) => !r.ok);
58034
+ console.log("");
58035
+ console.log(source_default.green(`\u2713 Refreshed ${ok.length}/${results.length} project(s).`));
58036
+ for (const f of failed) {
58037
+ console.log(source_default.yellow(` \u26A0 ${f.vault.name}: ${f.error ?? "update failed"}`));
58038
+ }
58039
+ }
58040
+ async function runUpgrade(check2, opts) {
57909
58041
  const installer = opts?.installer ?? defaultInstaller;
57910
58042
  if (check2) {
57911
58043
  const current = dreamcontextVersion();
@@ -57921,20 +58053,26 @@ function runUpgrade(check2, opts) {
57921
58053
  console.log(source_default.cyan("Installing dreamcontext@latest via npm..."));
57922
58054
  installer(["install", "-g", "dreamcontext@latest"]);
57923
58055
  console.log("");
57924
- console.log(source_default.green("CLI upgraded. Run") + " dreamcontext update " + source_default.green("to refresh your project files."));
58056
+ console.log(source_default.green("CLI upgraded."));
58057
+ await maybeUpdateApp(opts);
58058
+ await maybeUpdateAllProjects(opts);
58059
+ console.log(source_default.dim("\nIn each project, the new files take effect next session (hooks re-read them)."));
57925
58060
  }
57926
58061
  function registerUpgradeCommand(program2) {
57927
- program2.command("upgrade").description("Upgrade the dreamcontext CLI itself to the latest published version").option("--check", "Print current vs latest version and exit without installing").option("-y, --yes", "Non-interactive (no prompts; reserved for future use)").action((options2, _cmd, opts) => {
57928
- runUpgrade(Boolean(options2.check), opts);
58062
+ program2.command("upgrade").description("Upgrade the dreamcontext CLI, then offer to refresh every registered project to match").option("--check", "Print current vs latest version and exit without installing").option("-y, --yes", "Refresh every registered project without prompting (non-interactive)").action(async (options2, _cmd, opts) => {
58063
+ await runUpgrade(Boolean(options2.check), { ...opts, yes: options2.yes });
57929
58064
  });
57930
58065
  }
57931
58066
  var init_upgrade = __esm({
57932
58067
  "src/cli/commands/upgrade.ts"() {
57933
58068
  "use strict";
57934
58069
  init_source();
58070
+ init_esm16();
57935
58071
  init_manifest();
57936
58072
  init_version_check();
57937
58073
  init_context_path();
58074
+ init_vaults();
58075
+ init_app();
57938
58076
  }
57939
58077
  });
57940
58078
 
@@ -58502,7 +58640,7 @@ var init_people = __esm({
58502
58640
 
58503
58641
  // src/cli/commands/config.ts
58504
58642
  import { dirname as dirname54 } from "path";
58505
- import { existsSync as existsSync91, readFileSync as readFileSync69, writeFileSync as writeFileSync46 } from "fs";
58643
+ import { existsSync as existsSync92, readFileSync as readFileSync69, writeFileSync as writeFileSync46 } from "fs";
58506
58644
  import { join as join82 } from "path";
58507
58645
  function resolveProjectRoot() {
58508
58646
  const contextRoot = resolveContextRoot();
@@ -58764,7 +58902,7 @@ function registerConfigCommand(program2) {
58764
58902
  let userMdSynced = false;
58765
58903
  if (contextRoot) {
58766
58904
  const userMdPath = join82(contextRoot, "core", "1.user.md");
58767
- if (existsSync91(userMdPath)) {
58905
+ if (existsSync92(userMdPath)) {
58768
58906
  try {
58769
58907
  const before = readFileSync69(userMdPath, "utf-8");
58770
58908
  const after = ensurePeopleSection(before, roster);
@@ -58911,7 +59049,7 @@ var init_config4 = __esm({
58911
59049
  });
58912
59050
 
58913
59051
  // src/lib/feedback.ts
58914
- import { execFileSync as execFileSync9 } from "child_process";
59052
+ import { execFileSync as execFileSync10 } from "child_process";
58915
59053
  import { platform, release, arch } from "os";
58916
59054
  function isFeedbackCategory(value) {
58917
59055
  return Object.prototype.hasOwnProperty.call(CATEGORY_META, value);
@@ -59068,7 +59206,7 @@ var init_feedback = __esm({
59068
59206
  FEEDBACK_CATEGORIES = Object.keys(CATEGORY_META);
59069
59207
  defaultRunner = (cmd, args) => {
59070
59208
  try {
59071
- const stdout = execFileSync9(cmd, args, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
59209
+ const stdout = execFileSync10(cmd, args, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
59072
59210
  return { ok: true, stdout, stderr: "" };
59073
59211
  } catch (err) {
59074
59212
  const e = err;