@cortexkit/magic-context 0.17.2 → 0.19.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 +1004 -512
  2. package/package.json +1 -1
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");
7886
7930
  }
7887
- function getMagicContextLogPath() {
7888
- return join(tmpdir(), "magic-context.log");
7931
+ function getMagicContextLogPath2(harness) {
7932
+ return getMagicContextLogPath(harness);
7933
+ }
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,22 +7970,32 @@ 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
- import { execSync } from "node:child_process";
7978
+ import { execFileSync } from "node:child_process";
7930
7979
  import { existsSync as existsSync2, readFileSync, writeFileSync } from "node:fs";
7931
- import { dirname } from "node:path";
7980
+ import { homedir as homedir3 } from "node:os";
7981
+ import { dirname, join as join3 } from "node:path";
7932
7982
 
7933
7983
  class OpenCodeAdapter {
7934
7984
  kind = "opencode";
7935
7985
  displayName = "OpenCode";
7936
7986
  pluginPackageName = PLUGIN_NAME;
7937
7987
  isInstalled() {
7938
- if (existsSync2(`${process.env.HOME ?? ""}/.opencode/bin/opencode`))
7988
+ const isWindows = process.platform === "win32";
7989
+ const stockBin = isWindows ? join3(homedir3(), ".opencode", "bin", "opencode.exe") : join3(homedir3(), ".opencode", "bin", "opencode");
7990
+ if (existsSync2(stockBin))
7939
7991
  return true;
7992
+ const probe = isWindows ? { cmd: "where", args: ["opencode"] } : { cmd: "command", args: ["-v", "opencode"] };
7940
7993
  try {
7941
- execSync("command -v opencode", { stdio: "ignore" });
7994
+ if (!isWindows) {
7995
+ execFileSync("which", ["opencode"], { stdio: "ignore" });
7996
+ } else {
7997
+ execFileSync(probe.cmd, probe.args, { stdio: "ignore" });
7998
+ }
7942
7999
  return true;
7943
8000
  } catch {
7944
8001
  return false;
@@ -8104,15 +8161,15 @@ class OpenCodeAdapter {
8104
8161
  return "Install OpenCode: curl -fsSL https://opencode.ai/install | bash";
8105
8162
  }
8106
8163
  getPluginCacheInfo() {
8107
- const path = getOpenCodePluginCacheDir();
8164
+ const path2 = getOpenCodePluginCacheDir();
8108
8165
  return {
8109
- path,
8110
- exists: existsSync2(path),
8111
- sizeBytes: dirSizeBytes(path)
8166
+ path: path2,
8167
+ exists: existsSync2(path2),
8168
+ sizeBytes: dirSizeBytes(path2)
8112
8169
  };
8113
8170
  }
8114
8171
  getLogPath() {
8115
- return getMagicContextLogPath();
8172
+ return getMagicContextLogPath2("opencode");
8116
8173
  }
8117
8174
  getInstalledPluginVersion() {
8118
8175
  const cacheDir = getOpenCodePluginCacheDir();
@@ -8175,16 +8232,16 @@ var init_opencode = __esm(() => {
8175
8232
  });
8176
8233
 
8177
8234
  // src/lib/pi-helpers.ts
8178
- import { execFileSync, spawnSync } from "node:child_process";
8235
+ import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
8179
8236
  import { existsSync as existsSync3 } from "node:fs";
8180
- import { homedir as homedir2 } from "node:os";
8181
- import { join as join2 } from "node:path";
8237
+ import { homedir as homedir4 } from "node:os";
8238
+ import { join as join4 } from "node:path";
8182
8239
  function getStaticModels() {
8183
8240
  return [...STATIC_MODELS];
8184
8241
  }
8185
8242
  function commandExists(command, args) {
8186
8243
  try {
8187
- const output = execFileSync(command, args, {
8244
+ const output = execFileSync2(command, args, {
8188
8245
  encoding: "utf-8",
8189
8246
  stdio: ["ignore", "pipe", "ignore"]
8190
8247
  }).trim();
@@ -8200,7 +8257,7 @@ function detectPiBinary() {
8200
8257
  if (first)
8201
8258
  return { path: first, source: "path" };
8202
8259
  }
8203
- const homeCandidate = process.platform === "win32" ? join2(homedir2(), ".pi", "bin", "pi.cmd") : join2(homedir2(), ".pi", "bin", "pi");
8260
+ const homeCandidate = process.platform === "win32" ? join4(homedir4(), ".pi", "bin", "pi.cmd") : join4(homedir4(), ".pi", "bin", "pi");
8204
8261
  if (existsSync3(homeCandidate))
8205
8262
  return { path: homeCandidate, source: "home" };
8206
8263
  return null;
@@ -8224,7 +8281,7 @@ function getPiVersion(piPath) {
8224
8281
  }
8225
8282
  function runPi(piPath, args) {
8226
8283
  try {
8227
- return execFileSync(piPath, args, {
8284
+ return execFileSync2(piPath, args, {
8228
8285
  encoding: "utf-8",
8229
8286
  stdio: ["ignore", "pipe", "ignore"],
8230
8287
  timeout: 20000
@@ -8351,7 +8408,7 @@ var init_pi_helpers = __esm(() => {
8351
8408
  });
8352
8409
 
8353
8410
  // src/adapters/pi.ts
8354
- import { execFileSync as execFileSync2 } from "node:child_process";
8411
+ import { execFileSync as execFileSync3 } from "node:child_process";
8355
8412
  import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
8356
8413
  import { dirname as dirname2 } from "node:path";
8357
8414
 
@@ -8455,14 +8512,14 @@ class PiAdapter {
8455
8512
  return { path: null, exists: false, sizeBytes: 0 };
8456
8513
  }
8457
8514
  getLogPath() {
8458
- return getMagicContextLogPath();
8515
+ return getMagicContextLogPath2("pi");
8459
8516
  }
8460
8517
  getInstalledPluginVersion() {
8461
8518
  const piBin = detectPiBinary();
8462
8519
  if (!piBin)
8463
8520
  return null;
8464
8521
  try {
8465
- const output = execFileSync2(piBin.path, ["list"], {
8522
+ const output = execFileSync3(piBin.path, ["list"], {
8466
8523
  encoding: "utf-8",
8467
8524
  stdio: ["ignore", "pipe", "ignore"],
8468
8525
  timeout: 5000
@@ -10319,17 +10376,17 @@ function detectConfigFile(basePath) {
10319
10376
  var init_jsonc_parser = () => {};
10320
10377
 
10321
10378
  // ../plugin/src/shared/opencode-config-dir.ts
10322
- import { homedir as homedir3 } from "node:os";
10323
- import { join as join3, resolve } from "node:path";
10379
+ import { homedir as homedir5 } from "node:os";
10380
+ import { join as join5, resolve } from "node:path";
10324
10381
  function getCliConfigDir() {
10325
10382
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
10326
10383
  if (envConfigDir) {
10327
10384
  return resolve(envConfigDir);
10328
10385
  }
10329
10386
  if (process.platform === "win32") {
10330
- return join3(homedir3(), ".config", "opencode");
10387
+ return join5(homedir5(), ".config", "opencode");
10331
10388
  }
10332
- return join3(process.env.XDG_CONFIG_HOME || join3(homedir3(), ".config"), "opencode");
10389
+ return join5(process.env.XDG_CONFIG_HOME || join5(homedir5(), ".config"), "opencode");
10333
10390
  }
10334
10391
  function getOpenCodeConfigDir2(_options) {
10335
10392
  return getCliConfigDir();
@@ -10338,16 +10395,16 @@ function getOpenCodeConfigPaths(options) {
10338
10395
  const configDir = getOpenCodeConfigDir2(options);
10339
10396
  return {
10340
10397
  configDir,
10341
- configJson: join3(configDir, "opencode.json"),
10342
- configJsonc: join3(configDir, "opencode.jsonc"),
10343
- packageJson: join3(configDir, "package.json"),
10344
- omoConfig: join3(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")
10345
10402
  };
10346
10403
  }
10347
10404
  var init_opencode_config_dir = () => {};
10348
10405
 
10349
10406
  // ../plugin/src/shared/conflict-detector.ts
10350
- import { join as join4 } from "node:path";
10407
+ import { join as join6 } from "node:path";
10351
10408
  function detectConflicts(directory) {
10352
10409
  const conflicts = {
10353
10410
  compactionAuto: false,
@@ -10404,8 +10461,8 @@ function checkCompaction(directory) {
10404
10461
  return { auto: true, prune: false };
10405
10462
  }
10406
10463
  function readProjectCompaction(directory) {
10407
- const dotOcJsonc = join4(directory, ".opencode", "opencode.jsonc");
10408
- const dotOcJson = join4(directory, ".opencode", "opencode.json");
10464
+ const dotOcJsonc = join6(directory, ".opencode", "opencode.jsonc");
10465
+ const dotOcJson = join6(directory, ".opencode", "opencode.json");
10409
10466
  const dotOcConfig = readJsoncFile(dotOcJsonc) ?? readJsoncFile(dotOcJson);
10410
10467
  if (dotOcConfig?.compaction) {
10411
10468
  const c2 = dotOcConfig.compaction;
@@ -10413,8 +10470,8 @@ function readProjectCompaction(directory) {
10413
10470
  return { auto: c2.auto === true, prune: c2.prune === true, resolved: true };
10414
10471
  }
10415
10472
  }
10416
- const rootJsonc = join4(directory, "opencode.jsonc");
10417
- const rootJson = join4(directory, "opencode.json");
10473
+ const rootJsonc = join6(directory, "opencode.jsonc");
10474
+ const rootJson = join6(directory, "opencode.json");
10418
10475
  const rootConfig = readJsoncFile(rootJsonc) ?? readJsoncFile(rootJson);
10419
10476
  if (rootConfig?.compaction) {
10420
10477
  const c2 = rootConfig.compaction;
@@ -10468,10 +10525,10 @@ function collectPluginEntries(directory) {
10468
10525
  }
10469
10526
  };
10470
10527
  for (const configPath of [
10471
- join4(directory, ".opencode", "opencode.jsonc"),
10472
- join4(directory, ".opencode", "opencode.json"),
10473
- join4(directory, "opencode.jsonc"),
10474
- join4(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")
10475
10532
  ]) {
10476
10533
  const config = readJsoncFile(configPath);
10477
10534
  pushFrom(config?.plugin);
@@ -10512,7 +10569,7 @@ function readOmoDisabledHooks(directory) {
10512
10569
  try {
10513
10570
  const paths = getOpenCodeConfigPaths({ binary: "opencode" });
10514
10571
  for (const name of configNames) {
10515
- const configPath = join4(paths.configDir, name);
10572
+ const configPath = join6(paths.configDir, name);
10516
10573
  const config = readJsoncFile(configPath);
10517
10574
  if (config?.disabled_hooks) {
10518
10575
  for (const hook of config.disabled_hooks) {
@@ -10522,7 +10579,7 @@ function readOmoDisabledHooks(directory) {
10522
10579
  }
10523
10580
  } catch {}
10524
10581
  for (const name of configNames) {
10525
- const config = readJsoncFile(join4(directory, name));
10582
+ const config = readJsoncFile(join6(directory, name));
10526
10583
  if (config?.disabled_hooks) {
10527
10584
  for (const hook of config.disabled_hooks) {
10528
10585
  disabled.add(hook);
@@ -10540,11 +10597,8 @@ var init_conflict_detector = __esm(() => {
10540
10597
  });
10541
10598
 
10542
10599
  // ../plugin/src/shared/conflict-fixer.ts
10543
- import { existsSync as existsSync6, mkdirSync, writeFileSync as writeFileSync3 } from "node:fs";
10544
- import { dirname as dirname3, join as join5 } from "node:path";
10545
- function ensureParentDir(filePath) {
10546
- mkdirSync(dirname3(filePath), { recursive: true });
10547
- }
10600
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
10601
+ import { join as join7 } from "node:path";
10548
10602
  function isRecord(value) {
10549
10603
  return typeof value === "object" && value !== null && !Array.isArray(value);
10550
10604
  }
@@ -10553,13 +10607,17 @@ function asStringArray(value) {
10553
10607
  }
10554
10608
  function readConfig(filePath) {
10555
10609
  if (!existsSync6(filePath)) {
10556
- 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;
10557
10617
  }
10558
- return readJsoncFile(filePath);
10559
10618
  }
10560
10619
  function writeConfig(filePath, config) {
10561
- ensureParentDir(filePath);
10562
- writeFileSync3(filePath, `${JSON.stringify(config, null, 2)}
10620
+ writeFileSync3(filePath, `${import_comment_json3.stringify(config, null, 2)}
10563
10621
  `);
10564
10622
  }
10565
10623
  function resolveUserOpenCodeConfigPath() {
@@ -10571,12 +10629,14 @@ function resolveUserOpenCodeConfigPath() {
10571
10629
  function collectOpenCodeConfigPaths(directory) {
10572
10630
  const paths = new Set;
10573
10631
  const userConfig = resolveUserOpenCodeConfigPath();
10574
- paths.add(userConfig);
10632
+ if (existsSync6(userConfig)) {
10633
+ paths.add(userConfig);
10634
+ }
10575
10635
  for (const filePath of [
10576
- join5(directory, ".opencode", "opencode.jsonc"),
10577
- join5(directory, ".opencode", "opencode.json"),
10578
- join5(directory, "opencode.jsonc"),
10579
- join5(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")
10580
10640
  ]) {
10581
10641
  if (existsSync6(filePath)) {
10582
10642
  paths.add(filePath);
@@ -10588,8 +10648,8 @@ function collectOmoConfigPaths(directory) {
10588
10648
  const paths = new Set;
10589
10649
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
10590
10650
  for (const fileName of OMO_CONFIG_NAMES) {
10591
- const userPath = join5(configDir, fileName);
10592
- const projectPath = join5(directory, fileName);
10651
+ const userPath = join7(configDir, fileName);
10652
+ const projectPath = join7(directory, fileName);
10593
10653
  if (existsSync6(userPath)) {
10594
10654
  paths.add(userPath);
10595
10655
  }
@@ -10597,11 +10657,15 @@ function collectOmoConfigPaths(directory) {
10597
10657
  paths.add(projectPath);
10598
10658
  }
10599
10659
  }
10600
- if (paths.size === 0) {
10601
- paths.add(join5(configDir, "oh-my-openagent.json"));
10602
- }
10603
10660
  return [...paths];
10604
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
+ }
10605
10669
  function fixConflicts(directory, conflicts) {
10606
10670
  const actions = [];
10607
10671
  let updatedCompaction = false;
@@ -10615,7 +10679,7 @@ function fixConflicts(directory, conflicts) {
10615
10679
  }
10616
10680
  let changed = false;
10617
10681
  if (conflicts.compactionAuto || conflicts.compactionPrune) {
10618
- const compaction = isRecord(config.compaction) ? { ...config.compaction } : {};
10682
+ const compaction = isRecord(config.compaction) ? config.compaction : {};
10619
10683
  if (compaction.auto !== false) {
10620
10684
  compaction.auto = false;
10621
10685
  changed = true;
@@ -10629,10 +10693,10 @@ function fixConflicts(directory, conflicts) {
10629
10693
  config.compaction = compaction;
10630
10694
  }
10631
10695
  if (conflicts.dcpPlugin) {
10632
- const plugins = asStringArray(config.plugin);
10633
- const filteredPlugins = plugins.filter((plugin) => !plugin.includes("opencode-dcp"));
10634
- if (filteredPlugins.length !== plugins.length) {
10635
- 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;
10636
10700
  changed = true;
10637
10701
  removedDcpPlugin = true;
10638
10702
  }
@@ -10687,10 +10751,11 @@ function fixConflicts(directory, conflicts) {
10687
10751
  }
10688
10752
  return actions;
10689
10753
  }
10690
- var CONFLICTING_OMO_HOOKS, OMO_CONFIG_NAMES;
10754
+ var import_comment_json3, CONFLICTING_OMO_HOOKS, OMO_CONFIG_NAMES;
10691
10755
  var init_conflict_fixer = __esm(() => {
10692
- init_jsonc_parser();
10756
+ init_conflict_detector();
10693
10757
  init_opencode_config_dir();
10758
+ import_comment_json3 = __toESM(require_src2(), 1);
10694
10759
  CONFLICTING_OMO_HOOKS = [
10695
10760
  "context-window-monitor",
10696
10761
  "preemptive-compaction",
@@ -10705,10 +10770,10 @@ var init_conflict_fixer = __esm(() => {
10705
10770
  });
10706
10771
 
10707
10772
  // src/lib/opencode-helpers.ts
10708
- import { execSync as execSync2 } from "node:child_process";
10773
+ import { execSync } from "node:child_process";
10709
10774
  function isOpenCodeInstalled() {
10710
10775
  try {
10711
- execSync2("opencode --version", { stdio: "pipe" });
10776
+ execSync("opencode --version", { stdio: "pipe" });
10712
10777
  return true;
10713
10778
  } catch {
10714
10779
  return false;
@@ -10716,14 +10781,14 @@ function isOpenCodeInstalled() {
10716
10781
  }
10717
10782
  function getOpenCodeVersion() {
10718
10783
  try {
10719
- return execSync2("opencode --version", { stdio: "pipe" }).toString().trim();
10784
+ return execSync("opencode --version", { stdio: "pipe" }).toString().trim();
10720
10785
  } catch {
10721
10786
  return null;
10722
10787
  }
10723
10788
  }
10724
10789
  function getAvailableModels2() {
10725
10790
  try {
10726
- const output = execSync2("opencode models", { stdio: "pipe" }).toString().trim();
10791
+ const output = execSync("opencode models", { stdio: "pipe" }).toString().trim();
10727
10792
  return output.split(`
10728
10793
  `).map((l) => l.trim()).filter(Boolean);
10729
10794
  } catch {
@@ -10782,30 +10847,30 @@ function buildModelSelection2(allModels, role) {
10782
10847
  var init_opencode_helpers = () => {};
10783
10848
 
10784
10849
  // src/commands/setup-opencode.ts
10785
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
10786
- 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";
10787
10852
  function ensureDir3(dir) {
10788
10853
  if (!existsSync7(dir)) {
10789
- mkdirSync2(dir, { recursive: true });
10854
+ mkdirSync(dir, { recursive: true });
10790
10855
  }
10791
10856
  }
10792
- function readJsonc(path) {
10793
- const content = readFileSync4(path, "utf-8");
10857
+ function readJsonc(path2) {
10858
+ const content = readFileSync5(path2, "utf-8");
10794
10859
  try {
10795
- return import_comment_json3.parse(content);
10860
+ return import_comment_json4.parse(content);
10796
10861
  } catch (err) {
10797
- 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}`);
10798
10863
  return null;
10799
10864
  }
10800
10865
  }
10801
10866
  function addPluginToOpenCodeConfig(configPath, format) {
10802
- ensureDir3(dirname4(configPath));
10867
+ ensureDir3(dirname3(configPath));
10803
10868
  if (format === "none") {
10804
10869
  const config = {
10805
10870
  plugin: [PLUGIN_ENTRY2],
10806
10871
  compaction: { auto: false, prune: false }
10807
10872
  };
10808
- writeFileSync4(configPath, `${import_comment_json3.stringify(config, null, 2)}
10873
+ writeFileSync4(configPath, `${import_comment_json4.stringify(config, null, 2)}
10809
10874
  `);
10810
10875
  return;
10811
10876
  }
@@ -10825,13 +10890,13 @@ function addPluginToOpenCodeConfig(configPath, format) {
10825
10890
  compaction.auto = false;
10826
10891
  compaction.prune = false;
10827
10892
  existing.compaction = compaction;
10828
- writeFileSync4(configPath, `${import_comment_json3.stringify(existing, null, 2)}
10893
+ writeFileSync4(configPath, `${import_comment_json4.stringify(existing, null, 2)}
10829
10894
  `);
10830
10895
  }
10831
10896
  function addPluginToTuiConfig(configPath, format) {
10832
- ensureDir3(dirname4(configPath));
10897
+ ensureDir3(dirname3(configPath));
10833
10898
  if (format === "none") {
10834
- writeFileSync4(configPath, `${import_comment_json3.stringify({ plugin: [PLUGIN_ENTRY2] }, null, 2)}
10899
+ writeFileSync4(configPath, `${import_comment_json4.stringify({ plugin: [PLUGIN_ENTRY2] }, null, 2)}
10835
10900
  `);
10836
10901
  return;
10837
10902
  }
@@ -10847,9 +10912,42 @@ function addPluginToTuiConfig(configPath, format) {
10847
10912
  rawPlugins.push(PLUGIN_ENTRY2);
10848
10913
  }
10849
10914
  existing.plugin = rawPlugins;
10850
- writeFileSync4(configPath, `${import_comment_json3.stringify(existing, null, 2)}
10915
+ writeFileSync4(configPath, `${import_comment_json4.stringify(existing, null, 2)}
10851
10916
  `);
10852
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
+ }
10853
10951
  function writeMagicContextConfig(configPath, options) {
10854
10952
  const config = (existsSync7(configPath) ? readJsonc(configPath) : null) ?? {};
10855
10953
  if (!config.$schema) {
@@ -10888,7 +10986,7 @@ function writeMagicContextConfig(configPath, options) {
10888
10986
  cacheTtl["anthropic/claude-opus-4-6"] = "59m";
10889
10987
  config.cache_ttl = cacheTtl;
10890
10988
  }
10891
- writeFileSync4(configPath, `${import_comment_json3.stringify(config, null, 2)}
10989
+ writeFileSync4(configPath, `${import_comment_json4.stringify(config, null, 2)}
10892
10990
  `);
10893
10991
  }
10894
10992
  async function runSetup() {
@@ -10918,32 +11016,11 @@ async function runSetup() {
10918
11016
  }
10919
11017
  const paths = detectConfigPaths();
10920
11018
  const hadExistingSetup = paths.opencodeConfigFormat !== "none" || existsSync7(paths.magicContextConfig) || paths.tuiConfigFormat !== "none";
11019
+ await resolveDcpConflictBeforeSetup(paths.opencodeConfig, paths.opencodeConfigFormat);
10921
11020
  addPluginToOpenCodeConfig(paths.opencodeConfig, paths.opencodeConfigFormat);
10922
11021
  log.success(`Plugin added to ${paths.opencodeConfig}`);
10923
11022
  log.info("Disabled built-in compaction (auto=false, prune=false)");
10924
11023
  log.message("Magic Context handles context management — built-in compaction would interfere");
10925
- if (paths.opencodeConfigFormat !== "none") {
10926
- const ocConfig = readJsonc(paths.opencodeConfig);
10927
- if (ocConfig) {
10928
- const plugins = ocConfig.plugin ?? [];
10929
- const dcpIndex = plugins.findIndex((p2) => p2.startsWith("@tarquinen/opencode-dcp"));
10930
- if (dcpIndex !== -1) {
10931
- log.warn(`Found conflicting plugin: ${plugins[dcpIndex]}`);
10932
- log.message(`opencode-dcp (Dynamic Context Pruning) and Magic Context both manage context.
10933
- ` + "Running both simultaneously will cause unpredictable behavior.");
10934
- const shouldRemove = await confirm("Remove opencode-dcp from your config?", true);
10935
- if (shouldRemove) {
10936
- plugins.splice(dcpIndex, 1);
10937
- ocConfig.plugin = plugins;
10938
- writeFileSync4(paths.opencodeConfig, `${import_comment_json3.stringify(ocConfig, null, 2)}
10939
- `);
10940
- log.success("Removed opencode-dcp from plugin list");
10941
- } else {
10942
- log.warn("Skipped — you may experience context management conflicts");
10943
- }
10944
- }
10945
- }
10946
- }
10947
11024
  if (hadExistingSetup) {
10948
11025
  const conflicts = detectConflicts(process.cwd());
10949
11026
  if (conflicts.hasConflict) {
@@ -11062,8 +11139,8 @@ async function runSetup() {
11062
11139
  const shouldStar = await confirm("★ Star the repo on GitHub?", true);
11063
11140
  if (shouldStar) {
11064
11141
  try {
11065
- const { execSync: execSync3 } = await import("node:child_process");
11066
- execSync3("gh api --silent --method PUT /user/starred/cortexkit/magic-context", {
11142
+ const { execSync: execSync2 } = await import("node:child_process");
11143
+ execSync2("gh api --silent --method PUT /user/starred/cortexkit/magic-context", {
11067
11144
  stdio: "ignore",
11068
11145
  timeout: 1e4
11069
11146
  });
@@ -11076,7 +11153,7 @@ async function runSetup() {
11076
11153
  outro("Run 'opencode' to start!");
11077
11154
  return 0;
11078
11155
  }
11079
- 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";
11080
11157
  var init_setup_opencode = __esm(() => {
11081
11158
  init_conflict_detector();
11082
11159
  init_conflict_fixer();
@@ -11084,26 +11161,77 @@ var init_setup_opencode = __esm(() => {
11084
11161
  init_opencode_helpers();
11085
11162
  init_paths();
11086
11163
  init_prompts();
11087
- 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();
11088
11216
  });
11089
11217
 
11090
11218
  // src/commands/setup-pi.ts
11091
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
11092
- import { dirname as dirname5 } from "node:path";
11093
- function ensureDir4(path) {
11094
- if (!existsSync8(path))
11095
- 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 });
11096
11224
  }
11097
11225
  async function getDefaultPrompts() {
11098
11226
  const { promptIO: promptIO2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
11099
11227
  return promptIO2;
11100
11228
  }
11101
- function readJsonc2(path) {
11229
+ function readJsonc2(path2) {
11102
11230
  try {
11103
- return import_comment_json4.parse(readFileSync5(path, "utf-8"));
11231
+ return import_comment_json5.parse(readFileSync6(path2, "utf-8"));
11104
11232
  } catch (error) {
11105
11233
  const message = error instanceof Error ? error.message : String(error);
11106
- console.warn(`Could not parse ${path}: ${message}`);
11234
+ console.warn(`Could not parse ${path2}: ${message}`);
11107
11235
  return null;
11108
11236
  }
11109
11237
  }
@@ -11115,12 +11243,12 @@ function compactObject(obj) {
11115
11243
  return obj;
11116
11244
  }
11117
11245
  function comparePiVersion(a, b2) {
11118
- const parse = (v2) => {
11246
+ const parse2 = (v2) => {
11119
11247
  const match = v2.match(/(\d+)\.(\d+)\.(\d+)/);
11120
11248
  return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
11121
11249
  };
11122
- const left = parse(a);
11123
- const right = parse(b2);
11250
+ const left = parse2(a);
11251
+ const right = parse2(b2);
11124
11252
  if (!left || !right)
11125
11253
  return 0;
11126
11254
  for (let i = 0;i < 3; i += 1) {
@@ -11132,19 +11260,19 @@ function comparePiVersion(a, b2) {
11132
11260
  return 0;
11133
11261
  }
11134
11262
  function writePiSettingsPackage(settingsPath, packageSource = PI_PACKAGE_SOURCE) {
11135
- ensureDir4(dirname5(settingsPath));
11263
+ ensureDir4(dirname4(settingsPath));
11136
11264
  const settings = existsSync8(settingsPath) ? readJsonc2(settingsPath) ?? {} : {};
11137
- const packages = Array.isArray(settings.packages) ? settings.packages.filter((value) => typeof value === "string") : [];
11138
- 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);
11139
11267
  if (!hasPackage)
11140
11268
  packages.push(packageSource);
11141
11269
  settings.packages = packages;
11142
- writeFileSync5(settingsPath, `${import_comment_json4.stringify(settings, null, 2)}
11270
+ writeFileSync5(settingsPath, `${import_comment_json5.stringify(settings, null, 2)}
11143
11271
  `);
11144
11272
  return !hasPackage;
11145
11273
  }
11146
11274
  function writeMagicContextConfig2(configPath, options) {
11147
- ensureDir4(dirname5(configPath));
11275
+ ensureDir4(dirname4(configPath));
11148
11276
  const config = existsSync8(configPath) ? readJsonc2(configPath) ?? {} : {};
11149
11277
  if (!config.$schema) {
11150
11278
  config.$schema = "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json";
@@ -11172,7 +11300,7 @@ function writeMagicContextConfig2(configPath, options) {
11172
11300
  };
11173
11301
  }
11174
11302
  config.embedding = options.embedding;
11175
- writeFileSync5(configPath, `${import_comment_json4.stringify(config, null, 2)}
11303
+ writeFileSync5(configPath, `${import_comment_json5.stringify(config, null, 2)}
11176
11304
  `);
11177
11305
  }
11178
11306
  async function chooseModel(prompts, allModels, role, message) {
@@ -11298,11 +11426,12 @@ Run \`pi update\` (or \`npm install -g @mariozechner/pi-coding-agent@latest\`) b
11298
11426
  prompts.outro("Start a Pi session and try /ctx-aug");
11299
11427
  return 0;
11300
11428
  }
11301
- var import_comment_json4, DEFAULT_ENV;
11429
+ var import_comment_json5, DEFAULT_ENV;
11302
11430
  var init_setup_pi = __esm(() => {
11303
11431
  init_paths();
11304
11432
  init_pi_helpers();
11305
- import_comment_json4 = __toESM(require_src2(), 1);
11433
+ init_pi_package_entry();
11434
+ import_comment_json5 = __toESM(require_src2(), 1);
11306
11435
  DEFAULT_ENV = {
11307
11436
  detectPiBinary,
11308
11437
  getPiVersion,
@@ -11382,29 +11511,6 @@ var init_setup = __esm(() => {
11382
11511
  init_setup_pi();
11383
11512
  });
11384
11513
 
11385
- // ../plugin/src/shared/data-path.ts
11386
- import * as os from "node:os";
11387
- import * as path from "node:path";
11388
- function getDataDir() {
11389
- return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
11390
- }
11391
- function getOpenCodeStorageDir() {
11392
- return path.join(getDataDir(), "opencode", "storage");
11393
- }
11394
- function getMagicContextStorageDir() {
11395
- return path.join(getDataDir(), "cortexkit", "magic-context");
11396
- }
11397
- function getLegacyOpenCodeMagicContextStorageDir() {
11398
- return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
11399
- }
11400
- function getCacheDir() {
11401
- return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
11402
- }
11403
- function getOpenCodeCacheDir() {
11404
- return path.join(getCacheDir(), "opencode");
11405
- }
11406
- var init_data_path = () => {};
11407
-
11408
11514
  // ../../node_modules/.bun/nanotar@0.3.0/node_modules/nanotar/dist/index.mjs
11409
11515
  function parseTar(data, opts) {
11410
11516
  const buffer = data.buffer || data;
@@ -11583,8 +11689,16 @@ var init_dist5 = __esm(() => {
11583
11689
 
11584
11690
  // ../plugin/src/shared/logger.ts
11585
11691
  import * as fs from "node:fs";
11586
- import * as os2 from "node:os";
11587
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
+ }
11588
11702
  function flush() {
11589
11703
  if (flushTimer) {
11590
11704
  clearTimeout(flushTimer);
@@ -11595,6 +11709,8 @@ function flush() {
11595
11709
  const data = buffer.join("");
11596
11710
  buffer = [];
11597
11711
  try {
11712
+ const logFile = getMagicContextLogPath();
11713
+ ensureDir5(logFile);
11598
11714
  fs.appendFileSync(logFile, data);
11599
11715
  } catch {}
11600
11716
  }
@@ -11622,9 +11738,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
11622
11738
  }
11623
11739
  } catch {}
11624
11740
  }
11625
- 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;
11626
11742
  var init_logger = __esm(() => {
11627
- logFile = path2.join(os2.tmpdir(), "magic-context.log");
11743
+ init_data_path();
11628
11744
  buffer = [];
11629
11745
  if (!isTestEnv) {
11630
11746
  process.on("exit", flush);
@@ -11799,16 +11915,16 @@ __export(exports_migrate, {
11799
11915
  });
11800
11916
  import { randomBytes } from "node:crypto";
11801
11917
  import { existsSync as existsSync10, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
11802
- import { homedir as homedir5 } from "node:os";
11918
+ import { homedir as homedir6 } from "node:os";
11803
11919
  import { join as join9 } from "node:path";
11804
11920
  function defaultOpenCodeDbPath() {
11805
- return join9(homedir5(), ".local", "share", "opencode", "opencode.db");
11921
+ return join9(homedir6(), ".local", "share", "opencode", "opencode.db");
11806
11922
  }
11807
11923
  function defaultCortexkitDbPath() {
11808
11924
  return join9(getMagicContextStorageDir(), "context.db");
11809
11925
  }
11810
11926
  function defaultPiSessionsRoot() {
11811
- return join9(homedir5(), ".pi", "agent", "sessions");
11927
+ return join9(homedir6(), ".pi", "agent", "sessions");
11812
11928
  }
11813
11929
  function defaultFs() {
11814
11930
  return { existsSync: existsSync10, mkdirSync: mkdirSync5, writeFileSync: writeFileSync7 };
@@ -13318,7 +13434,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
13318
13434
  throw e2;
13319
13435
  }
13320
13436
  return result.value;
13321
- }, parse, _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
13437
+ }, parse2, _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
13322
13438
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
13323
13439
  let result = schema._zod.run({ value, issues: [] }, ctx);
13324
13440
  if (result instanceof Promise)
@@ -13373,7 +13489,7 @@ var init_parse = __esm(() => {
13373
13489
  init_core();
13374
13490
  init_errors();
13375
13491
  init_util();
13376
- parse = /* @__PURE__ */ _parse($ZodRealError);
13492
+ parse2 = /* @__PURE__ */ _parse($ZodRealError);
13377
13493
  parseAsync = /* @__PURE__ */ _parseAsync($ZodRealError);
13378
13494
  safeParse = /* @__PURE__ */ _safeParse($ZodRealError);
13379
13495
  safeParseAsync = /* @__PURE__ */ _safeParseAsync($ZodRealError);
@@ -16132,10 +16248,10 @@ var init_schemas = __esm(() => {
16132
16248
  throw new Error("implement() must be called with a function");
16133
16249
  }
16134
16250
  return function(...args) {
16135
- const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;
16251
+ const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
16136
16252
  const result = Reflect.apply(func, this, parsedArgs);
16137
16253
  if (inst._def.output) {
16138
- return parse(inst._def.output, result);
16254
+ return parse2(inst._def.output, result);
16139
16255
  }
16140
16256
  return result;
16141
16257
  };
@@ -23620,7 +23736,7 @@ function finalize(ctx, schema) {
23620
23736
  result.$schema = "http://json-schema.org/draft-07/schema#";
23621
23737
  } else if (ctx.target === "draft-04") {
23622
23738
  result.$schema = "http://json-schema.org/draft-04/schema#";
23623
- } else if (ctx.target === "openapi-3.0") {} else {}
23739
+ } else if (ctx.target === "openapi-3.0") {}
23624
23740
  if (ctx.external?.uri) {
23625
23741
  const id = ctx.external.registry.get(schema)?.id;
23626
23742
  if (!id)
@@ -23881,7 +23997,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
23881
23997
  if (val === undefined) {
23882
23998
  if (ctx.unrepresentable === "throw") {
23883
23999
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
23884
- } else {}
24000
+ }
23885
24001
  } else if (typeof val === "bigint") {
23886
24002
  if (ctx.unrepresentable === "throw") {
23887
24003
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -24328,7 +24444,7 @@ __export(exports_core2, {
24328
24444
  process: () => process2,
24329
24445
  prettifyError: () => prettifyError,
24330
24446
  parseAsync: () => parseAsync,
24331
- parse: () => parse,
24447
+ parse: () => parse2,
24332
24448
  meta: () => meta,
24333
24449
  locales: () => exports_locales,
24334
24450
  isValidJWT: () => isValidJWT,
@@ -24729,11 +24845,11 @@ var init_errors2 = __esm(() => {
24729
24845
  });
24730
24846
 
24731
24847
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/parse.js
24732
- 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;
24733
24849
  var init_parse2 = __esm(() => {
24734
24850
  init_core2();
24735
24851
  init_errors2();
24736
- parse3 = /* @__PURE__ */ _parse(ZodRealError);
24852
+ parse4 = /* @__PURE__ */ _parse(ZodRealError);
24737
24853
  parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
24738
24854
  safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
24739
24855
  safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -25453,7 +25569,7 @@ var init_schemas2 = __esm(() => {
25453
25569
  inst.def = def;
25454
25570
  inst.type = def.type;
25455
25571
  Object.defineProperty(inst, "_def", { value: def });
25456
- inst.parse = (data, params) => parse3(inst, data, params, { callee: inst.parse });
25572
+ inst.parse = (data, params) => parse4(inst, data, params, { callee: inst.parse });
25457
25573
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
25458
25574
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
25459
25575
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -26805,7 +26921,7 @@ __export(exports_external, {
26805
26921
  pipe: () => pipe,
26806
26922
  partialRecord: () => partialRecord,
26807
26923
  parseAsync: () => parseAsync2,
26808
- parse: () => parse3,
26924
+ parse: () => parse4,
26809
26925
  overwrite: () => _overwrite,
26810
26926
  optional: () => optional,
26811
26927
  object: () => object,
@@ -27228,8 +27344,8 @@ var init_magic_context = __esm(() => {
27228
27344
  });
27229
27345
 
27230
27346
  // ../plugin/src/config/variable.ts
27231
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "node:fs";
27232
- import { homedir as homedir6 } from "node:os";
27347
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
27348
+ import { homedir as homedir7 } from "node:os";
27233
27349
  import { dirname as dirname7, isAbsolute, resolve as resolve2 } from "node:path";
27234
27350
  function substituteConfigVariables(input) {
27235
27351
  const warnings = [];
@@ -27265,7 +27381,7 @@ function substituteConfigVariables(input) {
27265
27381
  }
27266
27382
  let filePath = rawPath.trim();
27267
27383
  if (filePath.startsWith("~/")) {
27268
- filePath = resolve2(homedir6(), filePath.slice(2));
27384
+ filePath = resolve2(homedir7(), filePath.slice(2));
27269
27385
  } else if (!isAbsolute(filePath)) {
27270
27386
  filePath = resolve2(configDir, filePath);
27271
27387
  }
@@ -27275,7 +27391,7 @@ function substituteConfigVariables(input) {
27275
27391
  }
27276
27392
  let contents;
27277
27393
  try {
27278
- contents = readFileSync6(filePath, "utf-8").trim();
27394
+ contents = readFileSync7(filePath, "utf-8").trim();
27279
27395
  } catch (error51) {
27280
27396
  const message = error51 instanceof Error ? error51.message : String(error51);
27281
27397
  warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
@@ -27293,11 +27409,11 @@ var init_variable = __esm(() => {
27293
27409
  });
27294
27410
 
27295
27411
  // ../plugin/src/config/index.ts
27296
- import { existsSync as existsSync12, readFileSync as readFileSync7 } from "node:fs";
27297
- import { homedir as homedir7 } from "node:os";
27412
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
27413
+ import { homedir as homedir8 } from "node:os";
27298
27414
  import { join as join10 } from "node:path";
27299
27415
  function getUserConfigBasePath() {
27300
- const configRoot = process.env.XDG_CONFIG_HOME ?? join10(homedir7(), ".config");
27416
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join10(homedir8(), ".config");
27301
27417
  return join10(configRoot, "opencode", CONFIG_FILE_BASENAME);
27302
27418
  }
27303
27419
  function getProjectConfigBasePath(directory) {
@@ -27308,7 +27424,7 @@ function loadConfigFile(configPath) {
27308
27424
  if (!existsSync12(configPath)) {
27309
27425
  return null;
27310
27426
  }
27311
- const rawText = readFileSync7(configPath, "utf-8");
27427
+ const rawText = readFileSync8(configPath, "utf-8");
27312
27428
  const substituted = substituteConfigVariables({ text: rawText, configPath });
27313
27429
  return {
27314
27430
  config: parseJsonc3(substituted.text),
@@ -27319,34 +27435,20 @@ function loadConfigFile(configPath) {
27319
27435
  return null;
27320
27436
  }
27321
27437
  }
27322
- function mergeConfigs(base, override, options = {}) {
27323
- const config2 = {
27324
- ...base,
27325
- ...override,
27326
- auto_update: options.allowUserOnlyFields ? override.auto_update : base.auto_update,
27327
- memory: {
27328
- ...base.memory ?? {},
27329
- ...override.memory ?? {}
27330
- },
27331
- embedding: override.embedding ?? base.embedding,
27332
- historian: override.historian ?? base.historian,
27333
- dreamer: override.dreamer ? {
27334
- ...base.dreamer ?? {},
27335
- ...override.dreamer
27336
- } : base.dreamer,
27337
- sidekick: override.sidekick ? {
27338
- ...base.sidekick ?? {},
27339
- ...override.sidekick
27340
- } : base.sidekick,
27341
- disabled_hooks: [
27342
- ...new Set([...base.disabled_hooks ?? [], ...override.disabled_hooks ?? []])
27343
- ],
27344
- command: {
27345
- ...base.command ?? {},
27346
- ...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;
27347
27449
  }
27348
- };
27349
- return config2;
27450
+ }
27451
+ return result;
27350
27452
  }
27351
27453
  function getProjectUserOnlyFields(config2) {
27352
27454
  return "auto_update" in config2 ? ["auto_update"] : [];
@@ -27487,30 +27589,38 @@ function loadPluginConfig(directory) {
27487
27589
  const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
27488
27590
  const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
27489
27591
  const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
27490
- let config2 = parsePluginConfig({});
27491
27592
  const allWarnings = [];
27593
+ let mergedRaw = {};
27492
27594
  if (userLoaded) {
27493
27595
  allWarnings.push(...userLoaded.warnings.map((w3) => `[user config] ${w3}`));
27494
- const parsed = parsePluginConfig(userLoaded.config);
27495
- if (parsed.configWarnings?.length) {
27496
- allWarnings.push(...parsed.configWarnings.map((w3) => `[user config] ${w3}`));
27497
- }
27498
- config2 = mergeConfigs(config2, parsed, { allowUserOnlyFields: true });
27596
+ mergedRaw = deepMergeRawConfig(mergedRaw, userLoaded.config);
27499
27597
  }
27500
27598
  if (projectLoaded) {
27501
27599
  allWarnings.push(...projectLoaded.warnings.map((w3) => `[project config] ${w3}`));
27502
- const parsed = parsePluginConfig(projectLoaded.config);
27503
- if (parsed.configWarnings?.length) {
27504
- allWarnings.push(...parsed.configWarnings.map((w3) => `[project config] ${w3}`));
27505
- }
27506
- const strippedUserOnlyFields = getProjectUserOnlyFields(projectLoaded.config);
27600
+ const projectRaw = { ...projectLoaded.config };
27601
+ const strippedUserOnlyFields = getProjectUserOnlyFields(projectRaw);
27507
27602
  if (strippedUserOnlyFields.length > 0) {
27603
+ for (const key of strippedUserOnlyFields) {
27604
+ delete projectRaw[key];
27605
+ }
27508
27606
  allWarnings.push(`[project config] Ignoring ${strippedUserOnlyFields.join(", ")} from project config (security: these settings only honor user-level config)`);
27509
27607
  }
27510
- 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
+ }));
27511
27619
  }
27512
27620
  if (allWarnings.length > 0) {
27513
27621
  config2.configWarnings = allWarnings;
27622
+ } else if ("configWarnings" in config2) {
27623
+ config2.configWarnings = undefined;
27514
27624
  }
27515
27625
  return config2;
27516
27626
  }
@@ -27626,7 +27736,7 @@ function truncate(text2) {
27626
27736
  var DEFAULT_TIMEOUT_MS = 1e4, MAX_PREVIEW_CHARS = 240;
27627
27737
 
27628
27738
  // ../plugin/src/shared/tui-config.ts
27629
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
27739
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
27630
27740
  import { dirname as dirname8, join as join11 } from "node:path";
27631
27741
  function isMagicContextEntry(entry) {
27632
27742
  if (!entry)
@@ -27654,8 +27764,8 @@ function ensureTuiPluginEntry() {
27654
27764
  const configPath = resolveTuiConfigPath();
27655
27765
  let config2 = {};
27656
27766
  if (existsSync13(configPath)) {
27657
- const raw = readFileSync8(configPath, "utf-8");
27658
- config2 = import_comment_json5.parse(raw) ?? {};
27767
+ const raw = readFileSync9(configPath, "utf-8");
27768
+ config2 = import_comment_json6.parse(raw) ?? {};
27659
27769
  }
27660
27770
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p2) => typeof p2 === "string") : [];
27661
27771
  const existingIdx = plugins.findIndex(isMagicContextEntry);
@@ -27674,7 +27784,7 @@ function ensureTuiPluginEntry() {
27674
27784
  }
27675
27785
  config2.plugin = plugins;
27676
27786
  mkdirSync6(dirname8(configPath), { recursive: true });
27677
- writeFileSync8(configPath, `${import_comment_json5.stringify(config2, null, 2)}
27787
+ writeFileSync8(configPath, `${import_comment_json6.stringify(config2, null, 2)}
27678
27788
  `);
27679
27789
  log2(`[magic-context] updated TUI plugin entry in ${configPath}`);
27680
27790
  return true;
@@ -27683,11 +27793,11 @@ function ensureTuiPluginEntry() {
27683
27793
  return false;
27684
27794
  }
27685
27795
  }
27686
- var import_comment_json5, PLUGIN_NAME4 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY3;
27796
+ var import_comment_json6, PLUGIN_NAME4 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY3;
27687
27797
  var init_tui_config = __esm(() => {
27688
27798
  init_logger();
27689
27799
  init_opencode_config_dir();
27690
- import_comment_json5 = __toESM(require_src2(), 1);
27800
+ import_comment_json6 = __toESM(require_src2(), 1);
27691
27801
  PLUGIN_ENTRY3 = `${PLUGIN_NAME4}@latest`;
27692
27802
  });
27693
27803
 
@@ -27741,10 +27851,125 @@ var init_compartment_parser = __esm(() => {
27741
27851
  USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
27742
27852
  });
27743
27853
 
27854
+ // src/lib/redaction.ts
27855
+ import { homedir as homedir9, userInfo } from "node:os";
27856
+ function escapeRegex2(value) {
27857
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27858
+ }
27859
+ function redactionTypeForKey(key) {
27860
+ const normalized = key.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "_");
27861
+ const suffix = normalized.split(".").filter(Boolean).at(-1) ?? normalized;
27862
+ return suffix || "secret";
27863
+ }
27864
+ function isSecretKey(key) {
27865
+ return SECRET_KEY_PATTERN.test(key);
27866
+ }
27867
+ function sanitizePathString(value) {
27868
+ const home = homedir9();
27869
+ const username = userInfo().username;
27870
+ let sanitized = value;
27871
+ if (home) {
27872
+ sanitized = sanitized.replace(new RegExp(escapeRegex2(home), "g"), "~");
27873
+ }
27874
+ sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
27875
+ sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
27876
+ sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
27877
+ if (username) {
27878
+ sanitized = sanitized.replace(new RegExp(escapeRegex2(username), "g"), "<USER>");
27879
+ }
27880
+ return sanitized;
27881
+ }
27882
+ function redactSecretText(value) {
27883
+ let redacted = value;
27884
+ for (const { pattern, replacement } of SECRET_TEXT_PATTERNS) {
27885
+ if (typeof replacement === "string") {
27886
+ redacted = redacted.replace(pattern, replacement);
27887
+ } else {
27888
+ redacted = redacted.replace(pattern, replacement);
27889
+ }
27890
+ }
27891
+ return redacted;
27892
+ }
27893
+ function sanitizeDiagnosticText(value) {
27894
+ return redactSecretText(sanitizePathString(value));
27895
+ }
27896
+ function sanitizeConfigValue(value, keyPath = []) {
27897
+ const key = keyPath.at(-1) ?? "";
27898
+ if (key && isSecretKey(key)) {
27899
+ return `<REDACTED:${redactionTypeForKey(key)}>`;
27900
+ }
27901
+ if (typeof value === "string")
27902
+ return sanitizeDiagnosticText(value);
27903
+ if (Array.isArray(value)) {
27904
+ return value.map((entry, index) => sanitizeConfigValue(entry, [...keyPath, String(index)]));
27905
+ }
27906
+ if (value && typeof value === "object") {
27907
+ return Object.fromEntries(Object.entries(value).map(([entryKey, entry]) => [
27908
+ entryKey,
27909
+ sanitizeConfigValue(entry, [...keyPath, entryKey])
27910
+ ]));
27911
+ }
27912
+ return value;
27913
+ }
27914
+ var SECRET_KEY_PATTERN, SECRET_TEXT_PATTERNS;
27915
+ var init_redaction = __esm(() => {
27916
+ SECRET_KEY_PATTERN = /(?:key|token|secret|password|auth|bearer|credential)/i;
27917
+ SECRET_TEXT_PATTERNS = [
27918
+ {
27919
+ pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
27920
+ replacement: "<ANTHROPIC_API_KEY_REDACTED>"
27921
+ },
27922
+ {
27923
+ pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/g,
27924
+ replacement: "<OPENAI_API_KEY_REDACTED>"
27925
+ },
27926
+ {
27927
+ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
27928
+ replacement: "<GITHUB_PAT_REDACTED>"
27929
+ },
27930
+ {
27931
+ pattern: /\b(?:gh[opsu]|ghr)_[A-Za-z0-9]{30,}/g,
27932
+ replacement: "<GITHUB_TOKEN_REDACTED>"
27933
+ },
27934
+ {
27935
+ pattern: /\bhf_[A-Za-z0-9]{30,}/g,
27936
+ replacement: "<HUGGINGFACE_TOKEN_REDACTED>"
27937
+ },
27938
+ {
27939
+ pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
27940
+ replacement: "<AWS_ACCESS_KEY_ID_REDACTED>"
27941
+ },
27942
+ {
27943
+ pattern: /\bxox[abprsuvc]-[A-Za-z0-9-]{10,}/g,
27944
+ replacement: "<SLACK_TOKEN_REDACTED>"
27945
+ },
27946
+ {
27947
+ pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g,
27948
+ replacement: "<GOOGLE_API_KEY_REDACTED>"
27949
+ },
27950
+ {
27951
+ pattern: /\b(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]{8,})/gi,
27952
+ replacement: (_full, prefix) => `${prefix}<REDACTED:bearer>`
27953
+ },
27954
+ {
27955
+ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
27956
+ replacement: "<JWT_REDACTED>"
27957
+ },
27958
+ {
27959
+ pattern: /(["'])([^"']*(?:key|token|secret|password|auth|bearer|credential)[^"']*)\1(\s*:\s*)(["'])([^"']*)\4/gi,
27960
+ replacement: (_full, quote, key, separator, valueQuote) => `${quote}${key}${quote}${separator}${valueQuote}<REDACTED:${redactionTypeForKey(key)}>${valueQuote}`
27961
+ },
27962
+ {
27963
+ pattern: /\b([A-Za-z0-9_.-]*(?:key|token|secret|password|auth|bearer|credential)[A-Za-z0-9_.-]*)\s*=\s*([^\s'"`]+)/gi,
27964
+ replacement: (_full, key) => `${key}=<REDACTED:${redactionTypeForKey(key)}>`
27965
+ }
27966
+ ];
27967
+ });
27968
+
27744
27969
  // src/lib/diagnostics-opencode.ts
27745
- import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
27970
+ import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
27746
27971
  import { createRequire as createRequire3 } from "node:module";
27747
- import { homedir as homedir8, tmpdir as tmpdir3, userInfo } from "node:os";
27972
+ import { homedir as homedir10 } from "node:os";
27748
27973
  import { join as join12 } from "node:path";
27749
27974
  function getSelfVersion() {
27750
27975
  const require2 = createRequire3(import.meta.url);
@@ -27764,7 +27989,7 @@ function getPluginCacheInfo() {
27764
27989
  try {
27765
27990
  const installedPkgPath = join12(path4, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
27766
27991
  if (existsSync14(installedPkgPath)) {
27767
- const pkg = JSON.parse(readFileSync9(installedPkgPath, "utf-8"));
27992
+ const pkg = JSON.parse(readFileSync10(installedPkgPath, "utf-8"));
27768
27993
  cached2 = typeof pkg.version === "string" ? pkg.version : undefined;
27769
27994
  }
27770
27995
  } catch {
@@ -27773,7 +27998,7 @@ function getPluginCacheInfo() {
27773
27998
  return { path: path4, cached: cached2, latest: getSelfVersion() };
27774
27999
  }
27775
28000
  function getStorageDir() {
27776
- const dataHome = process.env.XDG_DATA_HOME || join12(homedir8(), ".local", "share");
28001
+ const dataHome = process.env.XDG_DATA_HOME || join12(homedir10(), ".local", "share");
27777
28002
  return join12(dataHome, "cortexkit", "magic-context");
27778
28003
  }
27779
28004
  function fileSize(path4) {
@@ -27783,40 +28008,18 @@ function fileSize(path4) {
27783
28008
  return 0;
27784
28009
  }
27785
28010
  }
27786
- function escapeRegex2(value) {
27787
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27788
- }
27789
28011
  function sanitizeString(value) {
27790
- const home = homedir8();
27791
- const username = userInfo().username;
27792
- let sanitized = value;
27793
- if (home) {
27794
- sanitized = sanitized.replace(new RegExp(escapeRegex2(home), "g"), "~");
27795
- }
27796
- sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
27797
- sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
27798
- sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
27799
- if (username) {
27800
- sanitized = sanitized.replace(new RegExp(escapeRegex2(username), "g"), "<USER>");
27801
- }
27802
- return sanitized;
28012
+ return sanitizePathString(value);
27803
28013
  }
27804
28014
  function sanitizeValue(value) {
27805
- if (typeof value === "string")
27806
- return sanitizeString(value);
27807
- if (Array.isArray(value))
27808
- return value.map(sanitizeValue);
27809
- if (value && typeof value === "object") {
27810
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, sanitizeValue(entry)]));
27811
- }
27812
- return value;
28015
+ return sanitizeConfigValue(value);
27813
28016
  }
27814
28017
  function readConfig2(path4) {
27815
28018
  if (!existsSync14(path4))
27816
28019
  return { value: null };
27817
28020
  try {
27818
- const raw = readFileSync9(path4, "utf-8");
27819
- const value = import_comment_json6.parse(raw);
28021
+ const raw = readFileSync10(path4, "utf-8");
28022
+ const value = import_comment_json7.parse(raw);
27820
28023
  return { value };
27821
28024
  } catch (error51) {
27822
28025
  return { value: null, error: error51 instanceof Error ? error51.message : String(error51) };
@@ -27838,7 +28041,7 @@ function configHasPluginEntry(config2) {
27838
28041
  }
27839
28042
  function parseHistorianDumpMeta(path4) {
27840
28043
  try {
27841
- const xml = readFileSync9(path4, "utf-8");
28044
+ const xml = readFileSync10(path4, "utf-8");
27842
28045
  const parsed = parseCompartmentOutput(xml);
27843
28046
  const factCountByCategory = {};
27844
28047
  for (const fact of parsed.facts) {
@@ -27870,11 +28073,9 @@ function parseHistorianDumpMeta(path4) {
27870
28073
  return { error: error51 instanceof Error ? error51.message : String(error51) };
27871
28074
  }
27872
28075
  }
27873
- function collectHistorianDumps() {
27874
- const dir = join12(tmpdir3(), "magic-context-historian");
27875
- if (!existsSync14(dir)) {
27876
- return { dir, count: 0, recent: [] };
27877
- }
28076
+ function listDumpsInDir(dir, limit) {
28077
+ if (!existsSync14(dir))
28078
+ return { count: 0, recent: [] };
27878
28079
  try {
27879
28080
  const entries = readdirSync2(dir).filter((name) => name.endsWith(".xml")).map((name) => {
27880
28081
  const stat = statSync2(join12(dir, name));
@@ -27885,7 +28086,7 @@ function collectHistorianDumps() {
27885
28086
  };
27886
28087
  }).sort((a, b2) => b2.mtime - a.mtime);
27887
28088
  const now = Date.now();
27888
- const recent = entries.slice(0, 5).map((entry) => {
28089
+ const recent = entries.slice(0, limit).map((entry) => {
27889
28090
  const meta3 = parseHistorianDumpMeta(join12(dir, entry.name));
27890
28091
  const summary = {
27891
28092
  name: entry.name,
@@ -27899,9 +28100,80 @@ function collectHistorianDumps() {
27899
28100
  }
27900
28101
  return summary;
27901
28102
  });
27902
- return { dir, count: entries.length, recent };
28103
+ return { count: entries.length, recent };
27903
28104
  } catch {
27904
- return { dir, count: 0, recent: [] };
28105
+ return { count: 0, recent: [] };
28106
+ }
28107
+ }
28108
+ function collectHistorianDumps(recentSessions) {
28109
+ const buckets = new Map;
28110
+ for (const session of recentSessions) {
28111
+ const dir = session.directory;
28112
+ if (!dir)
28113
+ continue;
28114
+ const projectHistorianDir = getProjectMagicContextHistorianDir(dir);
28115
+ const listing = listDumpsInDir(projectHistorianDir, 5);
28116
+ const existing = buckets.get(dir);
28117
+ if (existing) {
28118
+ if (!existing.sessionIds.includes(session.sessionId)) {
28119
+ existing.sessionIds.push(session.sessionId);
28120
+ }
28121
+ continue;
28122
+ }
28123
+ if (listing.count === 0)
28124
+ continue;
28125
+ buckets.set(dir, {
28126
+ directory: dir,
28127
+ primarySessionId: session.sessionId,
28128
+ sessionIds: [session.sessionId],
28129
+ count: listing.count,
28130
+ recent: listing.recent
28131
+ });
28132
+ }
28133
+ const legacyDir = getMagicContextHistorianDir2("opencode");
28134
+ const legacyListing = listDumpsInDir(legacyDir, 5);
28135
+ return {
28136
+ byProject: [...buckets.values()],
28137
+ legacyDumps: {
28138
+ dir: legacyDir,
28139
+ count: legacyListing.count,
28140
+ recent: legacyListing.recent
28141
+ }
28142
+ };
28143
+ }
28144
+ async function collectRecentSessions() {
28145
+ const opencodeDbPath = join12(homedir10(), ".local", "share", "opencode", "opencode.db");
28146
+ if (!existsSync14(opencodeDbPath))
28147
+ return [];
28148
+ if (typeof globalThis.Bun === "undefined") {
28149
+ return [];
28150
+ }
28151
+ let DatabaseClass;
28152
+ try {
28153
+ const mod = await new Function("p", "return import(p)")("bun:sqlite");
28154
+ DatabaseClass = mod.Database;
28155
+ } catch {
28156
+ return [];
28157
+ }
28158
+ let db = null;
28159
+ try {
28160
+ db = new DatabaseClass(opencodeDbPath, { readonly: true });
28161
+ 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();
28162
+ return rows.flatMap((row) => {
28163
+ const sessionId = typeof row.id === "string" ? row.id : null;
28164
+ const directory = typeof row.directory === "string" ? row.directory : null;
28165
+ if (!sessionId || !directory)
28166
+ return [];
28167
+ const title = typeof row.title === "string" ? row.title : "";
28168
+ const lastActiveAt = typeof row.time_updated === "number" ? new Date(row.time_updated).toISOString() : "";
28169
+ return [{ sessionId, title, directory, lastActiveAt }];
28170
+ });
28171
+ } catch {
28172
+ return [];
28173
+ } finally {
28174
+ try {
28175
+ db?.close();
28176
+ } catch {}
27905
28177
  }
27906
28178
  }
27907
28179
  async function collectHistorianFailures(storageDirPath) {
@@ -27927,7 +28199,7 @@ async function collectHistorianFailures(storageDirPath) {
27927
28199
  const failureCount = typeof row.historian_failure_count === "number" ? row.historian_failure_count : 0;
27928
28200
  const rawError = typeof row.historian_last_error === "string" ? row.historian_last_error : "";
27929
28201
  const lastAt = typeof row.historian_last_failure_at === "number" ? new Date(row.historian_last_failure_at).toISOString() : "";
27930
- const lastError = sanitizeString(rawError.replace(/\s+/g, " ").trim().slice(0, 400));
28202
+ const lastError = sanitizeDiagnosticText(rawError.replace(/\s+/g, " ").trim().slice(0, 400));
27931
28203
  return { sessionId, failureCount, lastError, lastFailureAt: lastAt };
27932
28204
  });
27933
28205
  } catch {
@@ -27946,9 +28218,10 @@ async function collectDiagnostics() {
27946
28218
  const magicContextConfig = readConfig2(configPaths.magicContextConfig);
27947
28219
  const storageDirPath = getStorageDir();
27948
28220
  const contextDbPath = join12(storageDirPath, "context.db");
27949
- const logPath = join12(tmpdir3(), "magic-context.log");
28221
+ const logPath = getMagicContextLogPath2("opencode");
27950
28222
  const logFileSize = existsSync14(logPath) ? statSync2(logPath).size : 0;
27951
28223
  const conflictResult = detectConflicts(process.cwd());
28224
+ const recentSessions = await collectRecentSessions();
27952
28225
  return {
27953
28226
  timestamp: new Date().toISOString(),
27954
28227
  platform: process.platform,
@@ -27980,7 +28253,8 @@ async function collectDiagnostics() {
27980
28253
  exists: existsSync14(logPath),
27981
28254
  sizeKb: Math.round(logFileSize / 1024)
27982
28255
  },
27983
- historianDumps: collectHistorianDumps(),
28256
+ recentSessions,
28257
+ historianDumps: collectHistorianDumps(recentSessions),
27984
28258
  historianFailures: await collectHistorianFailures(storageDirPath)
27985
28259
  };
27986
28260
  }
@@ -28014,10 +28288,25 @@ function renderDiagnosticsMarkdown(report) {
28014
28288
  context_db_size: formatBytes(report.storageDir.contextDbSizeBytes)
28015
28289
  };
28016
28290
  const historianDumps = {
28017
- dir: sanitizeString(report.historianDumps.dir),
28018
- count: report.historianDumps.count,
28019
- recent: report.historianDumps.recent
28291
+ byProject: report.historianDumps.byProject.map((bucket) => ({
28292
+ directory: sanitizeString(bucket.directory),
28293
+ primarySessionId: bucket.primarySessionId,
28294
+ sessionIds: bucket.sessionIds,
28295
+ count: bucket.count,
28296
+ recent: bucket.recent
28297
+ })),
28298
+ legacyDumps: {
28299
+ dir: sanitizeString(report.historianDumps.legacyDumps.dir),
28300
+ count: report.historianDumps.legacyDumps.count,
28301
+ recent: report.historianDumps.legacyDumps.recent
28302
+ }
28020
28303
  };
28304
+ const recentSessions = report.recentSessions.map((session) => ({
28305
+ sessionId: session.sessionId,
28306
+ title: session.title,
28307
+ directory: sanitizeString(session.directory),
28308
+ lastActiveAt: session.lastActiveAt
28309
+ }));
28021
28310
  return [
28022
28311
  `- Timestamp: ${report.timestamp}`,
28023
28312
  `- Plugin: v${report.pluginVersion}`,
@@ -28036,7 +28325,7 @@ function renderDiagnosticsMarkdown(report) {
28036
28325
  "",
28037
28326
  "### magic-context.jsonc flags",
28038
28327
  "```jsonc",
28039
- JSON.stringify(report.magicContextConfig.flags, null, 2),
28328
+ JSON.stringify(sanitizeConfigValue(report.magicContextConfig.flags), null, 2),
28040
28329
  "```",
28041
28330
  "",
28042
28331
  "### Plugin cache",
@@ -28048,15 +28337,24 @@ function renderDiagnosticsMarkdown(report) {
28048
28337
  "```json",
28049
28338
  JSON.stringify(storage, null, 2),
28050
28339
  "```",
28340
+ "",
28341
+ "### Recent sessions",
28342
+ recentSessions.length === 0 ? "_No recent OpenCode sessions found (or OpenCode DB unavailable on this runtime)._" : ["```json", JSON.stringify(recentSessions, null, 2), "```"].join(`
28343
+ `),
28051
28344
  "",
28052
28345
  "### Historian dumps",
28053
28346
  "(Metadata only — XML content is not included in this report.)",
28347
+ "Dumps are stored per-project under `<project>/.opencode/magic-context/historian/`.",
28054
28348
  "```json",
28055
28349
  JSON.stringify(historianDumps, null, 2),
28056
28350
  "```",
28057
28351
  "",
28058
28352
  "### Historian failures (session_meta)",
28059
- report.historianFailures.length === 0 ? "_No sessions with historian failures._" : ["```json", JSON.stringify(report.historianFailures, null, 2), "```"].join(`
28353
+ report.historianFailures.length === 0 ? "_No sessions with historian failures._" : [
28354
+ "```json",
28355
+ JSON.stringify(sanitizeConfigValue(report.historianFailures), null, 2),
28356
+ "```"
28357
+ ].join(`
28060
28358
  `),
28061
28359
  "",
28062
28360
  "### Log file",
@@ -28066,45 +28364,24 @@ function renderDiagnosticsMarkdown(report) {
28066
28364
  ].join(`
28067
28365
  `);
28068
28366
  }
28069
- var import_comment_json6, PLUGIN_NAME5 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION;
28367
+ var import_comment_json7, PLUGIN_NAME5 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION;
28070
28368
  var init_diagnostics_opencode = __esm(() => {
28071
28369
  init_compartment_parser();
28072
28370
  init_conflict_detector();
28073
28371
  init_data_path();
28074
28372
  init_opencode_helpers();
28075
28373
  init_paths();
28076
- import_comment_json6 = __toESM(require_src2(), 1);
28374
+ init_redaction();
28375
+ import_comment_json7 = __toESM(require_src2(), 1);
28077
28376
  PLUGIN_ENTRY_WITH_VERSION = `${PLUGIN_NAME5}@latest`;
28078
28377
  });
28079
28378
 
28080
28379
  // src/lib/logs-opencode.ts
28081
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
28082
- import { homedir as homedir9, userInfo as userInfo2 } from "node:os";
28380
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "node:fs";
28381
+ import { homedir as homedir11 } from "node:os";
28083
28382
  import { join as join13 } from "node:path";
28084
- function escapeRegex3(value) {
28085
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28086
- }
28087
28383
  function sanitizeLogContent(content) {
28088
- const home = homedir9();
28089
- const username = userInfo2().username;
28090
- let sanitized = content;
28091
- if (home) {
28092
- sanitized = sanitized.replace(new RegExp(escapeRegex3(home), "g"), "~");
28093
- }
28094
- sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
28095
- sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
28096
- sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
28097
- if (username) {
28098
- sanitized = sanitized.replace(new RegExp(escapeRegex3(username), "g"), "<USER>");
28099
- }
28100
- for (const { pattern, replacement } of SECRET_PATTERNS) {
28101
- if (typeof replacement === "string") {
28102
- sanitized = sanitized.replace(pattern, replacement);
28103
- } else {
28104
- sanitized = sanitized.replace(pattern, replacement);
28105
- }
28106
- }
28107
- return sanitized;
28384
+ return sanitizeDiagnosticText(content);
28108
28385
  }
28109
28386
  function formatTimestamp(date5) {
28110
28387
  const pad = (value) => String(value).padStart(2, "0");
@@ -28131,16 +28408,28 @@ function extractHistorianFailureLines(sanitized, limit = 30) {
28131
28408
  }
28132
28409
  return matches.reverse();
28133
28410
  }
28134
- async function bundleIssueReport(report, description, _title) {
28411
+ function filterLogLinesBySession(lines, sessionId) {
28412
+ if (!sessionId)
28413
+ return lines;
28414
+ const otherSessionPattern = /\bses_[A-Za-z0-9]{8,32}\b/g;
28415
+ return lines.filter((line) => {
28416
+ const matches = line.match(otherSessionPattern);
28417
+ if (!matches)
28418
+ return true;
28419
+ return matches.every((id) => id === sessionId);
28420
+ });
28421
+ }
28422
+ async function bundleIssueReport(report, description, _title, sessionFilter = null) {
28135
28423
  const LOG_TAIL_LINES = 400;
28136
- const logLines = report.logFile.exists ? readFileSync10(report.logFile.path, "utf-8").split(/\r?\n/) : [];
28424
+ const allLogLines = report.logFile.exists ? readFileSync11(report.logFile.path, "utf-8").split(/\r?\n/) : [];
28425
+ const logLines = filterLogLinesBySession(allLogLines, sessionFilter);
28137
28426
  const recentLog = sanitizeLogContent(logLines.slice(-LOG_TAIL_LINES).join(`
28138
28427
  `)).trim();
28139
28428
  const historianScanWindow = sanitizeLogContent(logLines.slice(-4000).join(`
28140
28429
  `));
28141
28430
  const historianFailureLines = extractHistorianFailureLines(historianScanWindow, 30);
28142
- const configBody = JSON.stringify(report.magicContextConfig.flags, null, 2);
28143
- const sanitizedConfigPath = report.configPaths.magicContextConfig.replace(homedir9(), "~");
28431
+ const configBody = JSON.stringify(sanitizeConfigValue(report.magicContextConfig.flags), null, 2);
28432
+ const sanitizedConfigPath = report.configPaths.magicContextConfig.replace(homedir11(), "~");
28144
28433
  const bodyMarkdown = [
28145
28434
  "## Description",
28146
28435
  description,
@@ -28176,76 +28465,10 @@ async function bundleIssueReport(report, description, _title) {
28176
28465
  `);
28177
28466
  return { path: path4, bodyMarkdown };
28178
28467
  }
28179
- var SECRET_PATTERNS, HISTORIAN_LOG_PATTERNS;
28468
+ var HISTORIAN_LOG_PATTERNS;
28180
28469
  var init_logs_opencode = __esm(() => {
28181
28470
  init_diagnostics_opencode();
28182
- SECRET_PATTERNS = [
28183
- {
28184
- name: "anthropic_api_key",
28185
- pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
28186
- replacement: "<ANTHROPIC_API_KEY_REDACTED>"
28187
- },
28188
- {
28189
- name: "openai_api_key",
28190
- pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/g,
28191
- replacement: "<OPENAI_API_KEY_REDACTED>"
28192
- },
28193
- {
28194
- name: "github_pat_fine_grained",
28195
- pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
28196
- replacement: "<GITHUB_PAT_REDACTED>"
28197
- },
28198
- {
28199
- name: "github_token_classic",
28200
- pattern: /\b(?:gh[opsu]|ghr)_[A-Za-z0-9]{30,}/g,
28201
- replacement: "<GITHUB_TOKEN_REDACTED>"
28202
- },
28203
- {
28204
- name: "huggingface_token",
28205
- pattern: /\bhf_[A-Za-z0-9]{30,}/g,
28206
- replacement: "<HUGGINGFACE_TOKEN_REDACTED>"
28207
- },
28208
- {
28209
- name: "aws_access_key_id",
28210
- pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
28211
- replacement: "<AWS_ACCESS_KEY_ID_REDACTED>"
28212
- },
28213
- {
28214
- name: "aws_secret_access_key",
28215
- pattern: /\b(aws[_-]?secret[_-]?access[_-]?key\s*[=:]\s*)([A-Za-z0-9/+=]{40})\b/gi,
28216
- replacement: (_full, prefix) => `${prefix}<AWS_SECRET_REDACTED>`
28217
- },
28218
- {
28219
- name: "slack_token",
28220
- pattern: /\bxox[abprsuvc]-[A-Za-z0-9-]{10,}/g,
28221
- replacement: "<SLACK_TOKEN_REDACTED>"
28222
- },
28223
- {
28224
- name: "google_api_key",
28225
- pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g,
28226
- replacement: "<GOOGLE_API_KEY_REDACTED>"
28227
- },
28228
- {
28229
- name: "secret_env_assignment",
28230
- pattern: /\b([A-Z][A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|PRIVATE[_-]?KEY))\s*=\s*([^\s'"]+)/g,
28231
- replacement: (_full, key) => `${key}=<REDACTED>`
28232
- },
28233
- {
28234
- name: "secret_json_assignment",
28235
- pattern: /("(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer[_-]?token|client[_-]?secret|password|private[_-]?key|secret[_-]?key)"\s*:\s*)"([^"]+)"/gi,
28236
- replacement: (_full, prefix) => `${prefix}"<REDACTED>"`
28237
- },
28238
- {
28239
- name: "bearer_token",
28240
- pattern: /\b(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]{16,})/gi,
28241
- replacement: (_full, prefix) => `${prefix}<BEARER_TOKEN_REDACTED>`
28242
- },
28243
- {
28244
- name: "jwt_token",
28245
- pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
28246
- replacement: "<JWT_REDACTED>"
28247
- }
28248
- ];
28471
+ init_redaction();
28249
28472
  HISTORIAN_LOG_PATTERNS = [
28250
28473
  /historian failure:/,
28251
28474
  /historian failure recorded:/,
@@ -28258,10 +28481,10 @@ var init_logs_opencode = __esm(() => {
28258
28481
  });
28259
28482
 
28260
28483
  // src/commands/doctor-opencode.ts
28261
- import { execSync as execSync3, spawnSync as spawnSync2 } from "node:child_process";
28262
- import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync11, rmSync, statSync as statSync3, writeFileSync as writeFileSync10 } from "node:fs";
28484
+ import { execSync as execSync2, spawnSync as spawnSync2 } from "node:child_process";
28485
+ import { existsSync as existsSync15, readFileSync as readFileSync12, rmSync, statSync as statSync3, writeFileSync as writeFileSync10 } from "node:fs";
28263
28486
  import { createRequire as createRequire4 } from "node:module";
28264
- import { homedir as homedir10, tmpdir as tmpdir4 } from "node:os";
28487
+ import { homedir as homedir12 } from "node:os";
28265
28488
  import { join as join14 } from "node:path";
28266
28489
  async function fetchNpmLatest(pkg, timeoutMs = 5000) {
28267
28490
  try {
@@ -28320,7 +28543,7 @@ async function clearPluginCache(force = false) {
28320
28543
  try {
28321
28544
  const installedPkgPath = join14(pluginCacheDir, "node_modules", "@cortexkit", "opencode-magic-context", "package.json");
28322
28545
  if (existsSync15(installedPkgPath)) {
28323
- const pkg = JSON.parse(readFileSync11(installedPkgPath, "utf-8"));
28546
+ const pkg = JSON.parse(readFileSync12(installedPkgPath, "utf-8"));
28324
28547
  if (typeof pkg?.version === "string") {
28325
28548
  cachedVersion = pkg.version;
28326
28549
  }
@@ -28358,7 +28581,7 @@ async function clearPluginCache(force = false) {
28358
28581
  }
28359
28582
  function isGhInstalled() {
28360
28583
  try {
28361
- execSync3("gh --version", { stdio: "pipe" });
28584
+ execSync2("gh --version", { stdio: "pipe" });
28362
28585
  return true;
28363
28586
  } catch {
28364
28587
  return false;
@@ -28395,7 +28618,27 @@ async function runIssueFlow() {
28395
28618
  s.start("Collecting diagnostics");
28396
28619
  try {
28397
28620
  const report = await collectDiagnostics();
28398
- const bundled = await bundleIssueReport(report, description, title);
28621
+ s.stop("Diagnostics collected");
28622
+ let sessionFilter = null;
28623
+ if (report.recentSessions.length > 1) {
28624
+ const choice = await selectOne("Which session is this issue about? (filters log lines from other sessions)", [
28625
+ ...report.recentSessions.map((session, index) => {
28626
+ const displayTitle = session.title.trim() || "(no title)";
28627
+ const truncatedTitle = displayTitle.length > 50 ? `${displayTitle.slice(0, 47)}...` : displayTitle;
28628
+ return {
28629
+ label: `${truncatedTitle} — ${session.sessionId}${index === 0 ? " (most recent)" : ""}`,
28630
+ value: session.sessionId
28631
+ };
28632
+ }),
28633
+ {
28634
+ label: "All sessions (no filtering)",
28635
+ value: "__all__"
28636
+ }
28637
+ ]);
28638
+ sessionFilter = choice === "__all__" ? null : choice;
28639
+ }
28640
+ s.start("Bundling issue report");
28641
+ const bundled = await bundleIssueReport(report, description, title, sessionFilter);
28399
28642
  s.stop(`Report written to ${bundled.path}`);
28400
28643
  const shouldSubmit = await confirm("Submit this issue on GitHub now?", true);
28401
28644
  if (shouldSubmit && isGhInstalled()) {
@@ -28437,7 +28680,7 @@ async function checkEmbeddingConfig(magicContextConfigPath) {
28437
28680
  }
28438
28681
  let rawText;
28439
28682
  try {
28440
- rawText = readFileSync11(magicContextConfigPath, "utf-8");
28683
+ rawText = readFileSync12(magicContextConfigPath, "utf-8");
28441
28684
  } catch {
28442
28685
  log.warn("Could not read magic-context.jsonc for embedding check");
28443
28686
  return { issues: 1 };
@@ -28448,7 +28691,7 @@ async function checkEmbeddingConfig(magicContextConfigPath) {
28448
28691
  });
28449
28692
  let parsedConfig;
28450
28693
  try {
28451
- parsedConfig = import_comment_json7.parse(substituted.text);
28694
+ parsedConfig = import_comment_json8.parse(substituted.text);
28452
28695
  } catch (error51) {
28453
28696
  log.warn(`Embedding check skipped — could not parse magic-context.jsonc: ${error51 instanceof Error ? error51.message : String(error51)}`);
28454
28697
  return { issues: 1 };
@@ -28588,12 +28831,12 @@ async function runDoctor(options = {}) {
28588
28831
  if (existsSync15(paths.magicContextConfig)) {
28589
28832
  pass(`Magic Context config: ${paths.magicContextConfig}`);
28590
28833
  try {
28591
- const raw = readFileSync11(paths.magicContextConfig, "utf-8");
28834
+ const raw = readFileSync12(paths.magicContextConfig, "utf-8");
28592
28835
  const substituted = substituteConfigVariables({
28593
28836
  text: raw,
28594
28837
  configPath: paths.magicContextConfig
28595
28838
  }).text;
28596
- import_comment_json7.parse(substituted);
28839
+ import_comment_json8.parse(substituted);
28597
28840
  pass("magic-context.jsonc parses as valid JSONC");
28598
28841
  } catch (err) {
28599
28842
  fail(`magic-context.jsonc parse failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -28615,8 +28858,8 @@ async function runDoctor(options = {}) {
28615
28858
  }
28616
28859
  if (existsSync15(paths.magicContextConfig)) {
28617
28860
  try {
28618
- const mcRaw = readFileSync11(paths.magicContextConfig, "utf-8");
28619
- const mcConfig = import_comment_json7.parse(mcRaw);
28861
+ const mcRaw = readFileSync12(paths.magicContextConfig, "utf-8");
28862
+ const mcConfig = import_comment_json8.parse(mcRaw);
28620
28863
  let mcChanged = false;
28621
28864
  const experimental = mcConfig.experimental;
28622
28865
  if (experimental && "compaction_markers" in experimental) {
@@ -28691,7 +28934,7 @@ async function runDoctor(options = {}) {
28691
28934
  fixed++;
28692
28935
  }
28693
28936
  if (mcChanged) {
28694
- writeFileSync10(paths.magicContextConfig, `${import_comment_json7.stringify(mcConfig, null, 2)}
28937
+ writeFileSync10(paths.magicContextConfig, `${import_comment_json8.stringify(mcConfig, null, 2)}
28695
28938
  `);
28696
28939
  }
28697
28940
  } catch {
@@ -28700,8 +28943,8 @@ async function runDoctor(options = {}) {
28700
28943
  }
28701
28944
  if (paths.opencodeConfigFormat !== "none") {
28702
28945
  try {
28703
- const raw = readFileSync11(paths.opencodeConfig, "utf-8");
28704
- const config2 = import_comment_json7.parse(raw);
28946
+ const raw = readFileSync12(paths.opencodeConfig, "utf-8");
28947
+ const config2 = import_comment_json8.parse(raw);
28705
28948
  const rawPlugins = Array.isArray(config2?.plugin) ? config2.plugin : [];
28706
28949
  const existingIdx = rawPlugins.findIndex((entry) => matchesPluginEntry(entry, PLUGIN_NAME6) || isDevPathPluginEntry(entry));
28707
28950
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
@@ -28732,7 +28975,7 @@ async function runDoctor(options = {}) {
28732
28975
  rawPlugins[existingIdx] = PLUGIN_ENTRY_WITH_VERSION2;
28733
28976
  }
28734
28977
  config2.plugin = rawPlugins;
28735
- writeFileSync10(paths.opencodeConfig, `${import_comment_json7.stringify(config2, null, 2)}
28978
+ writeFileSync10(paths.opencodeConfig, `${import_comment_json8.stringify(config2, null, 2)}
28736
28979
  `);
28737
28980
  pass(`Upgraded plugin entry in ${configName}: ${oldEntryStr} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
28738
28981
  fixed++;
@@ -28741,7 +28984,7 @@ async function runDoctor(options = {}) {
28741
28984
  } else {
28742
28985
  rawPlugins.push(PLUGIN_ENTRY_WITH_VERSION2);
28743
28986
  config2.plugin = rawPlugins;
28744
- writeFileSync10(paths.opencodeConfig, `${import_comment_json7.stringify(config2, null, 2)}
28987
+ writeFileSync10(paths.opencodeConfig, `${import_comment_json8.stringify(config2, null, 2)}
28745
28988
  `);
28746
28989
  pass(`Added plugin to ${configName}`);
28747
28990
  fixed++;
@@ -28774,8 +29017,8 @@ async function runDoctor(options = {}) {
28774
29017
  fixed++;
28775
29018
  } else if (existsSync15(paths.tuiConfig)) {
28776
29019
  try {
28777
- const tuiRaw = readFileSync11(paths.tuiConfig, "utf-8");
28778
- const tuiConfig = import_comment_json7.parse(tuiRaw);
29020
+ const tuiRaw = readFileSync12(paths.tuiConfig, "utf-8");
29021
+ const tuiConfig = import_comment_json8.parse(tuiRaw);
28779
29022
  const tuiRawPlugins = Array.isArray(tuiConfig?.plugin) ? tuiConfig.plugin : [];
28780
29023
  const tuiIdx = tuiRawPlugins.findIndex((entry) => matchesPluginEntry(entry, PLUGIN_NAME6) || isDevPathPluginEntry(entry));
28781
29024
  const tuiEntryAsString = (entry) => {
@@ -28803,7 +29046,7 @@ async function runDoctor(options = {}) {
28803
29046
  tuiRawPlugins[tuiIdx] = PLUGIN_ENTRY_WITH_VERSION2;
28804
29047
  }
28805
29048
  tuiConfig.plugin = tuiRawPlugins;
28806
- writeFileSync10(paths.tuiConfig, `${import_comment_json7.stringify(tuiConfig, null, 2)}
29049
+ writeFileSync10(paths.tuiConfig, `${import_comment_json8.stringify(tuiConfig, null, 2)}
28807
29050
  `);
28808
29051
  pass(`Upgraded TUI plugin: ${tuiEntryStr} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
28809
29052
  fixed++;
@@ -28822,8 +29065,8 @@ async function runDoctor(options = {}) {
28822
29065
  }
28823
29066
  if (existsSync15(paths.magicContextConfig)) {
28824
29067
  try {
28825
- const mcRaw = readFileSync11(paths.magicContextConfig, "utf-8");
28826
- const mcConfig = import_comment_json7.parse(mcRaw);
29068
+ const mcRaw = readFileSync12(paths.magicContextConfig, "utf-8");
29069
+ const mcConfig = import_comment_json8.parse(mcRaw);
28827
29070
  const dreamerObj = mcConfig?.dreamer;
28828
29071
  const dreamerEnabled = dreamerObj?.enabled === true;
28829
29072
  const userMemObj = dreamerObj?.user_memories;
@@ -28902,10 +29145,10 @@ async function runDoctor(options = {}) {
28902
29145
  }
28903
29146
  {
28904
29147
  const ageWarnings = [];
28905
- const npmrcPath = join14(homedir10(), ".npmrc");
29148
+ const npmrcPath = join14(homedir12(), ".npmrc");
28906
29149
  if (existsSync15(npmrcPath)) {
28907
29150
  try {
28908
- const npmrc = readFileSync11(npmrcPath, "utf-8");
29151
+ const npmrc = readFileSync12(npmrcPath, "utf-8");
28909
29152
  for (const line of npmrc.split(`
28910
29153
  `)) {
28911
29154
  const trimmed = line.trim();
@@ -28928,7 +29171,7 @@ async function runDoctor(options = {}) {
28928
29171
  issues++;
28929
29172
  }
28930
29173
  }
28931
- const logPath = join14(tmpdir4(), "magic-context.log");
29174
+ const logPath = getMagicContextLogPath2("opencode");
28932
29175
  if (existsSync15(logPath)) {
28933
29176
  const logStat = statSync3(logPath);
28934
29177
  const sizeKb = (logStat.size / 1024).toFixed(0);
@@ -28936,25 +29179,27 @@ async function runDoctor(options = {}) {
28936
29179
  } else {
28937
29180
  log.info(`Log file: ${logPath} (not yet created)`);
28938
29181
  }
28939
- const historianDumpDir = join14(tmpdir4(), "magic-context-historian");
28940
- if (existsSync15(historianDumpDir)) {
28941
- try {
28942
- const dumps = readdirSync3(historianDumpDir).filter((f) => f.endsWith(".xml")).map((f) => ({
28943
- name: f,
28944
- mtime: statSync3(join14(historianDumpDir, f)).mtimeMs
28945
- })).sort((a, b2) => b2.mtime - a.mtime);
28946
- if (dumps.length > 0) {
28947
- warn(`Historian debug dumps: ${dumps.length} file(s) in ${historianDumpDir}`);
28948
- for (const dump of dumps.slice(0, 3)) {
28949
- const age = Math.round((Date.now() - dump.mtime) / 60000);
28950
- const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
28951
- log.info(` ${dump.name} (${ageStr})`);
28952
- }
28953
- if (dumps.length > 3) {
28954
- log.info(` ... and ${dumps.length - 3} more`);
28955
- }
29182
+ const diagnostics = await collectDiagnostics();
29183
+ const dumpBuckets = diagnostics.historianDumps.byProject;
29184
+ if (dumpBuckets.length > 0) {
29185
+ const totalCount = dumpBuckets.reduce((sum, b2) => sum + b2.count, 0);
29186
+ const sessionCount = dumpBuckets.length;
29187
+ warn(`Historian debug dumps: ${totalCount} file(s) across ${sessionCount} project(s)`);
29188
+ for (const bucket of dumpBuckets) {
29189
+ log.info(` [${bucket.directory}] ${bucket.count} file(s)`);
29190
+ for (const dump of bucket.recent.slice(0, 3)) {
29191
+ const age = dump.ageMinutes;
29192
+ const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
29193
+ log.info(` ${dump.name} (${ageStr})`);
28956
29194
  }
28957
- } catch {}
29195
+ if (bucket.count > 3) {
29196
+ log.info(` ... and ${bucket.count - 3} more`);
29197
+ }
29198
+ }
29199
+ }
29200
+ const legacy = diagnostics.historianDumps.legacyDumps;
29201
+ if (legacy.count > 0) {
29202
+ log.info(`Legacy historian dumps (pre-v0.18.x): ${legacy.count} file(s) in ${legacy.dir}`);
28958
29203
  }
28959
29204
  if (paths.omoConfig) {
28960
29205
  log.info(`OMO config found: ${paths.omoConfig}`);
@@ -28973,7 +29218,7 @@ async function runDoctor(options = {}) {
28973
29218
  }
28974
29219
  return 0;
28975
29220
  }
28976
- var import_comment_json7, PLUGIN_NAME6 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION2, CLI_PACKAGE_NAME = "@cortexkit/magic-context";
29221
+ var import_comment_json8, PLUGIN_NAME6 = "@cortexkit/opencode-magic-context", PLUGIN_ENTRY_WITH_VERSION2, CLI_PACKAGE_NAME = "@cortexkit/magic-context";
28977
29222
  var init_doctor_opencode = __esm(async () => {
28978
29223
  init_config();
28979
29224
  init_variable();
@@ -28988,9 +29233,10 @@ var init_doctor_opencode = __esm(async () => {
28988
29233
  init_paths();
28989
29234
  init_prompts();
28990
29235
  await init_sqlite();
28991
- import_comment_json7 = __toESM(require_src2(), 1);
29236
+ import_comment_json8 = __toESM(require_src2(), 1);
28992
29237
  PLUGIN_ENTRY_WITH_VERSION2 = `${PLUGIN_NAME6}@latest`;
28993
29238
  });
29239
+
28994
29240
  // ../plugin/src/features/magic-context/compartment-storage.ts
28995
29241
  var insertCompartmentStatements, insertFactStatements;
28996
29242
  var init_compartment_storage = __esm(() => {
@@ -29130,7 +29376,7 @@ function binarySearchBytes(sortedArray, key) {
29130
29376
  }
29131
29377
  return -1;
29132
29378
  }
29133
- function escapeRegex4(str) {
29379
+ function escapeRegex3(str) {
29134
29380
  return str.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
29135
29381
  }
29136
29382
  function bytePairMerge(piece, stringRanks, binaryFirstByteIndex) {
@@ -29240,7 +29486,7 @@ var Tokenizer = class {
29240
29486
  };
29241
29487
  this.specialTokenKeys = Object.keys(this.specialTokens);
29242
29488
  this.hasSpecialTokens = this.specialTokenKeys.length > 0;
29243
- this.specialTokensRegex = this.hasSpecialTokens ? new RegExp(this.specialTokenKeys.map(escapeRegex4).join("|"), "g") : null;
29489
+ this.specialTokensRegex = this.hasSpecialTokens ? new RegExp(this.specialTokenKeys.map(escapeRegex3).join("|"), "g") : null;
29244
29490
  this.inverseSpecialTokens = {};
29245
29491
  for (const [text2, rank] of Object.entries(this.specialTokens))
29246
29492
  this.inverseSpecialTokens[rank] = textEncoder.encode(text2);
@@ -29270,7 +29516,7 @@ var Tokenizer = class {
29270
29516
  const allowedSet = new Set(allowedSpecial === "all" ? this.specialTokenKeys : allowedSpecial);
29271
29517
  const disallowedSet = new Set(disallowedSpecial === "all" ? this.specialTokenKeys.filter((x2) => !allowedSet.has(x2)) : disallowedSpecial);
29272
29518
  if (disallowedSet.size > 0) {
29273
- const disallowedRegex = new RegExp([...disallowedSet].map(escapeRegex4).join("|"), "g");
29519
+ const disallowedRegex = new RegExp([...disallowedSet].map(escapeRegex3).join("|"), "g");
29274
29520
  const match = text2.match(disallowedRegex);
29275
29521
  if (match)
29276
29522
  throw new Error(`Text contains disallowed special token: ${match[0]}`);
@@ -161776,6 +162022,9 @@ var init_migrations = __esm(async () => {
161776
162022
  embedding BLOB NOT NULL,
161777
162023
  model_id TEXT NOT NULL,
161778
162024
  created_at INTEGER NOT NULL,
162025
+ -- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
162026
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
162027
+ -- every connection and v12 cleans historical orphan rows.
161779
162028
  FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
161780
162029
  );
161781
162030
 
@@ -161896,10 +162145,51 @@ var init_migrations = __esm(async () => {
161896
162145
  WHERE type = 'tool' AND tool_owner_message_id IS NULL;
161897
162146
  `);
161898
162147
  }
162148
+ },
162149
+ {
162150
+ version: 11,
162151
+ description: "Add todo state synthesis columns to session_meta",
162152
+ up: (db) => {
162153
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
162154
+ if (!cols.some((c2) => c2.name === "last_todo_state")) {
162155
+ db.exec("ALTER TABLE session_meta ADD COLUMN last_todo_state TEXT DEFAULT ''");
162156
+ }
162157
+ if (!cols.some((c2) => c2.name === "todo_synthetic_call_id")) {
162158
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_call_id TEXT DEFAULT ''");
162159
+ }
162160
+ if (!cols.some((c2) => c2.name === "todo_synthetic_anchor_message_id")) {
162161
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_anchor_message_id TEXT DEFAULT ''");
162162
+ }
162163
+ if (!cols.some((c2) => c2.name === "todo_synthetic_state_json")) {
162164
+ db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
162165
+ }
162166
+ }
162167
+ },
162168
+ {
162169
+ version: 12,
162170
+ description: "Clean orphan rows from FK-cascade embedding tables",
162171
+ up: (db) => {
162172
+ const hasTable = (name2) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
162173
+ const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
162174
+ WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
162175
+ log2(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
162176
+ const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
162177
+ WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
162178
+ log2(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
162179
+ }
162180
+ },
162181
+ {
162182
+ version: 13,
162183
+ description: "Add pending_compaction_marker_state column for deferred marker drain",
162184
+ up: (db) => {
162185
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
162186
+ if (!cols.some((c2) => c2.name === "pending_compaction_marker_state")) {
162187
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
162188
+ }
162189
+ }
161899
162190
  }
161900
162191
  ];
161901
162192
  });
161902
-
161903
162193
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
161904
162194
  function keyFor(providerID, modelID, agentName) {
161905
162195
  const agent = agentName && agentName.length > 0 ? agentName : "default";
@@ -162219,9 +162509,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
162219
162509
  }
162220
162510
  }
162221
162511
  function initializeDatabase(db) {
162512
+ db.exec("PRAGMA foreign_keys=ON");
162222
162513
  db.exec("PRAGMA journal_mode=WAL");
162223
162514
  db.exec("PRAGMA busy_timeout=5000");
162224
- db.exec("PRAGMA foreign_keys=ON");
162225
162515
  db.exec(`
162226
162516
  CREATE TABLE IF NOT EXISTS tags (
162227
162517
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -162319,6 +162609,9 @@ function initializeDatabase(db) {
162319
162609
  );
162320
162610
 
162321
162611
  CREATE TABLE IF NOT EXISTS memory_embeddings (
162612
+ -- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
162613
+ -- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
162614
+ -- every connection and v12 cleans historical orphan rows.
162322
162615
  memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
162323
162616
  embedding BLOB NOT NULL,
162324
162617
  model_id TEXT
@@ -162411,6 +162704,10 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162411
162704
  note_nudge_trigger_message_id TEXT DEFAULT '',
162412
162705
  note_nudge_sticky_text TEXT DEFAULT '',
162413
162706
  note_nudge_sticky_message_id TEXT DEFAULT '',
162707
+ last_todo_state TEXT DEFAULT '',
162708
+ todo_synthetic_call_id TEXT DEFAULT '',
162709
+ todo_synthetic_anchor_message_id TEXT DEFAULT '',
162710
+ todo_synthetic_state_json TEXT DEFAULT '',
162414
162711
  is_subagent INTEGER DEFAULT 0,
162415
162712
  last_context_percentage REAL DEFAULT 0,
162416
162713
  last_input_tokens INTEGER DEFAULT 0,
@@ -162422,7 +162719,13 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162422
162719
  system_prompt_hash TEXT DEFAULT '',
162423
162720
  memory_block_cache TEXT DEFAULT '',
162424
162721
  memory_block_count INTEGER DEFAULT 0,
162425
- memory_block_ids TEXT DEFAULT ''
162722
+ memory_block_ids TEXT DEFAULT '',
162723
+ -- pending_compaction_marker_state: intentionally NULLABLE without a
162724
+ -- default. Absence of a deferred marker is SQL NULL; presence is a
162725
+ -- valid JSON blob written via setPendingCompactionMarkerState.
162726
+ -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
162727
+ -- != empty-string defensively. Plan v6 section 3.
162728
+ pending_compaction_marker_state TEXT
162426
162729
  );
162427
162730
 
162428
162731
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
@@ -162475,6 +162778,10 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162475
162778
  ensureColumn(db, "session_meta", "note_nudge_trigger_message_id", "TEXT DEFAULT ''");
162476
162779
  ensureColumn(db, "session_meta", "note_nudge_sticky_text", "TEXT DEFAULT ''");
162477
162780
  ensureColumn(db, "session_meta", "note_nudge_sticky_message_id", "TEXT DEFAULT ''");
162781
+ ensureColumn(db, "session_meta", "last_todo_state", "TEXT DEFAULT ''");
162782
+ ensureColumn(db, "session_meta", "todo_synthetic_call_id", "TEXT DEFAULT ''");
162783
+ ensureColumn(db, "session_meta", "todo_synthetic_anchor_message_id", "TEXT DEFAULT ''");
162784
+ ensureColumn(db, "session_meta", "todo_synthetic_state_json", "TEXT DEFAULT ''");
162478
162785
  ensureColumn(db, "session_meta", "note_last_read_at", "INTEGER DEFAULT 0");
162479
162786
  ensureColumn(db, "session_meta", "times_execute_threshold_reached", "INTEGER DEFAULT 0");
162480
162787
  ensureColumn(db, "session_meta", "compartment_in_progress", "INTEGER DEFAULT 0");
@@ -162506,6 +162813,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
162506
162813
  ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
162507
162814
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
162508
162815
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
162816
+ ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
162509
162817
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
162510
162818
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
162511
162819
  ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -162539,6 +162847,10 @@ function healNullTextColumns(db) {
162539
162847
  ["note_nudge_trigger_message_id", ""],
162540
162848
  ["note_nudge_sticky_text", ""],
162541
162849
  ["note_nudge_sticky_message_id", ""],
162850
+ ["last_todo_state", ""],
162851
+ ["todo_synthetic_call_id", ""],
162852
+ ["todo_synthetic_anchor_message_id", ""],
162853
+ ["todo_synthetic_state_json", ""],
162542
162854
  ["system_prompt_hash", ""],
162543
162855
  ["stripped_placeholder_ids", ""],
162544
162856
  ["memory_block_cache", ""],
@@ -162733,15 +163045,15 @@ var init_storage = __esm(async () => {
162733
163045
  });
162734
163046
 
162735
163047
  // ../pi-plugin/src/config/index.ts
162736
- import { existsSync as existsSync18, readFileSync as readFileSync12 } from "node:fs";
162737
- import { homedir as homedir11 } from "node:os";
163048
+ import { existsSync as existsSync18, readFileSync as readFileSync13 } from "node:fs";
163049
+ import { homedir as homedir13 } from "node:os";
162738
163050
  import { join as join17 } from "node:path";
162739
163051
  function getProjectConfigPaths(cwd) {
162740
163052
  const basePath = join17(cwd, ".pi", CONFIG_FILE_NAME);
162741
163053
  return [`${basePath}.jsonc`, `${basePath}.json`];
162742
163054
  }
162743
163055
  function getUserConfigPaths() {
162744
- const home = process.env.HOME ?? homedir11();
163056
+ const home = process.env.HOME ?? homedir13();
162745
163057
  const basePath = join17(home, ".pi", "agent", CONFIG_FILE_NAME);
162746
163058
  return [`${basePath}.jsonc`, `${basePath}.json`];
162747
163059
  }
@@ -162750,7 +163062,7 @@ function resolveFirstExisting(paths) {
162750
163062
  }
162751
163063
  function loadConfigFile2(path4, scope) {
162752
163064
  try {
162753
- const rawText = readFileSync12(path4, "utf-8");
163065
+ const rawText = readFileSync13(path4, "utf-8");
162754
163066
  const substituted = substituteConfigVariables({
162755
163067
  text: rawText,
162756
163068
  configPath: path4
@@ -162758,7 +163070,7 @@ function loadConfigFile2(path4, scope) {
162758
163070
  return {
162759
163071
  path: path4,
162760
163072
  scope,
162761
- config: import_comment_json8.parse(substituted.text),
163073
+ config: import_comment_json9.parse(substituted.text),
162762
163074
  warnings: substituted.warnings.map((warning) => `${path4}: ${warning}`)
162763
163075
  };
162764
163076
  } catch (error51) {
@@ -162871,18 +163183,18 @@ function loadPiConfig(opts = {}) {
162871
163183
  loadedFromPaths: loadedFiles.map((loaded) => loaded.path)
162872
163184
  };
162873
163185
  }
162874
- var import_comment_json8, CONFIG_FILE_NAME = "magic-context";
163186
+ var import_comment_json9, CONFIG_FILE_NAME = "magic-context";
162875
163187
  var init_config2 = __esm(() => {
162876
163188
  init_magic_context();
162877
163189
  init_variable();
162878
- import_comment_json8 = __toESM(require_src2(), 1);
163190
+ import_comment_json9 = __toESM(require_src2(), 1);
162879
163191
  });
162880
163192
 
162881
163193
  // src/lib/diagnostics-pi.ts
162882
163194
  import { createHash } from "node:crypto";
162883
- import { existsSync as existsSync19, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
163195
+ import { existsSync as existsSync19, readdirSync as readdirSync3, readFileSync as readFileSync14, statSync as statSync4 } from "node:fs";
162884
163196
  import { createRequire as createRequire5 } from "node:module";
162885
- import { homedir as homedir12, tmpdir as tmpdir5, userInfo as userInfo3 } from "node:os";
163197
+ import { homedir as homedir14, userInfo as userInfo2 } from "node:os";
162886
163198
  import { join as join18 } from "node:path";
162887
163199
  function getSelfVersion3() {
162888
163200
  const req = createRequire5(import.meta.url);
@@ -162903,29 +163215,29 @@ function fileSize2(path4) {
162903
163215
  return 0;
162904
163216
  }
162905
163217
  }
162906
- function escapeRegex5(value) {
163218
+ function escapeRegex4(value) {
162907
163219
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
162908
163220
  }
162909
163221
  function currentUserHash() {
162910
- const username = userInfo3().username || "unknown";
163222
+ const username = userInfo2().username || "unknown";
162911
163223
  return createHash("sha256").update(username).digest("hex").slice(0, 12);
162912
163224
  }
162913
163225
  function redactSecretString(value) {
162914
163226
  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>");
162915
163227
  }
162916
163228
  function sanitizeString2(value) {
162917
- const home = process.env.HOME || homedir12();
162918
- const username = userInfo3().username;
163229
+ const home = process.env.HOME || homedir14();
163230
+ const username = userInfo2().username;
162919
163231
  const userHash = `<USER:${currentUserHash()}>`;
162920
163232
  let sanitized = redactSecretString(value);
162921
163233
  if (home) {
162922
- sanitized = sanitized.replace(new RegExp(escapeRegex5(home), "g"), "<HOME>");
163234
+ sanitized = sanitized.replace(new RegExp(escapeRegex4(home), "g"), "<HOME>");
162923
163235
  }
162924
163236
  sanitized = sanitized.replace(/\/Users\/[^/]+\//g, `/Users/${userHash}/`);
162925
163237
  sanitized = sanitized.replace(/\/home\/[^/]+\//g, `/home/${userHash}/`);
162926
163238
  sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, `C:\\Users\\${userHash}\\`);
162927
163239
  if (username) {
162928
- sanitized = sanitized.replace(new RegExp(escapeRegex5(username), "g"), userHash);
163240
+ sanitized = sanitized.replace(new RegExp(escapeRegex4(username), "g"), userHash);
162929
163241
  }
162930
163242
  return sanitized;
162931
163243
  }
@@ -162952,7 +163264,7 @@ function readJsonc3(path4) {
162952
163264
  return { value: {} };
162953
163265
  try {
162954
163266
  return {
162955
- value: import_comment_json9.parse(readFileSync13(path4, "utf-8"))
163267
+ value: import_comment_json10.parse(readFileSync14(path4, "utf-8"))
162956
163268
  };
162957
163269
  } catch (error51) {
162958
163270
  return {
@@ -162974,10 +163286,153 @@ function readConfigDiagnostic(path4) {
162974
163286
  };
162975
163287
  }
162976
163288
  function packageEntries(settings) {
162977
- return Array.isArray(settings.packages) ? settings.packages.filter((entry) => typeof entry === "string") : [];
163289
+ return Array.isArray(settings.packages) ? settings.packages : [];
163290
+ }
163291
+ function reverseSlugToDirectory(slug) {
163292
+ if (!slug.startsWith("--") || !slug.endsWith("--"))
163293
+ return null;
163294
+ const inner = slug.slice(2, -2);
163295
+ if (!inner)
163296
+ return null;
163297
+ return `/${inner.replace(/-/g, "/")}`;
163298
+ }
163299
+ function collectPiRecentSessions() {
163300
+ const sessionsRoot = join18(homedir14(), ".pi", "agent", "sessions");
163301
+ if (!existsSync19(sessionsRoot))
163302
+ return [];
163303
+ try {
163304
+ const slugs = readdirSync3(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
163305
+ const candidates = [];
163306
+ for (const slug of slugs) {
163307
+ const directory = reverseSlugToDirectory(slug);
163308
+ if (!directory)
163309
+ continue;
163310
+ const slugDir = join18(sessionsRoot, slug);
163311
+ let files;
163312
+ try {
163313
+ files = readdirSync3(slugDir).filter((name2) => name2.endsWith(".jsonl"));
163314
+ } catch {
163315
+ continue;
163316
+ }
163317
+ for (const file2 of files) {
163318
+ try {
163319
+ const mtime = statSync4(join18(slugDir, file2)).mtimeMs;
163320
+ const sessionId = file2.replace(/\.jsonl$/, "");
163321
+ candidates.push({ sessionId, directory, mtime });
163322
+ } catch {}
163323
+ }
163324
+ }
163325
+ candidates.sort((a, b2) => b2.mtime - a.mtime);
163326
+ return candidates.slice(0, 5).map((entry) => ({
163327
+ sessionId: entry.sessionId,
163328
+ directory: entry.directory,
163329
+ lastActiveAt: new Date(entry.mtime).toISOString()
163330
+ }));
163331
+ } catch {
163332
+ return [];
163333
+ }
163334
+ }
163335
+ function parseHistorianDumpMeta2(path4) {
163336
+ try {
163337
+ const xml = readFileSync14(path4, "utf-8");
163338
+ const parsed = parseCompartmentOutput(xml);
163339
+ const factCountByCategory = {};
163340
+ for (const fact of parsed.facts) {
163341
+ factCountByCategory[fact.category] = (factCountByCategory[fact.category] ?? 0) + 1;
163342
+ }
163343
+ const starts = parsed.compartments.map((c2) => c2.startMessage);
163344
+ const ends = parsed.compartments.map((c2) => c2.endMessage);
163345
+ let gaps = 0;
163346
+ let overlaps = 0;
163347
+ for (let i = 1;i < parsed.compartments.length; i++) {
163348
+ const prev = parsed.compartments[i - 1];
163349
+ const curr = parsed.compartments[i];
163350
+ if (curr.startMessage > prev.endMessage + 1)
163351
+ gaps += 1;
163352
+ else if (curr.startMessage <= prev.endMessage)
163353
+ overlaps += 1;
163354
+ }
163355
+ return {
163356
+ compartmentCount: parsed.compartments.length,
163357
+ minStart: starts.length > 0 ? Math.min(...starts) : null,
163358
+ maxEnd: ends.length > 0 ? Math.max(...ends) : null,
163359
+ unprocessedFrom: parsed.unprocessedFrom,
163360
+ factCountByCategory,
163361
+ userObservationCount: parsed.userObservations.length,
163362
+ ordinalGapCount: gaps,
163363
+ ordinalOverlapCount: overlaps
163364
+ };
163365
+ } catch (error51) {
163366
+ return { error: error51 instanceof Error ? error51.message : String(error51) };
163367
+ }
163368
+ }
163369
+ function listDumpsInDir2(dir, limit) {
163370
+ if (!existsSync19(dir))
163371
+ return { count: 0, recent: [] };
163372
+ try {
163373
+ const entries = readdirSync3(dir).filter((name2) => name2.endsWith(".xml")).map((name2) => {
163374
+ const stat = statSync4(join18(dir, name2));
163375
+ return {
163376
+ name: name2,
163377
+ mtime: stat.mtimeMs,
163378
+ sizeKb: Math.round(stat.size / 1024)
163379
+ };
163380
+ }).sort((a, b2) => b2.mtime - a.mtime);
163381
+ const now = Date.now();
163382
+ const recent = entries.slice(0, limit).map((entry) => {
163383
+ const meta3 = parseHistorianDumpMeta2(join18(dir, entry.name));
163384
+ const summary = {
163385
+ name: entry.name,
163386
+ ageMinutes: Math.round((now - entry.mtime) / 60000),
163387
+ sizeKb: entry.sizeKb
163388
+ };
163389
+ if ("error" in meta3) {
163390
+ summary.parseError = meta3.error;
163391
+ } else {
163392
+ summary.meta = meta3;
163393
+ }
163394
+ return summary;
163395
+ });
163396
+ return { count: entries.length, recent };
163397
+ } catch {
163398
+ return { count: 0, recent: [] };
163399
+ }
162978
163400
  }
162979
- function hasMagicContextPackage(packages) {
162980
- return packages.some((entry) => entry === PI_PACKAGE_SOURCE || entry === PACKAGE_NAME || entry.includes("pi-magic-context"));
163401
+ function collectPiHistorianDumps(recentSessions) {
163402
+ const buckets = new Map;
163403
+ for (const session of recentSessions) {
163404
+ const dir = session.directory;
163405
+ if (!dir)
163406
+ continue;
163407
+ const projectHistorianDir = getProjectMagicContextHistorianDir(dir);
163408
+ const listing = listDumpsInDir2(projectHistorianDir, 5);
163409
+ const existing = buckets.get(dir);
163410
+ if (existing) {
163411
+ if (!existing.sessionIds.includes(session.sessionId)) {
163412
+ existing.sessionIds.push(session.sessionId);
163413
+ }
163414
+ continue;
163415
+ }
163416
+ if (listing.count === 0)
163417
+ continue;
163418
+ buckets.set(dir, {
163419
+ directory: dir,
163420
+ primarySessionId: session.sessionId,
163421
+ sessionIds: [session.sessionId],
163422
+ count: listing.count,
163423
+ recent: listing.recent
163424
+ });
163425
+ }
163426
+ const legacyDir = getMagicContextHistorianDir2("pi");
163427
+ const legacyListing = listDumpsInDir2(legacyDir, 5);
163428
+ return {
163429
+ byProject: [...buckets.values()],
163430
+ legacyDumps: {
163431
+ dir: legacyDir,
163432
+ count: legacyListing.count,
163433
+ recent: legacyListing.recent
163434
+ }
163435
+ };
162981
163436
  }
162982
163437
  async function collectDiagnostics2(cwd = process.cwd()) {
162983
163438
  const pi = detectPiBinary();
@@ -162989,9 +163444,11 @@ async function collectDiagnostics2(cwd = process.cwd()) {
162989
163444
  const loaded = loadPiConfig({ cwd });
162990
163445
  const storageDirPath = getMagicContextStorageDir();
162991
163446
  const dbPath = join18(storageDirPath, "context.db");
162992
- const logPath = join18(tmpdir5(), "magic-context.log");
163447
+ const logPath = getMagicContextLogPath2("pi");
162993
163448
  const logFileSize = existsSync19(logPath) ? statSync4(logPath).size : 0;
162994
- const otherPiExtensions = packages.filter((entry) => !hasMagicContextPackage([entry]));
163449
+ const otherPiExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
163450
+ const recentSessions = collectPiRecentSessions();
163451
+ const historianDumps = collectPiHistorianDumps(recentSessions);
162995
163452
  return {
162996
163453
  timestamp: new Date().toISOString(),
162997
163454
  platform: process.platform,
@@ -163005,8 +163462,8 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163005
163462
  path: settingsPath,
163006
163463
  exists: existsSync19(settingsPath),
163007
163464
  ...settingsParsed.parseError ? { parseError: settingsParsed.parseError } : {},
163008
- hasMagicContextPackage: hasMagicContextPackage(packages),
163009
- packages: packages.map(sanitizeString2)
163465
+ hasMagicContextPackage: hasPiMagicContextPackage(packages),
163466
+ packages: sanitizeValue2(packages)
163010
163467
  },
163011
163468
  configPaths: {
163012
163469
  agentDir: getPiAgentConfigDir(),
@@ -163030,7 +163487,9 @@ async function collectDiagnostics2(cwd = process.cwd()) {
163030
163487
  path: logPath,
163031
163488
  exists: existsSync19(logPath),
163032
163489
  sizeKb: Math.round(logFileSize / 1024)
163033
- }
163490
+ },
163491
+ recentSessions,
163492
+ historianDumps
163034
163493
  };
163035
163494
  }
163036
163495
  function formatBytes2(bytes) {
@@ -163107,17 +163566,19 @@ function renderDiagnosticsMarkdown2(report) {
163107
163566
  ].join(`
163108
163567
  `);
163109
163568
  }
163110
- var import_comment_json9, PACKAGE_NAME = "@cortexkit/pi-magic-context";
163569
+ var import_comment_json10;
163111
163570
  var init_diagnostics_pi = __esm(() => {
163571
+ init_compartment_parser();
163112
163572
  init_data_path();
163113
163573
  init_config2();
163114
163574
  init_paths();
163115
163575
  init_pi_helpers();
163116
- import_comment_json9 = __toESM(require_src2(), 1);
163576
+ init_pi_package_entry();
163577
+ import_comment_json10 = __toESM(require_src2(), 1);
163117
163578
  });
163118
163579
 
163119
163580
  // src/lib/logs-pi.ts
163120
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
163581
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
163121
163582
  import { join as join19 } from "node:path";
163122
163583
  function sanitizeLogContent2(content) {
163123
163584
  return sanitizeString2(content);
@@ -163134,9 +163595,21 @@ function formatTimestamp2(date5) {
163134
163595
  pad(date5.getSeconds())
163135
163596
  ].join("");
163136
163597
  }
163598
+ function filterLogLinesBySession2(lines, sessionId) {
163599
+ if (!sessionId)
163600
+ return lines;
163601
+ const otherSessionPattern = /\bses_[A-Za-z0-9]{8,32}\b/g;
163602
+ return lines.filter((line) => {
163603
+ const matches = line.match(otherSessionPattern);
163604
+ if (!matches)
163605
+ return true;
163606
+ return matches.every((id) => id === sessionId);
163607
+ });
163608
+ }
163137
163609
  async function bundleIssueReport2(report, description, title, options = {}) {
163138
163610
  const LOG_TAIL_LINES = 400;
163139
- const logLines = report.logFile.exists ? readFileSync14(report.logFile.path, "utf-8").split(/\r?\n/) : [];
163611
+ const allLogLines = report.logFile.exists ? readFileSync15(report.logFile.path, "utf-8").split(/\r?\n/) : [];
163612
+ const logLines = filterLogLinesBySession2(allLogLines, options.sessionFilter ?? null);
163140
163613
  const recentLog = sanitizeLogContent2(logLines.slice(-LOG_TAIL_LINES).join(`
163141
163614
  `)).trim();
163142
163615
  const bodyMarkdown = [
@@ -163172,18 +163645,18 @@ var init_logs_pi = __esm(() => {
163172
163645
  });
163173
163646
 
163174
163647
  // src/commands/doctor-pi.ts
163175
- import { execFileSync as execFileSync3, spawnSync as spawnSync3 } from "node:child_process";
163648
+ import { execFileSync as execFileSync4, spawnSync as spawnSync3 } from "node:child_process";
163176
163649
  import {
163177
163650
  existsSync as existsSync20,
163178
163651
  mkdirSync as mkdirSync8,
163179
163652
  readdirSync as readdirSync4,
163180
- readFileSync as readFileSync15,
163653
+ readFileSync as readFileSync16,
163181
163654
  rmSync as rmSync2,
163182
163655
  statSync as statSync5,
163183
163656
  writeFileSync as writeFileSync12
163184
163657
  } from "node:fs";
163185
163658
  import { createRequire as createRequire6 } from "node:module";
163186
- import { homedir as homedir13, tmpdir as tmpdir6 } from "node:os";
163659
+ import { homedir as homedir15 } from "node:os";
163187
163660
  import { dirname as dirname9, join as join20 } from "node:path";
163188
163661
  function depsFrom(options) {
163189
163662
  return {
@@ -163217,7 +163690,7 @@ function selfVersion() {
163217
163690
  }
163218
163691
  function getLatestNpmVersion(packageName) {
163219
163692
  try {
163220
- return execFileSync3("npm", ["view", packageName, "version"], {
163693
+ return execFileSync4("npm", ["view", packageName, "version"], {
163221
163694
  encoding: "utf-8",
163222
163695
  stdio: ["ignore", "pipe", "ignore"],
163223
163696
  timeout: 1e4
@@ -163271,7 +163744,7 @@ function summarize(results) {
163271
163744
  function readJsonc4(path4) {
163272
163745
  try {
163273
163746
  return {
163274
- value: import_comment_json10.parse(readFileSync15(path4, "utf-8"))
163747
+ value: import_comment_json11.parse(readFileSync16(path4, "utf-8"))
163275
163748
  };
163276
163749
  } catch (error51) {
163277
163750
  return {
@@ -163281,10 +163754,7 @@ function readJsonc4(path4) {
163281
163754
  }
163282
163755
  }
163283
163756
  function packagesFrom(settings) {
163284
- return Array.isArray(settings.packages) ? settings.packages.filter((entry) => typeof entry === "string") : [];
163285
- }
163286
- function hasPiPackage(packages) {
163287
- return packages.some((entry) => entry === PI_PACKAGE_SOURCE || entry === PACKAGE_NAME2 || entry.includes("pi-magic-context"));
163757
+ return Array.isArray(settings.packages) ? settings.packages : [];
163288
163758
  }
163289
163759
  function projectConfigPath(cwd) {
163290
163760
  return join20(cwd, ".pi", "magic-context.jsonc");
@@ -163293,12 +163763,12 @@ function readConfigForEmbedding(path4) {
163293
163763
  if (!existsSync20(path4))
163294
163764
  return null;
163295
163765
  try {
163296
- const rawText = readFileSync15(path4, "utf-8");
163766
+ const rawText = readFileSync16(path4, "utf-8");
163297
163767
  const substituted = substituteConfigVariables({
163298
163768
  text: rawText,
163299
163769
  configPath: path4
163300
163770
  });
163301
- return import_comment_json10.parse(substituted.text);
163771
+ return import_comment_json11.parse(substituted.text);
163302
163772
  } catch {
163303
163773
  return null;
163304
163774
  }
@@ -163351,7 +163821,7 @@ function countTable(db, table) {
163351
163821
  }
163352
163822
  }
163353
163823
  function cacheRoots() {
163354
- const home = homedir13();
163824
+ const home = homedir15();
163355
163825
  const cacheHome = process.env.XDG_CACHE_HOME || join20(home, ".cache");
163356
163826
  return [
163357
163827
  join20(home, ".pi", "cache", "extensions"),
@@ -163377,10 +163847,10 @@ function findPiMagicContextCacheDirs(self, force = false) {
163377
163847
  const packageJson = join20(path4, "package.json");
163378
163848
  if (existsSync20(packageJson)) {
163379
163849
  try {
163380
- const pkg = JSON.parse(readFileSync15(packageJson, "utf-8"));
163850
+ const pkg = JSON.parse(readFileSync16(packageJson, "utf-8"));
163381
163851
  const name2 = typeof pkg.name === "string" ? pkg.name : "";
163382
163852
  const version2 = typeof pkg.version === "string" ? pkg.version : undefined;
163383
- if (name2 === PACKAGE_NAME2 || path4.includes("pi-magic-context")) {
163853
+ if (name2 === PACKAGE_NAME || path4.includes("pi-magic-context")) {
163384
163854
  if (force || !version2 || version2 !== self)
163385
163855
  found.set(path4, { path: path4, version: version2 });
163386
163856
  }
@@ -163443,7 +163913,7 @@ async function runHealthChecks(options) {
163443
163913
  } else {
163444
163914
  packages = packagesFrom(parsed.value);
163445
163915
  add(results, "pass", `Pi settings found at ${settingsPath}`);
163446
- if (hasPiPackage(packages)) {
163916
+ if (hasPiMagicContextPackage(packages)) {
163447
163917
  add(results, "pass", `${PI_PACKAGE_SOURCE} is registered in packages[]`);
163448
163918
  } else {
163449
163919
  add(results, "warn", `${PI_PACKAGE_SOURCE} is missing from packages[]`);
@@ -163548,13 +164018,13 @@ async function runHealthChecks(options) {
163548
164018
  } else {
163549
164019
  add(results, "pass", `Embedding provider: ${loadedConfig.config.embedding.provider}`);
163550
164020
  }
163551
- const piEntries = packages.filter((entry) => hasPiPackage([entry]));
164021
+ const piEntries = packages.filter(isPiMagicContextPackageEntry).map(describePiPackageEntry);
163552
164022
  if (piEntries.length > 1) {
163553
164023
  add(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
163554
164024
  } else {
163555
164025
  add(results, "pass", "No conflicting magic-context entries in Pi packages[]");
163556
164026
  }
163557
- const otherExtensions = packages.filter((entry) => !hasPiPackage([entry]));
164027
+ const otherExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
163558
164028
  if (otherExtensions.length > 0) {
163559
164029
  add(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
163560
164030
  } else {
@@ -163567,32 +164037,36 @@ async function runHealthChecks(options) {
163567
164037
  } else {
163568
164038
  add(results, "pass", "Pi extension cache clean (no stale cached package found)");
163569
164039
  }
163570
- const logPath = join20(tmpdir6(), "magic-context.log");
164040
+ const logPath = getMagicContextLogPath2("pi");
163571
164041
  if (existsSync20(logPath)) {
163572
164042
  const stat = statSync5(logPath);
163573
164043
  const sizeKb = (stat.size / 1024).toFixed(0);
163574
- const lines = readFileSync15(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
164044
+ const lines = readFileSync16(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
163575
164045
  add(results, "info", `Log file: ${logPath} (${sizeKb} KB)`);
163576
164046
  add(results, "info", `Last plugin log line: ${lines.at(-1) ?? "<empty log>"}`);
163577
164047
  } else {
163578
164048
  add(results, "info", `No plugin log file yet at ${logPath}`);
163579
164049
  }
163580
- const historianDumpDir = join20(tmpdir6(), "magic-context-historian");
163581
- if (existsSync20(historianDumpDir)) {
163582
- try {
163583
- const dumps = readdirSync4(historianDumpDir).filter((f) => f.endsWith(".xml")).map((f) => ({ name: f, mtime: statSync5(join20(historianDumpDir, f)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
163584
- if (dumps.length > 0) {
163585
- add(results, "warn", `Historian debug dumps: ${dumps.length} file(s) in ${historianDumpDir}`);
163586
- for (const dump of dumps.slice(0, 3)) {
163587
- const age = Math.round((Date.now() - dump.mtime) / 60000);
163588
- const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
163589
- add(results, "info", ` ${dump.name} (${ageStr})`);
163590
- }
163591
- if (dumps.length > 3) {
163592
- add(results, "info", ` ... and ${dumps.length - 3} more`);
163593
- }
164050
+ const diagnosticsForDumps = await collectDiagnostics2(options.cwd);
164051
+ const dumpBuckets = diagnosticsForDumps.historianDumps.byProject;
164052
+ if (dumpBuckets.length > 0) {
164053
+ const totalCount = dumpBuckets.reduce((sum, b2) => sum + b2.count, 0);
164054
+ add(results, "warn", `Historian debug dumps: ${totalCount} file(s) across ${dumpBuckets.length} project(s)`);
164055
+ for (const bucket of dumpBuckets) {
164056
+ add(results, "info", ` [${bucket.directory}] ${bucket.count} file(s)`);
164057
+ for (const dump of bucket.recent.slice(0, 3)) {
164058
+ const age = dump.ageMinutes;
164059
+ const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
164060
+ add(results, "info", ` ${dump.name} (${ageStr})`);
163594
164061
  }
163595
- } catch {}
164062
+ if (bucket.count > 3) {
164063
+ add(results, "info", ` ... and ${bucket.count - 3} more`);
164064
+ }
164065
+ }
164066
+ }
164067
+ const legacyDumps = diagnosticsForDumps.historianDumps.legacyDumps;
164068
+ if (legacyDumps.count > 0) {
164069
+ add(results, "info", `Legacy historian dumps (pre-v0.18.x): ${legacyDumps.count} file(s) in ${legacyDumps.dir}`);
163596
164070
  }
163597
164071
  if (!options.quiet) {
163598
164072
  for (const result of results)
@@ -163606,7 +164080,7 @@ function writeDefaultMagicContextConfig(path4) {
163606
164080
  $schema: "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json",
163607
164081
  ...MagicContextConfigSchema.parse({})
163608
164082
  };
163609
- writeFileSync12(path4, `${import_comment_json10.stringify(config2, null, 2)}
164083
+ writeFileSync12(path4, `${import_comment_json11.stringify(config2, null, 2)}
163610
164084
  `);
163611
164085
  }
163612
164086
  function repair(plan, prompts) {
@@ -163671,9 +164145,26 @@ async function runIssueFlow2(options) {
163671
164145
  spinner2.start("Collecting sanitized Pi diagnostics");
163672
164146
  try {
163673
164147
  const report = await options.deps.collectDiagnostics(options.cwd);
164148
+ spinner2.stop("Diagnostics collected");
164149
+ let sessionFilter = null;
164150
+ if (report.recentSessions.length > 1) {
164151
+ const choice = await options.prompts.selectOne("Which Pi session is this issue about? (filters log lines from other sessions)", [
164152
+ ...report.recentSessions.map((session, index) => ({
164153
+ label: `${session.directory} — ${session.sessionId}${index === 0 ? " (most recent)" : ""}`,
164154
+ value: session.sessionId
164155
+ })),
164156
+ {
164157
+ label: "All sessions (no filtering)",
164158
+ value: "__all__"
164159
+ }
164160
+ ]);
164161
+ sessionFilter = choice === "__all__" ? null : choice;
164162
+ }
164163
+ spinner2.start("Bundling Pi issue report");
163674
164164
  const bundled = await bundleIssueReport2(report, description, title, {
163675
164165
  cwd: options.cwd,
163676
- now: options.deps.now()
164166
+ now: options.deps.now(),
164167
+ sessionFilter
163677
164168
  });
163678
164169
  spinner2.stop(`Report written to ${bundled.path}`);
163679
164170
  if (ghAvailableAndAuthed(options.deps)) {
@@ -163748,7 +164239,7 @@ function parseDoctorArgs(args) {
163748
164239
  function doctor(args = []) {
163749
164240
  return runDoctor2(parseDoctorArgs(args));
163750
164241
  }
163751
- var import_comment_json10, PACKAGE_NAME2 = "@cortexkit/pi-magic-context", MIN_PI_VERSION = "0.71.0", ROW_COUNT_TABLES, DEFAULT_DEPS;
164242
+ var import_comment_json11, PACKAGE_NAME = "@cortexkit/pi-magic-context", MIN_PI_VERSION = "0.71.0", ROW_COUNT_TABLES, DEFAULT_DEPS;
163752
164243
  var init_doctor_pi = __esm(async () => {
163753
164244
  init_magic_context();
163754
164245
  init_variable();
@@ -163758,23 +164249,24 @@ var init_doctor_pi = __esm(async () => {
163758
164249
  init_logs_pi();
163759
164250
  init_paths();
163760
164251
  init_pi_helpers();
164252
+ init_pi_package_entry();
163761
164253
  init_prompts();
163762
164254
  init_setup_pi();
163763
164255
  await init_storage();
163764
- import_comment_json10 = __toESM(require_src2(), 1);
164256
+ import_comment_json11 = __toESM(require_src2(), 1);
163765
164257
  ROW_COUNT_TABLES = ["tags", "compartments", "memories", "notes", "dream_runs"];
163766
164258
  DEFAULT_DEPS = {
163767
164259
  prompts: promptIO,
163768
164260
  collectDiagnostics: collectDiagnostics2,
163769
164261
  detectPiBinary,
163770
164262
  getPiVersion,
163771
- getLatestNpmVersion: () => getLatestNpmVersion(PACKAGE_NAME2),
164263
+ getLatestNpmVersion: () => getLatestNpmVersion(PACKAGE_NAME),
163772
164264
  probeEmbeddingEndpoint,
163773
164265
  openDatabase,
163774
164266
  isDatabasePersisted,
163775
164267
  closeDatabase,
163776
164268
  now: () => new Date,
163777
- execFileSync: execFileSync3,
164269
+ execFileSync: execFileSync4,
163778
164270
  spawnSync: spawnSync3
163779
164271
  };
163780
164272
  });