@cortexkit/magic-context 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +372 -180
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -8300,7 +8300,7 @@ function parseModelListOutput(output) {
8300
8300
  if (!line || line.toLowerCase().includes("usage:"))
8301
8301
  continue;
8302
8302
  const token = line.split(/\s+/)[0]?.trim().replace(/,$/, "");
8303
- if (!token || !token.includes("/"))
8303
+ if (!token?.includes("/"))
8304
8304
  continue;
8305
8305
  if (/^https?:\/\//.test(token))
8306
8306
  continue;
@@ -8506,7 +8506,7 @@ class PiAdapter {
8506
8506
  }
8507
8507
  }
8508
8508
  getInstallHint() {
8509
- return "Install Pi: https://pi.coding/install (npm: @mariozechner/pi-coding-agent)";
8509
+ return "Install Pi: https://pi.coding/install (npm: @earendil-works/pi-coding-agent)";
8510
8510
  }
8511
8511
  getPluginCacheInfo() {
8512
8512
  return { path: null, exists: false, sizeBytes: 0 };
@@ -11353,11 +11353,11 @@ async function runSetup2(options = {}) {
11353
11353
  }
11354
11354
  const version = env.getPiVersion(pi.path);
11355
11355
  spinner2.stop(version ? `Pi ${version} detected at ${pi.path}` : `Pi detected at ${pi.path}`);
11356
- const MIN_PI_VERSION = "0.71.0";
11356
+ const MIN_PI_VERSION = "0.74.0";
11357
11357
  if (version && comparePiVersion(version, MIN_PI_VERSION) < 0) {
11358
11358
  prompts.log.warn(`Pi ${version} is older than the required ${MIN_PI_VERSION}.
11359
- Magic Context spawns subagents with \`--extension <path>\` (long form), which Pi 0.71.0 introduced. Older Pi versions hard-fail with "Unknown option" when historian/dreamer/sidekick tries to run.
11360
- Run \`pi update\` (or \`npm install -g @mariozechner/pi-coding-agent@latest\`) before continuing.`);
11359
+ Pi 0.74.0 renamed the npm package from \`@mariozechner/pi-coding-agent\` to \`@earendil-works/pi-coding-agent\`. Magic Context's peer dependency targets the new scope, so older Pi installs cannot load this extension.
11360
+ Run \`pi update --self\` (or \`npm install -g @earendil-works/pi-coding-agent@latest\`) before continuing.`);
11361
11361
  const proceed = await prompts.confirm("Continue with setup anyway? (subagents will fail at runtime)", false);
11362
11362
  if (!proceed) {
11363
11363
  prompts.outro("Setup cancelled — upgrade Pi and try again.");
@@ -27631,6 +27631,78 @@ var init_config = __esm(() => {
27631
27631
  init_variable();
27632
27632
  });
27633
27633
 
27634
+ // ../plugin/src/features/magic-context/key-files/aft-availability.ts
27635
+ import { existsSync as existsSync13, readFileSync as readFileSync9 } from "node:fs";
27636
+ import { homedir as homedir9 } from "node:os";
27637
+ import { join as join11 } from "node:path";
27638
+ function parseConfig(path4) {
27639
+ if (!existsSync13(path4))
27640
+ return null;
27641
+ return import_comment_json6.parse(readFileSync9(path4, "utf-8"));
27642
+ }
27643
+ function entryMatchesAft(entry) {
27644
+ const value = Array.isArray(entry) ? entry[0] : entry;
27645
+ return typeof value === "string" && (value.includes("@cortexkit/aft") || value.includes("aft-opencode") || value.includes("aft-pi"));
27646
+ }
27647
+ function hasAftInArray(value) {
27648
+ return Array.isArray(value) && value.some(entryMatchesAft);
27649
+ }
27650
+ function hasAftAtKeys(value, keys) {
27651
+ if (!value || typeof value !== "object")
27652
+ return false;
27653
+ const record2 = value;
27654
+ for (const key of keys) {
27655
+ if (hasAftInArray(record2[key]))
27656
+ return true;
27657
+ }
27658
+ return false;
27659
+ }
27660
+ function getAftAvailability() {
27661
+ const home = process.env.HOME || homedir9();
27662
+ const opencodePaths = [
27663
+ join11(home, ".config", "opencode", "opencode.jsonc"),
27664
+ join11(home, ".config", "opencode", "opencode.json")
27665
+ ];
27666
+ const piPaths = [join11(home, ".pi", "agent", "settings.json")];
27667
+ const checkedPaths = [...opencodePaths, ...piPaths];
27668
+ let opencode = false;
27669
+ for (const path4 of opencodePaths) {
27670
+ try {
27671
+ const config2 = parseConfig(path4);
27672
+ if (hasAftAtKeys(config2, ["plugin", "plugins", "mcp", "mcp_servers"])) {
27673
+ opencode = true;
27674
+ break;
27675
+ }
27676
+ } catch {}
27677
+ }
27678
+ let pi = false;
27679
+ for (const path4 of piPaths) {
27680
+ try {
27681
+ const config2 = parseConfig(path4);
27682
+ if (hasAftAtKeys(config2, ["packages", "extensions"])) {
27683
+ pi = true;
27684
+ break;
27685
+ }
27686
+ const agent = config2?.agent;
27687
+ if (hasAftAtKeys(agent, ["packages", "extensions"])) {
27688
+ pi = true;
27689
+ break;
27690
+ }
27691
+ } catch {}
27692
+ }
27693
+ const detected = opencode || pi;
27694
+ return {
27695
+ available: overrideAvailability ?? detected,
27696
+ opencode,
27697
+ pi,
27698
+ checkedPaths
27699
+ };
27700
+ }
27701
+ var import_comment_json6, overrideAvailability = null;
27702
+ var init_aft_availability = __esm(() => {
27703
+ import_comment_json6 = __toESM(require_src2(), 1);
27704
+ });
27705
+
27634
27706
  // ../plugin/src/features/magic-context/memory/embedding-probe.ts
27635
27707
  async function probeEmbeddingEndpoint(options) {
27636
27708
  const endpoint = options.endpoint.trim().replace(/\/+$/, "");
@@ -27736,8 +27808,8 @@ function truncate(text2) {
27736
27808
  var DEFAULT_TIMEOUT_MS = 1e4, MAX_PREVIEW_CHARS = 240;
27737
27809
 
27738
27810
  // ../plugin/src/shared/tui-config.ts
27739
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
27740
- import { dirname as dirname8, join as join11 } from "node:path";
27811
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "node:fs";
27812
+ import { dirname as dirname8, join as join12 } from "node:path";
27741
27813
  function isMagicContextEntry(entry) {
27742
27814
  if (!entry)
27743
27815
  return false;
@@ -27751,11 +27823,11 @@ function isMagicContextEntry(entry) {
27751
27823
  }
27752
27824
  function resolveTuiConfigPath() {
27753
27825
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
27754
- const jsoncPath = join11(configDir, "tui.jsonc");
27755
- const jsonPath = join11(configDir, "tui.json");
27756
- if (existsSync13(jsoncPath))
27826
+ const jsoncPath = join12(configDir, "tui.jsonc");
27827
+ const jsonPath = join12(configDir, "tui.json");
27828
+ if (existsSync14(jsoncPath))
27757
27829
  return jsoncPath;
27758
- if (existsSync13(jsonPath))
27830
+ if (existsSync14(jsonPath))
27759
27831
  return jsonPath;
27760
27832
  return jsonPath;
27761
27833
  }
@@ -27763,9 +27835,9 @@ function ensureTuiPluginEntry() {
27763
27835
  try {
27764
27836
  const configPath = resolveTuiConfigPath();
27765
27837
  let config2 = {};
27766
- if (existsSync13(configPath)) {
27767
- const raw = readFileSync9(configPath, "utf-8");
27768
- config2 = import_comment_json6.parse(raw) ?? {};
27838
+ if (existsSync14(configPath)) {
27839
+ const raw = readFileSync10(configPath, "utf-8");
27840
+ config2 = import_comment_json7.parse(raw) ?? {};
27769
27841
  }
27770
27842
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p2) => typeof p2 === "string") : [];
27771
27843
  const existingIdx = plugins.findIndex(isMagicContextEntry);
@@ -27784,7 +27856,7 @@ function ensureTuiPluginEntry() {
27784
27856
  }
27785
27857
  config2.plugin = plugins;
27786
27858
  mkdirSync6(dirname8(configPath), { recursive: true });
27787
- writeFileSync8(configPath, `${import_comment_json6.stringify(config2, null, 2)}
27859
+ writeFileSync8(configPath, `${import_comment_json7.stringify(config2, null, 2)}
27788
27860
  `);
27789
27861
  log2(`[magic-context] updated TUI plugin entry in ${configPath}`);
27790
27862
  return true;
@@ -27793,11 +27865,11 @@ function ensureTuiPluginEntry() {
27793
27865
  return false;
27794
27866
  }
27795
27867
  }
27796
- var import_comment_json6, PLUGIN_NAME4 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY3;
27868
+ var import_comment_json7, PLUGIN_NAME4 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY3;
27797
27869
  var init_tui_config = __esm(() => {
27798
27870
  init_logger();
27799
27871
  init_opencode_config_dir();
27800
- import_comment_json6 = __toESM(require_src2(), 1);
27872
+ import_comment_json7 = __toESM(require_src2(), 1);
27801
27873
  PLUGIN_ENTRY3 = `${PLUGIN_NAME4}@latest`;
27802
27874
  });
27803
27875
 
@@ -27852,7 +27924,7 @@ var init_compartment_parser = __esm(() => {
27852
27924
  });
27853
27925
 
27854
27926
  // src/lib/redaction.ts
27855
- import { homedir as homedir9, userInfo } from "node:os";
27927
+ import { homedir as homedir10, userInfo } from "node:os";
27856
27928
  function escapeRegex2(value) {
27857
27929
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27858
27930
  }
@@ -27865,7 +27937,7 @@ function isSecretKey(key) {
27865
27937
  return SECRET_KEY_PATTERN.test(key);
27866
27938
  }
27867
27939
  function sanitizePathString(value) {
27868
- const home = homedir9();
27940
+ const home = homedir10();
27869
27941
  const username = userInfo().username;
27870
27942
  let sanitized = value;
27871
27943
  if (home) {
@@ -27967,10 +28039,10 @@ var init_redaction = __esm(() => {
27967
28039
  });
27968
28040
 
27969
28041
  // src/lib/diagnostics-opencode.ts
27970
- import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
28042
+ import { existsSync as existsSync15, readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync2 } from "node:fs";
27971
28043
  import { createRequire as createRequire3 } from "node:module";
27972
- import { homedir as homedir10 } from "node:os";
27973
- import { join as join12 } from "node:path";
28044
+ import { homedir as homedir11 } from "node:os";
28045
+ import { join as join13 } from "node:path";
27974
28046
  function getSelfVersion() {
27975
28047
  const require2 = createRequire3(import.meta.url);
27976
28048
  for (const relPath of ["../../package.json", "../package.json"]) {
@@ -27984,12 +28056,12 @@ function getSelfVersion() {
27984
28056
  return "unknown";
27985
28057
  }
27986
28058
  function getPluginCacheInfo() {
27987
- const path4 = join12(getOpenCodeCacheDir(), "packages", PLUGIN_ENTRY_WITH_VERSION);
28059
+ const path4 = join13(getOpenCodeCacheDir(), "packages", PLUGIN_ENTRY_WITH_VERSION);
27988
28060
  let cached2;
27989
28061
  try {
27990
- const installedPkgPath = join12(path4, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
27991
- if (existsSync14(installedPkgPath)) {
27992
- const pkg = JSON.parse(readFileSync10(installedPkgPath, "utf-8"));
28062
+ const installedPkgPath = join13(path4, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
28063
+ if (existsSync15(installedPkgPath)) {
28064
+ const pkg = JSON.parse(readFileSync11(installedPkgPath, "utf-8"));
27993
28065
  cached2 = typeof pkg.version === "string" ? pkg.version : undefined;
27994
28066
  }
27995
28067
  } catch {
@@ -27998,12 +28070,12 @@ function getPluginCacheInfo() {
27998
28070
  return { path: path4, cached: cached2, latest: getSelfVersion() };
27999
28071
  }
28000
28072
  function getStorageDir() {
28001
- const dataHome = process.env.XDG_DATA_HOME || join12(homedir10(), ".local", "share");
28002
- return join12(dataHome, "cortexkit", "magic-context");
28073
+ const dataHome = process.env.XDG_DATA_HOME || join13(homedir11(), ".local", "share");
28074
+ return join13(dataHome, "cortexkit", "magic-context");
28003
28075
  }
28004
28076
  function fileSize(path4) {
28005
28077
  try {
28006
- return existsSync14(path4) ? statSync2(path4).size : 0;
28078
+ return existsSync15(path4) ? statSync2(path4).size : 0;
28007
28079
  } catch {
28008
28080
  return 0;
28009
28081
  }
@@ -28015,11 +28087,11 @@ function sanitizeValue(value) {
28015
28087
  return sanitizeConfigValue(value);
28016
28088
  }
28017
28089
  function readConfig2(path4) {
28018
- if (!existsSync14(path4))
28090
+ if (!existsSync15(path4))
28019
28091
  return { value: null };
28020
28092
  try {
28021
- const raw = readFileSync10(path4, "utf-8");
28022
- const value = import_comment_json7.parse(raw);
28093
+ const raw = readFileSync11(path4, "utf-8");
28094
+ const value = import_comment_json8.parse(raw);
28023
28095
  return { value };
28024
28096
  } catch (error51) {
28025
28097
  return { value: null, error: error51 instanceof Error ? error51.message : String(error51) };
@@ -28041,7 +28113,7 @@ function configHasPluginEntry(config2) {
28041
28113
  }
28042
28114
  function parseHistorianDumpMeta(path4) {
28043
28115
  try {
28044
- const xml = readFileSync10(path4, "utf-8");
28116
+ const xml = readFileSync11(path4, "utf-8");
28045
28117
  const parsed = parseCompartmentOutput(xml);
28046
28118
  const factCountByCategory = {};
28047
28119
  for (const fact of parsed.facts) {
@@ -28074,11 +28146,11 @@ function parseHistorianDumpMeta(path4) {
28074
28146
  }
28075
28147
  }
28076
28148
  function listDumpsInDir(dir, limit) {
28077
- if (!existsSync14(dir))
28149
+ if (!existsSync15(dir))
28078
28150
  return { count: 0, recent: [] };
28079
28151
  try {
28080
28152
  const entries = readdirSync2(dir).filter((name) => name.endsWith(".xml")).map((name) => {
28081
- const stat = statSync2(join12(dir, name));
28153
+ const stat = statSync2(join13(dir, name));
28082
28154
  return {
28083
28155
  name,
28084
28156
  mtime: stat.mtimeMs,
@@ -28087,7 +28159,7 @@ function listDumpsInDir(dir, limit) {
28087
28159
  }).sort((a, b2) => b2.mtime - a.mtime);
28088
28160
  const now = Date.now();
28089
28161
  const recent = entries.slice(0, limit).map((entry) => {
28090
- const meta3 = parseHistorianDumpMeta(join12(dir, entry.name));
28162
+ const meta3 = parseHistorianDumpMeta(join13(dir, entry.name));
28091
28163
  const summary = {
28092
28164
  name: entry.name,
28093
28165
  ageMinutes: Math.round((now - entry.mtime) / 60000),
@@ -28142,8 +28214,8 @@ function collectHistorianDumps(recentSessions) {
28142
28214
  };
28143
28215
  }
28144
28216
  async function collectRecentSessions() {
28145
- const opencodeDbPath = join12(homedir10(), ".local", "share", "opencode", "opencode.db");
28146
- if (!existsSync14(opencodeDbPath))
28217
+ const opencodeDbPath = join13(homedir11(), ".local", "share", "opencode", "opencode.db");
28218
+ if (!existsSync15(opencodeDbPath))
28147
28219
  return [];
28148
28220
  if (typeof globalThis.Bun === "undefined") {
28149
28221
  return [];
@@ -28177,8 +28249,8 @@ async function collectRecentSessions() {
28177
28249
  }
28178
28250
  }
28179
28251
  async function collectHistorianFailures(storageDirPath) {
28180
- const contextDbPath = join12(storageDirPath, "context.db");
28181
- if (!existsSync14(contextDbPath))
28252
+ const contextDbPath = join13(storageDirPath, "context.db");
28253
+ if (!existsSync15(contextDbPath))
28182
28254
  return [];
28183
28255
  if (typeof globalThis.Bun === "undefined") {
28184
28256
  return [];
@@ -28217,9 +28289,9 @@ async function collectDiagnostics() {
28217
28289
  const tuiConfig = readConfig2(configPaths.tuiConfig);
28218
28290
  const magicContextConfig = readConfig2(configPaths.magicContextConfig);
28219
28291
  const storageDirPath = getStorageDir();
28220
- const contextDbPath = join12(storageDirPath, "context.db");
28292
+ const contextDbPath = join13(storageDirPath, "context.db");
28221
28293
  const logPath = getMagicContextLogPath2("opencode");
28222
- const logFileSize = existsSync14(logPath) ? statSync2(logPath).size : 0;
28294
+ const logFileSize = existsSync15(logPath) ? statSync2(logPath).size : 0;
28223
28295
  const conflictResult = detectConflicts(process.cwd());
28224
28296
  const recentSessions = await collectRecentSessions();
28225
28297
  return {
@@ -28234,14 +28306,15 @@ async function collectDiagnostics() {
28234
28306
  opencodeConfigHasPlugin: configHasPluginEntry(opencodeConfig.value),
28235
28307
  tuiConfigHasPlugin: configHasPluginEntry(tuiConfig.value),
28236
28308
  magicContextConfig: {
28237
- exists: existsSync14(configPaths.magicContextConfig),
28309
+ exists: existsSync15(configPaths.magicContextConfig),
28238
28310
  ...magicContextConfig.error ? { parseError: magicContextConfig.error } : {},
28239
28311
  flags: sanitizeValue(magicContextConfig.value ?? {}) ?? {}
28240
28312
  },
28313
+ aft: getAftAvailability(),
28241
28314
  pluginCache: getPluginCacheInfo(),
28242
28315
  storageDir: {
28243
28316
  path: storageDirPath,
28244
- exists: existsSync14(storageDirPath),
28317
+ exists: existsSync15(storageDirPath),
28245
28318
  contextDbSizeBytes: fileSize(contextDbPath)
28246
28319
  },
28247
28320
  conflicts: {
@@ -28250,7 +28323,7 @@ async function collectDiagnostics() {
28250
28323
  },
28251
28324
  logFile: {
28252
28325
  path: logPath,
28253
- exists: existsSync14(logPath),
28326
+ exists: existsSync15(logPath),
28254
28327
  sizeKb: Math.round(logFileSize / 1024)
28255
28328
  },
28256
28329
  recentSessions,
@@ -28316,6 +28389,7 @@ function renderDiagnosticsMarkdown(report) {
28316
28389
  `- Plugin registered in opencode config: ${report.opencodeConfigHasPlugin}`,
28317
28390
  `- Plugin registered in tui config: ${report.tuiConfigHasPlugin}`,
28318
28391
  `- magic-context.jsonc parse error: ${report.magicContextConfig.parseError ?? "none"}`,
28392
+ `- AFT available: ${report.aft?.available ?? false} (opencode=${report.aft?.opencode ?? false}, pi=${report.aft?.pi ?? false})`,
28319
28393
  `- Conflicts detected: ${report.conflicts.hasConflict ? report.conflicts.reasons.join("; ") : "none"}`,
28320
28394
  "",
28321
28395
  "### Config paths",
@@ -28364,22 +28438,23 @@ function renderDiagnosticsMarkdown(report) {
28364
28438
  ].join(`
28365
28439
  `);
28366
28440
  }
28367
- var import_comment_json7, PLUGIN_NAME5 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION;
28441
+ var import_comment_json8, PLUGIN_NAME5 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION;
28368
28442
  var init_diagnostics_opencode = __esm(() => {
28443
+ init_aft_availability();
28369
28444
  init_compartment_parser();
28370
28445
  init_conflict_detector();
28371
28446
  init_data_path();
28372
28447
  init_opencode_helpers();
28373
28448
  init_paths();
28374
28449
  init_redaction();
28375
- import_comment_json7 = __toESM(require_src2(), 1);
28450
+ import_comment_json8 = __toESM(require_src2(), 1);
28376
28451
  PLUGIN_ENTRY_WITH_VERSION = `${PLUGIN_NAME5}@latest`;
28377
28452
  });
28378
28453
 
28379
28454
  // src/lib/logs-opencode.ts
28380
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "node:fs";
28381
- import { homedir as homedir11 } from "node:os";
28382
- import { join as join13 } from "node:path";
28455
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
28456
+ import { homedir as homedir12 } from "node:os";
28457
+ import { join as join14 } from "node:path";
28383
28458
  function sanitizeLogContent(content) {
28384
28459
  return sanitizeDiagnosticText(content);
28385
28460
  }
@@ -28421,7 +28496,7 @@ function filterLogLinesBySession(lines, sessionId) {
28421
28496
  }
28422
28497
  async function bundleIssueReport(report, description, _title, sessionFilter = null) {
28423
28498
  const LOG_TAIL_LINES = 400;
28424
- const allLogLines = report.logFile.exists ? readFileSync11(report.logFile.path, "utf-8").split(/\r?\n/) : [];
28499
+ const allLogLines = report.logFile.exists ? readFileSync12(report.logFile.path, "utf-8").split(/\r?\n/) : [];
28425
28500
  const logLines = filterLogLinesBySession(allLogLines, sessionFilter);
28426
28501
  const recentLog = sanitizeLogContent(logLines.slice(-LOG_TAIL_LINES).join(`
28427
28502
  `)).trim();
@@ -28429,7 +28504,7 @@ async function bundleIssueReport(report, description, _title, sessionFilter = nu
28429
28504
  `));
28430
28505
  const historianFailureLines = extractHistorianFailureLines(historianScanWindow, 30);
28431
28506
  const configBody = JSON.stringify(sanitizeConfigValue(report.magicContextConfig.flags), null, 2);
28432
- const sanitizedConfigPath = report.configPaths.magicContextConfig.replace(homedir11(), "~");
28507
+ const sanitizedConfigPath = report.configPaths.magicContextConfig.replace(homedir12(), "~");
28433
28508
  const bodyMarkdown = [
28434
28509
  "## Description",
28435
28510
  description,
@@ -28460,7 +28535,7 @@ async function bundleIssueReport(report, description, _title, sessionFilter = nu
28460
28535
  "```"
28461
28536
  ].join(`
28462
28537
  `);
28463
- const path4 = join13(process.cwd(), `magic-context-issue-${formatTimestamp(new Date)}.md`);
28538
+ const path4 = join14(process.cwd(), `magic-context-issue-${formatTimestamp(new Date)}.md`);
28464
28539
  writeFileSync9(path4, `${bodyMarkdown}
28465
28540
  `);
28466
28541
  return { path: path4, bodyMarkdown };
@@ -28482,10 +28557,10 @@ var init_logs_opencode = __esm(() => {
28482
28557
 
28483
28558
  // src/commands/doctor-opencode.ts
28484
28559
  import { execSync as execSync2, spawnSync as spawnSync2 } from "node:child_process";
28485
- import { existsSync as existsSync15, readFileSync as readFileSync12, rmSync, statSync as statSync3, writeFileSync as writeFileSync10 } from "node:fs";
28560
+ import { existsSync as existsSync16, readFileSync as readFileSync13, rmSync, statSync as statSync3, writeFileSync as writeFileSync10 } from "node:fs";
28486
28561
  import { createRequire as createRequire4 } from "node:module";
28487
- import { homedir as homedir12 } from "node:os";
28488
- import { join as join14 } from "node:path";
28562
+ import { homedir as homedir13 } from "node:os";
28563
+ import { join as join15 } from "node:path";
28489
28564
  async function fetchNpmLatest(pkg, timeoutMs = 5000) {
28490
28565
  try {
28491
28566
  const controller = new AbortController;
@@ -28535,15 +28610,15 @@ function compareVersions(a, b2) {
28535
28610
  }
28536
28611
  async function clearPluginCache(force = false) {
28537
28612
  const cacheDir = getOpenCodeCacheDir();
28538
- const pluginCacheDir = join14(cacheDir, "packages", PLUGIN_ENTRY_WITH_VERSION2);
28539
- if (!existsSync15(pluginCacheDir)) {
28613
+ const pluginCacheDir = join15(cacheDir, "packages", PLUGIN_ENTRY_WITH_VERSION2);
28614
+ if (!existsSync16(pluginCacheDir)) {
28540
28615
  return { action: "not_found", path: pluginCacheDir };
28541
28616
  }
28542
28617
  let cachedVersion;
28543
28618
  try {
28544
- const installedPkgPath = join14(pluginCacheDir, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
28545
- if (existsSync15(installedPkgPath)) {
28546
- const pkg = JSON.parse(readFileSync12(installedPkgPath, "utf-8"));
28619
+ const installedPkgPath = join15(pluginCacheDir, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
28620
+ if (existsSync16(installedPkgPath)) {
28621
+ const pkg = JSON.parse(readFileSync13(installedPkgPath, "utf-8"));
28547
28622
  if (typeof pkg?.version === "string") {
28548
28623
  cachedVersion = pkg.version;
28549
28624
  }
@@ -28675,12 +28750,12 @@ async function runIssueFlow() {
28675
28750
  }
28676
28751
  }
28677
28752
  async function checkEmbeddingConfig(magicContextConfigPath) {
28678
- if (!existsSync15(magicContextConfigPath)) {
28753
+ if (!existsSync16(magicContextConfigPath)) {
28679
28754
  return { issues: 0 };
28680
28755
  }
28681
28756
  let rawText;
28682
28757
  try {
28683
- rawText = readFileSync12(magicContextConfigPath, "utf-8");
28758
+ rawText = readFileSync13(magicContextConfigPath, "utf-8");
28684
28759
  } catch {
28685
28760
  log.warn("Could not read magic-context.jsonc for embedding check");
28686
28761
  return { issues: 1 };
@@ -28691,7 +28766,7 @@ async function checkEmbeddingConfig(magicContextConfigPath) {
28691
28766
  });
28692
28767
  let parsedConfig;
28693
28768
  try {
28694
- parsedConfig = import_comment_json8.parse(substituted.text);
28769
+ parsedConfig = import_comment_json9.parse(substituted.text);
28695
28770
  } catch (error51) {
28696
28771
  log.warn(`Embedding check skipped — could not parse magic-context.jsonc: ${error51 instanceof Error ? error51.message : String(error51)}`);
28697
28772
  return { issues: 1 };
@@ -28828,15 +28903,15 @@ async function runDoctor(options = {}) {
28828
28903
  } else {
28829
28904
  pass(`OpenCode config: ${paths.opencodeConfig}`);
28830
28905
  }
28831
- if (existsSync15(paths.magicContextConfig)) {
28906
+ if (existsSync16(paths.magicContextConfig)) {
28832
28907
  pass(`Magic Context config: ${paths.magicContextConfig}`);
28833
28908
  try {
28834
- const raw = readFileSync12(paths.magicContextConfig, "utf-8");
28909
+ const raw = readFileSync13(paths.magicContextConfig, "utf-8");
28835
28910
  const substituted = substituteConfigVariables({
28836
28911
  text: raw,
28837
28912
  configPath: paths.magicContextConfig
28838
28913
  }).text;
28839
- import_comment_json8.parse(substituted);
28914
+ import_comment_json9.parse(substituted);
28840
28915
  pass("magic-context.jsonc parses as valid JSONC");
28841
28916
  } catch (err) {
28842
28917
  fail(`magic-context.jsonc parse failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -28856,10 +28931,10 @@ async function runDoctor(options = {}) {
28856
28931
  warn(`No magic-context.jsonc found — using defaults`);
28857
28932
  log.info(" Run 'setup' to create one with model recommendations");
28858
28933
  }
28859
- if (existsSync15(paths.magicContextConfig)) {
28934
+ if (existsSync16(paths.magicContextConfig)) {
28860
28935
  try {
28861
- const mcRaw = readFileSync12(paths.magicContextConfig, "utf-8");
28862
- const mcConfig = import_comment_json8.parse(mcRaw);
28936
+ const mcRaw = readFileSync13(paths.magicContextConfig, "utf-8");
28937
+ const mcConfig = import_comment_json9.parse(mcRaw);
28863
28938
  let mcChanged = false;
28864
28939
  const experimental = mcConfig.experimental;
28865
28940
  if (experimental && "compaction_markers" in experimental) {
@@ -28934,7 +29009,7 @@ async function runDoctor(options = {}) {
28934
29009
  fixed++;
28935
29010
  }
28936
29011
  if (mcChanged) {
28937
- writeFileSync10(paths.magicContextConfig, `${import_comment_json8.stringify(mcConfig, null, 2)}
29012
+ writeFileSync10(paths.magicContextConfig, `${import_comment_json9.stringify(mcConfig, null, 2)}
28938
29013
  `);
28939
29014
  }
28940
29015
  } catch {
@@ -28943,8 +29018,8 @@ async function runDoctor(options = {}) {
28943
29018
  }
28944
29019
  if (paths.opencodeConfigFormat !== "none") {
28945
29020
  try {
28946
- const raw = readFileSync12(paths.opencodeConfig, "utf-8");
28947
- const config2 = import_comment_json8.parse(raw);
29021
+ const raw = readFileSync13(paths.opencodeConfig, "utf-8");
29022
+ const config2 = import_comment_json9.parse(raw);
28948
29023
  const rawPlugins = Array.isArray(config2?.plugin) ? config2.plugin : [];
28949
29024
  const existingIdx = rawPlugins.findIndex((entry) => matchesPluginEntry(entry, PLUGIN_NAME6) || isDevPathPluginEntry(entry));
28950
29025
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
@@ -28975,7 +29050,7 @@ async function runDoctor(options = {}) {
28975
29050
  rawPlugins[existingIdx] = PLUGIN_ENTRY_WITH_VERSION2;
28976
29051
  }
28977
29052
  config2.plugin = rawPlugins;
28978
- writeFileSync10(paths.opencodeConfig, `${import_comment_json8.stringify(config2, null, 2)}
29053
+ writeFileSync10(paths.opencodeConfig, `${import_comment_json9.stringify(config2, null, 2)}
28979
29054
  `);
28980
29055
  pass(`Upgraded plugin entry in ${configName}: ${oldEntryStr} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
28981
29056
  fixed++;
@@ -28984,7 +29059,7 @@ async function runDoctor(options = {}) {
28984
29059
  } else {
28985
29060
  rawPlugins.push(PLUGIN_ENTRY_WITH_VERSION2);
28986
29061
  config2.plugin = rawPlugins;
28987
- writeFileSync10(paths.opencodeConfig, `${import_comment_json8.stringify(config2, null, 2)}
29062
+ writeFileSync10(paths.opencodeConfig, `${import_comment_json9.stringify(config2, null, 2)}
28988
29063
  `);
28989
29064
  pass(`Added plugin to ${configName}`);
28990
29065
  fixed++;
@@ -29015,10 +29090,10 @@ async function runDoctor(options = {}) {
29015
29090
  pass("Added TUI sidebar plugin to tui.json");
29016
29091
  warn("Restart OpenCode to see the sidebar");
29017
29092
  fixed++;
29018
- } else if (existsSync15(paths.tuiConfig)) {
29093
+ } else if (existsSync16(paths.tuiConfig)) {
29019
29094
  try {
29020
- const tuiRaw = readFileSync12(paths.tuiConfig, "utf-8");
29021
- const tuiConfig = import_comment_json8.parse(tuiRaw);
29095
+ const tuiRaw = readFileSync13(paths.tuiConfig, "utf-8");
29096
+ const tuiConfig = import_comment_json9.parse(tuiRaw);
29022
29097
  const tuiRawPlugins = Array.isArray(tuiConfig?.plugin) ? tuiConfig.plugin : [];
29023
29098
  const tuiIdx = tuiRawPlugins.findIndex((entry) => matchesPluginEntry(entry, PLUGIN_NAME6) || isDevPathPluginEntry(entry));
29024
29099
  const tuiEntryAsString = (entry) => {
@@ -29046,7 +29121,7 @@ async function runDoctor(options = {}) {
29046
29121
  tuiRawPlugins[tuiIdx] = PLUGIN_ENTRY_WITH_VERSION2;
29047
29122
  }
29048
29123
  tuiConfig.plugin = tuiRawPlugins;
29049
- writeFileSync10(paths.tuiConfig, `${import_comment_json8.stringify(tuiConfig, null, 2)}
29124
+ writeFileSync10(paths.tuiConfig, `${import_comment_json9.stringify(tuiConfig, null, 2)}
29050
29125
  `);
29051
29126
  pass(`Upgraded TUI plugin: ${tuiEntryStr} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
29052
29127
  fixed++;
@@ -29063,10 +29138,10 @@ async function runDoctor(options = {}) {
29063
29138
  } else {
29064
29139
  pass("TUI sidebar plugin configured (tui.json created)");
29065
29140
  }
29066
- if (existsSync15(paths.magicContextConfig)) {
29141
+ if (existsSync16(paths.magicContextConfig)) {
29067
29142
  try {
29068
- const mcRaw = readFileSync12(paths.magicContextConfig, "utf-8");
29069
- const mcConfig = import_comment_json8.parse(mcRaw);
29143
+ const mcRaw = readFileSync13(paths.magicContextConfig, "utf-8");
29144
+ const mcConfig = import_comment_json9.parse(mcRaw);
29070
29145
  const dreamerObj = mcConfig?.dreamer;
29071
29146
  const dreamerEnabled = dreamerObj?.enabled === true;
29072
29147
  const userMemObj = dreamerObj?.user_memories;
@@ -29075,6 +29150,15 @@ async function runDoctor(options = {}) {
29075
29150
  log.warn("dreamer.user_memories is enabled (default) but dreamer itself is disabled — user memory candidates will be collected but never promoted to stable memories. Enable `dreamer.enabled: true` or set `dreamer.user_memories.enabled: false` to silence this.");
29076
29151
  issues++;
29077
29152
  }
29153
+ const pinKeyFilesObj = dreamerObj?.pin_key_files;
29154
+ if (pinKeyFilesObj?.enabled === true) {
29155
+ const aft = getAftAvailability();
29156
+ if (aft.available) {
29157
+ pass("AFT detected for dreamer.pin_key_files");
29158
+ } else {
29159
+ fail(`dreamer.pin_key_files is enabled but AFT is not configured. Install/register AFT (checked: ${aft.checkedPaths.join(", ")})`);
29160
+ }
29161
+ }
29078
29162
  } catch {}
29079
29163
  }
29080
29164
  const embeddingCheck = await checkEmbeddingConfig(paths.magicContextConfig);
@@ -29083,8 +29167,8 @@ async function runDoctor(options = {}) {
29083
29167
  failCount += embeddingCheck.issues;
29084
29168
  else
29085
29169
  passCount++;
29086
- const dbPath = join14(getMagicContextStorageDir(), "context.db");
29087
- if (!existsSync15(dbPath)) {
29170
+ const dbPath = join15(getMagicContextStorageDir(), "context.db");
29171
+ if (!existsSync16(dbPath)) {
29088
29172
  log.info(`Shared context DB not yet created at ${dbPath} (will be created on first run)`);
29089
29173
  } else {
29090
29174
  pass(`Shared context DB exists at ${dbPath}`);
@@ -29145,10 +29229,10 @@ async function runDoctor(options = {}) {
29145
29229
  }
29146
29230
  {
29147
29231
  const ageWarnings = [];
29148
- const npmrcPath = join14(homedir12(), ".npmrc");
29149
- if (existsSync15(npmrcPath)) {
29232
+ const npmrcPath = join15(homedir13(), ".npmrc");
29233
+ if (existsSync16(npmrcPath)) {
29150
29234
  try {
29151
- const npmrc = readFileSync12(npmrcPath, "utf-8");
29235
+ const npmrc = readFileSync13(npmrcPath, "utf-8");
29152
29236
  for (const line of npmrc.split(`
29153
29237
  `)) {
29154
29238
  const trimmed = line.trim();
@@ -29172,7 +29256,7 @@ async function runDoctor(options = {}) {
29172
29256
  }
29173
29257
  }
29174
29258
  const logPath = getMagicContextLogPath2("opencode");
29175
- if (existsSync15(logPath)) {
29259
+ if (existsSync16(logPath)) {
29176
29260
  const logStat = statSync3(logPath);
29177
29261
  const sizeKb = (logStat.size / 1024).toFixed(0);
29178
29262
  log.info(`Log file: ${logPath} (${sizeKb} KB)`);
@@ -29218,10 +29302,11 @@ async function runDoctor(options = {}) {
29218
29302
  }
29219
29303
  return 0;
29220
29304
  }
29221
- var import_comment_json8, PLUGIN_NAME6 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION2, CLI_PACKAGE_NAME = "@cortexkit/magic-context";
29305
+ var import_comment_json9, PLUGIN_NAME6 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION2, CLI_PACKAGE_NAME = "@cortexkit/magic-context";
29222
29306
  var init_doctor_opencode = __esm(async () => {
29223
29307
  init_config();
29224
29308
  init_variable();
29309
+ init_aft_availability();
29225
29310
  init_conflict_detector();
29226
29311
  init_conflict_fixer();
29227
29312
  init_data_path();
@@ -29233,7 +29318,7 @@ var init_doctor_opencode = __esm(async () => {
29233
29318
  init_paths();
29234
29319
  init_prompts();
29235
29320
  await init_sqlite();
29236
- import_comment_json8 = __toESM(require_src2(), 1);
29321
+ import_comment_json9 = __toESM(require_src2(), 1);
29237
29322
  PLUGIN_ENTRY_WITH_VERSION2 = `${PLUGIN_NAME6}@latest`;
29238
29323
  });
29239
29324
 
@@ -161820,6 +161905,34 @@ function getErrorMessage(error51) {
161820
161905
  return error51 instanceof Error ? error51.message : String(error51);
161821
161906
  }
161822
161907
 
161908
+ // ../plugin/src/features/magic-context/key-files/project-key-files.ts
161909
+ import { existsSync as existsSync17, readFileSync as readFileSync14, realpathSync } from "node:fs";
161910
+ function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
161911
+ const cutoff = now - ORPHAN_GRACE_MS;
161912
+ const rows = db.prepare(`SELECT project_path AS projectPath, MAX(generated_at) AS lastGen
161913
+ FROM project_key_files
161914
+ GROUP BY project_path
161915
+ HAVING lastGen < ?`).all(cutoff);
161916
+ let deletedProjects = 0;
161917
+ for (const row of rows) {
161918
+ if (existsSync17(row.projectPath))
161919
+ continue;
161920
+ try {
161921
+ db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
161922
+ db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
161923
+ deletedProjects++;
161924
+ } catch (error51) {
161925
+ log2(`[key-files] orphan GC failed for ${row.projectPath}:`, error51);
161926
+ }
161927
+ }
161928
+ return deletedProjects;
161929
+ }
161930
+ var ORPHAN_GRACE_MS;
161931
+ var init_project_key_files = __esm(() => {
161932
+ init_logger();
161933
+ ORPHAN_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
161934
+ });
161935
+
161823
161936
  // ../plugin/src/features/magic-context/migrations.ts
161824
161937
  function ensureMigrationsTable(db) {
161825
161938
  db.exec(`
@@ -162187,6 +162300,46 @@ var init_migrations = __esm(async () => {
162187
162300
  db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
162188
162301
  }
162189
162302
  }
162303
+ },
162304
+ {
162305
+ version: 14,
162306
+ description: "Add project-scoped key files and version counter",
162307
+ up: (db) => {
162308
+ db.exec(`
162309
+ CREATE TABLE IF NOT EXISTS project_key_files (
162310
+ project_path TEXT NOT NULL,
162311
+ path TEXT NOT NULL,
162312
+ content TEXT NOT NULL,
162313
+ content_hash TEXT NOT NULL,
162314
+ local_token_estimate INTEGER NOT NULL,
162315
+ generated_at INTEGER NOT NULL,
162316
+ generated_by_model TEXT,
162317
+ generation_config_hash TEXT NOT NULL,
162318
+ stale_reason TEXT,
162319
+ PRIMARY KEY (project_path, path)
162320
+ );
162321
+
162322
+ CREATE INDEX IF NOT EXISTS idx_project_key_files_project
162323
+ ON project_key_files(project_path);
162324
+ CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at
162325
+ ON project_key_files(project_path, generated_at);
162326
+
162327
+ CREATE TABLE IF NOT EXISTS project_key_files_version (
162328
+ project_path TEXT PRIMARY KEY,
162329
+ version INTEGER NOT NULL DEFAULT 0
162330
+ );
162331
+ `);
162332
+ }
162333
+ },
162334
+ {
162335
+ version: 15,
162336
+ description: "Add deferred_execute_state column for boundary execution drain",
162337
+ up: (db) => {
162338
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
162339
+ if (!cols.some((c2) => c2.name === "deferred_execute_state")) {
162340
+ db.exec("ALTER TABLE session_meta ADD COLUMN deferred_execute_state TEXT");
162341
+ }
162342
+ }
162190
162343
  }
162191
162344
  ];
162192
162345
  });
@@ -162224,10 +162377,10 @@ var init_tool_definition_tokens = __esm(() => {
162224
162377
  });
162225
162378
 
162226
162379
  // ../plugin/src/features/magic-context/tool-owner-backfill.ts
162227
- import { existsSync as existsSync16 } from "node:fs";
162228
- import { join as join15 } from "node:path";
162380
+ import { existsSync as existsSync18 } from "node:fs";
162381
+ import { join as join16 } from "node:path";
162229
162382
  function resolveOpencodeDbPath() {
162230
- return join15(getDataDir(), "opencode", "opencode.db");
162383
+ return join16(getDataDir(), "opencode", "opencode.db");
162231
162384
  }
162232
162385
  function ensureBackfillStateTable(db) {
162233
162386
  db.exec(`
@@ -162262,7 +162415,7 @@ function runToolOwnerBackfill(db) {
162262
162415
  return result;
162263
162416
  }
162264
162417
  const opencodeDbPath = resolveOpencodeDbPath();
162265
- if (!existsSync16(opencodeDbPath)) {
162418
+ if (!existsSync18(opencodeDbPath)) {
162266
162419
  log2(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
162267
162420
  markAllUnbackfilledSessionsSkipped(db);
162268
162421
  result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
@@ -162472,25 +162625,25 @@ var init_tool_owner_backfill = __esm(() => {
162472
162625
  });
162473
162626
 
162474
162627
  // ../plugin/src/features/magic-context/storage-db.ts
162475
- import { copyFileSync, cpSync, existsSync as existsSync17, mkdirSync as mkdirSync7 } from "node:fs";
162476
- import { join as join16 } from "node:path";
162628
+ import { copyFileSync, cpSync, existsSync as existsSync19, mkdirSync as mkdirSync7 } from "node:fs";
162629
+ import { join as join17 } from "node:path";
162477
162630
  function resolveDatabasePath() {
162478
162631
  const dbDir = getMagicContextStorageDir();
162479
- return { dbDir, dbPath: join16(dbDir, "context.db") };
162632
+ return { dbDir, dbPath: join17(dbDir, "context.db") };
162480
162633
  }
162481
162634
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
162482
- if (existsSync17(targetDbPath))
162635
+ if (existsSync19(targetDbPath))
162483
162636
  return;
162484
162637
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
162485
- const legacyDbPath = join16(legacyDir, "context.db");
162486
- if (!existsSync17(legacyDbPath))
162638
+ const legacyDbPath = join17(legacyDir, "context.db");
162639
+ if (!existsSync19(legacyDbPath))
162487
162640
  return;
162488
162641
  log2(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
162489
162642
  mkdirSync7(targetDbDir, { recursive: true });
162490
162643
  for (const suffix of ["", "-wal", "-shm"]) {
162491
162644
  const src = `${legacyDbPath}${suffix}`;
162492
- const dst = join16(targetDbDir, `context.db${suffix}`);
162493
- if (existsSync17(src)) {
162645
+ const dst = join17(targetDbDir, `context.db${suffix}`);
162646
+ if (existsSync19(src)) {
162494
162647
  try {
162495
162648
  copyFileSync(src, dst);
162496
162649
  } catch (error51) {
@@ -162498,9 +162651,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
162498
162651
  }
162499
162652
  }
162500
162653
  }
162501
- const legacyModelsDir = join16(legacyDir, "models");
162502
- const targetModelsDir = join16(targetDbDir, "models");
162503
- if (existsSync17(legacyModelsDir) && !existsSync17(targetModelsDir)) {
162654
+ const legacyModelsDir = join17(legacyDir, "models");
162655
+ const targetModelsDir = join17(targetDbDir, "models");
162656
+ if (existsSync19(legacyModelsDir) && !existsSync19(targetModelsDir)) {
162504
162657
  try {
162505
162658
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
162506
162659
  } catch (error51) {
@@ -162648,6 +162801,26 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162648
162801
  );
162649
162802
  CREATE INDEX IF NOT EXISTS idx_dream_runs_project ON dream_runs(project_path, finished_at DESC);
162650
162803
 
162804
+ CREATE TABLE IF NOT EXISTS project_key_files (
162805
+ project_path TEXT NOT NULL,
162806
+ path TEXT NOT NULL,
162807
+ content TEXT NOT NULL,
162808
+ content_hash TEXT NOT NULL,
162809
+ local_token_estimate INTEGER NOT NULL,
162810
+ generated_at INTEGER NOT NULL,
162811
+ generated_by_model TEXT,
162812
+ generation_config_hash TEXT NOT NULL,
162813
+ stale_reason TEXT,
162814
+ PRIMARY KEY (project_path, path)
162815
+ );
162816
+ CREATE INDEX IF NOT EXISTS idx_project_key_files_project ON project_key_files(project_path);
162817
+ CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at ON project_key_files(project_path, generated_at);
162818
+
162819
+ CREATE TABLE IF NOT EXISTS project_key_files_version (
162820
+ project_path TEXT PRIMARY KEY,
162821
+ version INTEGER NOT NULL DEFAULT 0
162822
+ );
162823
+
162651
162824
  -- (smart_notes: see note above; merged into unified notes table by migration v1)
162652
162825
 
162653
162826
  CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
@@ -162725,7 +162898,11 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162725
162898
  -- valid JSON blob written via setPendingCompactionMarkerState.
162726
162899
  -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
162727
162900
  -- != empty-string defensively. Plan v6 section 3.
162728
- pending_compaction_marker_state TEXT
162901
+ pending_compaction_marker_state TEXT,
162902
+ -- deferred_execute_state: intentionally NULLABLE without a default.
162903
+ -- Absence is SQL NULL; presence is a JSON blob written via
162904
+ -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
162905
+ deferred_execute_state TEXT
162729
162906
  );
162730
162907
 
162731
162908
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -162814,6 +162991,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162814
162991
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
162815
162992
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
162816
162993
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
162994
+ ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
162817
162995
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
162818
162996
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
162819
162997
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -162890,7 +163068,15 @@ function ensureColumn(db, table, column, definition) {
162890
163068
  if (rows.some((row) => row.name === column)) {
162891
163069
  return;
162892
163070
  }
162893
- db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
163071
+ try {
163072
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
163073
+ } catch (err) {
163074
+ const recheck = db.prepare(`PRAGMA table_info(${table})`).all();
163075
+ if (recheck.some((row) => row.name === column)) {
163076
+ return;
163077
+ }
163078
+ throw err;
163079
+ }
162894
163080
  }
162895
163081
  function openDatabase() {
162896
163082
  const { dbDir, dbPath } = resolveDatabasePath();
@@ -162907,6 +163093,11 @@ function openDatabase() {
162907
163093
  const db = new Database(dbPath);
162908
163094
  initializeDatabase(db);
162909
163095
  runMigrations(db);
163096
+ try {
163097
+ deleteOrphanProjectKeyFiles(db);
163098
+ } catch (error51) {
163099
+ log2(`[magic-context] key-files orphan GC failed: ${getErrorMessage(error51)}`);
163100
+ }
162910
163101
  try {
162911
163102
  runToolOwnerBackfill(db);
162912
163103
  } catch (error51) {
@@ -162942,6 +163133,7 @@ var databases, persistenceByDatabase, persistenceErrorByDatabase;
162942
163133
  var init_storage_db = __esm(async () => {
162943
163134
  init_data_path();
162944
163135
  init_logger();
163136
+ init_project_key_files();
162945
163137
  init_tool_definition_tokens();
162946
163138
  init_tool_owner_backfill();
162947
163139
  await __promiseAll([
@@ -163045,24 +163237,24 @@ var init_storage = __esm(async () => {
163045
163237
  });
163046
163238
 
163047
163239
  // ../pi-plugin/src/config/index.ts
163048
- import { existsSync as existsSync18, readFileSync as readFileSync13 } from "node:fs";
163049
- import { homedir as homedir13 } from "node:os";
163050
- import { join as join17 } from "node:path";
163240
+ import { existsSync as existsSync20, readFileSync as readFileSync15 } from "node:fs";
163241
+ import { homedir as homedir14 } from "node:os";
163242
+ import { join as join18 } from "node:path";
163051
163243
  function getProjectConfigPaths(cwd) {
163052
- const basePath = join17(cwd, ".pi", CONFIG_FILE_NAME);
163244
+ const basePath = join18(cwd, ".pi", CONFIG_FILE_NAME);
163053
163245
  return [`${basePath}.jsonc`, `${basePath}.json`];
163054
163246
  }
163055
163247
  function getUserConfigPaths() {
163056
- const home = process.env.HOME ?? homedir13();
163057
- const basePath = join17(home, ".pi", "agent", CONFIG_FILE_NAME);
163248
+ const home = process.env.HOME ?? homedir14();
163249
+ const basePath = join18(home, ".pi", "agent", CONFIG_FILE_NAME);
163058
163250
  return [`${basePath}.jsonc`, `${basePath}.json`];
163059
163251
  }
163060
163252
  function resolveFirstExisting(paths) {
163061
- return paths.find((path4) => existsSync18(path4));
163253
+ return paths.find((path4) => existsSync20(path4));
163062
163254
  }
163063
163255
  function loadConfigFile2(path4, scope) {
163064
163256
  try {
163065
- const rawText = readFileSync13(path4, "utf-8");
163257
+ const rawText = readFileSync15(path4, "utf-8");
163066
163258
  const substituted = substituteConfigVariables({
163067
163259
  text: rawText,
163068
163260
  configPath: path4
@@ -163070,7 +163262,7 @@ function loadConfigFile2(path4, scope) {
163070
163262
  return {
163071
163263
  path: path4,
163072
163264
  scope,
163073
- config: import_comment_json9.parse(substituted.text),
163265
+ config: import_comment_json10.parse(substituted.text),
163074
163266
  warnings: substituted.warnings.map((warning) => `${path4}: ${warning}`)
163075
163267
  };
163076
163268
  } catch (error51) {
@@ -163183,19 +163375,19 @@ function loadPiConfig(opts = {}) {
163183
163375
  loadedFromPaths: loadedFiles.map((loaded) => loaded.path)
163184
163376
  };
163185
163377
  }
163186
- var import_comment_json9, CONFIG_FILE_NAME = "magic-context";
163378
+ var import_comment_json10, CONFIG_FILE_NAME = "magic-context";
163187
163379
  var init_config2 = __esm(() => {
163188
163380
  init_magic_context();
163189
163381
  init_variable();
163190
- import_comment_json9 = __toESM(require_src2(), 1);
163382
+ import_comment_json10 = __toESM(require_src2(), 1);
163191
163383
  });
163192
163384
 
163193
163385
  // src/lib/diagnostics-pi.ts
163194
163386
  import { createHash } from "node:crypto";
163195
- import { existsSync as existsSync19, readdirSync as readdirSync3, readFileSync as readFileSync14, statSync as statSync4 } from "node:fs";
163387
+ import { existsSync as existsSync21, readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync4 } from "node:fs";
163196
163388
  import { createRequire as createRequire5 } from "node:module";
163197
- import { homedir as homedir14, userInfo as userInfo2 } from "node:os";
163198
- import { join as join18 } from "node:path";
163389
+ import { homedir as homedir15, userInfo as userInfo2 } from "node:os";
163390
+ import { join as join19 } from "node:path";
163199
163391
  function getSelfVersion3() {
163200
163392
  const req = createRequire5(import.meta.url);
163201
163393
  for (const relPath of ["../../package.json", "../package.json"]) {
@@ -163210,7 +163402,7 @@ function getSelfVersion3() {
163210
163402
  }
163211
163403
  function fileSize2(path4) {
163212
163404
  try {
163213
- return existsSync19(path4) ? statSync4(path4).size : 0;
163405
+ return existsSync21(path4) ? statSync4(path4).size : 0;
163214
163406
  } catch {
163215
163407
  return 0;
163216
163408
  }
@@ -163226,7 +163418,7 @@ function redactSecretString(value) {
163226
163418
  return value.replace(/Bearer\s+[A-Za-z0-9._~+\-/=]+/g, "Bearer <REDACTED>").replace(/sk-[A-Za-z0-9_-]{12,}/g, "sk-<REDACTED>").replace(/api[_-]?key=([^\s&]+)/gi, "api_key=<REDACTED>").replace(/token=([^\s&]+)/gi, "token=<REDACTED>");
163227
163419
  }
163228
163420
  function sanitizeString2(value) {
163229
- const home = process.env.HOME || homedir14();
163421
+ const home = process.env.HOME || homedir15();
163230
163422
  const username = userInfo2().username;
163231
163423
  const userHash = `<USER:${currentUserHash()}>`;
163232
163424
  let sanitized = redactSecretString(value);
@@ -163260,11 +163452,11 @@ function sanitizeValue2(value, key = "") {
163260
163452
  return value;
163261
163453
  }
163262
163454
  function readJsonc3(path4) {
163263
- if (!existsSync19(path4))
163455
+ if (!existsSync21(path4))
163264
163456
  return { value: {} };
163265
163457
  try {
163266
163458
  return {
163267
- value: import_comment_json10.parse(readFileSync14(path4, "utf-8"))
163459
+ value: import_comment_json11.parse(readFileSync16(path4, "utf-8"))
163268
163460
  };
163269
163461
  } catch (error51) {
163270
163462
  return {
@@ -163274,13 +163466,13 @@ function readJsonc3(path4) {
163274
163466
  }
163275
163467
  }
163276
163468
  function getProjectConfigPath(cwd) {
163277
- return join18(cwd, ".pi", "magic-context.jsonc");
163469
+ return join19(cwd, ".pi", "magic-context.jsonc");
163278
163470
  }
163279
163471
  function readConfigDiagnostic(path4) {
163280
163472
  const parsed = readJsonc3(path4);
163281
163473
  return {
163282
163474
  path: path4,
163283
- exists: existsSync19(path4),
163475
+ exists: existsSync21(path4),
163284
163476
  ...parsed.parseError ? { parseError: parsed.parseError } : {},
163285
163477
  flags: sanitizeValue2(parsed.value)
163286
163478
  };
@@ -163297,8 +163489,8 @@ function reverseSlugToDirectory(slug) {
163297
163489
  return `/${inner.replace(/-/g, "/")}`;
163298
163490
  }
163299
163491
  function collectPiRecentSessions() {
163300
- const sessionsRoot = join18(homedir14(), ".pi", "agent", "sessions");
163301
- if (!existsSync19(sessionsRoot))
163492
+ const sessionsRoot = join19(homedir15(), ".pi", "agent", "sessions");
163493
+ if (!existsSync21(sessionsRoot))
163302
163494
  return [];
163303
163495
  try {
163304
163496
  const slugs = readdirSync3(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -163307,7 +163499,7 @@ function collectPiRecentSessions() {
163307
163499
  const directory = reverseSlugToDirectory(slug);
163308
163500
  if (!directory)
163309
163501
  continue;
163310
- const slugDir = join18(sessionsRoot, slug);
163502
+ const slugDir = join19(sessionsRoot, slug);
163311
163503
  let files;
163312
163504
  try {
163313
163505
  files = readdirSync3(slugDir).filter((name2) => name2.endsWith(".jsonl"));
@@ -163316,7 +163508,7 @@ function collectPiRecentSessions() {
163316
163508
  }
163317
163509
  for (const file2 of files) {
163318
163510
  try {
163319
- const mtime = statSync4(join18(slugDir, file2)).mtimeMs;
163511
+ const mtime = statSync4(join19(slugDir, file2)).mtimeMs;
163320
163512
  const sessionId = file2.replace(/\.jsonl$/, "");
163321
163513
  candidates.push({ sessionId, directory, mtime });
163322
163514
  } catch {}
@@ -163334,7 +163526,7 @@ function collectPiRecentSessions() {
163334
163526
  }
163335
163527
  function parseHistorianDumpMeta2(path4) {
163336
163528
  try {
163337
- const xml = readFileSync14(path4, "utf-8");
163529
+ const xml = readFileSync16(path4, "utf-8");
163338
163530
  const parsed = parseCompartmentOutput(xml);
163339
163531
  const factCountByCategory = {};
163340
163532
  for (const fact of parsed.facts) {
@@ -163367,11 +163559,11 @@ function parseHistorianDumpMeta2(path4) {
163367
163559
  }
163368
163560
  }
163369
163561
  function listDumpsInDir2(dir, limit) {
163370
- if (!existsSync19(dir))
163562
+ if (!existsSync21(dir))
163371
163563
  return { count: 0, recent: [] };
163372
163564
  try {
163373
163565
  const entries = readdirSync3(dir).filter((name2) => name2.endsWith(".xml")).map((name2) => {
163374
- const stat = statSync4(join18(dir, name2));
163566
+ const stat = statSync4(join19(dir, name2));
163375
163567
  return {
163376
163568
  name: name2,
163377
163569
  mtime: stat.mtimeMs,
@@ -163380,7 +163572,7 @@ function listDumpsInDir2(dir, limit) {
163380
163572
  }).sort((a, b2) => b2.mtime - a.mtime);
163381
163573
  const now = Date.now();
163382
163574
  const recent = entries.slice(0, limit).map((entry) => {
163383
- const meta3 = parseHistorianDumpMeta2(join18(dir, entry.name));
163575
+ const meta3 = parseHistorianDumpMeta2(join19(dir, entry.name));
163384
163576
  const summary = {
163385
163577
  name: entry.name,
163386
163578
  ageMinutes: Math.round((now - entry.mtime) / 60000),
@@ -163443,9 +163635,9 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163443
163635
  const projectConfigPath = getProjectConfigPath(cwd);
163444
163636
  const loaded = loadPiConfig({ cwd });
163445
163637
  const storageDirPath = getMagicContextStorageDir();
163446
- const dbPath = join18(storageDirPath, "context.db");
163638
+ const dbPath = join19(storageDirPath, "context.db");
163447
163639
  const logPath = getMagicContextLogPath2("pi");
163448
- const logFileSize = existsSync19(logPath) ? statSync4(logPath).size : 0;
163640
+ const logFileSize = existsSync21(logPath) ? statSync4(logPath).size : 0;
163449
163641
  const otherPiExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
163450
163642
  const recentSessions = collectPiRecentSessions();
163451
163643
  const historianDumps = collectPiHistorianDumps(recentSessions);
@@ -163460,7 +163652,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163460
163652
  piVersion: pi ? getPiVersion(pi.path) : null,
163461
163653
  settings: {
163462
163654
  path: settingsPath,
163463
- exists: existsSync19(settingsPath),
163655
+ exists: existsSync21(settingsPath),
163464
163656
  ...settingsParsed.parseError ? { parseError: settingsParsed.parseError } : {},
163465
163657
  hasMagicContextPackage: hasPiMagicContextPackage(packages),
163466
163658
  packages: sanitizeValue2(packages)
@@ -163476,7 +163668,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163476
163668
  loadWarnings: loaded.warnings.map(sanitizeString2),
163477
163669
  storageDir: {
163478
163670
  path: storageDirPath,
163479
- exists: existsSync19(storageDirPath),
163671
+ exists: existsSync21(storageDirPath),
163480
163672
  contextDbSizeBytes: fileSize2(dbPath)
163481
163673
  },
163482
163674
  conflicts: {
@@ -163485,7 +163677,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163485
163677
  },
163486
163678
  logFile: {
163487
163679
  path: logPath,
163488
- exists: existsSync19(logPath),
163680
+ exists: existsSync21(logPath),
163489
163681
  sizeKb: Math.round(logFileSize / 1024)
163490
163682
  },
163491
163683
  recentSessions,
@@ -163566,7 +163758,7 @@ function renderDiagnosticsMarkdown2(report) {
163566
163758
  ].join(`
163567
163759
  `);
163568
163760
  }
163569
- var import_comment_json10;
163761
+ var import_comment_json11;
163570
163762
  var init_diagnostics_pi = __esm(() => {
163571
163763
  init_compartment_parser();
163572
163764
  init_data_path();
@@ -163574,12 +163766,12 @@ var init_diagnostics_pi = __esm(() => {
163574
163766
  init_paths();
163575
163767
  init_pi_helpers();
163576
163768
  init_pi_package_entry();
163577
- import_comment_json10 = __toESM(require_src2(), 1);
163769
+ import_comment_json11 = __toESM(require_src2(), 1);
163578
163770
  });
163579
163771
 
163580
163772
  // src/lib/logs-pi.ts
163581
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
163582
- import { join as join19 } from "node:path";
163773
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
163774
+ import { join as join20 } from "node:path";
163583
163775
  function sanitizeLogContent2(content) {
163584
163776
  return sanitizeString2(content);
163585
163777
  }
@@ -163608,7 +163800,7 @@ function filterLogLinesBySession2(lines, sessionId) {
163608
163800
  }
163609
163801
  async function bundleIssueReport2(report, description, title, options = {}) {
163610
163802
  const LOG_TAIL_LINES = 400;
163611
- const allLogLines = report.logFile.exists ? readFileSync15(report.logFile.path, "utf-8").split(/\r?\n/) : [];
163803
+ const allLogLines = report.logFile.exists ? readFileSync17(report.logFile.path, "utf-8").split(/\r?\n/) : [];
163612
163804
  const logLines = filterLogLinesBySession2(allLogLines, options.sessionFilter ?? null);
163613
163805
  const recentLog = sanitizeLogContent2(logLines.slice(-LOG_TAIL_LINES).join(`
163614
163806
  `)).trim();
@@ -163635,7 +163827,7 @@ async function bundleIssueReport2(report, description, title, options = {}) {
163635
163827
  ].join(`
163636
163828
  `);
163637
163829
  const cwd = options.cwd ?? process.cwd();
163638
- const path4 = join19(cwd, `magic-context-pi-issue-${formatTimestamp2(options.now ?? new Date)}.md`);
163830
+ const path4 = join20(cwd, `magic-context-pi-issue-${formatTimestamp2(options.now ?? new Date)}.md`);
163639
163831
  writeFileSync11(path4, `${bodyMarkdown}
163640
163832
  `);
163641
163833
  return { path: path4, bodyMarkdown };
@@ -163647,17 +163839,17 @@ var init_logs_pi = __esm(() => {
163647
163839
  // src/commands/doctor-pi.ts
163648
163840
  import { execFileSync as execFileSync4, spawnSync as spawnSync3 } from "node:child_process";
163649
163841
  import {
163650
- existsSync as existsSync20,
163842
+ existsSync as existsSync22,
163651
163843
  mkdirSync as mkdirSync8,
163652
163844
  readdirSync as readdirSync4,
163653
- readFileSync as readFileSync16,
163845
+ readFileSync as readFileSync18,
163654
163846
  rmSync as rmSync2,
163655
163847
  statSync as statSync5,
163656
163848
  writeFileSync as writeFileSync12
163657
163849
  } from "node:fs";
163658
163850
  import { createRequire as createRequire6 } from "node:module";
163659
- import { homedir as homedir15 } from "node:os";
163660
- import { dirname as dirname9, join as join20 } from "node:path";
163851
+ import { homedir as homedir16 } from "node:os";
163852
+ import { dirname as dirname9, join as join21 } from "node:path";
163661
163853
  function depsFrom(options) {
163662
163854
  return {
163663
163855
  ...DEFAULT_DEPS,
@@ -163744,7 +163936,7 @@ function summarize(results) {
163744
163936
  function readJsonc4(path4) {
163745
163937
  try {
163746
163938
  return {
163747
- value: import_comment_json11.parse(readFileSync16(path4, "utf-8"))
163939
+ value: import_comment_json12.parse(readFileSync18(path4, "utf-8"))
163748
163940
  };
163749
163941
  } catch (error51) {
163750
163942
  return {
@@ -163757,18 +163949,18 @@ function packagesFrom(settings) {
163757
163949
  return Array.isArray(settings.packages) ? settings.packages : [];
163758
163950
  }
163759
163951
  function projectConfigPath(cwd) {
163760
- return join20(cwd, ".pi", "magic-context.jsonc");
163952
+ return join21(cwd, ".pi", "magic-context.jsonc");
163761
163953
  }
163762
163954
  function readConfigForEmbedding(path4) {
163763
- if (!existsSync20(path4))
163955
+ if (!existsSync22(path4))
163764
163956
  return null;
163765
163957
  try {
163766
- const rawText = readFileSync16(path4, "utf-8");
163958
+ const rawText = readFileSync18(path4, "utf-8");
163767
163959
  const substituted = substituteConfigVariables({
163768
163960
  text: rawText,
163769
163961
  configPath: path4
163770
163962
  });
163771
- return import_comment_json11.parse(substituted.text);
163963
+ return import_comment_json12.parse(substituted.text);
163772
163964
  } catch {
163773
163965
  return null;
163774
163966
  }
@@ -163821,20 +164013,20 @@ function countTable(db, table) {
163821
164013
  }
163822
164014
  }
163823
164015
  function cacheRoots() {
163824
- const home = homedir15();
163825
- const cacheHome = process.env.XDG_CACHE_HOME || join20(home, ".cache");
164016
+ const home = homedir16();
164017
+ const cacheHome = process.env.XDG_CACHE_HOME || join21(home, ".cache");
163826
164018
  return [
163827
- join20(home, ".pi", "cache", "extensions"),
163828
- join20(home, ".pi", "cache", "packages"),
163829
- join20(cacheHome, "pi", "extensions"),
163830
- join20(cacheHome, "pi", "packages"),
163831
- join20(getPiAgentConfigDir(), "cache", "extensions")
164019
+ join21(home, ".pi", "cache", "extensions"),
164020
+ join21(home, ".pi", "cache", "packages"),
164021
+ join21(cacheHome, "pi", "extensions"),
164022
+ join21(cacheHome, "pi", "packages"),
164023
+ join21(getPiAgentConfigDir(), "cache", "extensions")
163832
164024
  ];
163833
164025
  }
163834
164026
  function findPiMagicContextCacheDirs(self, force = false) {
163835
164027
  const found = new Map;
163836
164028
  const visit = (path4, depth) => {
163837
- if (depth < 0 || !existsSync20(path4))
164029
+ if (depth < 0 || !existsSync22(path4))
163838
164030
  return;
163839
164031
  let stat;
163840
164032
  try {
@@ -163844,10 +164036,10 @@ function findPiMagicContextCacheDirs(self, force = false) {
163844
164036
  }
163845
164037
  if (!stat.isDirectory())
163846
164038
  return;
163847
- const packageJson = join20(path4, "package.json");
163848
- if (existsSync20(packageJson)) {
164039
+ const packageJson = join21(path4, "package.json");
164040
+ if (existsSync22(packageJson)) {
163849
164041
  try {
163850
- const pkg = JSON.parse(readFileSync16(packageJson, "utf-8"));
164042
+ const pkg = JSON.parse(readFileSync18(packageJson, "utf-8"));
163851
164043
  const name2 = typeof pkg.name === "string" ? pkg.name : "";
163852
164044
  const version2 = typeof pkg.version === "string" ? pkg.version : undefined;
163853
164045
  if (name2 === PACKAGE_NAME || path4.includes("pi-magic-context")) {
@@ -163866,7 +164058,7 @@ function findPiMagicContextCacheDirs(self, force = false) {
163866
164058
  return;
163867
164059
  }
163868
164060
  for (const entry of entries)
163869
- visit(join20(path4, entry), depth - 1);
164061
+ visit(join21(path4, entry), depth - 1);
163870
164062
  };
163871
164063
  for (const root of cacheRoots())
163872
164064
  visit(root, 5);
@@ -163888,7 +164080,7 @@ async function runHealthChecks(options) {
163888
164080
  add(results, "pass", version2 ? `Pi ${version2} detected at ${pi.path}` : `Pi detected at ${pi.path}`);
163889
164081
  const compare = compareSemver(version2, MIN_PI_VERSION);
163890
164082
  if (compare !== null && compare < 0) {
163891
- add(results, "fail", `Pi ${version2} is older than required ${MIN_PI_VERSION}. Subagents (historian/dreamer/sidekick) use the long-form \`--extension\` flag introduced in Pi 0.71.0; older versions hard-fail with "Unknown option". Run \`pi update\` (or \`npm install -g @mariozechner/pi-coding-agent@latest\`).`);
164083
+ add(results, "fail", `Pi ${version2} is older than required ${MIN_PI_VERSION}. Subagents (historian/dreamer/sidekick) use the long-form \`--extension\` flag introduced in Pi 0.71.0; older versions hard-fail with "Unknown option". Run \`pi update\` (or \`npm install -g @earendil-works/pi-coding-agent@latest\`).`);
163892
164084
  } else if (version2) {
163893
164085
  add(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
163894
164086
  }
@@ -163903,7 +164095,7 @@ async function runHealthChecks(options) {
163903
164095
  }
163904
164096
  const settingsPath = getPiUserExtensionsPath();
163905
164097
  let packages = [];
163906
- if (!existsSync20(settingsPath)) {
164098
+ if (!existsSync22(settingsPath)) {
163907
164099
  add(results, "warn", `Pi settings not found at ${settingsPath}`);
163908
164100
  repairPlan.addPackageEntry = true;
163909
164101
  } else {
@@ -163927,7 +164119,7 @@ async function runHealthChecks(options) {
163927
164119
  ["user", userConfigPath, true],
163928
164120
  ["project", projectPath, false]
163929
164121
  ]) {
163930
- if (!existsSync20(path4)) {
164122
+ if (!existsSync22(path4)) {
163931
164123
  if (required2) {
163932
164124
  add(results, "warn", `No ${label} magic-context.jsonc found at ${path4}`);
163933
164125
  repairPlan.writeUserConfig = true;
@@ -163960,8 +164152,8 @@ async function runHealthChecks(options) {
163960
164152
  add(results, "pass", `historian.model "${historianModel}" has thinking_level "${historianThinkingLevel}" configured`);
163961
164153
  }
163962
164154
  const storageDir = getMagicContextStorageDir();
163963
- const dbPath = join20(storageDir, "context.db");
163964
- const existedBeforeOpen = existsSync20(dbPath);
164155
+ const dbPath = join21(storageDir, "context.db");
164156
+ const existedBeforeOpen = existsSync22(dbPath);
163965
164157
  if (existedBeforeOpen)
163966
164158
  add(results, "pass", `Shared context DB exists at ${dbPath}`);
163967
164159
  else
@@ -164038,10 +164230,10 @@ async function runHealthChecks(options) {
164038
164230
  add(results, "pass", "Pi extension cache clean (no stale cached package found)");
164039
164231
  }
164040
164232
  const logPath = getMagicContextLogPath2("pi");
164041
- if (existsSync20(logPath)) {
164233
+ if (existsSync22(logPath)) {
164042
164234
  const stat = statSync5(logPath);
164043
164235
  const sizeKb = (stat.size / 1024).toFixed(0);
164044
- const lines = readFileSync16(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
164236
+ const lines = readFileSync18(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
164045
164237
  add(results, "info", `Log file: ${logPath} (${sizeKb} KB)`);
164046
164238
  add(results, "info", `Last plugin log line: ${lines.at(-1) ?? "<empty log>"}`);
164047
164239
  } else {
@@ -164080,7 +164272,7 @@ function writeDefaultMagicContextConfig(path4) {
164080
164272
  $schema: "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json",
164081
164273
  ...MagicContextConfigSchema.parse({})
164082
164274
  };
164083
- writeFileSync12(path4, `${import_comment_json11.stringify(config2, null, 2)}
164275
+ writeFileSync12(path4, `${import_comment_json12.stringify(config2, null, 2)}
164084
164276
  `);
164085
164277
  }
164086
164278
  function repair(plan, prompts) {
@@ -164097,7 +164289,7 @@ function repair(plan, prompts) {
164097
164289
  }
164098
164290
  if (plan.writeUserConfig) {
164099
164291
  const configPath = getPiUserConfigPath();
164100
- if (!existsSync20(configPath)) {
164292
+ if (!existsSync22(configPath)) {
164101
164293
  try {
164102
164294
  writeDefaultMagicContextConfig(configPath);
164103
164295
  prompts.log.success(`Wrote default Magic Context config to ${configPath}`);
@@ -164239,7 +164431,7 @@ function parseDoctorArgs(args) {
164239
164431
  function doctor(args = []) {
164240
164432
  return runDoctor2(parseDoctorArgs(args));
164241
164433
  }
164242
- var import_comment_json11, PACKAGE_NAME = "@cortexkit/pi-magic-context", MIN_PI_VERSION = "0.71.0", ROW_COUNT_TABLES, DEFAULT_DEPS;
164434
+ var import_comment_json12, PACKAGE_NAME = "@cortexkit/pi-magic-context", MIN_PI_VERSION = "0.74.0", ROW_COUNT_TABLES, DEFAULT_DEPS;
164243
164435
  var init_doctor_pi = __esm(async () => {
164244
164436
  init_magic_context();
164245
164437
  init_variable();
@@ -164253,7 +164445,7 @@ var init_doctor_pi = __esm(async () => {
164253
164445
  init_prompts();
164254
164446
  init_setup_pi();
164255
164447
  await init_storage();
164256
- import_comment_json11 = __toESM(require_src2(), 1);
164448
+ import_comment_json12 = __toESM(require_src2(), 1);
164257
164449
  ROW_COUNT_TABLES = ["tags", "compartments", "memories", "notes", "dream_runs"];
164258
164450
  DEFAULT_DEPS = {
164259
164451
  prompts: promptIO,
@@ -164276,7 +164468,7 @@ var exports_doctor = {};
164276
164468
  __export(exports_doctor, {
164277
164469
  runDoctor: () => runDoctor3
164278
164470
  });
164279
- import { existsSync as existsSync21, rmSync as rmSync3 } from "node:fs";
164471
+ import { existsSync as existsSync23, rmSync as rmSync3 } from "node:fs";
164280
164472
  async function runDoctor3(options) {
164281
164473
  if (options.clear)
164282
164474
  return runClear();
@@ -164360,7 +164552,7 @@ async function runClear() {
164360
164552
  const s = spinner();
164361
164553
  s.start(`Clearing ${item.path}`);
164362
164554
  try {
164363
- if (existsSync21(item.path)) {
164555
+ if (existsSync23(item.path)) {
164364
164556
  rmSync3(item.path, { recursive: true, force: true });
164365
164557
  }
164366
164558
  s.stop(`Cleared ${item.path}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/magic-context",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Unified CLI for Magic Context — setup, doctor, and migration across OpenCode and Pi",
5
5
  "keywords": [
6
6
  "opencode",
@@ -51,6 +51,6 @@
51
51
  "typescript": "^5.8.0"
52
52
  },
53
53
  "engines": {
54
- "node": ">=20.12.0"
54
+ "node": ">=24.0.0"
55
55
  }
56
56
  }