@cortexkit/magic-context 0.18.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 +1198 -553
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -7805,26 +7805,70 @@ var require_src2 = __commonJS((exports, module) => {
7805
7805
  };
7806
7806
  });
7807
7807
 
7808
+ // ../plugin/src/shared/harness.ts
7809
+ function getHarness() {
7810
+ return currentHarness;
7811
+ }
7812
+ var currentHarness = "opencode";
7813
+
7814
+ // ../plugin/src/shared/data-path.ts
7815
+ import * as os from "node:os";
7816
+ import * as path from "node:path";
7817
+ function getDataDir() {
7818
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
7819
+ }
7820
+ function getMagicContextTempDir(harness = getHarness()) {
7821
+ return path.join(os.tmpdir(), harness, "magic-context");
7822
+ }
7823
+ function getMagicContextLogPath(harness = getHarness()) {
7824
+ return path.join(getMagicContextTempDir(harness), "magic-context.log");
7825
+ }
7826
+ function getMagicContextHistorianDir(harness = getHarness()) {
7827
+ return path.join(getMagicContextTempDir(harness), "historian");
7828
+ }
7829
+ function getProjectMagicContextDir(directory) {
7830
+ return path.join(directory, ".opencode", "magic-context");
7831
+ }
7832
+ function getProjectMagicContextHistorianDir(directory) {
7833
+ return path.join(getProjectMagicContextDir(directory), "historian");
7834
+ }
7835
+ function getOpenCodeStorageDir() {
7836
+ return path.join(getDataDir(), "opencode", "storage");
7837
+ }
7838
+ function getMagicContextStorageDir() {
7839
+ return path.join(getDataDir(), "cortexkit", "magic-context");
7840
+ }
7841
+ function getLegacyOpenCodeMagicContextStorageDir() {
7842
+ return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
7843
+ }
7844
+ function getCacheDir() {
7845
+ return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
7846
+ }
7847
+ function getOpenCodeCacheDir() {
7848
+ return path.join(getCacheDir(), "opencode");
7849
+ }
7850
+ var init_data_path = () => {};
7851
+
7808
7852
  // src/lib/paths.ts
7809
7853
  import { existsSync, readdirSync, statSync } from "node:fs";
7810
- import { homedir, tmpdir } from "node:os";
7811
- import { join } from "node:path";
7854
+ import { homedir as homedir2 } from "node:os";
7855
+ import { join as join2 } from "node:path";
7812
7856
  function getOpenCodeConfigDir() {
7813
7857
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
7814
7858
  if (envDir)
7815
7859
  return envDir;
7816
7860
  if (process.platform === "win32") {
7817
- return join(homedir(), ".config", "opencode");
7861
+ return join2(homedir2(), ".config", "opencode");
7818
7862
  }
7819
- const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
7820
- return join(xdgConfig, "opencode");
7863
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
7864
+ return join2(xdgConfig, "opencode");
7821
7865
  }
7822
7866
  function findOmoConfig(configDir) {
7823
7867
  const locations = [
7824
- join(configDir, "oh-my-openagent.jsonc"),
7825
- join(configDir, "oh-my-openagent.json"),
7826
- join(configDir, "oh-my-opencode.jsonc"),
7827
- join(configDir, "oh-my-opencode.json")
7868
+ join2(configDir, "oh-my-openagent.jsonc"),
7869
+ join2(configDir, "oh-my-openagent.json"),
7870
+ join2(configDir, "oh-my-opencode.jsonc"),
7871
+ join2(configDir, "oh-my-opencode.json")
7828
7872
  ];
7829
7873
  for (const loc of locations) {
7830
7874
  if (existsSync(loc))
@@ -7838,8 +7882,8 @@ function detectConfigPaths() {
7838
7882
  let opencodeConfigFormat;
7839
7883
  let tuiConfig;
7840
7884
  let tuiConfigFormat;
7841
- const jsoncPath = join(configDir, "opencode.jsonc");
7842
- const jsonPath = join(configDir, "opencode.json");
7885
+ const jsoncPath = join2(configDir, "opencode.jsonc");
7886
+ const jsonPath = join2(configDir, "opencode.json");
7843
7887
  if (existsSync(jsoncPath)) {
7844
7888
  opencodeConfig = jsoncPath;
7845
7889
  opencodeConfigFormat = "jsonc";
@@ -7850,8 +7894,8 @@ function detectConfigPaths() {
7850
7894
  opencodeConfig = jsonPath;
7851
7895
  opencodeConfigFormat = "none";
7852
7896
  }
7853
- const tuiJsoncPath = join(configDir, "tui.jsonc");
7854
- const tuiJsonPath = join(configDir, "tui.json");
7897
+ const tuiJsoncPath = join2(configDir, "tui.jsonc");
7898
+ const tuiJsonPath = join2(configDir, "tui.json");
7855
7899
  if (existsSync(tuiJsoncPath)) {
7856
7900
  tuiConfig = tuiJsoncPath;
7857
7901
  tuiConfigFormat = "jsonc";
@@ -7866,7 +7910,7 @@ function detectConfigPaths() {
7866
7910
  configDir,
7867
7911
  opencodeConfig,
7868
7912
  opencodeConfigFormat,
7869
- magicContextConfig: join(configDir, "magic-context.jsonc"),
7913
+ magicContextConfig: join2(configDir, "magic-context.jsonc"),
7870
7914
  omoConfig: findOmoConfig(configDir),
7871
7915
  tuiConfig,
7872
7916
  tuiConfigFormat
@@ -7876,33 +7920,36 @@ function getPiAgentConfigDir() {
7876
7920
  const envDir = process.env.PI_CODING_AGENT_DIR?.trim();
7877
7921
  if (envDir)
7878
7922
  return envDir;
7879
- return join(homedir(), ".pi", "agent");
7923
+ return join2(homedir2(), ".pi", "agent");
7880
7924
  }
7881
7925
  function getPiUserConfigPath() {
7882
- return join(getPiAgentConfigDir(), "magic-context.jsonc");
7926
+ return join2(getPiAgentConfigDir(), "magic-context.jsonc");
7883
7927
  }
7884
7928
  function getPiUserExtensionsPath() {
7885
- return join(getPiAgentConfigDir(), "settings.json");
7929
+ return join2(getPiAgentConfigDir(), "settings.json");
7930
+ }
7931
+ function getMagicContextLogPath2(harness) {
7932
+ return getMagicContextLogPath(harness);
7886
7933
  }
7887
- function getMagicContextLogPath() {
7888
- return join(tmpdir(), "magic-context.log");
7934
+ function getMagicContextHistorianDir2(harness) {
7935
+ return getMagicContextHistorianDir(harness);
7889
7936
  }
7890
7937
  function getOpenCodePluginCacheDir() {
7891
- const xdg = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
7892
- return join(xdg, "opencode", "packages");
7938
+ const xdg = process.env.XDG_CACHE_HOME || join2(homedir2(), ".cache");
7939
+ return join2(xdg, "opencode", "packages");
7893
7940
  }
7894
- function isDir(path) {
7941
+ function isDir(path2) {
7895
7942
  try {
7896
- return statSync(path).isDirectory();
7943
+ return statSync(path2).isDirectory();
7897
7944
  } catch {
7898
7945
  return false;
7899
7946
  }
7900
7947
  }
7901
- function dirSizeBytes(path) {
7902
- if (!isDir(path))
7948
+ function dirSizeBytes(path2) {
7949
+ if (!isDir(path2))
7903
7950
  return 0;
7904
7951
  let total = 0;
7905
- const stack = [path];
7952
+ const stack = [path2];
7906
7953
  while (stack.length > 0) {
7907
7954
  const cur = stack.pop();
7908
7955
  if (cur === undefined)
@@ -7910,7 +7957,7 @@ function dirSizeBytes(path) {
7910
7957
  try {
7911
7958
  const entries = readdirSync(cur, { withFileTypes: true });
7912
7959
  for (const entry of entries) {
7913
- const child = join(cur, entry.name);
7960
+ const child = join2(cur, entry.name);
7914
7961
  if (entry.isDirectory()) {
7915
7962
  stack.push(child);
7916
7963
  } else if (entry.isFile()) {
@@ -7923,13 +7970,15 @@ function dirSizeBytes(path) {
7923
7970
  }
7924
7971
  return total;
7925
7972
  }
7926
- var init_paths = () => {};
7973
+ var init_paths = __esm(() => {
7974
+ init_data_path();
7975
+ });
7927
7976
 
7928
7977
  // src/adapters/opencode.ts
7929
7978
  import { execFileSync } from "node:child_process";
7930
7979
  import { existsSync as existsSync2, readFileSync, writeFileSync } from "node:fs";
7931
- import { homedir as homedir2 } from "node:os";
7932
- import { dirname, join as join2 } from "node:path";
7980
+ import { homedir as homedir3 } from "node:os";
7981
+ import { dirname, join as join3 } from "node:path";
7933
7982
 
7934
7983
  class OpenCodeAdapter {
7935
7984
  kind = "opencode";
@@ -7937,7 +7986,7 @@ class OpenCodeAdapter {
7937
7986
  pluginPackageName = PLUGIN_NAME;
7938
7987
  isInstalled() {
7939
7988
  const isWindows = process.platform === "win32";
7940
- const stockBin = isWindows ? join2(homedir2(), ".opencode", "bin", "opencode.exe") : join2(homedir2(), ".opencode", "bin", "opencode");
7989
+ const stockBin = isWindows ? join3(homedir3(), ".opencode", "bin", "opencode.exe") : join3(homedir3(), ".opencode", "bin", "opencode");
7941
7990
  if (existsSync2(stockBin))
7942
7991
  return true;
7943
7992
  const probe = isWindows ? { cmd: "where", args: ["opencode"] } : { cmd: "command", args: ["-v", "opencode"] };
@@ -8112,15 +8161,15 @@ class OpenCodeAdapter {
8112
8161
  return "Install OpenCode: curl -fsSL https://opencode.ai/install | bash";
8113
8162
  }
8114
8163
  getPluginCacheInfo() {
8115
- const path = getOpenCodePluginCacheDir();
8164
+ const path2 = getOpenCodePluginCacheDir();
8116
8165
  return {
8117
- path,
8118
- exists: existsSync2(path),
8119
- sizeBytes: dirSizeBytes(path)
8166
+ path: path2,
8167
+ exists: existsSync2(path2),
8168
+ sizeBytes: dirSizeBytes(path2)
8120
8169
  };
8121
8170
  }
8122
8171
  getLogPath() {
8123
- return getMagicContextLogPath();
8172
+ return getMagicContextLogPath2("opencode");
8124
8173
  }
8125
8174
  getInstalledPluginVersion() {
8126
8175
  const cacheDir = getOpenCodePluginCacheDir();
@@ -8185,8 +8234,8 @@ var init_opencode = __esm(() => {
8185
8234
  // src/lib/pi-helpers.ts
8186
8235
  import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
8187
8236
  import { existsSync as existsSync3 } from "node:fs";
8188
- import { homedir as homedir3 } from "node:os";
8189
- import { join as join3 } from "node:path";
8237
+ import { homedir as homedir4 } from "node:os";
8238
+ import { join as join4 } from "node:path";
8190
8239
  function getStaticModels() {
8191
8240
  return [...STATIC_MODELS];
8192
8241
  }
@@ -8208,7 +8257,7 @@ function detectPiBinary() {
8208
8257
  if (first)
8209
8258
  return { path: first, source: "path" };
8210
8259
  }
8211
- const homeCandidate = process.platform === "win32" ? join3(homedir3(), ".pi", "bin", "pi.cmd") : join3(homedir3(), ".pi", "bin", "pi");
8260
+ const homeCandidate = process.platform === "win32" ? join4(homedir4(), ".pi", "bin", "pi.cmd") : join4(homedir4(), ".pi", "bin", "pi");
8212
8261
  if (existsSync3(homeCandidate))
8213
8262
  return { path: homeCandidate, source: "home" };
8214
8263
  return null;
@@ -8251,7 +8300,7 @@ function parseModelListOutput(output) {
8251
8300
  if (!line || line.toLowerCase().includes("usage:"))
8252
8301
  continue;
8253
8302
  const token = line.split(/\s+/)[0]?.trim().replace(/,$/, "");
8254
- if (!token || !token.includes("/"))
8303
+ if (!token?.includes("/"))
8255
8304
  continue;
8256
8305
  if (/^https?:\/\//.test(token))
8257
8306
  continue;
@@ -8457,13 +8506,13 @@ class PiAdapter {
8457
8506
  }
8458
8507
  }
8459
8508
  getInstallHint() {
8460
- 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)";
8461
8510
  }
8462
8511
  getPluginCacheInfo() {
8463
8512
  return { path: null, exists: false, sizeBytes: 0 };
8464
8513
  }
8465
8514
  getLogPath() {
8466
- return getMagicContextLogPath();
8515
+ return getMagicContextLogPath2("pi");
8467
8516
  }
8468
8517
  getInstalledPluginVersion() {
8469
8518
  const piBin = detectPiBinary();
@@ -10327,17 +10376,17 @@ function detectConfigFile(basePath) {
10327
10376
  var init_jsonc_parser = () => {};
10328
10377
 
10329
10378
  // ../plugin/src/shared/opencode-config-dir.ts
10330
- import { homedir as homedir4 } from "node:os";
10331
- import { join as join4, resolve } from "node:path";
10379
+ import { homedir as homedir5 } from "node:os";
10380
+ import { join as join5, resolve } from "node:path";
10332
10381
  function getCliConfigDir() {
10333
10382
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
10334
10383
  if (envConfigDir) {
10335
10384
  return resolve(envConfigDir);
10336
10385
  }
10337
10386
  if (process.platform === "win32") {
10338
- return join4(homedir4(), ".config", "opencode");
10387
+ return join5(homedir5(), ".config", "opencode");
10339
10388
  }
10340
- return join4(process.env.XDG_CONFIG_HOME || join4(homedir4(), ".config"), "opencode");
10389
+ return join5(process.env.XDG_CONFIG_HOME || join5(homedir5(), ".config"), "opencode");
10341
10390
  }
10342
10391
  function getOpenCodeConfigDir2(_options) {
10343
10392
  return getCliConfigDir();
@@ -10346,16 +10395,16 @@ function getOpenCodeConfigPaths(options) {
10346
10395
  const configDir = getOpenCodeConfigDir2(options);
10347
10396
  return {
10348
10397
  configDir,
10349
- configJson: join4(configDir, "opencode.json"),
10350
- configJsonc: join4(configDir, "opencode.jsonc"),
10351
- packageJson: join4(configDir, "package.json"),
10352
- omoConfig: join4(configDir, "magic-context.jsonc")
10398
+ configJson: join5(configDir, "opencode.json"),
10399
+ configJsonc: join5(configDir, "opencode.jsonc"),
10400
+ packageJson: join5(configDir, "package.json"),
10401
+ omoConfig: join5(configDir, "magic-context.jsonc")
10353
10402
  };
10354
10403
  }
10355
10404
  var init_opencode_config_dir = () => {};
10356
10405
 
10357
10406
  // ../plugin/src/shared/conflict-detector.ts
10358
- import { join as join5 } from "node:path";
10407
+ import { join as join6 } from "node:path";
10359
10408
  function detectConflicts(directory) {
10360
10409
  const conflicts = {
10361
10410
  compactionAuto: false,
@@ -10412,8 +10461,8 @@ function checkCompaction(directory) {
10412
10461
  return { auto: true, prune: false };
10413
10462
  }
10414
10463
  function readProjectCompaction(directory) {
10415
- const dotOcJsonc = join5(directory, ".opencode", "opencode.jsonc");
10416
- const dotOcJson = join5(directory, ".opencode", "opencode.json");
10464
+ const dotOcJsonc = join6(directory, ".opencode", "opencode.jsonc");
10465
+ const dotOcJson = join6(directory, ".opencode", "opencode.json");
10417
10466
  const dotOcConfig = readJsoncFile(dotOcJsonc) ?? readJsoncFile(dotOcJson);
10418
10467
  if (dotOcConfig?.compaction) {
10419
10468
  const c2 = dotOcConfig.compaction;
@@ -10421,8 +10470,8 @@ function readProjectCompaction(directory) {
10421
10470
  return { auto: c2.auto === true, prune: c2.prune === true, resolved: true };
10422
10471
  }
10423
10472
  }
10424
- const rootJsonc = join5(directory, "opencode.jsonc");
10425
- const rootJson = join5(directory, "opencode.json");
10473
+ const rootJsonc = join6(directory, "opencode.jsonc");
10474
+ const rootJson = join6(directory, "opencode.json");
10426
10475
  const rootConfig = readJsoncFile(rootJsonc) ?? readJsoncFile(rootJson);
10427
10476
  if (rootConfig?.compaction) {
10428
10477
  const c2 = rootConfig.compaction;
@@ -10476,10 +10525,10 @@ function collectPluginEntries(directory) {
10476
10525
  }
10477
10526
  };
10478
10527
  for (const configPath of [
10479
- join5(directory, ".opencode", "opencode.jsonc"),
10480
- join5(directory, ".opencode", "opencode.json"),
10481
- join5(directory, "opencode.jsonc"),
10482
- join5(directory, "opencode.json")
10528
+ join6(directory, ".opencode", "opencode.jsonc"),
10529
+ join6(directory, ".opencode", "opencode.json"),
10530
+ join6(directory, "opencode.jsonc"),
10531
+ join6(directory, "opencode.json")
10483
10532
  ]) {
10484
10533
  const config = readJsoncFile(configPath);
10485
10534
  pushFrom(config?.plugin);
@@ -10520,7 +10569,7 @@ function readOmoDisabledHooks(directory) {
10520
10569
  try {
10521
10570
  const paths = getOpenCodeConfigPaths({ binary: "opencode" });
10522
10571
  for (const name of configNames) {
10523
- const configPath = join5(paths.configDir, name);
10572
+ const configPath = join6(paths.configDir, name);
10524
10573
  const config = readJsoncFile(configPath);
10525
10574
  if (config?.disabled_hooks) {
10526
10575
  for (const hook of config.disabled_hooks) {
@@ -10530,7 +10579,7 @@ function readOmoDisabledHooks(directory) {
10530
10579
  }
10531
10580
  } catch {}
10532
10581
  for (const name of configNames) {
10533
- const config = readJsoncFile(join5(directory, name));
10582
+ const config = readJsoncFile(join6(directory, name));
10534
10583
  if (config?.disabled_hooks) {
10535
10584
  for (const hook of config.disabled_hooks) {
10536
10585
  disabled.add(hook);
@@ -10548,11 +10597,8 @@ var init_conflict_detector = __esm(() => {
10548
10597
  });
10549
10598
 
10550
10599
  // ../plugin/src/shared/conflict-fixer.ts
10551
- import { existsSync as existsSync6, mkdirSync, writeFileSync as writeFileSync3 } from "node:fs";
10552
- import { dirname as dirname3, join as join6 } from "node:path";
10553
- function ensureParentDir(filePath) {
10554
- mkdirSync(dirname3(filePath), { recursive: true });
10555
- }
10600
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
10601
+ import { join as join7 } from "node:path";
10556
10602
  function isRecord(value) {
10557
10603
  return typeof value === "object" && value !== null && !Array.isArray(value);
10558
10604
  }
@@ -10561,13 +10607,17 @@ function asStringArray(value) {
10561
10607
  }
10562
10608
  function readConfig(filePath) {
10563
10609
  if (!existsSync6(filePath)) {
10564
- return {};
10610
+ return null;
10611
+ }
10612
+ try {
10613
+ const parsed = import_comment_json3.parse(readFileSync4(filePath, "utf-8"));
10614
+ return isRecord(parsed) ? parsed : null;
10615
+ } catch {
10616
+ return null;
10565
10617
  }
10566
- return readJsoncFile(filePath);
10567
10618
  }
10568
10619
  function writeConfig(filePath, config) {
10569
- ensureParentDir(filePath);
10570
- writeFileSync3(filePath, `${JSON.stringify(config, null, 2)}
10620
+ writeFileSync3(filePath, `${import_comment_json3.stringify(config, null, 2)}
10571
10621
  `);
10572
10622
  }
10573
10623
  function resolveUserOpenCodeConfigPath() {
@@ -10579,12 +10629,14 @@ function resolveUserOpenCodeConfigPath() {
10579
10629
  function collectOpenCodeConfigPaths(directory) {
10580
10630
  const paths = new Set;
10581
10631
  const userConfig = resolveUserOpenCodeConfigPath();
10582
- paths.add(userConfig);
10632
+ if (existsSync6(userConfig)) {
10633
+ paths.add(userConfig);
10634
+ }
10583
10635
  for (const filePath of [
10584
- join6(directory, ".opencode", "opencode.jsonc"),
10585
- join6(directory, ".opencode", "opencode.json"),
10586
- join6(directory, "opencode.jsonc"),
10587
- join6(directory, "opencode.json")
10636
+ join7(directory, ".opencode", "opencode.jsonc"),
10637
+ join7(directory, ".opencode", "opencode.json"),
10638
+ join7(directory, "opencode.jsonc"),
10639
+ join7(directory, "opencode.json")
10588
10640
  ]) {
10589
10641
  if (existsSync6(filePath)) {
10590
10642
  paths.add(filePath);
@@ -10596,8 +10648,8 @@ function collectOmoConfigPaths(directory) {
10596
10648
  const paths = new Set;
10597
10649
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
10598
10650
  for (const fileName of OMO_CONFIG_NAMES) {
10599
- const userPath = join6(configDir, fileName);
10600
- const projectPath = join6(directory, fileName);
10651
+ const userPath = join7(configDir, fileName);
10652
+ const projectPath = join7(directory, fileName);
10601
10653
  if (existsSync6(userPath)) {
10602
10654
  paths.add(userPath);
10603
10655
  }
@@ -10605,11 +10657,15 @@ function collectOmoConfigPaths(directory) {
10605
10657
  paths.add(projectPath);
10606
10658
  }
10607
10659
  }
10608
- if (paths.size === 0) {
10609
- paths.add(join6(configDir, "oh-my-openagent.json"));
10610
- }
10611
10660
  return [...paths];
10612
10661
  }
10662
+ function filterDcpPluginEntries(entries) {
10663
+ const plugins = entries.filter((entry) => {
10664
+ const name = extractPluginName(entry);
10665
+ return name ? !matchesPackageName(name, DCP_PACKAGE_NAMES) : true;
10666
+ });
10667
+ return { plugins, removed: plugins.length !== entries.length };
10668
+ }
10613
10669
  function fixConflicts(directory, conflicts) {
10614
10670
  const actions = [];
10615
10671
  let updatedCompaction = false;
@@ -10623,7 +10679,7 @@ function fixConflicts(directory, conflicts) {
10623
10679
  }
10624
10680
  let changed = false;
10625
10681
  if (conflicts.compactionAuto || conflicts.compactionPrune) {
10626
- const compaction = isRecord(config.compaction) ? { ...config.compaction } : {};
10682
+ const compaction = isRecord(config.compaction) ? config.compaction : {};
10627
10683
  if (compaction.auto !== false) {
10628
10684
  compaction.auto = false;
10629
10685
  changed = true;
@@ -10637,10 +10693,10 @@ function fixConflicts(directory, conflicts) {
10637
10693
  config.compaction = compaction;
10638
10694
  }
10639
10695
  if (conflicts.dcpPlugin) {
10640
- const plugins = asStringArray(config.plugin);
10641
- const filteredPlugins = plugins.filter((plugin) => !plugin.includes("opencode-dcp"));
10642
- if (filteredPlugins.length !== plugins.length) {
10643
- config.plugin = filteredPlugins;
10696
+ const plugins = Array.isArray(config.plugin) ? config.plugin : [];
10697
+ const filtered = filterDcpPluginEntries(plugins);
10698
+ if (filtered.removed) {
10699
+ config.plugin = filtered.plugins;
10644
10700
  changed = true;
10645
10701
  removedDcpPlugin = true;
10646
10702
  }
@@ -10695,10 +10751,11 @@ function fixConflicts(directory, conflicts) {
10695
10751
  }
10696
10752
  return actions;
10697
10753
  }
10698
- var CONFLICTING_OMO_HOOKS, OMO_CONFIG_NAMES;
10754
+ var import_comment_json3, CONFLICTING_OMO_HOOKS, OMO_CONFIG_NAMES;
10699
10755
  var init_conflict_fixer = __esm(() => {
10700
- init_jsonc_parser();
10756
+ init_conflict_detector();
10701
10757
  init_opencode_config_dir();
10758
+ import_comment_json3 = __toESM(require_src2(), 1);
10702
10759
  CONFLICTING_OMO_HOOKS = [
10703
10760
  "context-window-monitor",
10704
10761
  "preemptive-compaction",
@@ -10790,30 +10847,30 @@ function buildModelSelection2(allModels, role) {
10790
10847
  var init_opencode_helpers = () => {};
10791
10848
 
10792
10849
  // src/commands/setup-opencode.ts
10793
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
10794
- import { dirname as dirname4 } from "node:path";
10850
+ import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
10851
+ import { dirname as dirname3 } from "node:path";
10795
10852
  function ensureDir3(dir) {
10796
10853
  if (!existsSync7(dir)) {
10797
- mkdirSync2(dir, { recursive: true });
10854
+ mkdirSync(dir, { recursive: true });
10798
10855
  }
10799
10856
  }
10800
- function readJsonc(path) {
10801
- const content = readFileSync4(path, "utf-8");
10857
+ function readJsonc(path2) {
10858
+ const content = readFileSync5(path2, "utf-8");
10802
10859
  try {
10803
- return import_comment_json3.parse(content);
10860
+ return import_comment_json4.parse(content);
10804
10861
  } catch (err) {
10805
- console.error(` ⚠ Failed to parse ${path}: ${err instanceof Error ? err.message : err}`);
10862
+ console.error(` ⚠ Failed to parse ${path2}: ${err instanceof Error ? err.message : err}`);
10806
10863
  return null;
10807
10864
  }
10808
10865
  }
10809
10866
  function addPluginToOpenCodeConfig(configPath, format) {
10810
- ensureDir3(dirname4(configPath));
10867
+ ensureDir3(dirname3(configPath));
10811
10868
  if (format === "none") {
10812
10869
  const config = {
10813
10870
  plugin: [PLUGIN_ENTRY2],
10814
10871
  compaction: { auto: false, prune: false }
10815
10872
  };
10816
- writeFileSync4(configPath, `${import_comment_json3.stringify(config, null, 2)}
10873
+ writeFileSync4(configPath, `${import_comment_json4.stringify(config, null, 2)}
10817
10874
  `);
10818
10875
  return;
10819
10876
  }
@@ -10833,13 +10890,13 @@ function addPluginToOpenCodeConfig(configPath, format) {
10833
10890
  compaction.auto = false;
10834
10891
  compaction.prune = false;
10835
10892
  existing.compaction = compaction;
10836
- writeFileSync4(configPath, `${import_comment_json3.stringify(existing, null, 2)}
10893
+ writeFileSync4(configPath, `${import_comment_json4.stringify(existing, null, 2)}
10837
10894
  `);
10838
10895
  }
10839
10896
  function addPluginToTuiConfig(configPath, format) {
10840
- ensureDir3(dirname4(configPath));
10897
+ ensureDir3(dirname3(configPath));
10841
10898
  if (format === "none") {
10842
- writeFileSync4(configPath, `${import_comment_json3.stringify({ plugin: [PLUGIN_ENTRY2] }, null, 2)}
10899
+ writeFileSync4(configPath, `${import_comment_json4.stringify({ plugin: [PLUGIN_ENTRY2] }, null, 2)}
10843
10900
  `);
10844
10901
  return;
10845
10902
  }
@@ -10855,9 +10912,42 @@ function addPluginToTuiConfig(configPath, format) {
10855
10912
  rawPlugins.push(PLUGIN_ENTRY2);
10856
10913
  }
10857
10914
  existing.plugin = rawPlugins;
10858
- writeFileSync4(configPath, `${import_comment_json3.stringify(existing, null, 2)}
10915
+ writeFileSync4(configPath, `${import_comment_json4.stringify(existing, null, 2)}
10859
10916
  `);
10860
10917
  }
10918
+ function findDcpPluginIndexes(plugins) {
10919
+ return plugins.map((plugin, index) => matchesPluginEntry(plugin, DCP_PLUGIN_NAME) ? index : -1).filter((index) => index >= 0);
10920
+ }
10921
+ function pluginEntryName(entry) {
10922
+ if (typeof entry === "string")
10923
+ return entry;
10924
+ if (Array.isArray(entry) && typeof entry[0] === "string")
10925
+ return entry[0];
10926
+ return String(entry);
10927
+ }
10928
+ async function resolveDcpConflictBeforeSetup(configPath, format) {
10929
+ if (format === "none")
10930
+ return;
10931
+ const ocConfig = readJsonc(configPath);
10932
+ if (!ocConfig)
10933
+ return;
10934
+ const plugins = Array.isArray(ocConfig.plugin) ? ocConfig.plugin : [];
10935
+ const dcpIndexes = findDcpPluginIndexes(plugins);
10936
+ if (dcpIndexes.length === 0)
10937
+ return;
10938
+ log.warn(`Found conflicting plugin: ${pluginEntryName(plugins[dcpIndexes[0]])}`);
10939
+ log.message(`opencode-dcp (Dynamic Context Pruning) and Magic Context both manage context.
10940
+ ` + "Running both simultaneously will cause unpredictable behavior.");
10941
+ const shouldRemove = await confirm("Remove opencode-dcp from your config?", true);
10942
+ if (shouldRemove) {
10943
+ ocConfig.plugin = plugins.filter((_plugin, index) => !dcpIndexes.includes(index));
10944
+ writeFileSync4(configPath, `${import_comment_json4.stringify(ocConfig, null, 2)}
10945
+ `);
10946
+ log.success("Removed opencode-dcp from plugin list");
10947
+ } else {
10948
+ log.warn("Skipped — you may experience context management conflicts");
10949
+ }
10950
+ }
10861
10951
  function writeMagicContextConfig(configPath, options) {
10862
10952
  const config = (existsSync7(configPath) ? readJsonc(configPath) : null) ?? {};
10863
10953
  if (!config.$schema) {
@@ -10896,7 +10986,7 @@ function writeMagicContextConfig(configPath, options) {
10896
10986
  cacheTtl["anthropic/claude-opus-4-6"] = "59m";
10897
10987
  config.cache_ttl = cacheTtl;
10898
10988
  }
10899
- writeFileSync4(configPath, `${import_comment_json3.stringify(config, null, 2)}
10989
+ writeFileSync4(configPath, `${import_comment_json4.stringify(config, null, 2)}
10900
10990
  `);
10901
10991
  }
10902
10992
  async function runSetup() {
@@ -10926,32 +11016,11 @@ async function runSetup() {
10926
11016
  }
10927
11017
  const paths = detectConfigPaths();
10928
11018
  const hadExistingSetup = paths.opencodeConfigFormat !== "none" || existsSync7(paths.magicContextConfig) || paths.tuiConfigFormat !== "none";
11019
+ await resolveDcpConflictBeforeSetup(paths.opencodeConfig, paths.opencodeConfigFormat);
10929
11020
  addPluginToOpenCodeConfig(paths.opencodeConfig, paths.opencodeConfigFormat);
10930
11021
  log.success(`Plugin added to ${paths.opencodeConfig}`);
10931
11022
  log.info("Disabled built-in compaction (auto=false, prune=false)");
10932
11023
  log.message("Magic Context handles context management — built-in compaction would interfere");
10933
- if (paths.opencodeConfigFormat !== "none") {
10934
- const ocConfig = readJsonc(paths.opencodeConfig);
10935
- if (ocConfig) {
10936
- const plugins = ocConfig.plugin ?? [];
10937
- const dcpIndex = plugins.findIndex((p2) => p2.startsWith("@tarquinen/opencode-dcp"));
10938
- if (dcpIndex !== -1) {
10939
- log.warn(`Found conflicting plugin: ${plugins[dcpIndex]}`);
10940
- log.message(`opencode-dcp (Dynamic Context Pruning) and Magic Context both manage context.
10941
- ` + "Running both simultaneously will cause unpredictable behavior.");
10942
- const shouldRemove = await confirm("Remove opencode-dcp from your config?", true);
10943
- if (shouldRemove) {
10944
- plugins.splice(dcpIndex, 1);
10945
- ocConfig.plugin = plugins;
10946
- writeFileSync4(paths.opencodeConfig, `${import_comment_json3.stringify(ocConfig, null, 2)}
10947
- `);
10948
- log.success("Removed opencode-dcp from plugin list");
10949
- } else {
10950
- log.warn("Skipped — you may experience context management conflicts");
10951
- }
10952
- }
10953
- }
10954
- }
10955
11024
  if (hadExistingSetup) {
10956
11025
  const conflicts = detectConflicts(process.cwd());
10957
11026
  if (conflicts.hasConflict) {
@@ -11084,7 +11153,7 @@ async function runSetup() {
11084
11153
  outro("Run 'opencode' to start!");
11085
11154
  return 0;
11086
11155
  }
11087
- var import_comment_json3, PLUGIN_NAME3 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY2 = "@cortexkit/opencode-magic-context@latest";
11156
+ var import_comment_json4, PLUGIN_NAME3 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY2 = "@cortexkit/opencode-magic-context@latest", DCP_PLUGIN_NAME = "@tarquinen/opencode-dcp";
11088
11157
  var init_setup_opencode = __esm(() => {
11089
11158
  init_conflict_detector();
11090
11159
  init_conflict_fixer();
@@ -11092,26 +11161,77 @@ var init_setup_opencode = __esm(() => {
11092
11161
  init_opencode_helpers();
11093
11162
  init_paths();
11094
11163
  init_prompts();
11095
- import_comment_json3 = __toESM(require_src2(), 1);
11164
+ import_comment_json4 = __toESM(require_src2(), 1);
11165
+ });
11166
+
11167
+ // src/lib/pi-package-entry.ts
11168
+ function stripNpmPrefix(value) {
11169
+ return value.startsWith("npm:") ? value.slice("npm:".length) : value;
11170
+ }
11171
+ function packageNameFromSpecifier(value) {
11172
+ const normalized = stripNpmPrefix(value.trim());
11173
+ if (!normalized)
11174
+ return normalized;
11175
+ if (normalized.startsWith("@")) {
11176
+ const slash = normalized.indexOf("/");
11177
+ if (slash < 0)
11178
+ return normalized;
11179
+ const versionAt2 = normalized.indexOf("@", slash + 1);
11180
+ return versionAt2 > 0 ? normalized.slice(0, versionAt2) : normalized;
11181
+ }
11182
+ const versionAt = normalized.indexOf("@");
11183
+ return versionAt > 0 ? normalized.slice(0, versionAt) : normalized;
11184
+ }
11185
+ function getPiPackageEntryName(entry) {
11186
+ if (typeof entry === "string")
11187
+ return packageNameFromSpecifier(entry);
11188
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
11189
+ const name = entry.name;
11190
+ if (typeof name === "string")
11191
+ return packageNameFromSpecifier(name);
11192
+ }
11193
+ return null;
11194
+ }
11195
+ function isPiMagicContextPackageEntry(entry) {
11196
+ return getPiPackageEntryName(entry) === PI_MAGIC_CONTEXT_PACKAGE_NAME;
11197
+ }
11198
+ function hasPiMagicContextPackage(entries) {
11199
+ return entries.some(isPiMagicContextPackageEntry);
11200
+ }
11201
+ function describePiPackageEntry(entry) {
11202
+ if (typeof entry === "string")
11203
+ return entry;
11204
+ const name = getPiPackageEntryName(entry);
11205
+ if (name)
11206
+ return name;
11207
+ try {
11208
+ return JSON.stringify(entry);
11209
+ } catch {
11210
+ return String(entry);
11211
+ }
11212
+ }
11213
+ var PI_MAGIC_CONTEXT_PACKAGE_NAME = "@cortexkit/pi-magic-context";
11214
+ var init_pi_package_entry = __esm(() => {
11215
+ init_pi_helpers();
11096
11216
  });
11097
11217
 
11098
11218
  // src/commands/setup-pi.ts
11099
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
11100
- import { dirname as dirname5 } from "node:path";
11101
- function ensureDir4(path) {
11102
- if (!existsSync8(path))
11103
- mkdirSync3(path, { recursive: true });
11219
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
11220
+ import { dirname as dirname4 } from "node:path";
11221
+ function ensureDir4(path2) {
11222
+ if (!existsSync8(path2))
11223
+ mkdirSync2(path2, { recursive: true });
11104
11224
  }
11105
11225
  async function getDefaultPrompts() {
11106
11226
  const { promptIO: promptIO2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
11107
11227
  return promptIO2;
11108
11228
  }
11109
- function readJsonc2(path) {
11229
+ function readJsonc2(path2) {
11110
11230
  try {
11111
- return import_comment_json4.parse(readFileSync5(path, "utf-8"));
11231
+ return import_comment_json5.parse(readFileSync6(path2, "utf-8"));
11112
11232
  } catch (error) {
11113
11233
  const message = error instanceof Error ? error.message : String(error);
11114
- console.warn(`Could not parse ${path}: ${message}`);
11234
+ console.warn(`Could not parse ${path2}: ${message}`);
11115
11235
  return null;
11116
11236
  }
11117
11237
  }
@@ -11123,12 +11243,12 @@ function compactObject(obj) {
11123
11243
  return obj;
11124
11244
  }
11125
11245
  function comparePiVersion(a, b2) {
11126
- const parse = (v2) => {
11246
+ const parse2 = (v2) => {
11127
11247
  const match = v2.match(/(\d+)\.(\d+)\.(\d+)/);
11128
11248
  return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
11129
11249
  };
11130
- const left = parse(a);
11131
- const right = parse(b2);
11250
+ const left = parse2(a);
11251
+ const right = parse2(b2);
11132
11252
  if (!left || !right)
11133
11253
  return 0;
11134
11254
  for (let i = 0;i < 3; i += 1) {
@@ -11140,19 +11260,19 @@ function comparePiVersion(a, b2) {
11140
11260
  return 0;
11141
11261
  }
11142
11262
  function writePiSettingsPackage(settingsPath, packageSource = PI_PACKAGE_SOURCE) {
11143
- ensureDir4(dirname5(settingsPath));
11263
+ ensureDir4(dirname4(settingsPath));
11144
11264
  const settings = existsSync8(settingsPath) ? readJsonc2(settingsPath) ?? {} : {};
11145
- const packages = Array.isArray(settings.packages) ? settings.packages.filter((value) => typeof value === "string") : [];
11146
- const hasPackage = packages.some((source) => source === packageSource || source === packageSource.replace(/^npm:/, "") || source.includes("pi-magic-context"));
11265
+ const packages = Array.isArray(settings.packages) ? settings.packages : [];
11266
+ const hasPackage = hasPiMagicContextPackage(packages);
11147
11267
  if (!hasPackage)
11148
11268
  packages.push(packageSource);
11149
11269
  settings.packages = packages;
11150
- writeFileSync5(settingsPath, `${import_comment_json4.stringify(settings, null, 2)}
11270
+ writeFileSync5(settingsPath, `${import_comment_json5.stringify(settings, null, 2)}
11151
11271
  `);
11152
11272
  return !hasPackage;
11153
11273
  }
11154
11274
  function writeMagicContextConfig2(configPath, options) {
11155
- ensureDir4(dirname5(configPath));
11275
+ ensureDir4(dirname4(configPath));
11156
11276
  const config = existsSync8(configPath) ? readJsonc2(configPath) ?? {} : {};
11157
11277
  if (!config.$schema) {
11158
11278
  config.$schema = "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json";
@@ -11180,7 +11300,7 @@ function writeMagicContextConfig2(configPath, options) {
11180
11300
  };
11181
11301
  }
11182
11302
  config.embedding = options.embedding;
11183
- writeFileSync5(configPath, `${import_comment_json4.stringify(config, null, 2)}
11303
+ writeFileSync5(configPath, `${import_comment_json5.stringify(config, null, 2)}
11184
11304
  `);
11185
11305
  }
11186
11306
  async function chooseModel(prompts, allModels, role, message) {
@@ -11233,11 +11353,11 @@ async function runSetup2(options = {}) {
11233
11353
  }
11234
11354
  const version = env.getPiVersion(pi.path);
11235
11355
  spinner2.stop(version ? `Pi ${version} detected at ${pi.path}` : `Pi detected at ${pi.path}`);
11236
- const MIN_PI_VERSION = "0.71.0";
11356
+ const MIN_PI_VERSION = "0.74.0";
11237
11357
  if (version && comparePiVersion(version, MIN_PI_VERSION) < 0) {
11238
11358
  prompts.log.warn(`Pi ${version} is older than the required ${MIN_PI_VERSION}.
11239
- 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.
11240
- 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.`);
11241
11361
  const proceed = await prompts.confirm("Continue with setup anyway? (subagents will fail at runtime)", false);
11242
11362
  if (!proceed) {
11243
11363
  prompts.outro("Setup cancelled — upgrade Pi and try again.");
@@ -11306,11 +11426,12 @@ Run \`pi update\` (or \`npm install -g @mariozechner/pi-coding-agent@latest\`) b
11306
11426
  prompts.outro("Start a Pi session and try /ctx-aug");
11307
11427
  return 0;
11308
11428
  }
11309
- var import_comment_json4, DEFAULT_ENV;
11429
+ var import_comment_json5, DEFAULT_ENV;
11310
11430
  var init_setup_pi = __esm(() => {
11311
11431
  init_paths();
11312
11432
  init_pi_helpers();
11313
- import_comment_json4 = __toESM(require_src2(), 1);
11433
+ init_pi_package_entry();
11434
+ import_comment_json5 = __toESM(require_src2(), 1);
11314
11435
  DEFAULT_ENV = {
11315
11436
  detectPiBinary,
11316
11437
  getPiVersion,
@@ -11390,29 +11511,6 @@ var init_setup = __esm(() => {
11390
11511
  init_setup_pi();
11391
11512
  });
11392
11513
 
11393
- // ../plugin/src/shared/data-path.ts
11394
- import * as os from "node:os";
11395
- import * as path from "node:path";
11396
- function getDataDir() {
11397
- return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
11398
- }
11399
- function getOpenCodeStorageDir() {
11400
- return path.join(getDataDir(), "opencode", "storage");
11401
- }
11402
- function getMagicContextStorageDir() {
11403
- return path.join(getDataDir(), "cortexkit", "magic-context");
11404
- }
11405
- function getLegacyOpenCodeMagicContextStorageDir() {
11406
- return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
11407
- }
11408
- function getCacheDir() {
11409
- return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
11410
- }
11411
- function getOpenCodeCacheDir() {
11412
- return path.join(getCacheDir(), "opencode");
11413
- }
11414
- var init_data_path = () => {};
11415
-
11416
11514
  // ../../node_modules/.bun/nanotar@0.3.0/node_modules/nanotar/dist/index.mjs
11417
11515
  function parseTar(data, opts) {
11418
11516
  const buffer = data.buffer || data;
@@ -11591,8 +11689,16 @@ var init_dist5 = __esm(() => {
11591
11689
 
11592
11690
  // ../plugin/src/shared/logger.ts
11593
11691
  import * as fs from "node:fs";
11594
- import * as os2 from "node:os";
11595
11692
  import * as path2 from "node:path";
11693
+ function ensureDir5(filePath) {
11694
+ const dir = path2.dirname(filePath);
11695
+ if (dir === lastEnsuredDir)
11696
+ return;
11697
+ try {
11698
+ fs.mkdirSync(dir, { recursive: true });
11699
+ lastEnsuredDir = dir;
11700
+ } catch {}
11701
+ }
11596
11702
  function flush() {
11597
11703
  if (flushTimer) {
11598
11704
  clearTimeout(flushTimer);
@@ -11603,6 +11709,8 @@ function flush() {
11603
11709
  const data = buffer.join("");
11604
11710
  buffer = [];
11605
11711
  try {
11712
+ const logFile = getMagicContextLogPath();
11713
+ ensureDir5(logFile);
11606
11714
  fs.appendFileSync(logFile, data);
11607
11715
  } catch {}
11608
11716
  }
@@ -11630,9 +11738,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
11630
11738
  }
11631
11739
  } catch {}
11632
11740
  }
11633
- var logFile, isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
11741
+ var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, lastEnsuredDir = null;
11634
11742
  var init_logger = __esm(() => {
11635
- logFile = path2.join(os2.tmpdir(), "magic-context.log");
11743
+ init_data_path();
11636
11744
  buffer = [];
11637
11745
  if (!isTestEnv) {
11638
11746
  process.on("exit", flush);
@@ -11808,15 +11916,15 @@ __export(exports_migrate, {
11808
11916
  import { randomBytes } from "node:crypto";
11809
11917
  import { existsSync as existsSync10, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
11810
11918
  import { homedir as homedir6 } from "node:os";
11811
- import { join as join10 } from "node:path";
11919
+ import { join as join9 } from "node:path";
11812
11920
  function defaultOpenCodeDbPath() {
11813
- return join10(homedir6(), ".local", "share", "opencode", "opencode.db");
11921
+ return join9(homedir6(), ".local", "share", "opencode", "opencode.db");
11814
11922
  }
11815
11923
  function defaultCortexkitDbPath() {
11816
- return join10(getMagicContextStorageDir(), "context.db");
11924
+ return join9(getMagicContextStorageDir(), "context.db");
11817
11925
  }
11818
11926
  function defaultPiSessionsRoot() {
11819
- return join10(homedir6(), ".pi", "agent", "sessions");
11927
+ return join9(homedir6(), ".pi", "agent", "sessions");
11820
11928
  }
11821
11929
  function defaultFs() {
11822
11930
  return { existsSync: existsSync10, mkdirSync: mkdirSync5, writeFileSync: writeFileSync7 };
@@ -12242,7 +12350,7 @@ function migrateOpenCodeSessionToPi(opts) {
12242
12350
  const provider = opts.provider ?? model.provider;
12243
12351
  const modelId = opts.modelId ?? model.modelId;
12244
12352
  const cwd = session.directory ?? session.path ?? process.cwd();
12245
- const outputDir = join10(piSessionsRoot, projectPathToPiDirSlug(cwd));
12353
+ const outputDir = join9(piSessionsRoot, projectPathToPiDirSlug(cwd));
12246
12354
  const buildResult = buildPiEntries({
12247
12355
  session,
12248
12356
  messages,
@@ -12268,7 +12376,7 @@ function migrateOpenCodeSessionToPi(opts) {
12268
12376
  });
12269
12377
  }
12270
12378
  const compactionMarker = insertCompactionMarker(buildResult.entries, copyResult.lastCompartmentEndPiEntryId);
12271
- const outputPath = join10(outputDir, `${formatPiFilenameTimestamp(now)}_${buildResult.piSessionId}.jsonl`);
12379
+ const outputPath = join9(outputDir, `${formatPiFilenameTimestamp(now)}_${buildResult.piSessionId}.jsonl`);
12272
12380
  const jsonl = `${buildResult.entries.map((entry) => JSON.stringify(entry)).join(`
12273
12381
  `)}
12274
12382
  `;
@@ -13326,7 +13434,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
13326
13434
  throw e2;
13327
13435
  }
13328
13436
  return result.value;
13329
- }, parse, _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
13437
+ }, parse2, _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
13330
13438
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
13331
13439
  let result = schema._zod.run({ value, issues: [] }, ctx);
13332
13440
  if (result instanceof Promise)
@@ -13381,7 +13489,7 @@ var init_parse = __esm(() => {
13381
13489
  init_core();
13382
13490
  init_errors();
13383
13491
  init_util();
13384
- parse = /* @__PURE__ */ _parse($ZodRealError);
13492
+ parse2 = /* @__PURE__ */ _parse($ZodRealError);
13385
13493
  parseAsync = /* @__PURE__ */ _parseAsync($ZodRealError);
13386
13494
  safeParse = /* @__PURE__ */ _safeParse($ZodRealError);
13387
13495
  safeParseAsync = /* @__PURE__ */ _safeParseAsync($ZodRealError);
@@ -16140,10 +16248,10 @@ var init_schemas = __esm(() => {
16140
16248
  throw new Error("implement() must be called with a function");
16141
16249
  }
16142
16250
  return function(...args) {
16143
- const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;
16251
+ const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
16144
16252
  const result = Reflect.apply(func, this, parsedArgs);
16145
16253
  if (inst._def.output) {
16146
- return parse(inst._def.output, result);
16254
+ return parse2(inst._def.output, result);
16147
16255
  }
16148
16256
  return result;
16149
16257
  };
@@ -23628,7 +23736,7 @@ function finalize(ctx, schema) {
23628
23736
  result.$schema = "http://json-schema.org/draft-07/schema#";
23629
23737
  } else if (ctx.target === "draft-04") {
23630
23738
  result.$schema = "http://json-schema.org/draft-04/schema#";
23631
- } else if (ctx.target === "openapi-3.0") {} else {}
23739
+ } else if (ctx.target === "openapi-3.0") {}
23632
23740
  if (ctx.external?.uri) {
23633
23741
  const id = ctx.external.registry.get(schema)?.id;
23634
23742
  if (!id)
@@ -23889,7 +23997,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
23889
23997
  if (val === undefined) {
23890
23998
  if (ctx.unrepresentable === "throw") {
23891
23999
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
23892
- } else {}
24000
+ }
23893
24001
  } else if (typeof val === "bigint") {
23894
24002
  if (ctx.unrepresentable === "throw") {
23895
24003
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -24336,7 +24444,7 @@ __export(exports_core2, {
24336
24444
  process: () => process2,
24337
24445
  prettifyError: () => prettifyError,
24338
24446
  parseAsync: () => parseAsync,
24339
- parse: () => parse,
24447
+ parse: () => parse2,
24340
24448
  meta: () => meta,
24341
24449
  locales: () => exports_locales,
24342
24450
  isValidJWT: () => isValidJWT,
@@ -24737,11 +24845,11 @@ var init_errors2 = __esm(() => {
24737
24845
  });
24738
24846
 
24739
24847
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/parse.js
24740
- var parse3, parseAsync2, safeParse2, safeParseAsync2, encode2, decode2, encodeAsync2, decodeAsync2, safeEncode2, safeDecode2, safeEncodeAsync2, safeDecodeAsync2;
24848
+ var parse4, parseAsync2, safeParse2, safeParseAsync2, encode2, decode2, encodeAsync2, decodeAsync2, safeEncode2, safeDecode2, safeEncodeAsync2, safeDecodeAsync2;
24741
24849
  var init_parse2 = __esm(() => {
24742
24850
  init_core2();
24743
24851
  init_errors2();
24744
- parse3 = /* @__PURE__ */ _parse(ZodRealError);
24852
+ parse4 = /* @__PURE__ */ _parse(ZodRealError);
24745
24853
  parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
24746
24854
  safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
24747
24855
  safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -25461,7 +25569,7 @@ var init_schemas2 = __esm(() => {
25461
25569
  inst.def = def;
25462
25570
  inst.type = def.type;
25463
25571
  Object.defineProperty(inst, "_def", { value: def });
25464
- inst.parse = (data, params) => parse3(inst, data, params, { callee: inst.parse });
25572
+ inst.parse = (data, params) => parse4(inst, data, params, { callee: inst.parse });
25465
25573
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
25466
25574
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
25467
25575
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -26813,7 +26921,7 @@ __export(exports_external, {
26813
26921
  pipe: () => pipe,
26814
26922
  partialRecord: () => partialRecord,
26815
26923
  parseAsync: () => parseAsync2,
26816
- parse: () => parse3,
26924
+ parse: () => parse4,
26817
26925
  overwrite: () => _overwrite,
26818
26926
  optional: () => optional,
26819
26927
  object: () => object,
@@ -27236,7 +27344,7 @@ var init_magic_context = __esm(() => {
27236
27344
  });
27237
27345
 
27238
27346
  // ../plugin/src/config/variable.ts
27239
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "node:fs";
27347
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
27240
27348
  import { homedir as homedir7 } from "node:os";
27241
27349
  import { dirname as dirname7, isAbsolute, resolve as resolve2 } from "node:path";
27242
27350
  function substituteConfigVariables(input) {
@@ -27283,7 +27391,7 @@ function substituteConfigVariables(input) {
27283
27391
  }
27284
27392
  let contents;
27285
27393
  try {
27286
- contents = readFileSync6(filePath, "utf-8").trim();
27394
+ contents = readFileSync7(filePath, "utf-8").trim();
27287
27395
  } catch (error51) {
27288
27396
  const message = error51 instanceof Error ? error51.message : String(error51);
27289
27397
  warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
@@ -27301,22 +27409,22 @@ var init_variable = __esm(() => {
27301
27409
  });
27302
27410
 
27303
27411
  // ../plugin/src/config/index.ts
27304
- import { existsSync as existsSync12, readFileSync as readFileSync7 } from "node:fs";
27412
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
27305
27413
  import { homedir as homedir8 } from "node:os";
27306
- import { join as join11 } from "node:path";
27414
+ import { join as join10 } from "node:path";
27307
27415
  function getUserConfigBasePath() {
27308
- const configRoot = process.env.XDG_CONFIG_HOME ?? join11(homedir8(), ".config");
27309
- return join11(configRoot, "opencode", CONFIG_FILE_BASENAME);
27416
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join10(homedir8(), ".config");
27417
+ return join10(configRoot, "opencode", CONFIG_FILE_BASENAME);
27310
27418
  }
27311
27419
  function getProjectConfigBasePath(directory) {
27312
- return join11(directory, ".opencode", CONFIG_FILE_BASENAME);
27420
+ return join10(directory, ".opencode", CONFIG_FILE_BASENAME);
27313
27421
  }
27314
27422
  function loadConfigFile(configPath) {
27315
27423
  try {
27316
27424
  if (!existsSync12(configPath)) {
27317
27425
  return null;
27318
27426
  }
27319
- const rawText = readFileSync7(configPath, "utf-8");
27427
+ const rawText = readFileSync8(configPath, "utf-8");
27320
27428
  const substituted = substituteConfigVariables({ text: rawText, configPath });
27321
27429
  return {
27322
27430
  config: parseJsonc3(substituted.text),
@@ -27327,34 +27435,20 @@ function loadConfigFile(configPath) {
27327
27435
  return null;
27328
27436
  }
27329
27437
  }
27330
- function mergeConfigs(base, override, options = {}) {
27331
- const config2 = {
27332
- ...base,
27333
- ...override,
27334
- auto_update: options.allowUserOnlyFields ? override.auto_update : base.auto_update,
27335
- memory: {
27336
- ...base.memory ?? {},
27337
- ...override.memory ?? {}
27338
- },
27339
- embedding: override.embedding ?? base.embedding,
27340
- historian: override.historian ?? base.historian,
27341
- dreamer: override.dreamer ? {
27342
- ...base.dreamer ?? {},
27343
- ...override.dreamer
27344
- } : base.dreamer,
27345
- sidekick: override.sidekick ? {
27346
- ...base.sidekick ?? {},
27347
- ...override.sidekick
27348
- } : base.sidekick,
27349
- disabled_hooks: [
27350
- ...new Set([...base.disabled_hooks ?? [], ...override.disabled_hooks ?? []])
27351
- ],
27352
- command: {
27353
- ...base.command ?? {},
27354
- ...override.command ?? {}
27438
+ function deepMergeRawConfig(base, override) {
27439
+ const result = { ...base };
27440
+ for (const key of Object.keys(override)) {
27441
+ const baseVal = base[key];
27442
+ const overrideVal = override[key];
27443
+ if (baseVal !== null && typeof baseVal === "object" && !Array.isArray(baseVal) && overrideVal !== null && typeof overrideVal === "object" && !Array.isArray(overrideVal)) {
27444
+ result[key] = deepMergeRawConfig(baseVal, overrideVal);
27445
+ } else if (key === "disabled_hooks" && Array.isArray(baseVal) && Array.isArray(overrideVal)) {
27446
+ result[key] = [...new Set([...baseVal, ...overrideVal])];
27447
+ } else {
27448
+ result[key] = overrideVal;
27355
27449
  }
27356
- };
27357
- return config2;
27450
+ }
27451
+ return result;
27358
27452
  }
27359
27453
  function getProjectUserOnlyFields(config2) {
27360
27454
  return "auto_update" in config2 ? ["auto_update"] : [];
@@ -27490,35 +27584,43 @@ function parsePluginConfig(rawConfig) {
27490
27584
  }
27491
27585
  function loadPluginConfig(directory) {
27492
27586
  const userDetected = detectConfigFile(getUserConfigBasePath());
27493
- const rootDetected = detectConfigFile(join11(directory, CONFIG_FILE_BASENAME));
27587
+ const rootDetected = detectConfigFile(join10(directory, CONFIG_FILE_BASENAME));
27494
27588
  const dotOpenCodeDetected = detectConfigFile(getProjectConfigBasePath(directory));
27495
27589
  const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
27496
27590
  const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
27497
27591
  const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
27498
- let config2 = parsePluginConfig({});
27499
27592
  const allWarnings = [];
27593
+ let mergedRaw = {};
27500
27594
  if (userLoaded) {
27501
27595
  allWarnings.push(...userLoaded.warnings.map((w3) => `[user config] ${w3}`));
27502
- const parsed = parsePluginConfig(userLoaded.config);
27503
- if (parsed.configWarnings?.length) {
27504
- allWarnings.push(...parsed.configWarnings.map((w3) => `[user config] ${w3}`));
27505
- }
27506
- config2 = mergeConfigs(config2, parsed, { allowUserOnlyFields: true });
27596
+ mergedRaw = deepMergeRawConfig(mergedRaw, userLoaded.config);
27507
27597
  }
27508
27598
  if (projectLoaded) {
27509
27599
  allWarnings.push(...projectLoaded.warnings.map((w3) => `[project config] ${w3}`));
27510
- const parsed = parsePluginConfig(projectLoaded.config);
27511
- if (parsed.configWarnings?.length) {
27512
- allWarnings.push(...parsed.configWarnings.map((w3) => `[project config] ${w3}`));
27513
- }
27514
- const strippedUserOnlyFields = getProjectUserOnlyFields(projectLoaded.config);
27600
+ const projectRaw = { ...projectLoaded.config };
27601
+ const strippedUserOnlyFields = getProjectUserOnlyFields(projectRaw);
27515
27602
  if (strippedUserOnlyFields.length > 0) {
27603
+ for (const key of strippedUserOnlyFields) {
27604
+ delete projectRaw[key];
27605
+ }
27516
27606
  allWarnings.push(`[project config] Ignoring ${strippedUserOnlyFields.join(", ")} from project config (security: these settings only honor user-level config)`);
27517
27607
  }
27518
- config2 = mergeConfigs(config2, parsed);
27608
+ mergedRaw = deepMergeRawConfig(mergedRaw, projectRaw);
27609
+ }
27610
+ const config2 = parsePluginConfig(mergedRaw);
27611
+ if (config2.configWarnings?.length) {
27612
+ allWarnings.push(...config2.configWarnings.map((w3) => {
27613
+ if (userLoaded && projectLoaded)
27614
+ return `[config] ${w3}`;
27615
+ if (userLoaded)
27616
+ return `[user config] ${w3}`;
27617
+ return `[project config] ${w3}`;
27618
+ }));
27519
27619
  }
27520
27620
  if (allWarnings.length > 0) {
27521
27621
  config2.configWarnings = allWarnings;
27622
+ } else if ("configWarnings" in config2) {
27623
+ config2.configWarnings = undefined;
27522
27624
  }
27523
27625
  return config2;
27524
27626
  }
@@ -27529,6 +27631,78 @@ var init_config = __esm(() => {
27529
27631
  init_variable();
27530
27632
  });
27531
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
+
27532
27706
  // ../plugin/src/features/magic-context/memory/embedding-probe.ts
27533
27707
  async function probeEmbeddingEndpoint(options) {
27534
27708
  const endpoint = options.endpoint.trim().replace(/\/+$/, "");
@@ -27634,7 +27808,7 @@ function truncate(text2) {
27634
27808
  var DEFAULT_TIMEOUT_MS = 1e4, MAX_PREVIEW_CHARS = 240;
27635
27809
 
27636
27810
  // ../plugin/src/shared/tui-config.ts
27637
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
27811
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "node:fs";
27638
27812
  import { dirname as dirname8, join as join12 } from "node:path";
27639
27813
  function isMagicContextEntry(entry) {
27640
27814
  if (!entry)
@@ -27651,9 +27825,9 @@ function resolveTuiConfigPath() {
27651
27825
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
27652
27826
  const jsoncPath = join12(configDir, "tui.jsonc");
27653
27827
  const jsonPath = join12(configDir, "tui.json");
27654
- if (existsSync13(jsoncPath))
27828
+ if (existsSync14(jsoncPath))
27655
27829
  return jsoncPath;
27656
- if (existsSync13(jsonPath))
27830
+ if (existsSync14(jsonPath))
27657
27831
  return jsonPath;
27658
27832
  return jsonPath;
27659
27833
  }
@@ -27661,9 +27835,9 @@ function ensureTuiPluginEntry() {
27661
27835
  try {
27662
27836
  const configPath = resolveTuiConfigPath();
27663
27837
  let config2 = {};
27664
- if (existsSync13(configPath)) {
27665
- const raw = readFileSync8(configPath, "utf-8");
27666
- config2 = import_comment_json5.parse(raw) ?? {};
27838
+ if (existsSync14(configPath)) {
27839
+ const raw = readFileSync10(configPath, "utf-8");
27840
+ config2 = import_comment_json7.parse(raw) ?? {};
27667
27841
  }
27668
27842
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p2) => typeof p2 === "string") : [];
27669
27843
  const existingIdx = plugins.findIndex(isMagicContextEntry);
@@ -27682,7 +27856,7 @@ function ensureTuiPluginEntry() {
27682
27856
  }
27683
27857
  config2.plugin = plugins;
27684
27858
  mkdirSync6(dirname8(configPath), { recursive: true });
27685
- writeFileSync8(configPath, `${import_comment_json5.stringify(config2, null, 2)}
27859
+ writeFileSync8(configPath, `${import_comment_json7.stringify(config2, null, 2)}
27686
27860
  `);
27687
27861
  log2(`[magic-context] updated TUI plugin entry in ${configPath}`);
27688
27862
  return true;
@@ -27691,11 +27865,11 @@ function ensureTuiPluginEntry() {
27691
27865
  return false;
27692
27866
  }
27693
27867
  }
27694
- var import_comment_json5, PLUGIN_NAME4 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY3;
27868
+ var import_comment_json7, PLUGIN_NAME4 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY3;
27695
27869
  var init_tui_config = __esm(() => {
27696
27870
  init_logger();
27697
27871
  init_opencode_config_dir();
27698
- import_comment_json5 = __toESM(require_src2(), 1);
27872
+ import_comment_json7 = __toESM(require_src2(), 1);
27699
27873
  PLUGIN_ENTRY3 = `${PLUGIN_NAME4}@latest`;
27700
27874
  });
27701
27875
 
@@ -27749,10 +27923,125 @@ var init_compartment_parser = __esm(() => {
27749
27923
  USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
27750
27924
  });
27751
27925
 
27926
+ // src/lib/redaction.ts
27927
+ import { homedir as homedir10, userInfo } from "node:os";
27928
+ function escapeRegex2(value) {
27929
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27930
+ }
27931
+ function redactionTypeForKey(key) {
27932
+ const normalized = key.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "_");
27933
+ const suffix = normalized.split(".").filter(Boolean).at(-1) ?? normalized;
27934
+ return suffix || "secret";
27935
+ }
27936
+ function isSecretKey(key) {
27937
+ return SECRET_KEY_PATTERN.test(key);
27938
+ }
27939
+ function sanitizePathString(value) {
27940
+ const home = homedir10();
27941
+ const username = userInfo().username;
27942
+ let sanitized = value;
27943
+ if (home) {
27944
+ sanitized = sanitized.replace(new RegExp(escapeRegex2(home), "g"), "~");
27945
+ }
27946
+ sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
27947
+ sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
27948
+ sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
27949
+ if (username) {
27950
+ sanitized = sanitized.replace(new RegExp(escapeRegex2(username), "g"), "<USER>");
27951
+ }
27952
+ return sanitized;
27953
+ }
27954
+ function redactSecretText(value) {
27955
+ let redacted = value;
27956
+ for (const { pattern, replacement } of SECRET_TEXT_PATTERNS) {
27957
+ if (typeof replacement === "string") {
27958
+ redacted = redacted.replace(pattern, replacement);
27959
+ } else {
27960
+ redacted = redacted.replace(pattern, replacement);
27961
+ }
27962
+ }
27963
+ return redacted;
27964
+ }
27965
+ function sanitizeDiagnosticText(value) {
27966
+ return redactSecretText(sanitizePathString(value));
27967
+ }
27968
+ function sanitizeConfigValue(value, keyPath = []) {
27969
+ const key = keyPath.at(-1) ?? "";
27970
+ if (key && isSecretKey(key)) {
27971
+ return `<REDACTED:${redactionTypeForKey(key)}>`;
27972
+ }
27973
+ if (typeof value === "string")
27974
+ return sanitizeDiagnosticText(value);
27975
+ if (Array.isArray(value)) {
27976
+ return value.map((entry, index) => sanitizeConfigValue(entry, [...keyPath, String(index)]));
27977
+ }
27978
+ if (value && typeof value === "object") {
27979
+ return Object.fromEntries(Object.entries(value).map(([entryKey, entry]) => [
27980
+ entryKey,
27981
+ sanitizeConfigValue(entry, [...keyPath, entryKey])
27982
+ ]));
27983
+ }
27984
+ return value;
27985
+ }
27986
+ var SECRET_KEY_PATTERN, SECRET_TEXT_PATTERNS;
27987
+ var init_redaction = __esm(() => {
27988
+ SECRET_KEY_PATTERN = /(?:key|token|secret|password|auth|bearer|credential)/i;
27989
+ SECRET_TEXT_PATTERNS = [
27990
+ {
27991
+ pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
27992
+ replacement: "<ANTHROPIC_API_KEY_REDACTED>"
27993
+ },
27994
+ {
27995
+ pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/g,
27996
+ replacement: "<OPENAI_API_KEY_REDACTED>"
27997
+ },
27998
+ {
27999
+ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
28000
+ replacement: "<GITHUB_PAT_REDACTED>"
28001
+ },
28002
+ {
28003
+ pattern: /\b(?:gh[opsu]|ghr)_[A-Za-z0-9]{30,}/g,
28004
+ replacement: "<GITHUB_TOKEN_REDACTED>"
28005
+ },
28006
+ {
28007
+ pattern: /\bhf_[A-Za-z0-9]{30,}/g,
28008
+ replacement: "<HUGGINGFACE_TOKEN_REDACTED>"
28009
+ },
28010
+ {
28011
+ pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
28012
+ replacement: "<AWS_ACCESS_KEY_ID_REDACTED>"
28013
+ },
28014
+ {
28015
+ pattern: /\bxox[abprsuvc]-[A-Za-z0-9-]{10,}/g,
28016
+ replacement: "<SLACK_TOKEN_REDACTED>"
28017
+ },
28018
+ {
28019
+ pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g,
28020
+ replacement: "<GOOGLE_API_KEY_REDACTED>"
28021
+ },
28022
+ {
28023
+ pattern: /\b(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]{8,})/gi,
28024
+ replacement: (_full, prefix) => `${prefix}<REDACTED:bearer>`
28025
+ },
28026
+ {
28027
+ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
28028
+ replacement: "<JWT_REDACTED>"
28029
+ },
28030
+ {
28031
+ pattern: /(["'])([^"']*(?:key|token|secret|password|auth|bearer|credential)[^"']*)\1(\s*:\s*)(["'])([^"']*)\4/gi,
28032
+ replacement: (_full, quote, key, separator, valueQuote) => `${quote}${key}${quote}${separator}${valueQuote}<REDACTED:${redactionTypeForKey(key)}>${valueQuote}`
28033
+ },
28034
+ {
28035
+ pattern: /\b([A-Za-z0-9_.-]*(?:key|token|secret|password|auth|bearer|credential)[A-Za-z0-9_.-]*)\s*=\s*([^\s'"`]+)/gi,
28036
+ replacement: (_full, key) => `${key}=<REDACTED:${redactionTypeForKey(key)}>`
28037
+ }
28038
+ ];
28039
+ });
28040
+
27752
28041
  // src/lib/diagnostics-opencode.ts
27753
- import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
28042
+ import { existsSync as existsSync15, readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync2 } from "node:fs";
27754
28043
  import { createRequire as createRequire3 } from "node:module";
27755
- import { homedir as homedir9, tmpdir as tmpdir3, userInfo } from "node:os";
28044
+ import { homedir as homedir11 } from "node:os";
27756
28045
  import { join as join13 } from "node:path";
27757
28046
  function getSelfVersion() {
27758
28047
  const require2 = createRequire3(import.meta.url);
@@ -27771,8 +28060,8 @@ function getPluginCacheInfo() {
27771
28060
  let cached2;
27772
28061
  try {
27773
28062
  const installedPkgPath = join13(path4, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
27774
- if (existsSync14(installedPkgPath)) {
27775
- const pkg = JSON.parse(readFileSync9(installedPkgPath, "utf-8"));
28063
+ if (existsSync15(installedPkgPath)) {
28064
+ const pkg = JSON.parse(readFileSync11(installedPkgPath, "utf-8"));
27776
28065
  cached2 = typeof pkg.version === "string" ? pkg.version : undefined;
27777
28066
  }
27778
28067
  } catch {
@@ -27781,50 +28070,28 @@ function getPluginCacheInfo() {
27781
28070
  return { path: path4, cached: cached2, latest: getSelfVersion() };
27782
28071
  }
27783
28072
  function getStorageDir() {
27784
- const dataHome = process.env.XDG_DATA_HOME || join13(homedir9(), ".local", "share");
28073
+ const dataHome = process.env.XDG_DATA_HOME || join13(homedir11(), ".local", "share");
27785
28074
  return join13(dataHome, "cortexkit", "magic-context");
27786
28075
  }
27787
28076
  function fileSize(path4) {
27788
28077
  try {
27789
- return existsSync14(path4) ? statSync2(path4).size : 0;
28078
+ return existsSync15(path4) ? statSync2(path4).size : 0;
27790
28079
  } catch {
27791
28080
  return 0;
27792
28081
  }
27793
28082
  }
27794
- function escapeRegex2(value) {
27795
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27796
- }
27797
28083
  function sanitizeString(value) {
27798
- const home = homedir9();
27799
- const username = userInfo().username;
27800
- let sanitized = value;
27801
- if (home) {
27802
- sanitized = sanitized.replace(new RegExp(escapeRegex2(home), "g"), "~");
27803
- }
27804
- sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
27805
- sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
27806
- sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
27807
- if (username) {
27808
- sanitized = sanitized.replace(new RegExp(escapeRegex2(username), "g"), "<USER>");
27809
- }
27810
- return sanitized;
28084
+ return sanitizePathString(value);
27811
28085
  }
27812
28086
  function sanitizeValue(value) {
27813
- if (typeof value === "string")
27814
- return sanitizeString(value);
27815
- if (Array.isArray(value))
27816
- return value.map(sanitizeValue);
27817
- if (value && typeof value === "object") {
27818
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, sanitizeValue(entry)]));
27819
- }
27820
- return value;
28087
+ return sanitizeConfigValue(value);
27821
28088
  }
27822
28089
  function readConfig2(path4) {
27823
- if (!existsSync14(path4))
28090
+ if (!existsSync15(path4))
27824
28091
  return { value: null };
27825
28092
  try {
27826
- const raw = readFileSync9(path4, "utf-8");
27827
- const value = import_comment_json6.parse(raw);
28093
+ const raw = readFileSync11(path4, "utf-8");
28094
+ const value = import_comment_json8.parse(raw);
27828
28095
  return { value };
27829
28096
  } catch (error51) {
27830
28097
  return { value: null, error: error51 instanceof Error ? error51.message : String(error51) };
@@ -27846,7 +28113,7 @@ function configHasPluginEntry(config2) {
27846
28113
  }
27847
28114
  function parseHistorianDumpMeta(path4) {
27848
28115
  try {
27849
- const xml = readFileSync9(path4, "utf-8");
28116
+ const xml = readFileSync11(path4, "utf-8");
27850
28117
  const parsed = parseCompartmentOutput(xml);
27851
28118
  const factCountByCategory = {};
27852
28119
  for (const fact of parsed.facts) {
@@ -27878,11 +28145,9 @@ function parseHistorianDumpMeta(path4) {
27878
28145
  return { error: error51 instanceof Error ? error51.message : String(error51) };
27879
28146
  }
27880
28147
  }
27881
- function collectHistorianDumps() {
27882
- const dir = join13(tmpdir3(), "magic-context-historian");
27883
- if (!existsSync14(dir)) {
27884
- return { dir, count: 0, recent: [] };
27885
- }
28148
+ function listDumpsInDir(dir, limit) {
28149
+ if (!existsSync15(dir))
28150
+ return { count: 0, recent: [] };
27886
28151
  try {
27887
28152
  const entries = readdirSync2(dir).filter((name) => name.endsWith(".xml")).map((name) => {
27888
28153
  const stat = statSync2(join13(dir, name));
@@ -27893,7 +28158,7 @@ function collectHistorianDumps() {
27893
28158
  };
27894
28159
  }).sort((a, b2) => b2.mtime - a.mtime);
27895
28160
  const now = Date.now();
27896
- const recent = entries.slice(0, 5).map((entry) => {
28161
+ const recent = entries.slice(0, limit).map((entry) => {
27897
28162
  const meta3 = parseHistorianDumpMeta(join13(dir, entry.name));
27898
28163
  const summary = {
27899
28164
  name: entry.name,
@@ -27907,14 +28172,85 @@ function collectHistorianDumps() {
27907
28172
  }
27908
28173
  return summary;
27909
28174
  });
27910
- return { dir, count: entries.length, recent };
28175
+ return { count: entries.length, recent };
28176
+ } catch {
28177
+ return { count: 0, recent: [] };
28178
+ }
28179
+ }
28180
+ function collectHistorianDumps(recentSessions) {
28181
+ const buckets = new Map;
28182
+ for (const session of recentSessions) {
28183
+ const dir = session.directory;
28184
+ if (!dir)
28185
+ continue;
28186
+ const projectHistorianDir = getProjectMagicContextHistorianDir(dir);
28187
+ const listing = listDumpsInDir(projectHistorianDir, 5);
28188
+ const existing = buckets.get(dir);
28189
+ if (existing) {
28190
+ if (!existing.sessionIds.includes(session.sessionId)) {
28191
+ existing.sessionIds.push(session.sessionId);
28192
+ }
28193
+ continue;
28194
+ }
28195
+ if (listing.count === 0)
28196
+ continue;
28197
+ buckets.set(dir, {
28198
+ directory: dir,
28199
+ primarySessionId: session.sessionId,
28200
+ sessionIds: [session.sessionId],
28201
+ count: listing.count,
28202
+ recent: listing.recent
28203
+ });
28204
+ }
28205
+ const legacyDir = getMagicContextHistorianDir2("opencode");
28206
+ const legacyListing = listDumpsInDir(legacyDir, 5);
28207
+ return {
28208
+ byProject: [...buckets.values()],
28209
+ legacyDumps: {
28210
+ dir: legacyDir,
28211
+ count: legacyListing.count,
28212
+ recent: legacyListing.recent
28213
+ }
28214
+ };
28215
+ }
28216
+ async function collectRecentSessions() {
28217
+ const opencodeDbPath = join13(homedir11(), ".local", "share", "opencode", "opencode.db");
28218
+ if (!existsSync15(opencodeDbPath))
28219
+ return [];
28220
+ if (typeof globalThis.Bun === "undefined") {
28221
+ return [];
28222
+ }
28223
+ let DatabaseClass;
28224
+ try {
28225
+ const mod = await new Function("p", "return import(p)")("bun:sqlite");
28226
+ DatabaseClass = mod.Database;
28227
+ } catch {
28228
+ return [];
28229
+ }
28230
+ let db = null;
28231
+ try {
28232
+ db = new DatabaseClass(opencodeDbPath, { readonly: true });
28233
+ const rows = db.prepare("SELECT id, directory, title, time_updated FROM session " + "WHERE time_archived IS NULL AND parent_id IS NULL " + "ORDER BY time_updated DESC LIMIT 5").all();
28234
+ return rows.flatMap((row) => {
28235
+ const sessionId = typeof row.id === "string" ? row.id : null;
28236
+ const directory = typeof row.directory === "string" ? row.directory : null;
28237
+ if (!sessionId || !directory)
28238
+ return [];
28239
+ const title = typeof row.title === "string" ? row.title : "";
28240
+ const lastActiveAt = typeof row.time_updated === "number" ? new Date(row.time_updated).toISOString() : "";
28241
+ return [{ sessionId, title, directory, lastActiveAt }];
28242
+ });
27911
28243
  } catch {
27912
- return { dir, count: 0, recent: [] };
28244
+ return [];
28245
+ } finally {
28246
+ try {
28247
+ db?.close();
28248
+ } catch {}
27913
28249
  }
27914
28250
  }
27915
28251
  async function collectHistorianFailures(storageDirPath) {
27916
28252
  const contextDbPath = join13(storageDirPath, "context.db");
27917
- if (!existsSync14(contextDbPath))
28253
+ if (!existsSync15(contextDbPath))
27918
28254
  return [];
27919
28255
  if (typeof globalThis.Bun === "undefined") {
27920
28256
  return [];
@@ -27935,7 +28271,7 @@ async function collectHistorianFailures(storageDirPath) {
27935
28271
  const failureCount = typeof row.historian_failure_count === "number" ? row.historian_failure_count : 0;
27936
28272
  const rawError = typeof row.historian_last_error === "string" ? row.historian_last_error : "";
27937
28273
  const lastAt = typeof row.historian_last_failure_at === "number" ? new Date(row.historian_last_failure_at).toISOString() : "";
27938
- const lastError = sanitizeString(rawError.replace(/\s+/g, " ").trim().slice(0, 400));
28274
+ const lastError = sanitizeDiagnosticText(rawError.replace(/\s+/g, " ").trim().slice(0, 400));
27939
28275
  return { sessionId, failureCount, lastError, lastFailureAt: lastAt };
27940
28276
  });
27941
28277
  } catch {
@@ -27954,9 +28290,10 @@ async function collectDiagnostics() {
27954
28290
  const magicContextConfig = readConfig2(configPaths.magicContextConfig);
27955
28291
  const storageDirPath = getStorageDir();
27956
28292
  const contextDbPath = join13(storageDirPath, "context.db");
27957
- const logPath = join13(tmpdir3(), "magic-context.log");
27958
- const logFileSize = existsSync14(logPath) ? statSync2(logPath).size : 0;
28293
+ const logPath = getMagicContextLogPath2("opencode");
28294
+ const logFileSize = existsSync15(logPath) ? statSync2(logPath).size : 0;
27959
28295
  const conflictResult = detectConflicts(process.cwd());
28296
+ const recentSessions = await collectRecentSessions();
27960
28297
  return {
27961
28298
  timestamp: new Date().toISOString(),
27962
28299
  platform: process.platform,
@@ -27969,14 +28306,15 @@ async function collectDiagnostics() {
27969
28306
  opencodeConfigHasPlugin: configHasPluginEntry(opencodeConfig.value),
27970
28307
  tuiConfigHasPlugin: configHasPluginEntry(tuiConfig.value),
27971
28308
  magicContextConfig: {
27972
- exists: existsSync14(configPaths.magicContextConfig),
28309
+ exists: existsSync15(configPaths.magicContextConfig),
27973
28310
  ...magicContextConfig.error ? { parseError: magicContextConfig.error } : {},
27974
28311
  flags: sanitizeValue(magicContextConfig.value ?? {}) ?? {}
27975
28312
  },
28313
+ aft: getAftAvailability(),
27976
28314
  pluginCache: getPluginCacheInfo(),
27977
28315
  storageDir: {
27978
28316
  path: storageDirPath,
27979
- exists: existsSync14(storageDirPath),
28317
+ exists: existsSync15(storageDirPath),
27980
28318
  contextDbSizeBytes: fileSize(contextDbPath)
27981
28319
  },
27982
28320
  conflicts: {
@@ -27985,10 +28323,11 @@ async function collectDiagnostics() {
27985
28323
  },
27986
28324
  logFile: {
27987
28325
  path: logPath,
27988
- exists: existsSync14(logPath),
28326
+ exists: existsSync15(logPath),
27989
28327
  sizeKb: Math.round(logFileSize / 1024)
27990
28328
  },
27991
- historianDumps: collectHistorianDumps(),
28329
+ recentSessions,
28330
+ historianDumps: collectHistorianDumps(recentSessions),
27992
28331
  historianFailures: await collectHistorianFailures(storageDirPath)
27993
28332
  };
27994
28333
  }
@@ -28022,10 +28361,25 @@ function renderDiagnosticsMarkdown(report) {
28022
28361
  context_db_size: formatBytes(report.storageDir.contextDbSizeBytes)
28023
28362
  };
28024
28363
  const historianDumps = {
28025
- dir: sanitizeString(report.historianDumps.dir),
28026
- count: report.historianDumps.count,
28027
- recent: report.historianDumps.recent
28364
+ byProject: report.historianDumps.byProject.map((bucket) => ({
28365
+ directory: sanitizeString(bucket.directory),
28366
+ primarySessionId: bucket.primarySessionId,
28367
+ sessionIds: bucket.sessionIds,
28368
+ count: bucket.count,
28369
+ recent: bucket.recent
28370
+ })),
28371
+ legacyDumps: {
28372
+ dir: sanitizeString(report.historianDumps.legacyDumps.dir),
28373
+ count: report.historianDumps.legacyDumps.count,
28374
+ recent: report.historianDumps.legacyDumps.recent
28375
+ }
28028
28376
  };
28377
+ const recentSessions = report.recentSessions.map((session) => ({
28378
+ sessionId: session.sessionId,
28379
+ title: session.title,
28380
+ directory: sanitizeString(session.directory),
28381
+ lastActiveAt: session.lastActiveAt
28382
+ }));
28029
28383
  return [
28030
28384
  `- Timestamp: ${report.timestamp}`,
28031
28385
  `- Plugin: v${report.pluginVersion}`,
@@ -28035,6 +28389,7 @@ function renderDiagnosticsMarkdown(report) {
28035
28389
  `- Plugin registered in opencode config: ${report.opencodeConfigHasPlugin}`,
28036
28390
  `- Plugin registered in tui config: ${report.tuiConfigHasPlugin}`,
28037
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})`,
28038
28393
  `- Conflicts detected: ${report.conflicts.hasConflict ? report.conflicts.reasons.join("; ") : "none"}`,
28039
28394
  "",
28040
28395
  "### Config paths",
@@ -28044,7 +28399,7 @@ function renderDiagnosticsMarkdown(report) {
28044
28399
  "",
28045
28400
  "### magic-context.jsonc flags",
28046
28401
  "```jsonc",
28047
- JSON.stringify(report.magicContextConfig.flags, null, 2),
28402
+ JSON.stringify(sanitizeConfigValue(report.magicContextConfig.flags), null, 2),
28048
28403
  "```",
28049
28404
  "",
28050
28405
  "### Plugin cache",
@@ -28056,15 +28411,24 @@ function renderDiagnosticsMarkdown(report) {
28056
28411
  "```json",
28057
28412
  JSON.stringify(storage, null, 2),
28058
28413
  "```",
28414
+ "",
28415
+ "### Recent sessions",
28416
+ recentSessions.length === 0 ? "_No recent OpenCode sessions found (or OpenCode DB unavailable on this runtime)._" : ["```json", JSON.stringify(recentSessions, null, 2), "```"].join(`
28417
+ `),
28059
28418
  "",
28060
28419
  "### Historian dumps",
28061
28420
  "(Metadata only — XML content is not included in this report.)",
28421
+ "Dumps are stored per-project under `<project>/.opencode/magic-context/historian/`.",
28062
28422
  "```json",
28063
28423
  JSON.stringify(historianDumps, null, 2),
28064
28424
  "```",
28065
28425
  "",
28066
28426
  "### Historian failures (session_meta)",
28067
- report.historianFailures.length === 0 ? "_No sessions with historian failures._" : ["```json", JSON.stringify(report.historianFailures, null, 2), "```"].join(`
28427
+ report.historianFailures.length === 0 ? "_No sessions with historian failures._" : [
28428
+ "```json",
28429
+ JSON.stringify(sanitizeConfigValue(report.historianFailures), null, 2),
28430
+ "```"
28431
+ ].join(`
28068
28432
  `),
28069
28433
  "",
28070
28434
  "### Log file",
@@ -28074,45 +28438,25 @@ function renderDiagnosticsMarkdown(report) {
28074
28438
  ].join(`
28075
28439
  `);
28076
28440
  }
28077
- var import_comment_json6, 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;
28078
28442
  var init_diagnostics_opencode = __esm(() => {
28443
+ init_aft_availability();
28079
28444
  init_compartment_parser();
28080
28445
  init_conflict_detector();
28081
28446
  init_data_path();
28082
28447
  init_opencode_helpers();
28083
28448
  init_paths();
28084
- import_comment_json6 = __toESM(require_src2(), 1);
28449
+ init_redaction();
28450
+ import_comment_json8 = __toESM(require_src2(), 1);
28085
28451
  PLUGIN_ENTRY_WITH_VERSION = `${PLUGIN_NAME5}@latest`;
28086
28452
  });
28087
28453
 
28088
28454
  // src/lib/logs-opencode.ts
28089
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
28090
- import { homedir as homedir10, userInfo as userInfo2 } from "node:os";
28455
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
28456
+ import { homedir as homedir12 } from "node:os";
28091
28457
  import { join as join14 } from "node:path";
28092
- function escapeRegex3(value) {
28093
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28094
- }
28095
28458
  function sanitizeLogContent(content) {
28096
- const home = homedir10();
28097
- const username = userInfo2().username;
28098
- let sanitized = content;
28099
- if (home) {
28100
- sanitized = sanitized.replace(new RegExp(escapeRegex3(home), "g"), "~");
28101
- }
28102
- sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
28103
- sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
28104
- sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
28105
- if (username) {
28106
- sanitized = sanitized.replace(new RegExp(escapeRegex3(username), "g"), "<USER>");
28107
- }
28108
- for (const { pattern, replacement } of SECRET_PATTERNS) {
28109
- if (typeof replacement === "string") {
28110
- sanitized = sanitized.replace(pattern, replacement);
28111
- } else {
28112
- sanitized = sanitized.replace(pattern, replacement);
28113
- }
28114
- }
28115
- return sanitized;
28459
+ return sanitizeDiagnosticText(content);
28116
28460
  }
28117
28461
  function formatTimestamp(date5) {
28118
28462
  const pad = (value) => String(value).padStart(2, "0");
@@ -28139,16 +28483,28 @@ function extractHistorianFailureLines(sanitized, limit = 30) {
28139
28483
  }
28140
28484
  return matches.reverse();
28141
28485
  }
28142
- async function bundleIssueReport(report, description, _title) {
28486
+ function filterLogLinesBySession(lines, sessionId) {
28487
+ if (!sessionId)
28488
+ return lines;
28489
+ const otherSessionPattern = /\bses_[A-Za-z0-9]{8,32}\b/g;
28490
+ return lines.filter((line) => {
28491
+ const matches = line.match(otherSessionPattern);
28492
+ if (!matches)
28493
+ return true;
28494
+ return matches.every((id) => id === sessionId);
28495
+ });
28496
+ }
28497
+ async function bundleIssueReport(report, description, _title, sessionFilter = null) {
28143
28498
  const LOG_TAIL_LINES = 400;
28144
- const logLines = report.logFile.exists ? readFileSync10(report.logFile.path, "utf-8").split(/\r?\n/) : [];
28499
+ const allLogLines = report.logFile.exists ? readFileSync12(report.logFile.path, "utf-8").split(/\r?\n/) : [];
28500
+ const logLines = filterLogLinesBySession(allLogLines, sessionFilter);
28145
28501
  const recentLog = sanitizeLogContent(logLines.slice(-LOG_TAIL_LINES).join(`
28146
28502
  `)).trim();
28147
28503
  const historianScanWindow = sanitizeLogContent(logLines.slice(-4000).join(`
28148
28504
  `));
28149
28505
  const historianFailureLines = extractHistorianFailureLines(historianScanWindow, 30);
28150
- const configBody = JSON.stringify(report.magicContextConfig.flags, null, 2);
28151
- const sanitizedConfigPath = report.configPaths.magicContextConfig.replace(homedir10(), "~");
28506
+ const configBody = JSON.stringify(sanitizeConfigValue(report.magicContextConfig.flags), null, 2);
28507
+ const sanitizedConfigPath = report.configPaths.magicContextConfig.replace(homedir12(), "~");
28152
28508
  const bodyMarkdown = [
28153
28509
  "## Description",
28154
28510
  description,
@@ -28184,76 +28540,10 @@ async function bundleIssueReport(report, description, _title) {
28184
28540
  `);
28185
28541
  return { path: path4, bodyMarkdown };
28186
28542
  }
28187
- var SECRET_PATTERNS, HISTORIAN_LOG_PATTERNS;
28543
+ var HISTORIAN_LOG_PATTERNS;
28188
28544
  var init_logs_opencode = __esm(() => {
28189
28545
  init_diagnostics_opencode();
28190
- SECRET_PATTERNS = [
28191
- {
28192
- name: "anthropic_api_key",
28193
- pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
28194
- replacement: "<ANTHROPIC_API_KEY_REDACTED>"
28195
- },
28196
- {
28197
- name: "openai_api_key",
28198
- pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/g,
28199
- replacement: "<OPENAI_API_KEY_REDACTED>"
28200
- },
28201
- {
28202
- name: "github_pat_fine_grained",
28203
- pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
28204
- replacement: "<GITHUB_PAT_REDACTED>"
28205
- },
28206
- {
28207
- name: "github_token_classic",
28208
- pattern: /\b(?:gh[opsu]|ghr)_[A-Za-z0-9]{30,}/g,
28209
- replacement: "<GITHUB_TOKEN_REDACTED>"
28210
- },
28211
- {
28212
- name: "huggingface_token",
28213
- pattern: /\bhf_[A-Za-z0-9]{30,}/g,
28214
- replacement: "<HUGGINGFACE_TOKEN_REDACTED>"
28215
- },
28216
- {
28217
- name: "aws_access_key_id",
28218
- pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
28219
- replacement: "<AWS_ACCESS_KEY_ID_REDACTED>"
28220
- },
28221
- {
28222
- name: "aws_secret_access_key",
28223
- pattern: /\b(aws[_-]?secret[_-]?access[_-]?key\s*[=:]\s*)([A-Za-z0-9/+=]{40})\b/gi,
28224
- replacement: (_full, prefix) => `${prefix}<AWS_SECRET_REDACTED>`
28225
- },
28226
- {
28227
- name: "slack_token",
28228
- pattern: /\bxox[abprsuvc]-[A-Za-z0-9-]{10,}/g,
28229
- replacement: "<SLACK_TOKEN_REDACTED>"
28230
- },
28231
- {
28232
- name: "google_api_key",
28233
- pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g,
28234
- replacement: "<GOOGLE_API_KEY_REDACTED>"
28235
- },
28236
- {
28237
- name: "secret_env_assignment",
28238
- pattern: /\b([A-Z][A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|PRIVATE[_-]?KEY))\s*=\s*([^\s'"]+)/g,
28239
- replacement: (_full, key) => `${key}=<REDACTED>`
28240
- },
28241
- {
28242
- name: "secret_json_assignment",
28243
- pattern: /("(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer[_-]?token|client[_-]?secret|password|private[_-]?key|secret[_-]?key)"\s*:\s*)"([^"]+)"/gi,
28244
- replacement: (_full, prefix) => `${prefix}"<REDACTED>"`
28245
- },
28246
- {
28247
- name: "bearer_token",
28248
- pattern: /\b(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]{16,})/gi,
28249
- replacement: (_full, prefix) => `${prefix}<BEARER_TOKEN_REDACTED>`
28250
- },
28251
- {
28252
- name: "jwt_token",
28253
- pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
28254
- replacement: "<JWT_REDACTED>"
28255
- }
28256
- ];
28546
+ init_redaction();
28257
28547
  HISTORIAN_LOG_PATTERNS = [
28258
28548
  /historian failure:/,
28259
28549
  /historian failure recorded:/,
@@ -28267,9 +28557,9 @@ var init_logs_opencode = __esm(() => {
28267
28557
 
28268
28558
  // src/commands/doctor-opencode.ts
28269
28559
  import { execSync as execSync2, spawnSync as spawnSync2 } from "node:child_process";
28270
- import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync11, 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";
28271
28561
  import { createRequire as createRequire4 } from "node:module";
28272
- import { homedir as homedir11, tmpdir as tmpdir4 } from "node:os";
28562
+ import { homedir as homedir13 } from "node:os";
28273
28563
  import { join as join15 } from "node:path";
28274
28564
  async function fetchNpmLatest(pkg, timeoutMs = 5000) {
28275
28565
  try {
@@ -28321,14 +28611,14 @@ function compareVersions(a, b2) {
28321
28611
  async function clearPluginCache(force = false) {
28322
28612
  const cacheDir = getOpenCodeCacheDir();
28323
28613
  const pluginCacheDir = join15(cacheDir, "packages", PLUGIN_ENTRY_WITH_VERSION2);
28324
- if (!existsSync15(pluginCacheDir)) {
28614
+ if (!existsSync16(pluginCacheDir)) {
28325
28615
  return { action: "not_found", path: pluginCacheDir };
28326
28616
  }
28327
28617
  let cachedVersion;
28328
28618
  try {
28329
28619
  const installedPkgPath = join15(pluginCacheDir, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
28330
- if (existsSync15(installedPkgPath)) {
28331
- const pkg = JSON.parse(readFileSync11(installedPkgPath, "utf-8"));
28620
+ if (existsSync16(installedPkgPath)) {
28621
+ const pkg = JSON.parse(readFileSync13(installedPkgPath, "utf-8"));
28332
28622
  if (typeof pkg?.version === "string") {
28333
28623
  cachedVersion = pkg.version;
28334
28624
  }
@@ -28403,7 +28693,27 @@ async function runIssueFlow() {
28403
28693
  s.start("Collecting diagnostics");
28404
28694
  try {
28405
28695
  const report = await collectDiagnostics();
28406
- const bundled = await bundleIssueReport(report, description, title);
28696
+ s.stop("Diagnostics collected");
28697
+ let sessionFilter = null;
28698
+ if (report.recentSessions.length > 1) {
28699
+ const choice = await selectOne("Which session is this issue about? (filters log lines from other sessions)", [
28700
+ ...report.recentSessions.map((session, index) => {
28701
+ const displayTitle = session.title.trim() || "(no title)";
28702
+ const truncatedTitle = displayTitle.length > 50 ? `${displayTitle.slice(0, 47)}...` : displayTitle;
28703
+ return {
28704
+ label: `${truncatedTitle} — ${session.sessionId}${index === 0 ? " (most recent)" : ""}`,
28705
+ value: session.sessionId
28706
+ };
28707
+ }),
28708
+ {
28709
+ label: "All sessions (no filtering)",
28710
+ value: "__all__"
28711
+ }
28712
+ ]);
28713
+ sessionFilter = choice === "__all__" ? null : choice;
28714
+ }
28715
+ s.start("Bundling issue report");
28716
+ const bundled = await bundleIssueReport(report, description, title, sessionFilter);
28407
28717
  s.stop(`Report written to ${bundled.path}`);
28408
28718
  const shouldSubmit = await confirm("Submit this issue on GitHub now?", true);
28409
28719
  if (shouldSubmit && isGhInstalled()) {
@@ -28440,12 +28750,12 @@ async function runIssueFlow() {
28440
28750
  }
28441
28751
  }
28442
28752
  async function checkEmbeddingConfig(magicContextConfigPath) {
28443
- if (!existsSync15(magicContextConfigPath)) {
28753
+ if (!existsSync16(magicContextConfigPath)) {
28444
28754
  return { issues: 0 };
28445
28755
  }
28446
28756
  let rawText;
28447
28757
  try {
28448
- rawText = readFileSync11(magicContextConfigPath, "utf-8");
28758
+ rawText = readFileSync13(magicContextConfigPath, "utf-8");
28449
28759
  } catch {
28450
28760
  log.warn("Could not read magic-context.jsonc for embedding check");
28451
28761
  return { issues: 1 };
@@ -28456,7 +28766,7 @@ async function checkEmbeddingConfig(magicContextConfigPath) {
28456
28766
  });
28457
28767
  let parsedConfig;
28458
28768
  try {
28459
- parsedConfig = import_comment_json7.parse(substituted.text);
28769
+ parsedConfig = import_comment_json9.parse(substituted.text);
28460
28770
  } catch (error51) {
28461
28771
  log.warn(`Embedding check skipped — could not parse magic-context.jsonc: ${error51 instanceof Error ? error51.message : String(error51)}`);
28462
28772
  return { issues: 1 };
@@ -28593,15 +28903,15 @@ async function runDoctor(options = {}) {
28593
28903
  } else {
28594
28904
  pass(`OpenCode config: ${paths.opencodeConfig}`);
28595
28905
  }
28596
- if (existsSync15(paths.magicContextConfig)) {
28906
+ if (existsSync16(paths.magicContextConfig)) {
28597
28907
  pass(`Magic Context config: ${paths.magicContextConfig}`);
28598
28908
  try {
28599
- const raw = readFileSync11(paths.magicContextConfig, "utf-8");
28909
+ const raw = readFileSync13(paths.magicContextConfig, "utf-8");
28600
28910
  const substituted = substituteConfigVariables({
28601
28911
  text: raw,
28602
28912
  configPath: paths.magicContextConfig
28603
28913
  }).text;
28604
- import_comment_json7.parse(substituted);
28914
+ import_comment_json9.parse(substituted);
28605
28915
  pass("magic-context.jsonc parses as valid JSONC");
28606
28916
  } catch (err) {
28607
28917
  fail(`magic-context.jsonc parse failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -28621,10 +28931,10 @@ async function runDoctor(options = {}) {
28621
28931
  warn(`No magic-context.jsonc found — using defaults`);
28622
28932
  log.info(" Run 'setup' to create one with model recommendations");
28623
28933
  }
28624
- if (existsSync15(paths.magicContextConfig)) {
28934
+ if (existsSync16(paths.magicContextConfig)) {
28625
28935
  try {
28626
- const mcRaw = readFileSync11(paths.magicContextConfig, "utf-8");
28627
- const mcConfig = import_comment_json7.parse(mcRaw);
28936
+ const mcRaw = readFileSync13(paths.magicContextConfig, "utf-8");
28937
+ const mcConfig = import_comment_json9.parse(mcRaw);
28628
28938
  let mcChanged = false;
28629
28939
  const experimental = mcConfig.experimental;
28630
28940
  if (experimental && "compaction_markers" in experimental) {
@@ -28699,7 +29009,7 @@ async function runDoctor(options = {}) {
28699
29009
  fixed++;
28700
29010
  }
28701
29011
  if (mcChanged) {
28702
- writeFileSync10(paths.magicContextConfig, `${import_comment_json7.stringify(mcConfig, null, 2)}
29012
+ writeFileSync10(paths.magicContextConfig, `${import_comment_json9.stringify(mcConfig, null, 2)}
28703
29013
  `);
28704
29014
  }
28705
29015
  } catch {
@@ -28708,8 +29018,8 @@ async function runDoctor(options = {}) {
28708
29018
  }
28709
29019
  if (paths.opencodeConfigFormat !== "none") {
28710
29020
  try {
28711
- const raw = readFileSync11(paths.opencodeConfig, "utf-8");
28712
- const config2 = import_comment_json7.parse(raw);
29021
+ const raw = readFileSync13(paths.opencodeConfig, "utf-8");
29022
+ const config2 = import_comment_json9.parse(raw);
28713
29023
  const rawPlugins = Array.isArray(config2?.plugin) ? config2.plugin : [];
28714
29024
  const existingIdx = rawPlugins.findIndex((entry) => matchesPluginEntry(entry, PLUGIN_NAME6) || isDevPathPluginEntry(entry));
28715
29025
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
@@ -28740,7 +29050,7 @@ async function runDoctor(options = {}) {
28740
29050
  rawPlugins[existingIdx] = PLUGIN_ENTRY_WITH_VERSION2;
28741
29051
  }
28742
29052
  config2.plugin = rawPlugins;
28743
- writeFileSync10(paths.opencodeConfig, `${import_comment_json7.stringify(config2, null, 2)}
29053
+ writeFileSync10(paths.opencodeConfig, `${import_comment_json9.stringify(config2, null, 2)}
28744
29054
  `);
28745
29055
  pass(`Upgraded plugin entry in ${configName}: ${oldEntryStr} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
28746
29056
  fixed++;
@@ -28749,7 +29059,7 @@ async function runDoctor(options = {}) {
28749
29059
  } else {
28750
29060
  rawPlugins.push(PLUGIN_ENTRY_WITH_VERSION2);
28751
29061
  config2.plugin = rawPlugins;
28752
- writeFileSync10(paths.opencodeConfig, `${import_comment_json7.stringify(config2, null, 2)}
29062
+ writeFileSync10(paths.opencodeConfig, `${import_comment_json9.stringify(config2, null, 2)}
28753
29063
  `);
28754
29064
  pass(`Added plugin to ${configName}`);
28755
29065
  fixed++;
@@ -28780,10 +29090,10 @@ async function runDoctor(options = {}) {
28780
29090
  pass("Added TUI sidebar plugin to tui.json");
28781
29091
  warn("Restart OpenCode to see the sidebar");
28782
29092
  fixed++;
28783
- } else if (existsSync15(paths.tuiConfig)) {
29093
+ } else if (existsSync16(paths.tuiConfig)) {
28784
29094
  try {
28785
- const tuiRaw = readFileSync11(paths.tuiConfig, "utf-8");
28786
- const tuiConfig = import_comment_json7.parse(tuiRaw);
29095
+ const tuiRaw = readFileSync13(paths.tuiConfig, "utf-8");
29096
+ const tuiConfig = import_comment_json9.parse(tuiRaw);
28787
29097
  const tuiRawPlugins = Array.isArray(tuiConfig?.plugin) ? tuiConfig.plugin : [];
28788
29098
  const tuiIdx = tuiRawPlugins.findIndex((entry) => matchesPluginEntry(entry, PLUGIN_NAME6) || isDevPathPluginEntry(entry));
28789
29099
  const tuiEntryAsString = (entry) => {
@@ -28811,7 +29121,7 @@ async function runDoctor(options = {}) {
28811
29121
  tuiRawPlugins[tuiIdx] = PLUGIN_ENTRY_WITH_VERSION2;
28812
29122
  }
28813
29123
  tuiConfig.plugin = tuiRawPlugins;
28814
- writeFileSync10(paths.tuiConfig, `${import_comment_json7.stringify(tuiConfig, null, 2)}
29124
+ writeFileSync10(paths.tuiConfig, `${import_comment_json9.stringify(tuiConfig, null, 2)}
28815
29125
  `);
28816
29126
  pass(`Upgraded TUI plugin: ${tuiEntryStr} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
28817
29127
  fixed++;
@@ -28828,10 +29138,10 @@ async function runDoctor(options = {}) {
28828
29138
  } else {
28829
29139
  pass("TUI sidebar plugin configured (tui.json created)");
28830
29140
  }
28831
- if (existsSync15(paths.magicContextConfig)) {
29141
+ if (existsSync16(paths.magicContextConfig)) {
28832
29142
  try {
28833
- const mcRaw = readFileSync11(paths.magicContextConfig, "utf-8");
28834
- const mcConfig = import_comment_json7.parse(mcRaw);
29143
+ const mcRaw = readFileSync13(paths.magicContextConfig, "utf-8");
29144
+ const mcConfig = import_comment_json9.parse(mcRaw);
28835
29145
  const dreamerObj = mcConfig?.dreamer;
28836
29146
  const dreamerEnabled = dreamerObj?.enabled === true;
28837
29147
  const userMemObj = dreamerObj?.user_memories;
@@ -28840,6 +29150,15 @@ async function runDoctor(options = {}) {
28840
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.");
28841
29151
  issues++;
28842
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
+ }
28843
29162
  } catch {}
28844
29163
  }
28845
29164
  const embeddingCheck = await checkEmbeddingConfig(paths.magicContextConfig);
@@ -28849,7 +29168,7 @@ async function runDoctor(options = {}) {
28849
29168
  else
28850
29169
  passCount++;
28851
29170
  const dbPath = join15(getMagicContextStorageDir(), "context.db");
28852
- if (!existsSync15(dbPath)) {
29171
+ if (!existsSync16(dbPath)) {
28853
29172
  log.info(`Shared context DB not yet created at ${dbPath} (will be created on first run)`);
28854
29173
  } else {
28855
29174
  pass(`Shared context DB exists at ${dbPath}`);
@@ -28910,10 +29229,10 @@ async function runDoctor(options = {}) {
28910
29229
  }
28911
29230
  {
28912
29231
  const ageWarnings = [];
28913
- const npmrcPath = join15(homedir11(), ".npmrc");
28914
- if (existsSync15(npmrcPath)) {
29232
+ const npmrcPath = join15(homedir13(), ".npmrc");
29233
+ if (existsSync16(npmrcPath)) {
28915
29234
  try {
28916
- const npmrc = readFileSync11(npmrcPath, "utf-8");
29235
+ const npmrc = readFileSync13(npmrcPath, "utf-8");
28917
29236
  for (const line of npmrc.split(`
28918
29237
  `)) {
28919
29238
  const trimmed = line.trim();
@@ -28936,33 +29255,35 @@ async function runDoctor(options = {}) {
28936
29255
  issues++;
28937
29256
  }
28938
29257
  }
28939
- const logPath = join15(tmpdir4(), "magic-context.log");
28940
- if (existsSync15(logPath)) {
29258
+ const logPath = getMagicContextLogPath2("opencode");
29259
+ if (existsSync16(logPath)) {
28941
29260
  const logStat = statSync3(logPath);
28942
29261
  const sizeKb = (logStat.size / 1024).toFixed(0);
28943
29262
  log.info(`Log file: ${logPath} (${sizeKb} KB)`);
28944
29263
  } else {
28945
29264
  log.info(`Log file: ${logPath} (not yet created)`);
28946
29265
  }
28947
- const historianDumpDir = join15(tmpdir4(), "magic-context-historian");
28948
- if (existsSync15(historianDumpDir)) {
28949
- try {
28950
- const dumps = readdirSync3(historianDumpDir).filter((f) => f.endsWith(".xml")).map((f) => ({
28951
- name: f,
28952
- mtime: statSync3(join15(historianDumpDir, f)).mtimeMs
28953
- })).sort((a, b2) => b2.mtime - a.mtime);
28954
- if (dumps.length > 0) {
28955
- warn(`Historian debug dumps: ${dumps.length} file(s) in ${historianDumpDir}`);
28956
- for (const dump of dumps.slice(0, 3)) {
28957
- const age = Math.round((Date.now() - dump.mtime) / 60000);
28958
- const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
28959
- log.info(` ${dump.name} (${ageStr})`);
28960
- }
28961
- if (dumps.length > 3) {
28962
- log.info(` ... and ${dumps.length - 3} more`);
28963
- }
29266
+ const diagnostics = await collectDiagnostics();
29267
+ const dumpBuckets = diagnostics.historianDumps.byProject;
29268
+ if (dumpBuckets.length > 0) {
29269
+ const totalCount = dumpBuckets.reduce((sum, b2) => sum + b2.count, 0);
29270
+ const sessionCount = dumpBuckets.length;
29271
+ warn(`Historian debug dumps: ${totalCount} file(s) across ${sessionCount} project(s)`);
29272
+ for (const bucket of dumpBuckets) {
29273
+ log.info(` [${bucket.directory}] ${bucket.count} file(s)`);
29274
+ for (const dump of bucket.recent.slice(0, 3)) {
29275
+ const age = dump.ageMinutes;
29276
+ const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
29277
+ log.info(` ${dump.name} (${ageStr})`);
28964
29278
  }
28965
- } catch {}
29279
+ if (bucket.count > 3) {
29280
+ log.info(` ... and ${bucket.count - 3} more`);
29281
+ }
29282
+ }
29283
+ }
29284
+ const legacy = diagnostics.historianDumps.legacyDumps;
29285
+ if (legacy.count > 0) {
29286
+ log.info(`Legacy historian dumps (pre-v0.18.x): ${legacy.count} file(s) in ${legacy.dir}`);
28966
29287
  }
28967
29288
  if (paths.omoConfig) {
28968
29289
  log.info(`OMO config found: ${paths.omoConfig}`);
@@ -28981,10 +29302,11 @@ async function runDoctor(options = {}) {
28981
29302
  }
28982
29303
  return 0;
28983
29304
  }
28984
- var import_comment_json7, 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";
28985
29306
  var init_doctor_opencode = __esm(async () => {
28986
29307
  init_config();
28987
29308
  init_variable();
29309
+ init_aft_availability();
28988
29310
  init_conflict_detector();
28989
29311
  init_conflict_fixer();
28990
29312
  init_data_path();
@@ -28996,9 +29318,10 @@ var init_doctor_opencode = __esm(async () => {
28996
29318
  init_paths();
28997
29319
  init_prompts();
28998
29320
  await init_sqlite();
28999
- import_comment_json7 = __toESM(require_src2(), 1);
29321
+ import_comment_json9 = __toESM(require_src2(), 1);
29000
29322
  PLUGIN_ENTRY_WITH_VERSION2 = `${PLUGIN_NAME6}@latest`;
29001
29323
  });
29324
+
29002
29325
  // ../plugin/src/features/magic-context/compartment-storage.ts
29003
29326
  var insertCompartmentStatements, insertFactStatements;
29004
29327
  var init_compartment_storage = __esm(() => {
@@ -29138,7 +29461,7 @@ function binarySearchBytes(sortedArray, key) {
29138
29461
  }
29139
29462
  return -1;
29140
29463
  }
29141
- function escapeRegex4(str) {
29464
+ function escapeRegex3(str) {
29142
29465
  return str.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
29143
29466
  }
29144
29467
  function bytePairMerge(piece, stringRanks, binaryFirstByteIndex) {
@@ -29248,7 +29571,7 @@ var Tokenizer = class {
29248
29571
  };
29249
29572
  this.specialTokenKeys = Object.keys(this.specialTokens);
29250
29573
  this.hasSpecialTokens = this.specialTokenKeys.length > 0;
29251
- this.specialTokensRegex = this.hasSpecialTokens ? new RegExp(this.specialTokenKeys.map(escapeRegex4).join("|"), "g") : null;
29574
+ this.specialTokensRegex = this.hasSpecialTokens ? new RegExp(this.specialTokenKeys.map(escapeRegex3).join("|"), "g") : null;
29252
29575
  this.inverseSpecialTokens = {};
29253
29576
  for (const [text2, rank] of Object.entries(this.specialTokens))
29254
29577
  this.inverseSpecialTokens[rank] = textEncoder.encode(text2);
@@ -29278,7 +29601,7 @@ var Tokenizer = class {
29278
29601
  const allowedSet = new Set(allowedSpecial === "all" ? this.specialTokenKeys : allowedSpecial);
29279
29602
  const disallowedSet = new Set(disallowedSpecial === "all" ? this.specialTokenKeys.filter((x2) => !allowedSet.has(x2)) : disallowedSpecial);
29280
29603
  if (disallowedSet.size > 0) {
29281
- const disallowedRegex = new RegExp([...disallowedSet].map(escapeRegex4).join("|"), "g");
29604
+ const disallowedRegex = new RegExp([...disallowedSet].map(escapeRegex3).join("|"), "g");
29282
29605
  const match = text2.match(disallowedRegex);
29283
29606
  if (match)
29284
29607
  throw new Error(`Text contains disallowed special token: ${match[0]}`);
@@ -161582,6 +161905,34 @@ function getErrorMessage(error51) {
161582
161905
  return error51 instanceof Error ? error51.message : String(error51);
161583
161906
  }
161584
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
+
161585
161936
  // ../plugin/src/features/magic-context/migrations.ts
161586
161937
  function ensureMigrationsTable(db) {
161587
161938
  db.exec(`
@@ -161784,6 +162135,9 @@ var init_migrations = __esm(async () => {
161784
162135
  embedding BLOB NOT NULL,
161785
162136
  model_id TEXT NOT NULL,
161786
162137
  created_at INTEGER NOT NULL,
162138
+ -- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
162139
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
162140
+ -- every connection and v12 cleans historical orphan rows.
161787
162141
  FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
161788
162142
  );
161789
162143
 
@@ -161923,10 +162277,72 @@ var init_migrations = __esm(async () => {
161923
162277
  db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
161924
162278
  }
161925
162279
  }
162280
+ },
162281
+ {
162282
+ version: 12,
162283
+ description: "Clean orphan rows from FK-cascade embedding tables",
162284
+ up: (db) => {
162285
+ const hasTable = (name2) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
162286
+ const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
162287
+ WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
162288
+ log2(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
162289
+ const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
162290
+ WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
162291
+ log2(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
162292
+ }
162293
+ },
162294
+ {
162295
+ version: 13,
162296
+ description: "Add pending_compaction_marker_state column for deferred marker drain",
162297
+ up: (db) => {
162298
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
162299
+ if (!cols.some((c2) => c2.name === "pending_compaction_marker_state")) {
162300
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
162301
+ }
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
+ }
161926
162343
  }
161927
162344
  ];
161928
162345
  });
161929
-
161930
162346
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
161931
162347
  function keyFor(providerID, modelID, agentName) {
161932
162348
  const agent = agentName && agentName.length > 0 ? agentName : "default";
@@ -161961,7 +162377,7 @@ var init_tool_definition_tokens = __esm(() => {
161961
162377
  });
161962
162378
 
161963
162379
  // ../plugin/src/features/magic-context/tool-owner-backfill.ts
161964
- import { existsSync as existsSync16 } from "node:fs";
162380
+ import { existsSync as existsSync18 } from "node:fs";
161965
162381
  import { join as join16 } from "node:path";
161966
162382
  function resolveOpencodeDbPath() {
161967
162383
  return join16(getDataDir(), "opencode", "opencode.db");
@@ -161999,7 +162415,7 @@ function runToolOwnerBackfill(db) {
161999
162415
  return result;
162000
162416
  }
162001
162417
  const opencodeDbPath = resolveOpencodeDbPath();
162002
- if (!existsSync16(opencodeDbPath)) {
162418
+ if (!existsSync18(opencodeDbPath)) {
162003
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.`);
162004
162420
  markAllUnbackfilledSessionsSkipped(db);
162005
162421
  result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
@@ -162209,25 +162625,25 @@ var init_tool_owner_backfill = __esm(() => {
162209
162625
  });
162210
162626
 
162211
162627
  // ../plugin/src/features/magic-context/storage-db.ts
162212
- import { copyFileSync, cpSync, existsSync as existsSync17, mkdirSync as mkdirSync7 } from "node:fs";
162628
+ import { copyFileSync, cpSync, existsSync as existsSync19, mkdirSync as mkdirSync7 } from "node:fs";
162213
162629
  import { join as join17 } from "node:path";
162214
162630
  function resolveDatabasePath() {
162215
162631
  const dbDir = getMagicContextStorageDir();
162216
162632
  return { dbDir, dbPath: join17(dbDir, "context.db") };
162217
162633
  }
162218
162634
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
162219
- if (existsSync17(targetDbPath))
162635
+ if (existsSync19(targetDbPath))
162220
162636
  return;
162221
162637
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
162222
162638
  const legacyDbPath = join17(legacyDir, "context.db");
162223
- if (!existsSync17(legacyDbPath))
162639
+ if (!existsSync19(legacyDbPath))
162224
162640
  return;
162225
162641
  log2(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
162226
162642
  mkdirSync7(targetDbDir, { recursive: true });
162227
162643
  for (const suffix of ["", "-wal", "-shm"]) {
162228
162644
  const src = `${legacyDbPath}${suffix}`;
162229
162645
  const dst = join17(targetDbDir, `context.db${suffix}`);
162230
- if (existsSync17(src)) {
162646
+ if (existsSync19(src)) {
162231
162647
  try {
162232
162648
  copyFileSync(src, dst);
162233
162649
  } catch (error51) {
@@ -162237,7 +162653,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
162237
162653
  }
162238
162654
  const legacyModelsDir = join17(legacyDir, "models");
162239
162655
  const targetModelsDir = join17(targetDbDir, "models");
162240
- if (existsSync17(legacyModelsDir) && !existsSync17(targetModelsDir)) {
162656
+ if (existsSync19(legacyModelsDir) && !existsSync19(targetModelsDir)) {
162241
162657
  try {
162242
162658
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
162243
162659
  } catch (error51) {
@@ -162246,9 +162662,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
162246
162662
  }
162247
162663
  }
162248
162664
  function initializeDatabase(db) {
162665
+ db.exec("PRAGMA foreign_keys=ON");
162249
162666
  db.exec("PRAGMA journal_mode=WAL");
162250
162667
  db.exec("PRAGMA busy_timeout=5000");
162251
- db.exec("PRAGMA foreign_keys=ON");
162252
162668
  db.exec(`
162253
162669
  CREATE TABLE IF NOT EXISTS tags (
162254
162670
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -162346,6 +162762,9 @@ function initializeDatabase(db) {
162346
162762
  );
162347
162763
 
162348
162764
  CREATE TABLE IF NOT EXISTS memory_embeddings (
162765
+ -- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
162766
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
162767
+ -- every connection and v12 cleans historical orphan rows.
162349
162768
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
162350
162769
  embedding BLOB NOT NULL,
162351
162770
  model_id TEXT
@@ -162382,6 +162801,26 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162382
162801
  );
162383
162802
  CREATE INDEX IF NOT EXISTS idx_dream_runs_project ON dream_runs(project_path, finished_at DESC);
162384
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
+
162385
162824
  -- (smart_notes: see note above; merged into unified notes table by migration v1)
162386
162825
 
162387
162826
  CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
@@ -162453,7 +162892,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162453
162892
  system_prompt_hash TEXT DEFAULT '',
162454
162893
  memory_block_cache TEXT DEFAULT '',
162455
162894
  memory_block_count INTEGER DEFAULT 0,
162456
- memory_block_ids TEXT DEFAULT ''
162895
+ memory_block_ids TEXT DEFAULT '',
162896
+ -- pending_compaction_marker_state: intentionally NULLABLE without a
162897
+ -- default. Absence of a deferred marker is SQL NULL; presence is a
162898
+ -- valid JSON blob written via setPendingCompactionMarkerState.
162899
+ -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
162900
+ -- != empty-string defensively. Plan v6 section 3.
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
162457
162906
  );
162458
162907
 
162459
162908
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -162541,6 +162990,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162541
162990
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
162542
162991
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
162543
162992
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
162993
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
162994
+ ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
162544
162995
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
162545
162996
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
162546
162997
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -162617,7 +163068,15 @@ function ensureColumn(db, table, column, definition) {
162617
163068
  if (rows.some((row) => row.name === column)) {
162618
163069
  return;
162619
163070
  }
162620
- 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
+ }
162621
163080
  }
162622
163081
  function openDatabase() {
162623
163082
  const { dbDir, dbPath } = resolveDatabasePath();
@@ -162634,6 +163093,11 @@ function openDatabase() {
162634
163093
  const db = new Database(dbPath);
162635
163094
  initializeDatabase(db);
162636
163095
  runMigrations(db);
163096
+ try {
163097
+ deleteOrphanProjectKeyFiles(db);
163098
+ } catch (error51) {
163099
+ log2(`[magic-context] key-files orphan GC failed: ${getErrorMessage(error51)}`);
163100
+ }
162637
163101
  try {
162638
163102
  runToolOwnerBackfill(db);
162639
163103
  } catch (error51) {
@@ -162669,6 +163133,7 @@ var databases, persistenceByDatabase, persistenceErrorByDatabase;
162669
163133
  var init_storage_db = __esm(async () => {
162670
163134
  init_data_path();
162671
163135
  init_logger();
163136
+ init_project_key_files();
162672
163137
  init_tool_definition_tokens();
162673
163138
  init_tool_owner_backfill();
162674
163139
  await __promiseAll([
@@ -162772,24 +163237,24 @@ var init_storage = __esm(async () => {
162772
163237
  });
162773
163238
 
162774
163239
  // ../pi-plugin/src/config/index.ts
162775
- import { existsSync as existsSync18, readFileSync as readFileSync12 } from "node:fs";
162776
- import { homedir as homedir12 } from "node:os";
163240
+ import { existsSync as existsSync20, readFileSync as readFileSync15 } from "node:fs";
163241
+ import { homedir as homedir14 } from "node:os";
162777
163242
  import { join as join18 } from "node:path";
162778
163243
  function getProjectConfigPaths(cwd) {
162779
163244
  const basePath = join18(cwd, ".pi", CONFIG_FILE_NAME);
162780
163245
  return [`${basePath}.jsonc`, `${basePath}.json`];
162781
163246
  }
162782
163247
  function getUserConfigPaths() {
162783
- const home = process.env.HOME ?? homedir12();
163248
+ const home = process.env.HOME ?? homedir14();
162784
163249
  const basePath = join18(home, ".pi", "agent", CONFIG_FILE_NAME);
162785
163250
  return [`${basePath}.jsonc`, `${basePath}.json`];
162786
163251
  }
162787
163252
  function resolveFirstExisting(paths) {
162788
- return paths.find((path4) => existsSync18(path4));
163253
+ return paths.find((path4) => existsSync20(path4));
162789
163254
  }
162790
163255
  function loadConfigFile2(path4, scope) {
162791
163256
  try {
162792
- const rawText = readFileSync12(path4, "utf-8");
163257
+ const rawText = readFileSync15(path4, "utf-8");
162793
163258
  const substituted = substituteConfigVariables({
162794
163259
  text: rawText,
162795
163260
  configPath: path4
@@ -162797,7 +163262,7 @@ function loadConfigFile2(path4, scope) {
162797
163262
  return {
162798
163263
  path: path4,
162799
163264
  scope,
162800
- config: import_comment_json8.parse(substituted.text),
163265
+ config: import_comment_json10.parse(substituted.text),
162801
163266
  warnings: substituted.warnings.map((warning) => `${path4}: ${warning}`)
162802
163267
  };
162803
163268
  } catch (error51) {
@@ -162910,18 +163375,18 @@ function loadPiConfig(opts = {}) {
162910
163375
  loadedFromPaths: loadedFiles.map((loaded) => loaded.path)
162911
163376
  };
162912
163377
  }
162913
- var import_comment_json8, CONFIG_FILE_NAME = "magic-context";
163378
+ var import_comment_json10, CONFIG_FILE_NAME = "magic-context";
162914
163379
  var init_config2 = __esm(() => {
162915
163380
  init_magic_context();
162916
163381
  init_variable();
162917
- import_comment_json8 = __toESM(require_src2(), 1);
163382
+ import_comment_json10 = __toESM(require_src2(), 1);
162918
163383
  });
162919
163384
 
162920
163385
  // src/lib/diagnostics-pi.ts
162921
163386
  import { createHash } from "node:crypto";
162922
- import { existsSync as existsSync19, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
163387
+ import { existsSync as existsSync21, readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync4 } from "node:fs";
162923
163388
  import { createRequire as createRequire5 } from "node:module";
162924
- import { homedir as homedir13, tmpdir as tmpdir5, userInfo as userInfo3 } from "node:os";
163389
+ import { homedir as homedir15, userInfo as userInfo2 } from "node:os";
162925
163390
  import { join as join19 } from "node:path";
162926
163391
  function getSelfVersion3() {
162927
163392
  const req = createRequire5(import.meta.url);
@@ -162937,34 +163402,34 @@ function getSelfVersion3() {
162937
163402
  }
162938
163403
  function fileSize2(path4) {
162939
163404
  try {
162940
- return existsSync19(path4) ? statSync4(path4).size : 0;
163405
+ return existsSync21(path4) ? statSync4(path4).size : 0;
162941
163406
  } catch {
162942
163407
  return 0;
162943
163408
  }
162944
163409
  }
162945
- function escapeRegex5(value) {
163410
+ function escapeRegex4(value) {
162946
163411
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
162947
163412
  }
162948
163413
  function currentUserHash() {
162949
- const username = userInfo3().username || "unknown";
163414
+ const username = userInfo2().username || "unknown";
162950
163415
  return createHash("sha256").update(username).digest("hex").slice(0, 12);
162951
163416
  }
162952
163417
  function redactSecretString(value) {
162953
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>");
162954
163419
  }
162955
163420
  function sanitizeString2(value) {
162956
- const home = process.env.HOME || homedir13();
162957
- const username = userInfo3().username;
163421
+ const home = process.env.HOME || homedir15();
163422
+ const username = userInfo2().username;
162958
163423
  const userHash = `<USER:${currentUserHash()}>`;
162959
163424
  let sanitized = redactSecretString(value);
162960
163425
  if (home) {
162961
- sanitized = sanitized.replace(new RegExp(escapeRegex5(home), "g"), "<HOME>");
163426
+ sanitized = sanitized.replace(new RegExp(escapeRegex4(home), "g"), "<HOME>");
162962
163427
  }
162963
163428
  sanitized = sanitized.replace(/\/Users\/[^/]+\//g, `/Users/${userHash}/`);
162964
163429
  sanitized = sanitized.replace(/\/home\/[^/]+\//g, `/home/${userHash}/`);
162965
163430
  sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, `C:\\Users\\${userHash}\\`);
162966
163431
  if (username) {
162967
- sanitized = sanitized.replace(new RegExp(escapeRegex5(username), "g"), userHash);
163432
+ sanitized = sanitized.replace(new RegExp(escapeRegex4(username), "g"), userHash);
162968
163433
  }
162969
163434
  return sanitized;
162970
163435
  }
@@ -162987,11 +163452,11 @@ function sanitizeValue2(value, key = "") {
162987
163452
  return value;
162988
163453
  }
162989
163454
  function readJsonc3(path4) {
162990
- if (!existsSync19(path4))
163455
+ if (!existsSync21(path4))
162991
163456
  return { value: {} };
162992
163457
  try {
162993
163458
  return {
162994
- value: import_comment_json9.parse(readFileSync13(path4, "utf-8"))
163459
+ value: import_comment_json11.parse(readFileSync16(path4, "utf-8"))
162995
163460
  };
162996
163461
  } catch (error51) {
162997
163462
  return {
@@ -163007,16 +163472,159 @@ function readConfigDiagnostic(path4) {
163007
163472
  const parsed = readJsonc3(path4);
163008
163473
  return {
163009
163474
  path: path4,
163010
- exists: existsSync19(path4),
163475
+ exists: existsSync21(path4),
163011
163476
  ...parsed.parseError ? { parseError: parsed.parseError } : {},
163012
163477
  flags: sanitizeValue2(parsed.value)
163013
163478
  };
163014
163479
  }
163015
163480
  function packageEntries(settings) {
163016
- return Array.isArray(settings.packages) ? settings.packages.filter((entry) => typeof entry === "string") : [];
163481
+ return Array.isArray(settings.packages) ? settings.packages : [];
163482
+ }
163483
+ function reverseSlugToDirectory(slug) {
163484
+ if (!slug.startsWith("--") || !slug.endsWith("--"))
163485
+ return null;
163486
+ const inner = slug.slice(2, -2);
163487
+ if (!inner)
163488
+ return null;
163489
+ return `/${inner.replace(/-/g, "/")}`;
163490
+ }
163491
+ function collectPiRecentSessions() {
163492
+ const sessionsRoot = join19(homedir15(), ".pi", "agent", "sessions");
163493
+ if (!existsSync21(sessionsRoot))
163494
+ return [];
163495
+ try {
163496
+ const slugs = readdirSync3(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
163497
+ const candidates = [];
163498
+ for (const slug of slugs) {
163499
+ const directory = reverseSlugToDirectory(slug);
163500
+ if (!directory)
163501
+ continue;
163502
+ const slugDir = join19(sessionsRoot, slug);
163503
+ let files;
163504
+ try {
163505
+ files = readdirSync3(slugDir).filter((name2) => name2.endsWith(".jsonl"));
163506
+ } catch {
163507
+ continue;
163508
+ }
163509
+ for (const file2 of files) {
163510
+ try {
163511
+ const mtime = statSync4(join19(slugDir, file2)).mtimeMs;
163512
+ const sessionId = file2.replace(/\.jsonl$/, "");
163513
+ candidates.push({ sessionId, directory, mtime });
163514
+ } catch {}
163515
+ }
163516
+ }
163517
+ candidates.sort((a, b2) => b2.mtime - a.mtime);
163518
+ return candidates.slice(0, 5).map((entry) => ({
163519
+ sessionId: entry.sessionId,
163520
+ directory: entry.directory,
163521
+ lastActiveAt: new Date(entry.mtime).toISOString()
163522
+ }));
163523
+ } catch {
163524
+ return [];
163525
+ }
163526
+ }
163527
+ function parseHistorianDumpMeta2(path4) {
163528
+ try {
163529
+ const xml = readFileSync16(path4, "utf-8");
163530
+ const parsed = parseCompartmentOutput(xml);
163531
+ const factCountByCategory = {};
163532
+ for (const fact of parsed.facts) {
163533
+ factCountByCategory[fact.category] = (factCountByCategory[fact.category] ?? 0) + 1;
163534
+ }
163535
+ const starts = parsed.compartments.map((c2) => c2.startMessage);
163536
+ const ends = parsed.compartments.map((c2) => c2.endMessage);
163537
+ let gaps = 0;
163538
+ let overlaps = 0;
163539
+ for (let i = 1;i < parsed.compartments.length; i++) {
163540
+ const prev = parsed.compartments[i - 1];
163541
+ const curr = parsed.compartments[i];
163542
+ if (curr.startMessage > prev.endMessage + 1)
163543
+ gaps += 1;
163544
+ else if (curr.startMessage <= prev.endMessage)
163545
+ overlaps += 1;
163546
+ }
163547
+ return {
163548
+ compartmentCount: parsed.compartments.length,
163549
+ minStart: starts.length > 0 ? Math.min(...starts) : null,
163550
+ maxEnd: ends.length > 0 ? Math.max(...ends) : null,
163551
+ unprocessedFrom: parsed.unprocessedFrom,
163552
+ factCountByCategory,
163553
+ userObservationCount: parsed.userObservations.length,
163554
+ ordinalGapCount: gaps,
163555
+ ordinalOverlapCount: overlaps
163556
+ };
163557
+ } catch (error51) {
163558
+ return { error: error51 instanceof Error ? error51.message : String(error51) };
163559
+ }
163017
163560
  }
163018
- function hasMagicContextPackage(packages) {
163019
- return packages.some((entry) => entry === PI_PACKAGE_SOURCE || entry === PACKAGE_NAME || entry.includes("pi-magic-context"));
163561
+ function listDumpsInDir2(dir, limit) {
163562
+ if (!existsSync21(dir))
163563
+ return { count: 0, recent: [] };
163564
+ try {
163565
+ const entries = readdirSync3(dir).filter((name2) => name2.endsWith(".xml")).map((name2) => {
163566
+ const stat = statSync4(join19(dir, name2));
163567
+ return {
163568
+ name: name2,
163569
+ mtime: stat.mtimeMs,
163570
+ sizeKb: Math.round(stat.size / 1024)
163571
+ };
163572
+ }).sort((a, b2) => b2.mtime - a.mtime);
163573
+ const now = Date.now();
163574
+ const recent = entries.slice(0, limit).map((entry) => {
163575
+ const meta3 = parseHistorianDumpMeta2(join19(dir, entry.name));
163576
+ const summary = {
163577
+ name: entry.name,
163578
+ ageMinutes: Math.round((now - entry.mtime) / 60000),
163579
+ sizeKb: entry.sizeKb
163580
+ };
163581
+ if ("error" in meta3) {
163582
+ summary.parseError = meta3.error;
163583
+ } else {
163584
+ summary.meta = meta3;
163585
+ }
163586
+ return summary;
163587
+ });
163588
+ return { count: entries.length, recent };
163589
+ } catch {
163590
+ return { count: 0, recent: [] };
163591
+ }
163592
+ }
163593
+ function collectPiHistorianDumps(recentSessions) {
163594
+ const buckets = new Map;
163595
+ for (const session of recentSessions) {
163596
+ const dir = session.directory;
163597
+ if (!dir)
163598
+ continue;
163599
+ const projectHistorianDir = getProjectMagicContextHistorianDir(dir);
163600
+ const listing = listDumpsInDir2(projectHistorianDir, 5);
163601
+ const existing = buckets.get(dir);
163602
+ if (existing) {
163603
+ if (!existing.sessionIds.includes(session.sessionId)) {
163604
+ existing.sessionIds.push(session.sessionId);
163605
+ }
163606
+ continue;
163607
+ }
163608
+ if (listing.count === 0)
163609
+ continue;
163610
+ buckets.set(dir, {
163611
+ directory: dir,
163612
+ primarySessionId: session.sessionId,
163613
+ sessionIds: [session.sessionId],
163614
+ count: listing.count,
163615
+ recent: listing.recent
163616
+ });
163617
+ }
163618
+ const legacyDir = getMagicContextHistorianDir2("pi");
163619
+ const legacyListing = listDumpsInDir2(legacyDir, 5);
163620
+ return {
163621
+ byProject: [...buckets.values()],
163622
+ legacyDumps: {
163623
+ dir: legacyDir,
163624
+ count: legacyListing.count,
163625
+ recent: legacyListing.recent
163626
+ }
163627
+ };
163020
163628
  }
163021
163629
  async function collectDiagnostics2(cwd = process.cwd()) {
163022
163630
  const pi = detectPiBinary();
@@ -163028,9 +163636,11 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163028
163636
  const loaded = loadPiConfig({ cwd });
163029
163637
  const storageDirPath = getMagicContextStorageDir();
163030
163638
  const dbPath = join19(storageDirPath, "context.db");
163031
- const logPath = join19(tmpdir5(), "magic-context.log");
163032
- const logFileSize = existsSync19(logPath) ? statSync4(logPath).size : 0;
163033
- const otherPiExtensions = packages.filter((entry) => !hasMagicContextPackage([entry]));
163639
+ const logPath = getMagicContextLogPath2("pi");
163640
+ const logFileSize = existsSync21(logPath) ? statSync4(logPath).size : 0;
163641
+ const otherPiExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
163642
+ const recentSessions = collectPiRecentSessions();
163643
+ const historianDumps = collectPiHistorianDumps(recentSessions);
163034
163644
  return {
163035
163645
  timestamp: new Date().toISOString(),
163036
163646
  platform: process.platform,
@@ -163042,10 +163652,10 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163042
163652
  piVersion: pi ? getPiVersion(pi.path) : null,
163043
163653
  settings: {
163044
163654
  path: settingsPath,
163045
- exists: existsSync19(settingsPath),
163655
+ exists: existsSync21(settingsPath),
163046
163656
  ...settingsParsed.parseError ? { parseError: settingsParsed.parseError } : {},
163047
- hasMagicContextPackage: hasMagicContextPackage(packages),
163048
- packages: packages.map(sanitizeString2)
163657
+ hasMagicContextPackage: hasPiMagicContextPackage(packages),
163658
+ packages: sanitizeValue2(packages)
163049
163659
  },
163050
163660
  configPaths: {
163051
163661
  agentDir: getPiAgentConfigDir(),
@@ -163058,7 +163668,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163058
163668
  loadWarnings: loaded.warnings.map(sanitizeString2),
163059
163669
  storageDir: {
163060
163670
  path: storageDirPath,
163061
- exists: existsSync19(storageDirPath),
163671
+ exists: existsSync21(storageDirPath),
163062
163672
  contextDbSizeBytes: fileSize2(dbPath)
163063
163673
  },
163064
163674
  conflicts: {
@@ -163067,9 +163677,11 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163067
163677
  },
163068
163678
  logFile: {
163069
163679
  path: logPath,
163070
- exists: existsSync19(logPath),
163680
+ exists: existsSync21(logPath),
163071
163681
  sizeKb: Math.round(logFileSize / 1024)
163072
- }
163682
+ },
163683
+ recentSessions,
163684
+ historianDumps
163073
163685
  };
163074
163686
  }
163075
163687
  function formatBytes2(bytes) {
@@ -163146,17 +163758,19 @@ function renderDiagnosticsMarkdown2(report) {
163146
163758
  ].join(`
163147
163759
  `);
163148
163760
  }
163149
- var import_comment_json9, PACKAGE_NAME = "@cortexkit/pi-magic-context";
163761
+ var import_comment_json11;
163150
163762
  var init_diagnostics_pi = __esm(() => {
163763
+ init_compartment_parser();
163151
163764
  init_data_path();
163152
163765
  init_config2();
163153
163766
  init_paths();
163154
163767
  init_pi_helpers();
163155
- import_comment_json9 = __toESM(require_src2(), 1);
163768
+ init_pi_package_entry();
163769
+ import_comment_json11 = __toESM(require_src2(), 1);
163156
163770
  });
163157
163771
 
163158
163772
  // src/lib/logs-pi.ts
163159
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
163773
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
163160
163774
  import { join as join20 } from "node:path";
163161
163775
  function sanitizeLogContent2(content) {
163162
163776
  return sanitizeString2(content);
@@ -163173,9 +163787,21 @@ function formatTimestamp2(date5) {
163173
163787
  pad(date5.getSeconds())
163174
163788
  ].join("");
163175
163789
  }
163790
+ function filterLogLinesBySession2(lines, sessionId) {
163791
+ if (!sessionId)
163792
+ return lines;
163793
+ const otherSessionPattern = /\bses_[A-Za-z0-9]{8,32}\b/g;
163794
+ return lines.filter((line) => {
163795
+ const matches = line.match(otherSessionPattern);
163796
+ if (!matches)
163797
+ return true;
163798
+ return matches.every((id) => id === sessionId);
163799
+ });
163800
+ }
163176
163801
  async function bundleIssueReport2(report, description, title, options = {}) {
163177
163802
  const LOG_TAIL_LINES = 400;
163178
- const logLines = report.logFile.exists ? readFileSync14(report.logFile.path, "utf-8").split(/\r?\n/) : [];
163803
+ const allLogLines = report.logFile.exists ? readFileSync17(report.logFile.path, "utf-8").split(/\r?\n/) : [];
163804
+ const logLines = filterLogLinesBySession2(allLogLines, options.sessionFilter ?? null);
163179
163805
  const recentLog = sanitizeLogContent2(logLines.slice(-LOG_TAIL_LINES).join(`
163180
163806
  `)).trim();
163181
163807
  const bodyMarkdown = [
@@ -163213,16 +163839,16 @@ var init_logs_pi = __esm(() => {
163213
163839
  // src/commands/doctor-pi.ts
163214
163840
  import { execFileSync as execFileSync4, spawnSync as spawnSync3 } from "node:child_process";
163215
163841
  import {
163216
- existsSync as existsSync20,
163842
+ existsSync as existsSync22,
163217
163843
  mkdirSync as mkdirSync8,
163218
163844
  readdirSync as readdirSync4,
163219
- readFileSync as readFileSync15,
163845
+ readFileSync as readFileSync18,
163220
163846
  rmSync as rmSync2,
163221
163847
  statSync as statSync5,
163222
163848
  writeFileSync as writeFileSync12
163223
163849
  } from "node:fs";
163224
163850
  import { createRequire as createRequire6 } from "node:module";
163225
- import { homedir as homedir14, tmpdir as tmpdir6 } from "node:os";
163851
+ import { homedir as homedir16 } from "node:os";
163226
163852
  import { dirname as dirname9, join as join21 } from "node:path";
163227
163853
  function depsFrom(options) {
163228
163854
  return {
@@ -163310,7 +163936,7 @@ function summarize(results) {
163310
163936
  function readJsonc4(path4) {
163311
163937
  try {
163312
163938
  return {
163313
- value: import_comment_json10.parse(readFileSync15(path4, "utf-8"))
163939
+ value: import_comment_json12.parse(readFileSync18(path4, "utf-8"))
163314
163940
  };
163315
163941
  } catch (error51) {
163316
163942
  return {
@@ -163320,24 +163946,21 @@ function readJsonc4(path4) {
163320
163946
  }
163321
163947
  }
163322
163948
  function packagesFrom(settings) {
163323
- return Array.isArray(settings.packages) ? settings.packages.filter((entry) => typeof entry === "string") : [];
163324
- }
163325
- function hasPiPackage(packages) {
163326
- return packages.some((entry) => entry === PI_PACKAGE_SOURCE || entry === PACKAGE_NAME2 || entry.includes("pi-magic-context"));
163949
+ return Array.isArray(settings.packages) ? settings.packages : [];
163327
163950
  }
163328
163951
  function projectConfigPath(cwd) {
163329
163952
  return join21(cwd, ".pi", "magic-context.jsonc");
163330
163953
  }
163331
163954
  function readConfigForEmbedding(path4) {
163332
- if (!existsSync20(path4))
163955
+ if (!existsSync22(path4))
163333
163956
  return null;
163334
163957
  try {
163335
- const rawText = readFileSync15(path4, "utf-8");
163958
+ const rawText = readFileSync18(path4, "utf-8");
163336
163959
  const substituted = substituteConfigVariables({
163337
163960
  text: rawText,
163338
163961
  configPath: path4
163339
163962
  });
163340
- return import_comment_json10.parse(substituted.text);
163963
+ return import_comment_json12.parse(substituted.text);
163341
163964
  } catch {
163342
163965
  return null;
163343
163966
  }
@@ -163390,7 +164013,7 @@ function countTable(db, table) {
163390
164013
  }
163391
164014
  }
163392
164015
  function cacheRoots() {
163393
- const home = homedir14();
164016
+ const home = homedir16();
163394
164017
  const cacheHome = process.env.XDG_CACHE_HOME || join21(home, ".cache");
163395
164018
  return [
163396
164019
  join21(home, ".pi", "cache", "extensions"),
@@ -163403,7 +164026,7 @@ function cacheRoots() {
163403
164026
  function findPiMagicContextCacheDirs(self, force = false) {
163404
164027
  const found = new Map;
163405
164028
  const visit = (path4, depth) => {
163406
- if (depth < 0 || !existsSync20(path4))
164029
+ if (depth < 0 || !existsSync22(path4))
163407
164030
  return;
163408
164031
  let stat;
163409
164032
  try {
@@ -163414,12 +164037,12 @@ function findPiMagicContextCacheDirs(self, force = false) {
163414
164037
  if (!stat.isDirectory())
163415
164038
  return;
163416
164039
  const packageJson = join21(path4, "package.json");
163417
- if (existsSync20(packageJson)) {
164040
+ if (existsSync22(packageJson)) {
163418
164041
  try {
163419
- const pkg = JSON.parse(readFileSync15(packageJson, "utf-8"));
164042
+ const pkg = JSON.parse(readFileSync18(packageJson, "utf-8"));
163420
164043
  const name2 = typeof pkg.name === "string" ? pkg.name : "";
163421
164044
  const version2 = typeof pkg.version === "string" ? pkg.version : undefined;
163422
- if (name2 === PACKAGE_NAME2 || path4.includes("pi-magic-context")) {
164045
+ if (name2 === PACKAGE_NAME || path4.includes("pi-magic-context")) {
163423
164046
  if (force || !version2 || version2 !== self)
163424
164047
  found.set(path4, { path: path4, version: version2 });
163425
164048
  }
@@ -163457,7 +164080,7 @@ async function runHealthChecks(options) {
163457
164080
  add(results, "pass", version2 ? `Pi ${version2} detected at ${pi.path}` : `Pi detected at ${pi.path}`);
163458
164081
  const compare = compareSemver(version2, MIN_PI_VERSION);
163459
164082
  if (compare !== null && compare < 0) {
163460
- 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\`).`);
163461
164084
  } else if (version2) {
163462
164085
  add(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
163463
164086
  }
@@ -163472,7 +164095,7 @@ async function runHealthChecks(options) {
163472
164095
  }
163473
164096
  const settingsPath = getPiUserExtensionsPath();
163474
164097
  let packages = [];
163475
- if (!existsSync20(settingsPath)) {
164098
+ if (!existsSync22(settingsPath)) {
163476
164099
  add(results, "warn", `Pi settings not found at ${settingsPath}`);
163477
164100
  repairPlan.addPackageEntry = true;
163478
164101
  } else {
@@ -163482,7 +164105,7 @@ async function runHealthChecks(options) {
163482
164105
  } else {
163483
164106
  packages = packagesFrom(parsed.value);
163484
164107
  add(results, "pass", `Pi settings found at ${settingsPath}`);
163485
- if (hasPiPackage(packages)) {
164108
+ if (hasPiMagicContextPackage(packages)) {
163486
164109
  add(results, "pass", `${PI_PACKAGE_SOURCE} is registered in packages[]`);
163487
164110
  } else {
163488
164111
  add(results, "warn", `${PI_PACKAGE_SOURCE} is missing from packages[]`);
@@ -163496,7 +164119,7 @@ async function runHealthChecks(options) {
163496
164119
  ["user", userConfigPath, true],
163497
164120
  ["project", projectPath, false]
163498
164121
  ]) {
163499
- if (!existsSync20(path4)) {
164122
+ if (!existsSync22(path4)) {
163500
164123
  if (required2) {
163501
164124
  add(results, "warn", `No ${label} magic-context.jsonc found at ${path4}`);
163502
164125
  repairPlan.writeUserConfig = true;
@@ -163530,7 +164153,7 @@ async function runHealthChecks(options) {
163530
164153
  }
163531
164154
  const storageDir = getMagicContextStorageDir();
163532
164155
  const dbPath = join21(storageDir, "context.db");
163533
- const existedBeforeOpen = existsSync20(dbPath);
164156
+ const existedBeforeOpen = existsSync22(dbPath);
163534
164157
  if (existedBeforeOpen)
163535
164158
  add(results, "pass", `Shared context DB exists at ${dbPath}`);
163536
164159
  else
@@ -163587,13 +164210,13 @@ async function runHealthChecks(options) {
163587
164210
  } else {
163588
164211
  add(results, "pass", `Embedding provider: ${loadedConfig.config.embedding.provider}`);
163589
164212
  }
163590
- const piEntries = packages.filter((entry) => hasPiPackage([entry]));
164213
+ const piEntries = packages.filter(isPiMagicContextPackageEntry).map(describePiPackageEntry);
163591
164214
  if (piEntries.length > 1) {
163592
164215
  add(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
163593
164216
  } else {
163594
164217
  add(results, "pass", "No conflicting magic-context entries in Pi packages[]");
163595
164218
  }
163596
- const otherExtensions = packages.filter((entry) => !hasPiPackage([entry]));
164219
+ const otherExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
163597
164220
  if (otherExtensions.length > 0) {
163598
164221
  add(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
163599
164222
  } else {
@@ -163606,32 +164229,36 @@ async function runHealthChecks(options) {
163606
164229
  } else {
163607
164230
  add(results, "pass", "Pi extension cache clean (no stale cached package found)");
163608
164231
  }
163609
- const logPath = join21(tmpdir6(), "magic-context.log");
163610
- if (existsSync20(logPath)) {
164232
+ const logPath = getMagicContextLogPath2("pi");
164233
+ if (existsSync22(logPath)) {
163611
164234
  const stat = statSync5(logPath);
163612
164235
  const sizeKb = (stat.size / 1024).toFixed(0);
163613
- const lines = readFileSync15(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);
163614
164237
  add(results, "info", `Log file: ${logPath} (${sizeKb} KB)`);
163615
164238
  add(results, "info", `Last plugin log line: ${lines.at(-1) ?? "<empty log>"}`);
163616
164239
  } else {
163617
164240
  add(results, "info", `No plugin log file yet at ${logPath}`);
163618
164241
  }
163619
- const historianDumpDir = join21(tmpdir6(), "magic-context-historian");
163620
- if (existsSync20(historianDumpDir)) {
163621
- try {
163622
- const dumps = readdirSync4(historianDumpDir).filter((f) => f.endsWith(".xml")).map((f) => ({ name: f, mtime: statSync5(join21(historianDumpDir, f)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
163623
- if (dumps.length > 0) {
163624
- add(results, "warn", `Historian debug dumps: ${dumps.length} file(s) in ${historianDumpDir}`);
163625
- for (const dump of dumps.slice(0, 3)) {
163626
- const age = Math.round((Date.now() - dump.mtime) / 60000);
163627
- const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
163628
- add(results, "info", ` ${dump.name} (${ageStr})`);
163629
- }
163630
- if (dumps.length > 3) {
163631
- add(results, "info", ` ... and ${dumps.length - 3} more`);
163632
- }
164242
+ const diagnosticsForDumps = await collectDiagnostics2(options.cwd);
164243
+ const dumpBuckets = diagnosticsForDumps.historianDumps.byProject;
164244
+ if (dumpBuckets.length > 0) {
164245
+ const totalCount = dumpBuckets.reduce((sum, b2) => sum + b2.count, 0);
164246
+ add(results, "warn", `Historian debug dumps: ${totalCount} file(s) across ${dumpBuckets.length} project(s)`);
164247
+ for (const bucket of dumpBuckets) {
164248
+ add(results, "info", ` [${bucket.directory}] ${bucket.count} file(s)`);
164249
+ for (const dump of bucket.recent.slice(0, 3)) {
164250
+ const age = dump.ageMinutes;
164251
+ const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
164252
+ add(results, "info", ` ${dump.name} (${ageStr})`);
163633
164253
  }
163634
- } catch {}
164254
+ if (bucket.count > 3) {
164255
+ add(results, "info", ` ... and ${bucket.count - 3} more`);
164256
+ }
164257
+ }
164258
+ }
164259
+ const legacyDumps = diagnosticsForDumps.historianDumps.legacyDumps;
164260
+ if (legacyDumps.count > 0) {
164261
+ add(results, "info", `Legacy historian dumps (pre-v0.18.x): ${legacyDumps.count} file(s) in ${legacyDumps.dir}`);
163635
164262
  }
163636
164263
  if (!options.quiet) {
163637
164264
  for (const result of results)
@@ -163645,7 +164272,7 @@ function writeDefaultMagicContextConfig(path4) {
163645
164272
  $schema: "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json",
163646
164273
  ...MagicContextConfigSchema.parse({})
163647
164274
  };
163648
- writeFileSync12(path4, `${import_comment_json10.stringify(config2, null, 2)}
164275
+ writeFileSync12(path4, `${import_comment_json12.stringify(config2, null, 2)}
163649
164276
  `);
163650
164277
  }
163651
164278
  function repair(plan, prompts) {
@@ -163662,7 +164289,7 @@ function repair(plan, prompts) {
163662
164289
  }
163663
164290
  if (plan.writeUserConfig) {
163664
164291
  const configPath = getPiUserConfigPath();
163665
- if (!existsSync20(configPath)) {
164292
+ if (!existsSync22(configPath)) {
163666
164293
  try {
163667
164294
  writeDefaultMagicContextConfig(configPath);
163668
164295
  prompts.log.success(`Wrote default Magic Context config to ${configPath}`);
@@ -163710,9 +164337,26 @@ async function runIssueFlow2(options) {
163710
164337
  spinner2.start("Collecting sanitized Pi diagnostics");
163711
164338
  try {
163712
164339
  const report = await options.deps.collectDiagnostics(options.cwd);
164340
+ spinner2.stop("Diagnostics collected");
164341
+ let sessionFilter = null;
164342
+ if (report.recentSessions.length > 1) {
164343
+ const choice = await options.prompts.selectOne("Which Pi session is this issue about? (filters log lines from other sessions)", [
164344
+ ...report.recentSessions.map((session, index) => ({
164345
+ label: `${session.directory} — ${session.sessionId}${index === 0 ? " (most recent)" : ""}`,
164346
+ value: session.sessionId
164347
+ })),
164348
+ {
164349
+ label: "All sessions (no filtering)",
164350
+ value: "__all__"
164351
+ }
164352
+ ]);
164353
+ sessionFilter = choice === "__all__" ? null : choice;
164354
+ }
164355
+ spinner2.start("Bundling Pi issue report");
163713
164356
  const bundled = await bundleIssueReport2(report, description, title, {
163714
164357
  cwd: options.cwd,
163715
- now: options.deps.now()
164358
+ now: options.deps.now(),
164359
+ sessionFilter
163716
164360
  });
163717
164361
  spinner2.stop(`Report written to ${bundled.path}`);
163718
164362
  if (ghAvailableAndAuthed(options.deps)) {
@@ -163787,7 +164431,7 @@ function parseDoctorArgs(args) {
163787
164431
  function doctor(args = []) {
163788
164432
  return runDoctor2(parseDoctorArgs(args));
163789
164433
  }
163790
- var import_comment_json10, PACKAGE_NAME2 = "@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;
163791
164435
  var init_doctor_pi = __esm(async () => {
163792
164436
  init_magic_context();
163793
164437
  init_variable();
@@ -163797,17 +164441,18 @@ var init_doctor_pi = __esm(async () => {
163797
164441
  init_logs_pi();
163798
164442
  init_paths();
163799
164443
  init_pi_helpers();
164444
+ init_pi_package_entry();
163800
164445
  init_prompts();
163801
164446
  init_setup_pi();
163802
164447
  await init_storage();
163803
- import_comment_json10 = __toESM(require_src2(), 1);
164448
+ import_comment_json12 = __toESM(require_src2(), 1);
163804
164449
  ROW_COUNT_TABLES = ["tags", "compartments", "memories", "notes", "dream_runs"];
163805
164450
  DEFAULT_DEPS = {
163806
164451
  prompts: promptIO,
163807
164452
  collectDiagnostics: collectDiagnostics2,
163808
164453
  detectPiBinary,
163809
164454
  getPiVersion,
163810
- getLatestNpmVersion: () => getLatestNpmVersion(PACKAGE_NAME2),
164455
+ getLatestNpmVersion: () => getLatestNpmVersion(PACKAGE_NAME),
163811
164456
  probeEmbeddingEndpoint,
163812
164457
  openDatabase,
163813
164458
  isDatabasePersisted,
@@ -163823,7 +164468,7 @@ var exports_doctor = {};
163823
164468
  __export(exports_doctor, {
163824
164469
  runDoctor: () => runDoctor3
163825
164470
  });
163826
- import { existsSync as existsSync21, rmSync as rmSync3 } from "node:fs";
164471
+ import { existsSync as existsSync23, rmSync as rmSync3 } from "node:fs";
163827
164472
  async function runDoctor3(options) {
163828
164473
  if (options.clear)
163829
164474
  return runClear();
@@ -163907,7 +164552,7 @@ async function runClear() {
163907
164552
  const s = spinner();
163908
164553
  s.start(`Clearing ${item.path}`);
163909
164554
  try {
163910
- if (existsSync21(item.path)) {
164555
+ if (existsSync23(item.path)) {
163911
164556
  rmSync3(item.path, { recursive: true, force: true });
163912
164557
  }
163913
164558
  s.stop(`Cleared ${item.path}`);