@cortexkit/magic-context 0.44.0 → 0.44.2

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 +1256 -463
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1647,6 +1647,7 @@ var init_prompts = __esm(() => {
1647
1647
  });
1648
1648
 
1649
1649
  // ../plugin/src/shared/sqlite.ts
1650
+ import { AsyncLocalStorage } from "node:async_hooks";
1650
1651
  function registerSlowWriteReporter(reporter) {
1651
1652
  reportSlowPrivilegedWrite = reporter;
1652
1653
  }
@@ -1680,6 +1681,62 @@ async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = impo
1680
1681
  throw error;
1681
1682
  }
1682
1683
  }
1684
+ function installTransactionRouting(db, readonly) {
1685
+ const nativeExec = db.exec.bind(db);
1686
+ Object.defineProperty(db, "exec", {
1687
+ configurable: true,
1688
+ writable: true,
1689
+ value: (sql) => {
1690
+ if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
1691
+ retryAcquisition(() => nativeExec(sql), sql.trim());
1692
+ return db;
1693
+ }
1694
+ return nativeExec(sql);
1695
+ }
1696
+ });
1697
+ Object.defineProperty(db, "transaction", {
1698
+ configurable: true,
1699
+ writable: true,
1700
+ value: (fn) => {
1701
+ const make = (mode) => function(...args) {
1702
+ const nested = isInTransaction(db);
1703
+ const savepoint = "mc_tx_sp";
1704
+ db.exec(nested ? `SAVEPOINT ${savepoint}` : `BEGIN ${mode}`);
1705
+ try {
1706
+ const result = fn.apply(this, args);
1707
+ db.exec(nested ? `RELEASE ${savepoint}` : "COMMIT");
1708
+ return result;
1709
+ } catch (error) {
1710
+ if (isInTransaction(db)) {
1711
+ if (nested) {
1712
+ db.exec(`ROLLBACK TO ${savepoint}`);
1713
+ db.exec(`RELEASE ${savepoint}`);
1714
+ } else
1715
+ db.exec("ROLLBACK");
1716
+ }
1717
+ throw error;
1718
+ }
1719
+ };
1720
+ const defaultTransaction = make(readonly ? "DEFERRED" : "IMMEDIATE");
1721
+ const variants = {
1722
+ default: defaultTransaction,
1723
+ deferred: make("DEFERRED"),
1724
+ immediate: make("IMMEDIATE"),
1725
+ exclusive: make("EXCLUSIVE"),
1726
+ database: db
1727
+ };
1728
+ for (const transaction of [
1729
+ variants.default,
1730
+ variants.deferred,
1731
+ variants.immediate,
1732
+ variants.exclusive
1733
+ ]) {
1734
+ Object.assign(transaction, variants);
1735
+ }
1736
+ return defaultTransaction;
1737
+ }
1738
+ });
1739
+ }
1683
1740
  function trackSqliteConnection(db, filename, options) {
1684
1741
  const originalClose = db.close.bind(db);
1685
1742
  const sequence = nextSqliteConnectionSequence++;
@@ -1688,6 +1745,7 @@ function trackSqliteConnection(db, filename, options) {
1688
1745
  filename: typeof filename === "string" ? filename : Buffer.isBuffer(filename) ? "<buffer>" : ":memory:",
1689
1746
  readonly: Boolean(options) && typeof options === "object" && (options.readonly === true || options.readOnly === true)
1690
1747
  };
1748
+ installTransactionRouting(db, metadata.readonly);
1691
1749
  Object.defineProperty(db, "close", {
1692
1750
  configurable: true,
1693
1751
  value: (...args) => {
@@ -1705,7 +1763,6 @@ function trackSqliteConnection(db, filename, options) {
1705
1763
  return db;
1706
1764
  }
1707
1765
  function buildNodeSqliteDatabaseClass(DatabaseSync) {
1708
- const SAVEPOINT = "mc_tx_sp";
1709
1766
 
1710
1767
  class NodeSqliteDatabase extends DatabaseSync {
1711
1768
  constructor(filename, options) {
@@ -1724,45 +1781,34 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
1724
1781
  }
1725
1782
  return stmt;
1726
1783
  }
1727
- transaction(fn) {
1728
- const self = this;
1729
- const execute = (mode, receiver, args) => {
1730
- const nested = self.isTransaction === true;
1731
- self.exec(nested ? `SAVEPOINT ${SAVEPOINT}` : `BEGIN${mode ? ` ${mode}` : ""}`);
1732
- try {
1733
- const result = fn.apply(receiver, args);
1734
- self.exec(nested ? `RELEASE ${SAVEPOINT}` : "COMMIT");
1735
- return result;
1736
- } catch (error) {
1737
- if (nested) {
1738
- self.exec(`ROLLBACK TO ${SAVEPOINT}`);
1739
- self.exec(`RELEASE ${SAVEPOINT}`);
1740
- } else {
1741
- self.exec("ROLLBACK");
1742
- }
1743
- throw error;
1744
- }
1745
- };
1746
- const wrapped = function(...args) {
1747
- return execute("", this, args);
1748
- };
1749
- wrapped.default = function(...args) {
1750
- return execute("", this, args);
1751
- };
1752
- wrapped.deferred = function(...args) {
1753
- return execute("DEFERRED", this, args);
1754
- };
1755
- wrapped.immediate = function(...args) {
1756
- return execute("IMMEDIATE", this, args);
1757
- };
1758
- wrapped.exclusive = function(...args) {
1759
- return execute("EXCLUSIVE", this, args);
1760
- };
1761
- return wrapped;
1762
- }
1763
1784
  }
1764
1785
  return NodeSqliteDatabase;
1765
1786
  }
1787
+ function isTransientSqliteError(error) {
1788
+ if (!error || typeof error !== "object")
1789
+ return false;
1790
+ const value = error;
1791
+ return typeof value.code === "string" && /^(SQLITE_BUSY|SQLITE_LOCKED)(_|$)/.test(value.code) || typeof value.errcode === "number" && [5, 6].includes(value.errcode & 255);
1792
+ }
1793
+ function retryAcquisition(acquire, stage) {
1794
+ if (!transformPassScope.getStore()?.active) {
1795
+ acquire();
1796
+ return;
1797
+ }
1798
+ const delays = [500, 1000];
1799
+ for (let attempt = 0;; attempt++) {
1800
+ try {
1801
+ acquire();
1802
+ return;
1803
+ } catch (error) {
1804
+ if (error instanceof SqliteAcquisitionBusyError || !isTransientSqliteError(error))
1805
+ throw error;
1806
+ if (attempt === delays.length)
1807
+ throw new SqliteAcquisitionBusyError(error, stage);
1808
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delays[attempt]);
1809
+ }
1810
+ }
1811
+ }
1766
1812
  function isInTransaction(db) {
1767
1813
  const candidate = db;
1768
1814
  return candidate.inTransaction === true || candidate.isTransaction === true;
@@ -1775,7 +1821,7 @@ function withPrivilegedWriter(db, operation) {
1775
1821
  if (nested) {
1776
1822
  db.exec(`SAVEPOINT ${savepoint}`);
1777
1823
  } else {
1778
- db.exec("BEGIN IMMEDIATE");
1824
+ retryAcquisition(() => db.exec("BEGIN IMMEDIATE"), "BEGIN IMMEDIATE");
1779
1825
  }
1780
1826
  privilegeDepth.set(db, previousDepth + 1);
1781
1827
  try {
@@ -1814,7 +1860,7 @@ function withPrivilegedWriter(db, operation) {
1814
1860
  throw error;
1815
1861
  }
1816
1862
  }
1817
- var reportSlowPrivilegedWrite, bunSpec, nodeSpec, SqliteRuntimeUnavailableError, detectedRuntime, isBun, sqliteModule, DatabaseImpl, trackedSqliteConnections, nextSqliteConnectionSequence = 1, TrackedDatabase, Database, privilegeDepth;
1863
+ var reportSlowPrivilegedWrite, bunSpec, nodeSpec, SqliteRuntimeUnavailableError, detectedRuntime, isBun, sqliteModule, DatabaseImpl, trackedSqliteConnections, nextSqliteConnectionSequence = 1, TrackedDatabase, Database, privilegeDepth, transformPassScope, SqliteAcquisitionBusyError;
1818
1864
  var init_sqlite = __esm(async () => {
1819
1865
  bunSpec = "bun:" + "sqlite";
1820
1866
  nodeSpec = "node:" + "sqlite";
@@ -1842,6 +1888,16 @@ var init_sqlite = __esm(async () => {
1842
1888
  });
1843
1889
  Database = TrackedDatabase;
1844
1890
  privilegeDepth = new WeakMap;
1891
+ transformPassScope = new AsyncLocalStorage;
1892
+ SqliteAcquisitionBusyError = class SqliteAcquisitionBusyError extends Error {
1893
+ code = "SQLITE_BUSY";
1894
+ stage;
1895
+ constructor(cause, stage = "BEGIN IMMEDIATE") {
1896
+ super("SQLite writer acquisition remained busy after 3 attempts", { cause });
1897
+ this.name = "SqliteAcquisitionBusyError";
1898
+ this.stage = stage;
1899
+ }
1900
+ };
1845
1901
  });
1846
1902
 
1847
1903
  // src/lib/docs-proposal-gitignore.ts
@@ -4270,6 +4326,7 @@ function getPiVersion(piPath) {
4270
4326
  try {
4271
4327
  const invocation = getPiCommandInvocation(piPath, ["--version"]);
4272
4328
  const result = spawnSync(invocation.command, invocation.args, {
4329
+ windowsHide: true,
4273
4330
  encoding: "utf-8",
4274
4331
  timeout: 1e4
4275
4332
  });
@@ -4288,6 +4345,7 @@ function runPiCommand(piPath, args, timeout = 20000) {
4288
4345
  try {
4289
4346
  const invocation = getPiCommandInvocation(piPath, args);
4290
4347
  return execFileSync(invocation.command, invocation.args, {
4348
+ windowsHide: true,
4291
4349
  encoding: "utf-8",
4292
4350
  stdio: ["ignore", "pipe", "ignore"],
4293
4351
  timeout
@@ -4403,6 +4461,7 @@ function runOmpCommand(ompPath, args, timeout = 30000, overrides = {}) {
4403
4461
  try {
4404
4462
  const invocation = deps.getInvocation(ompPath, args);
4405
4463
  const result = deps.spawnSync(invocation.command, invocation.args, {
4464
+ windowsHide: true,
4406
4465
  encoding: "utf-8",
4407
4466
  timeout,
4408
4467
  maxBuffer: 10 * 1024 * 1024,
@@ -12794,7 +12853,7 @@ var init_opencode_plugin_cache = __esm(() => {
12794
12853
  import { spawnSync as spawnSync3 } from "node:child_process";
12795
12854
  import { existsSync as existsSync8, rmSync as rmSync2 } from "node:fs";
12796
12855
  function runLsof(args, spawn) {
12797
- const result = spawn("lsof", args, { encoding: "utf-8", timeout: 15000 });
12856
+ const result = spawn("lsof", args, { encoding: "utf-8", timeout: 15000, windowsHide: true });
12798
12857
  if (result.error)
12799
12858
  return { error: result.error.message };
12800
12859
  if (result.status !== 0 && result.status !== 1) {
@@ -13160,7 +13219,11 @@ function getOpenCodeCommandInvocation(binary, args) {
13160
13219
  }
13161
13220
  function runOpenCode(args, binary, timeoutMs) {
13162
13221
  try {
13163
- const options = { stdio: "pipe", ...timeoutMs ? { timeout: timeoutMs } : {} };
13222
+ const options = {
13223
+ stdio: "pipe",
13224
+ windowsHide: true,
13225
+ ...timeoutMs ? { timeout: timeoutMs } : {}
13226
+ };
13164
13227
  if (binary) {
13165
13228
  const invocation = getOpenCodeCommandInvocation(binary, args);
13166
13229
  return execFileSync2(invocation.command, invocation.args, {
@@ -14676,6 +14739,49 @@ function parseCron(expression) {
14676
14739
  function isValidCron(expression) {
14677
14740
  return parseCron(expression).ok;
14678
14741
  }
14742
+ function matchesDay(cron, date) {
14743
+ const dom = cron.dom.has(date.getDate());
14744
+ const dow = cron.dow.has(date.getDay());
14745
+ if (cron.domRestricted && cron.dowRestricted)
14746
+ return dom || dow;
14747
+ if (cron.domRestricted)
14748
+ return dom;
14749
+ if (cron.dowRestricted)
14750
+ return dow;
14751
+ return true;
14752
+ }
14753
+ function matchesCron(cron, date) {
14754
+ return cron.minute.has(date.getMinutes()) && cron.hour.has(date.getHours()) && cron.month.has(date.getMonth() + 1) && matchesDay(cron, date);
14755
+ }
14756
+ function civilMinuteKey(date) {
14757
+ const p = (n, w = 2) => String(n).padStart(w, "0");
14758
+ return `${p(date.getFullYear(), 4)}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}`;
14759
+ }
14760
+ function nextOccurrence(cron, after, excludeCivilMinute, maxSearchMs = MAX_SEARCH_MS) {
14761
+ const afterMs = after.getTime();
14762
+ let cursorMs = Math.floor(afterMs / MINUTE_MS) * MINUTE_MS + MINUTE_MS;
14763
+ const capMs = afterMs + Math.max(0, Math.min(MAX_SEARCH_MS, maxSearchMs));
14764
+ while (cursorMs <= capMs) {
14765
+ const candidate = new Date(cursorMs);
14766
+ if (matchesCron(cron, candidate)) {
14767
+ if (!excludeCivilMinute || civilMinuteKey(candidate) !== excludeCivilMinute) {
14768
+ return candidate;
14769
+ }
14770
+ }
14771
+ cursorMs += MINUTE_MS;
14772
+ }
14773
+ return null;
14774
+ }
14775
+ function nextDueAtMs(expression, afterMs, consumedScheduledAtMs, maxSearchMs = MAX_SEARCH_MS) {
14776
+ if (expression.trim().length === 0)
14777
+ return null;
14778
+ const parsed = parseCron(expression);
14779
+ if (!parsed.ok)
14780
+ return null;
14781
+ const exclude = consumedScheduledAtMs !== undefined ? civilMinuteKey(new Date(consumedScheduledAtMs)) : undefined;
14782
+ const next = nextOccurrence(parsed.cron, new Date(afterMs), exclude, maxSearchMs);
14783
+ return next ? next.getTime() : null;
14784
+ }
14679
14785
  var FIELDS, MINUTE_MS = 60000, MAX_SEARCH_MS;
14680
14786
  var init_cron = __esm(() => {
14681
14787
  FIELDS = [
@@ -21156,9 +21262,9 @@ import { homedir as homedir9 } from "node:os";
21156
21262
  function defaultTaskConfig(task) {
21157
21263
  const base = { schedule: DEFAULT_TASK_SCHEDULES2[task] };
21158
21264
  if (task === "review-user-memories")
21159
- base.promotion_threshold = 3;
21265
+ base.promotion_threshold = DREAM_TASK_PROMOTION_DEFAULTS["review-user-memories"];
21160
21266
  if (task === "promote-primers")
21161
- base.promotion_threshold = 2;
21267
+ base.promotion_threshold = DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"];
21162
21268
  return base;
21163
21269
  }
21164
21270
  function expandConfigPath(value) {
@@ -21169,7 +21275,7 @@ function expandConfigPath(value) {
21169
21275
  return `${homedir9()}/${trimmed.slice(2)}`;
21170
21276
  return trimmed;
21171
21277
  }
21172
- var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 90% for cache safety: output capacity is reserved from the usable context window, and the remaining 10% absorbs mid-turn growth before the absolute 95% emergency wall. Use a value between 20 and 90.", DEFAULT_HISTORIAN_TIMEOUT_MS = 600000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, PROTECTED_TOKENS_MIN = 4000, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", PiThinkingLevelSchema, OmpThinkingLevelSchema, PiConfigSchema, PromptSurfacePresetSchema, PromptSurfaceModelKeySchema, PromptSurfaceToolKeySchema, PromptSurfaceConfigSchema, PER_HARNESS_MIGRATION_INVENTORY, PER_HARNESS_MODEL_KEYS, OcEntryObjectSchema, OcEntrySchema, PiEntryObjectSchema, PiEntrySchema, OmpEntryObjectSchema, OmpEntrySchema, OpenCodeHarnessBlockSchema, PiHarnessBlockSchema, OmpHarnessBlockSchema, OpenCodeTaskExecutionSchema, PiTaskExecutionSchema, OmpTaskExecutionSchema, DreamerOpenCodeHarnessBlockSchema, DreamerPiHarnessBlockSchema, DreamerOmpHarnessBlockSchema, ProfileOpenCodeModelBlockSchema, ProfilePiModelBlockSchema, ProfileOmpModelBlockSchema, ProfileHistorianSchema, ProfileDreamerSchema, ConfigProfileSchema, ConfigProfilesSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, RetrospectiveTaskConfigSchema, DEFAULT_TASK_SCHEDULES2, DreamTasksSchema, AgentMetadataSchema, DreamerConfigSchema, HistorianConfigSchema, EmbeddingFallbackProviderSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema, LIVE_RELOAD_CONFIG_PATHS;
21278
+ var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 90% for cache safety: output capacity is reserved from the usable context window, and the remaining 10% absorbs mid-turn growth before the absolute 95% emergency wall. Use a value between 20 and 90.", DEFAULT_HISTORIAN_TIMEOUT_MS = 600000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, PROTECTED_TOKENS_MIN = 4000, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", PiThinkingLevelSchema, OmpThinkingLevelSchema, PiConfigSchema, PromptSurfacePresetSchema, PromptSurfaceModelKeySchema, PromptSurfaceToolKeySchema, PromptSurfaceConfigSchema, PER_HARNESS_MIGRATION_INVENTORY, PER_HARNESS_MODEL_KEYS, OcEntryObjectSchema, OcEntrySchema, PiEntryObjectSchema, PiEntrySchema, OmpEntryObjectSchema, OmpEntrySchema, OpenCodeHarnessBlockSchema, PiHarnessBlockSchema, OmpHarnessBlockSchema, OpenCodeTaskExecutionSchema, PiTaskExecutionSchema, OmpTaskExecutionSchema, DreamerOpenCodeHarnessBlockSchema, DreamerPiHarnessBlockSchema, DreamerOmpHarnessBlockSchema, ProfileOpenCodeModelBlockSchema, ProfilePiModelBlockSchema, ProfileOmpModelBlockSchema, ProfileHistorianSchema, ProfileDreamerSchema, ConfigProfileSchema, ConfigProfilesSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, DREAM_TASK_PROMOTION_DEFAULTS, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, RetrospectiveTaskConfigSchema, DEFAULT_TASK_SCHEDULES2, DreamTasksSchema, AgentMetadataSchema, DreamerConfigSchema, HistorianConfigSchema, EmbeddingFallbackProviderSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema, LIVE_RELOAD_CONFIG_PATHS;
21173
21279
  var init_magic_context = __esm(() => {
21174
21280
  init_zod();
21175
21281
  init_language_directive();
@@ -21346,10 +21452,15 @@ var init_magic_context = __esm(() => {
21346
21452
  message: 'Invalid schedule: use a 5-field cron expression (e.g. "0 3 * * *" for 3am daily, "0 3 * * 0" for Sunday 3am, "0 */6 * * *" every 6h) or "" to disable.'
21347
21453
  }).describe('5-field cron schedule (e.g. "0 3 * * *"), or "" to disable this task.');
21348
21454
  DreamTaskBaseConfigSchema = object({
21349
- schedule: CronScheduleSchema.default("")
21455
+ schedule: CronScheduleSchema.default(""),
21456
+ token_budget: number2().int().positive().optional().describe("Maximum cumulative prompt tokens (input + cache read + cache write) for one tool-loop child. Defaults vary by task.")
21350
21457
  }).strict();
21351
- PromotionThresholdSchema = number2().min(2).max(20).optional().describe("review-user-memories: min candidate observations before promotion is considered (default: 3)");
21352
- PrimerPromotionThresholdSchema = number2().min(2).max(20).optional().describe("promote-primers: min recurring source days before promotion is considered (default: 2)");
21458
+ DREAM_TASK_PROMOTION_DEFAULTS = {
21459
+ "review-user-memories": 3,
21460
+ "promote-primers": 2
21461
+ };
21462
+ PromotionThresholdSchema = number2().min(2).max(20).default(DREAM_TASK_PROMOTION_DEFAULTS["review-user-memories"]).describe("review-user-memories: min candidate observations before promotion is considered (default: 3)");
21463
+ PrimerPromotionThresholdSchema = number2().min(2).max(20).default(DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"]).describe("promote-primers: min recurring source days before promotion is considered (default: 2)");
21353
21464
  DreamTaskConfigSchema = DreamTaskBaseConfigSchema.extend({
21354
21465
  promotion_threshold: PromotionThresholdSchema
21355
21466
  });
@@ -21526,7 +21637,7 @@ var init_magic_context = __esm(() => {
21526
21637
  });
21527
21638
  MagicContextConfigSchema = object({
21528
21639
  enabled: boolean2().default(true).describe("Enable magic context (default: true)"),
21529
- allow_home_project: boolean2().default(false).describe("Allow Magic Context sessions launched from the exact canonical home directory. The home session uses its deterministic dir: identity so pre-gate memories reconnect. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace."),
21640
+ allow_home_project: boolean2().default(false).describe("Allow Magic Context sessions launched from the exact canonical home directory. A non-git home uses its deterministic dir: identity; a home repository uses its git: identity. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace."),
21530
21641
  mural: object({
21531
21642
  enabled: boolean2().default(false),
21532
21643
  model: string2().trim().min(1).optional().describe("Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model).")
@@ -21541,7 +21652,7 @@ var init_magic_context = __esm(() => {
21541
21652
  smart_notes: object({
21542
21653
  retina_handoff: boolean2().default(false).describe("When true, dreamer skips smart notes whose surface conditions compiled to retina provider configs at authoring time. Default false keeps both paths active until the retina consumer is deployed.")
21543
21654
  }).default({ retina_handoff: false }).describe("Smart-note ownership transition controls."),
21544
- cache_ttl: union([string2(), object({ default: string2() }).catchall(string2())]).default("5m").describe(`How long Magic Context assumes the provider's cached prefix stays valid. This is MC's own deferral gate — it does not change the provider's actual cache lifetime. String (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "provider/model": "1h", "provider/*": "never" }); keys resolve most-specific first (exact provider/model, bare model ID, shorter dash-prefixes, then the provider/* wildcard, then default). Set to "never" to mean MC never assumes expiry (for lanes kept warm externally by a cache-keep tool) — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time. Provider-side extended TTL is a separate request-level concern (cache_control: { ttl } in the request body).`),
21655
+ cache_ttl: union([string2(), object({ default: string2() }).catchall(string2())]).default("5m").describe(`How long Magic Context assumes the provider's cached prefix stays valid. This is MC's own deferral gate — it does not change the provider's actual cache lifetime. String (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "provider/model": "1h", "provider/*": "never" }); keys resolve most-specific first (exact provider/model, bare model ID, shorter dash-prefixes, then the provider/* wildcard). Explicit per-model entries win; otherwise GPT-5.6 and later (including gpt-6*, through any provider prefix) use a built-in 30m lifetime before the object default or 5m fallback. An unset or global "5m" opts into built-in defaults; any other global string is an explicit policy and wins. Policy is frozen per session, including across restarts; a model switch resolves against that frozen policy. /ctx-status shows the effective value and source. OpenAI documents at least 30 minutes since the latest write or reuse: https://developers.openai.com/api/docs/guides/prompt-caching (Cache lifetime and Summary of model differences). Set to "never" to mean MC never assumes expiry (for lanes kept warm externally by a cache-keep tool) — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time. Provider-side extended TTL is a separate request-level concern (cache_control: { ttl } in the request body).`),
21545
21656
  prompt_surface: PromptSurfaceConfigSchema.default({ default: "full" }).describe("Prompt-surface presets: default is full; models use bare model IDs, provider/model, or provider/* routing keys. Guidance and tool-description overrides are user-level only. OpenCode 1.x, Pi, and OMP register tool descriptions once per process (they follow the default preset). OpenCode 2 rewrites the five ctx_* descriptions per request from the draft model."),
21546
21657
  output_reserve: union([
21547
21658
  number2().min(0),
@@ -21600,9 +21711,9 @@ var init_magic_context = __esm(() => {
21600
21711
  enabled: boolean2().default(true).describe("When false, Magic Context stops managing the context window and keeps its knowledge layer: memory and docs/user-profile/key-files injection through additive m[0]/m[1], raw-message FTS indexing, dreamer, notes, ctx_search, ctx_expand, ctx_memory, and /ctx-embed remain available. MC's historian/compartment preparation, tagging, markers, pruning, folding, drops, strips, splicing, synthetic context-management todos, temporal markers, nudges, and fail-closed blocking stop; ctx_expand remains a knowledge-surface tool. fail_closed_blocking is inert: a transform failure passes the input messages through without blocking or cancelling. This setting does not enable native compaction: OpenCode's compaction.auto / compaction.prune or Pi's equivalent owns the window, or nothing does. MC's compaction.enabled in magic-context.jsonc is distinct from OpenCode's compaction.auto / compaction.prune in opencode.jsonc; they are different files and different owners. On the first turn after disabling, a long session may trigger one native compaction cycle; MC removes only its own marker boundary, leaves native boundaries and stored compartments intact, and does no pre-trimming mitigation. Marker cleanup is lazy per session, so an unresumed session is cleaned when it is next resumed. If compaction is enabled again, run /ctx-wrapup when the historian is runnable to catch up. OpenCode peer verification against v1.18.4 confirms native compaction covers child sessions: subagents receive additive memory/docs injection and no MC reclaim in this mode, so keep subagent tasks small or leave compaction.enabled on for long subagent runs. This is boot-resolved and requires a process restart; project-tier compaction.enabled is stripped so a cloned repository cannot disable the user's setting. The sidebar reports raw usage as Context: <pct>% · native compaction or Context: <pct>% · no active compaction and does not show an MC execute-threshold fill. /ctx-wrapup, /ctx-recomp, and /ctx-flush refuse without context-management side effects; /ctx-embed remains functional. Raw content hidden by a native boundary before Magic Context's first pass is not retroactively indexed.")
21601
21712
  }).default({ enabled: true }).describe("Compaction-off mode gate. Default true (MC manages the context window as today). Set compaction.enabled=false to keep the knowledge layer while letting native compaction (or nothing) own the window. Boot-resolved; requires a restart to change."),
21602
21713
  todowrite: object({
21603
- enabled: boolean2().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
21714
+ enabled: boolean2().default(false).describe("Pi only: off by default. Set todowrite.enabled=true to register Magic Context's todowrite task-list tool and /todos command. OpenCode ships its own built-in todowrite; this setting has no effect there."),
21604
21715
  overlay: boolean2().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
21605
- }).default({ enabled: true, overlay: true }).describe("Pi-only todowrite tool and overlay controls. Pi registers tools and widgets at extension boot, so changing this after /cd requires /reload or restart."),
21716
+ }).default({ enabled: false, overlay: true }).describe("Pi-only todowrite tool and overlay controls. Pi registers tools and widgets at extension boot, so changing this after /cd requires /reload or restart."),
21606
21717
  pi: PiConfigSchema.describe("Pi-only child-process extension controls. This setting is user-level only; project configuration cannot choose which extensions a user's subagent children load."),
21607
21718
  smart_drops: boolean2().default(false).describe("Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 3), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart."),
21608
21719
  caveman_text_compression: object({
@@ -21651,10 +21762,12 @@ var init_magic_context = __esm(() => {
21651
21762
  "historian.omp.fallback_models",
21652
21763
  "historian.omp.thinking_level",
21653
21764
  "historian.two_pass",
21765
+ "historian.maxTokens",
21654
21766
  "historian_timeout_ms",
21655
21767
  "commit_cluster_trigger.enabled",
21656
21768
  "commit_cluster_trigger.min_clusters",
21657
21769
  "memory.auto_promote",
21770
+ "dreamer.maxTokens",
21658
21771
  "dreamer.opencode.model",
21659
21772
  "dreamer.opencode.fallback_models",
21660
21773
  "dreamer.opencode.variant",
@@ -24903,11 +25016,48 @@ var init_context_authority = __esm(async () => {
24903
25016
  mirrorFlights = new WeakMap;
24904
25017
  });
24905
25018
 
25019
+ // ../plugin/src/features/magic-context/memory/project-identity-cache.ts
25020
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
25021
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, renameSync as renameSync4, writeFileSync as writeFileSync5 } from "node:fs";
25022
+ import path3 from "node:path";
25023
+ function projectDirectoryKey(directory) {
25024
+ const slashed = directory.replaceAll("\\", "/").replace(/^\/\/\?\/UNC\//i, "//").replace(/^\/\/\?\//, "");
25025
+ if (/^[a-z]:\//i.test(slashed) || slashed.startsWith("//")) {
25026
+ return path3.win32.normalize(slashed).replaceAll("\\", "/").replace(/\/$/, "").toLowerCase();
25027
+ }
25028
+ return path3.resolve(directory);
25029
+ }
25030
+ function cachePath(directory) {
25031
+ const hash = createHash2("sha256").update(projectDirectoryKey(directory)).digest("hex");
25032
+ return path3.join(getMagicContextStorageDir(), "project-identities", `${hash}.json`);
25033
+ }
25034
+ function rememberGitIdentity(directory, identity) {
25035
+ try {
25036
+ const destination = cachePath(directory);
25037
+ mkdirSync6(path3.dirname(destination), { recursive: true });
25038
+ const temporary = `${destination}.${randomUUID2()}.tmp`;
25039
+ writeFileSync5(temporary, JSON.stringify({ directory: projectDirectoryKey(directory), identity }), { mode: 384 });
25040
+ renameSync4(temporary, destination);
25041
+ } catch {}
25042
+ }
25043
+ function readRememberedGitIdentity(directory) {
25044
+ try {
25045
+ const record = JSON.parse(readFileSync14(cachePath(directory), "utf8"));
25046
+ if (record.directory === projectDirectoryKey(directory) && /^git:[0-9a-f]{7,64}$/.test(record.identity))
25047
+ return record.identity;
25048
+ } catch {}
25049
+ return;
25050
+ }
25051
+ var init_project_identity_cache = __esm(() => {
25052
+ init_data_path();
25053
+ });
25054
+
24906
25055
  // ../plugin/src/features/magic-context/memory/project-identity.ts
24907
25056
  import { execFileSync as execFileSync3 } from "node:child_process";
24908
- import { createHash as createHash2 } from "node:crypto";
25057
+ import { createHash as createHash3 } from "node:crypto";
24909
25058
  import { existsSync as existsSync21, realpathSync as realpathSync2, statSync as statSync9 } from "node:fs";
24910
- import path3 from "node:path";
25059
+ import { homedir as homedir14 } from "node:os";
25060
+ import path4 from "node:path";
24911
25061
  function asError(error) {
24912
25062
  return error instanceof Error ? error : undefined;
24913
25063
  }
@@ -24945,8 +25095,8 @@ function getErrorStderr(error) {
24945
25095
  return "";
24946
25096
  }
24947
25097
  function directoryFallback(directory) {
24948
- const canonical = path3.resolve(directory);
24949
- const hash = createHash2("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
25098
+ const canonical = path4.resolve(directory);
25099
+ const hash = createHash3("md5").update(canonical, "utf8").digest("hex").slice(0, 12);
24950
25100
  return `dir:${hash}`;
24951
25101
  }
24952
25102
  function assertDirectoryUsable(canonicalDirectory, rawDirectory) {
@@ -24991,8 +25141,9 @@ function classifyGitError(error, rawDirectory) {
24991
25141
  }
24992
25142
  return new ProjectIdentityError("unknown", rawDirectory, "git rev-list failed while resolving project identity", asError(error));
24993
25143
  }
24994
- function resolveProjectIdentityStrict(directory) {
24995
- const canonical = path3.resolve(directory);
25144
+ function resolveProjectIdentityStrict(directory, allowHomeProject = false) {
25145
+ assertProjectAllowed(directory, allowHomeProject);
25146
+ const canonical = path4.resolve(directory);
24996
25147
  const cached = identityCache.get(canonical);
24997
25148
  if (cached !== undefined) {
24998
25149
  return cached;
@@ -25008,7 +25159,8 @@ function resolveProjectIdentityStrict(directory) {
25008
25159
  encoding: "utf8",
25009
25160
  env: { ...process.env, LC_ALL: "C", LANG: "C" },
25010
25161
  stdio: ["ignore", "pipe", "pipe"],
25011
- timeout: GIT_TIMEOUT_MS
25162
+ timeout: GIT_TIMEOUT_MS,
25163
+ windowsHide: true
25012
25164
  });
25013
25165
  } catch (error) {
25014
25166
  throw classifyGitError(error, directory);
@@ -25021,13 +25173,17 @@ function resolveProjectIdentityStrict(directory) {
25021
25173
  const identity = `git:${rootCommit}`;
25022
25174
  identityCache.set(canonical, identity);
25023
25175
  lastKnownGitIdentityCache.set(canonical, identity);
25176
+ rememberGitIdentity(canonical, identity);
25177
+ const root = gitRootDirectory(canonical);
25178
+ if (root && root !== canonical)
25179
+ rememberGitIdentity(root, identity);
25024
25180
  transientFailureCooldown.delete(canonical);
25025
25181
  dubiousOwnershipFallbackDirectories.delete(canonical);
25026
25182
  transientGitIdentityReuseLoggedDirectories.delete(canonical);
25027
25183
  return identity;
25028
25184
  }
25029
25185
  function shouldUseDirectoryFallback(error) {
25030
- return error.errorClass !== "permission_denied";
25186
+ return error.errorClass !== "home_project_disabled";
25031
25187
  }
25032
25188
  function getActiveCooldown(canonical) {
25033
25189
  const until = transientFailureCooldown.get(canonical);
@@ -25039,7 +25195,7 @@ function getActiveCooldown(canonical) {
25039
25195
  return;
25040
25196
  }
25041
25197
  function lastKnownGitIdentity(canonical) {
25042
- return lastKnownGitIdentityCache.get(canonical) ?? identityCache.get(canonical);
25198
+ return lastKnownGitIdentityCache.get(canonical) ?? identityCache.get(canonical) ?? readRememberedGitIdentity(canonical);
25043
25199
  }
25044
25200
  function nearestLastKnownGitIdentity(canonical) {
25045
25201
  const visited = new Set;
@@ -25050,7 +25206,9 @@ function nearestLastKnownGitIdentity(canonical) {
25050
25206
  const cached = lastKnownGitIdentity(current);
25051
25207
  if (cached !== undefined)
25052
25208
  return { identity: cached, source: current };
25053
- const parent = path3.dirname(current);
25209
+ if (existsSync21(path4.join(current, ".git")))
25210
+ break;
25211
+ const parent = path4.dirname(current);
25054
25212
  if (parent === current)
25055
25213
  break;
25056
25214
  current = parent;
@@ -25079,7 +25237,7 @@ function reuseLastKnownGitIdentity(canonical) {
25079
25237
  return cached.identity;
25080
25238
  }
25081
25239
  function formatDubiousOwnershipWarning(canonical) {
25082
- return `Magic Context: git refused to read ${canonical} (dubious ownership — the repo is owned by a different user). Using a directory-based project identity for now, which keeps memory separate from this repo's normal identity. Fix: git config --global --add safe.directory ${canonical}`;
25240
+ return `Magic Context: git refused to read ${canonical} (dubious ownership — the repo is owned by a different user). Memory features are paused unless a previous git identity is available. Fix: git config --global --add safe.directory ${canonical}`;
25083
25241
  }
25084
25242
  function recordDubiousOwnershipFallback(canonical) {
25085
25243
  dubiousOwnershipFallbackDirectories.add(canonical);
@@ -25088,8 +25246,32 @@ function recordDubiousOwnershipFallback(canonical) {
25088
25246
  dubiousOwnershipLoggedDirectories.add(canonical);
25089
25247
  log3(`[magic-context] ${formatDubiousOwnershipWarning(canonical)}`);
25090
25248
  }
25091
- function resolveProjectIdentity(directory) {
25092
- const canonical = path3.resolve(directory);
25249
+ function canonicalUserHomeDirectory() {
25250
+ const homeDirectory = userHomeDirectoryForIdentity();
25251
+ try {
25252
+ return realpathSync2.native(homeDirectory);
25253
+ } catch {
25254
+ return homeDirectory;
25255
+ }
25256
+ }
25257
+ function isUserHomeDirectory(directory) {
25258
+ if (projectDirectoryKey(directory) === projectDirectoryKey(userHomeDirectoryForIdentity()))
25259
+ return true;
25260
+ try {
25261
+ return projectDirectoryKey(realpathSync2.native(path4.resolve(directory))) === projectDirectoryKey(canonicalUserHomeDirectory());
25262
+ } catch {
25263
+ return false;
25264
+ }
25265
+ }
25266
+ function assertProjectAllowed(directory, allowHomeProject) {
25267
+ const canonical = path4.resolve(directory);
25268
+ if (!allowHomeProject && (isUserHomeDirectory(directory) || isUserHomeDirectory(gitRootDirectory(canonical) ?? canonical))) {
25269
+ throw new ProjectIdentityError("home_project_disabled", directory, "Home project memory is disabled; set allow_home_project to opt in");
25270
+ }
25271
+ }
25272
+ function resolveProjectIdentity(directory, allowHomeProject = false) {
25273
+ assertProjectAllowed(directory, allowHomeProject);
25274
+ const canonical = path4.resolve(directory);
25093
25275
  const cachedFallback = directoryFallbackCache.get(canonical);
25094
25276
  if (cachedFallback !== undefined) {
25095
25277
  if (!hasGitDir(canonical)) {
@@ -25103,27 +25285,30 @@ function resolveProjectIdentity(directory) {
25103
25285
  if (cachedGitIdentity !== undefined) {
25104
25286
  return cachedGitIdentity;
25105
25287
  }
25288
+ throw new ProjectIdentityError("git_identity_unavailable", directory, "Git identity unavailable; memory features paused until the retry cooldown expires");
25106
25289
  }
25107
25290
  return directoryFallback(canonical);
25108
25291
  }
25109
25292
  try {
25110
- return resolveProjectIdentityStrict(directory);
25293
+ return resolveProjectIdentityStrict(directory, allowHomeProject);
25111
25294
  } catch (error) {
25112
25295
  if (error instanceof ProjectIdentityError && shouldUseDirectoryFallback(error)) {
25113
25296
  const fallback = directoryFallback(canonical);
25114
25297
  const hasGitMetadata = hasGitDir(canonical);
25115
25298
  if (!hasGitMetadata) {
25299
+ if (error.errorClass === "permission_denied")
25300
+ throw error;
25116
25301
  directoryFallbackCache.set(canonical, fallback);
25117
25302
  transientFailureCooldown.delete(canonical);
25118
25303
  } else {
25119
25304
  transientFailureCooldown.set(canonical, nowMs() + TRANSIENT_FAILURE_COOLDOWN_MS);
25120
25305
  const cachedGitIdentity = reuseLastKnownGitIdentity(canonical);
25306
+ if (error.errorClass === "dubious_ownership")
25307
+ recordDubiousOwnershipFallback(canonical);
25121
25308
  if (cachedGitIdentity !== undefined) {
25122
25309
  return cachedGitIdentity;
25123
25310
  }
25124
- }
25125
- if (error.errorClass === "dubious_ownership") {
25126
- recordDubiousOwnershipFallback(canonical);
25311
+ throw error;
25127
25312
  }
25128
25313
  return fallback;
25129
25314
  }
@@ -25144,14 +25329,14 @@ function hasGitDir(canonical) {
25144
25329
  function gitRootInAncestorChain(startDirectory) {
25145
25330
  let current = startDirectory;
25146
25331
  while (true) {
25147
- if (existsSync21(path3.join(current, ".git"))) {
25332
+ if (existsSync21(path4.join(current, ".git"))) {
25148
25333
  try {
25149
25334
  return realpathSync2.native(current);
25150
25335
  } catch {
25151
- return path3.resolve(current);
25336
+ return path4.resolve(current);
25152
25337
  }
25153
25338
  }
25154
- const parent = path3.dirname(current);
25339
+ const parent = path4.dirname(current);
25155
25340
  if (parent === current) {
25156
25341
  return null;
25157
25342
  }
@@ -25161,9 +25346,21 @@ function gitRootInAncestorChain(startDirectory) {
25161
25346
  function hasGitDirInAncestorChain(startDirectory) {
25162
25347
  return gitRootInAncestorChain(startDirectory) !== null;
25163
25348
  }
25164
- var GIT_TIMEOUT_MS = 5000, TRANSIENT_FAILURE_COOLDOWN_MS, identityCache, linkedGitWorktreeCache, lastKnownGitIdentityCache, directoryFallbackCache, transientFailureCooldown, dubiousOwnershipFallbackDirectories, dubiousOwnershipLoggedDirectories, dubiousOwnershipWarnedDirectories, transientGitIdentityReuseLoggedDirectories, sessionIdentityCache, execFileSyncForIdentity, nowMs = () => Date.now(), ProjectIdentityError;
25349
+ function gitRootDirectory(canonical) {
25350
+ const direct = gitRootInAncestorChain(canonical);
25351
+ if (direct)
25352
+ return direct;
25353
+ try {
25354
+ const realCanonical = realpathSync2.native(canonical);
25355
+ return realCanonical === canonical ? null : gitRootInAncestorChain(realCanonical);
25356
+ } catch {
25357
+ return null;
25358
+ }
25359
+ }
25360
+ var GIT_TIMEOUT_MS = 5000, TRANSIENT_FAILURE_COOLDOWN_MS, identityCache, linkedGitWorktreeCache, lastKnownGitIdentityCache, directoryFallbackCache, transientFailureCooldown, dubiousOwnershipFallbackDirectories, dubiousOwnershipLoggedDirectories, dubiousOwnershipWarnedDirectories, transientGitIdentityReuseLoggedDirectories, sessionIdentityCache, execFileSyncForIdentity, userHomeDirectoryForIdentity = () => homedir14(), nowMs = () => Date.now(), ProjectIdentityError;
25165
25361
  var init_project_identity = __esm(() => {
25166
25362
  init_logger();
25363
+ init_project_identity_cache();
25167
25364
  TRANSIENT_FAILURE_COOLDOWN_MS = 5 * 60 * 1000;
25168
25365
  identityCache = new Map;
25169
25366
  linkedGitWorktreeCache = new Map;
@@ -26863,6 +27060,11 @@ var init_user_facing_codes = __esm(() => {
26863
27060
  sentence: "Memory maintenance stopped at its hidden agent step limit.",
26864
27061
  action: "This task needs less work per run; changing the model connection will not help."
26865
27062
  },
27063
+ dream_token_budget: {
27064
+ code: "MC-D11",
27065
+ sentence: "Memory maintenance reached its prompt-token budget.",
27066
+ action: "The unfinished items will be retried on the next run."
27067
+ },
26866
27068
  dream_empty_completion: {
26867
27069
  code: "MC-D03",
26868
27070
  sentence: "Memory maintenance received no usable response.",
@@ -27774,8 +27976,8 @@ var init_pi_executable = __esm(() => {
27774
27976
  });
27775
27977
 
27776
27978
  // ../plugin/src/shared/rpc-utils.ts
27777
- import { execFileSync as execFileSync4 } from "node:child_process";
27778
- import { readFileSync as readFileSync14 } from "node:fs";
27979
+ import { execFile, execFileSync as execFileSync4 } from "node:child_process";
27980
+ import { readFileSync as readFileSync15 } from "node:fs";
27779
27981
  function isOwnRpcServerRecord(record) {
27780
27982
  return record.pid === process.pid && record.instance_id !== undefined && ownRpcServerInstanceIds.has(record.instance_id);
27781
27983
  }
@@ -27818,7 +28020,8 @@ function readPsProcessStartTime(pid) {
27818
28020
  const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
27819
28021
  encoding: "utf8",
27820
28022
  timeout: PS_PROBE_TIMEOUT_MS,
27821
- stdio: ["ignore", "pipe", "pipe"]
28023
+ stdio: ["ignore", "pipe", "pipe"],
28024
+ windowsHide: true
27822
28025
  });
27823
28026
  const processStartTime = Date.parse(String(output).trim());
27824
28027
  return Number.isFinite(processStartTime) ? processStartTime : null;
@@ -27893,7 +28096,8 @@ function readWindowsProcess(pid) {
27893
28096
  const output = rpcIdentityExecFileSync("tasklist", ["/FO", "CSV", "/FI", `PID eq ${pid}`], {
27894
28097
  encoding: "utf8",
27895
28098
  timeout: PS_PROBE_TIMEOUT_MS,
27896
- stdio: ["ignore", "pipe", "pipe"]
28099
+ stdio: ["ignore", "pipe", "pipe"],
28100
+ windowsHide: true
27897
28101
  });
27898
28102
  const entries = parseTasklistOutput(String(output));
27899
28103
  if (entries === null)
@@ -27926,7 +28130,8 @@ function readPsProcessCommand(pid) {
27926
28130
  const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "command="], {
27927
28131
  encoding: "utf8",
27928
28132
  timeout: PS_PROBE_TIMEOUT_MS,
27929
- stdio: ["ignore", "pipe", "pipe"]
28133
+ stdio: ["ignore", "pipe", "pipe"],
28134
+ windowsHide: true
27930
28135
  });
27931
28136
  return String(output);
27932
28137
  } catch {
@@ -28037,7 +28242,8 @@ function execProcessList(exec, file, args, timeout = PS_PROBE_TIMEOUT_MS) {
28037
28242
  return String(exec(file, [...args], {
28038
28243
  encoding: "utf8",
28039
28244
  timeout,
28040
- stdio: ["ignore", "pipe", "pipe"]
28245
+ stdio: ["ignore", "pipe", "pipe"],
28246
+ windowsHide: true
28041
28247
  }));
28042
28248
  }
28043
28249
  function parseWindowsCreationDate(value) {
@@ -28099,7 +28305,7 @@ function parseWindowsCimOutput(output) {
28099
28305
  pid,
28100
28306
  parentPid: Number.isInteger(parentPid) && parentPid > 0 ? parentPid : null,
28101
28307
  commandLine,
28102
- imageName: commandLine ? executableName(commandTokens(commandLine)[0]) : null,
28308
+ imageName: typeof record.Name === "string" ? record.Name : commandLine ? executableName(commandTokens(commandLine)[0]) : null,
28103
28309
  startTime: parseWindowsCreationDate(record.CreationDate)
28104
28310
  });
28105
28311
  }
@@ -28177,14 +28383,14 @@ function readPosixParentPid(pid) {
28177
28383
  return null;
28178
28384
  }
28179
28385
  }
28180
- function collectAncestorPids(selfPid, parentByPid) {
28386
+ function collectAncestorPids(selfPid, parentByPid, probeParents = true) {
28181
28387
  const ancestors = new Set;
28182
28388
  let current = selfPid;
28183
28389
  for (let depth = 0;depth < MAX_ANCESTOR_WALK_DEPTH; depth += 1) {
28184
28390
  let ppid = null;
28185
28391
  if (parentByPid.has(current)) {
28186
28392
  ppid = parentByPid.get(current) ?? null;
28187
- } else if (rpcIdentityPlatform !== "win32") {
28393
+ } else if (rpcIdentityPlatform !== "win32" && probeParents) {
28188
28394
  ppid = readPosixParentPid(current);
28189
28395
  if (ppid == null && current === process.pid && process.ppid > 0) {
28190
28396
  ppid = process.ppid;
@@ -28201,8 +28407,8 @@ function collectAncestorPids(selfPid, parentByPid) {
28201
28407
  }
28202
28408
  return ancestors;
28203
28409
  }
28204
- function classifyLivePiSnapshot(snapshot) {
28205
- const ancestors = collectAncestorPids(process.pid, snapshot.parentByPid);
28410
+ function classifyLivePiSnapshot(snapshot, probeParents = true) {
28411
+ const ancestors = collectAncestorPids(process.pid, snapshot.parentByPid, probeParents);
28206
28412
  const processIds = new Set;
28207
28413
  const inconclusivePids = new Set;
28208
28414
  const skippedAncestorPids = [];
@@ -28247,8 +28453,9 @@ function inspectLivePiProcesses() {
28247
28453
  if (false) {}
28248
28454
  try {
28249
28455
  if (rpcIdentityPlatform === "win32") {
28250
- const cim = tryReadWindowsCimSnapshot(rpcProcessListExecFileSync);
28251
- const snapshot = cim ?? tryReadWindowsTasklistSnapshot();
28456
+ const now = rpcIdentityNowMs();
28457
+ const cached = windowsProcessSnapshotCache;
28458
+ const snapshot = cached && now >= cached.at && now - cached.at < WINDOWS_PROCESS_SNAPSHOT_TTL_MS ? cached.snapshot : tryReadWindowsCimSnapshot(rpcProcessListExecFileSync) ?? tryReadWindowsTasklistSnapshot();
28252
28459
  if (!snapshot) {
28253
28460
  return {
28254
28461
  state: "unreadable",
@@ -28256,6 +28463,8 @@ function inspectLivePiProcesses() {
28256
28463
  error: "process list unavailable"
28257
28464
  };
28258
28465
  }
28466
+ if (snapshot !== cached?.snapshot)
28467
+ windowsProcessSnapshotCache = { snapshot, at: now };
28259
28468
  rememberWindowsProcessFacts(snapshot.facts);
28260
28469
  return classifyLivePiSnapshot(snapshot);
28261
28470
  }
@@ -28301,7 +28510,81 @@ function parseRpcPortFile(content, fallbackPid = 0) {
28301
28510
  function isValidPort(port) {
28302
28511
  return Number.isInteger(port) && port > 0 && port <= 65535;
28303
28512
  }
28304
- var ownRpcServerInstanceIds, RPC_IDENTITY_SKEW_TOLERANCE_MS = 120000, LINUX_CLOCK_TICKS_PER_SECOND = 100, PS_PROBE_TIMEOUT_MS = 1000, WINDOWS_CIM_PROBE_TIMEOUT_MS = 5000, MAX_ANCESTOR_WALK_DEPTH = 16, OPEN_CODE_COMMAND_MARKERS, TASKLIST_NO_TASKS_PATTERN, WINDOWS_CIM_COMMAND = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine,CreationDate | ConvertTo-Json -Compress", PI_HARNESS_ARC_MARKERS, rpcIdentityReadFileSync, rpcIdentityExecFileSync, rpcIdentityProcessKill, rpcProcessListExecFileSync, rpcIdentityPlatform, rpcIdentityNowMs = () => Date.now(), windowsProcessFactsCache = null;
28513
+ function execProcessListAsync(file, args, timeout) {
28514
+ return new Promise((resolve, reject) => {
28515
+ execFile(file, args, { encoding: "utf8", timeout, windowsHide: true, maxBuffer: 8 * 1024 * 1024 }, (error, stdout) => {
28516
+ if (error)
28517
+ reject(error);
28518
+ else
28519
+ resolve(stdout);
28520
+ });
28521
+ });
28522
+ }
28523
+ function inspectProcessesAsync(forceFresh = false) {
28524
+ if (!forceFresh && asyncInspection && rpcIdentityNowMs() < asyncInspectionExpires)
28525
+ return asyncInspection;
28526
+ asyncInspectionExpires = Number.POSITIVE_INFINITY;
28527
+ asyncInspection = (async () => {
28528
+ let snapshot = null;
28529
+ try {
28530
+ if (rpcIdentityPlatform === "win32") {
28531
+ try {
28532
+ const output = await asyncProcessExec("powershell", ["-NoProfile", "-Command", WINDOWS_CIM_COMMAND], WINDOWS_CIM_PROBE_TIMEOUT_MS);
28533
+ const facts = parseWindowsCimOutput(output);
28534
+ if (facts)
28535
+ snapshot = snapshotFromFacts(facts, "cim");
28536
+ } catch {}
28537
+ if (!snapshot) {
28538
+ const entries = parseTasklistOutput(await asyncProcessExec("tasklist", ["/FO", "CSV"], PS_PROBE_TIMEOUT_MS));
28539
+ if (entries)
28540
+ snapshot = snapshotFromFacts(entries.map(({ pid, command }) => ({
28541
+ pid,
28542
+ parentPid: null,
28543
+ commandLine: null,
28544
+ imageName: command,
28545
+ startTime: null
28546
+ })), "tasklist");
28547
+ }
28548
+ } else {
28549
+ const output = await asyncProcessExec("ps", ["-axo", "pid=,ppid=,lstart=,command="], PS_PROBE_TIMEOUT_MS);
28550
+ const facts = [];
28551
+ for (const line of output.split(/\r?\n/)) {
28552
+ const match = /^\s*(\d+)\s+(\d+)\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+(.+)$/.exec(line);
28553
+ if (!match)
28554
+ continue;
28555
+ const startTime = Date.parse(match[3]);
28556
+ facts.push({
28557
+ pid: Number(match[1]),
28558
+ parentPid: Number(match[2]),
28559
+ commandLine: match[4],
28560
+ imageName: null,
28561
+ startTime: Number.isFinite(startTime) ? startTime : null
28562
+ });
28563
+ }
28564
+ snapshot = facts.length > 0 ? snapshotFromFacts(facts, "ps") : null;
28565
+ }
28566
+ } catch {}
28567
+ const fresh = snapshot !== null;
28568
+ if (snapshot)
28569
+ lastAsyncSnapshot = snapshot;
28570
+ else
28571
+ snapshot = lastAsyncSnapshot;
28572
+ const byPid = new Map(snapshot?.facts.map((fact) => [fact.pid, fact]));
28573
+ return {
28574
+ pi: snapshot ? classifyLivePiSnapshot(snapshot, false) : { state: "unreadable", processIds: [] },
28575
+ ...fresh && snapshot ? { processSnapshot: { source: snapshot.source, facts: snapshot.facts } } : {},
28576
+ evidence: (pid) => ({
28577
+ startTime: byPid.get(pid)?.startTime ?? null,
28578
+ commandLine: byPid.get(pid)?.commandLine ?? byPid.get(pid)?.imageName ?? null
28579
+ }),
28580
+ liveness: (pid) => byPid.has(pid) ? "alive" : fresh ? "dead" : "inconclusive"
28581
+ };
28582
+ })().finally(() => {
28583
+ asyncInspectionExpires = rpcIdentityNowMs() + WINDOWS_PROCESS_SNAPSHOT_TTL_MS;
28584
+ });
28585
+ return asyncInspection;
28586
+ }
28587
+ var ownRpcServerInstanceIds, RPC_IDENTITY_SKEW_TOLERANCE_MS = 120000, LINUX_CLOCK_TICKS_PER_SECOND = 100, PS_PROBE_TIMEOUT_MS = 1000, WINDOWS_CIM_PROBE_TIMEOUT_MS = 5000, WINDOWS_PROCESS_SNAPSHOT_TTL_MS = 2000, MAX_ANCESTOR_WALK_DEPTH = 16, OPEN_CODE_COMMAND_MARKERS, TASKLIST_NO_TASKS_PATTERN, WINDOWS_CIM_COMMAND = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine,CreationDate | ConvertTo-Json -Compress", PI_HARNESS_ARC_MARKERS, rpcIdentityReadFileSync, rpcIdentityExecFileSync, rpcIdentityProcessKill, rpcProcessListExecFileSync, rpcIdentityPlatform, rpcIdentityNowMs = () => Date.now(), windowsProcessFactsCache = null, windowsProcessSnapshotCache = null, asyncProcessExec, asyncInspection, asyncInspectionExpires = 0, lastAsyncSnapshot = null;
28305
28588
  var init_rpc_utils = __esm(() => {
28306
28589
  init_logger();
28307
28590
  init_pi_executable();
@@ -28315,11 +28598,12 @@ var init_rpc_utils = __esm(() => {
28315
28598
  "cljs/dist",
28316
28599
  "dist/bundle/cli"
28317
28600
  ];
28318
- rpcIdentityReadFileSync = readFileSync14;
28601
+ rpcIdentityReadFileSync = readFileSync15;
28319
28602
  rpcIdentityExecFileSync = execFileSync4;
28320
28603
  rpcIdentityProcessKill = process.kill;
28321
28604
  rpcProcessListExecFileSync = execFileSync4;
28322
28605
  rpcIdentityPlatform = process.platform;
28606
+ asyncProcessExec = execProcessListAsync;
28323
28607
  });
28324
28608
  // ../plugin/src/features/magic-context/fail-closed-block.ts
28325
28609
  function attachFailClosedBlockingProcessEvidence(process2, evidence) {
@@ -28461,11 +28745,11 @@ var init_fail_closed_block = __esm(() => {
28461
28745
  });
28462
28746
 
28463
28747
  // ../plugin/src/features/magic-context/message-fts-rowid-map.ts
28464
- import { createHash as createHash3 } from "node:crypto";
28748
+ import { createHash as createHash4 } from "node:crypto";
28465
28749
  var EMPTY_INDEX_CONTENT_HASH, upsertMapStatements, rangeReadyStatements, updateTimeStatements, activeBackfills;
28466
28750
  var init_message_fts_rowid_map = __esm(() => {
28467
28751
  init_write_transaction_timing();
28468
- EMPTY_INDEX_CONTENT_HASH = createHash3("sha256").update("").digest("hex");
28752
+ EMPTY_INDEX_CONTENT_HASH = createHash4("sha256").update("").digest("hex");
28469
28753
  upsertMapStatements = new WeakMap;
28470
28754
  rangeReadyStatements = new WeakMap;
28471
28755
  updateTimeStatements = new WeakMap;
@@ -31649,9 +31933,9 @@ import {
31649
31933
  copyFileSync,
31650
31934
  cpSync,
31651
31935
  existsSync as existsSync22,
31652
- mkdirSync as mkdirSync6,
31936
+ mkdirSync as mkdirSync7,
31653
31937
  readdirSync as readdirSync4,
31654
- readFileSync as readFileSync15,
31938
+ readFileSync as readFileSync16,
31655
31939
  statSync as statSync10,
31656
31940
  unlinkSync as unlinkSync3
31657
31941
  } from "node:fs";
@@ -31734,7 +32018,7 @@ function classifyJunkDiscovery(portFile, raw, staleFiles) {
31734
32018
  log3(`[magic-context] removing stale RPC discovery file ${portFile}: ${reason} record older than 10 minutes`);
31735
32019
  return null;
31736
32020
  }
31737
- function inspectRpcServerDiscovery(storageDir) {
32021
+ function inspectRpcServerDiscovery(storageDir, processes) {
31738
32022
  const rpcRoot = join18(storageDir, "rpc");
31739
32023
  let projectEntries;
31740
32024
  try {
@@ -31792,12 +32076,12 @@ function inspectRpcServerDiscovery(storageDir) {
31792
32076
  }
31793
32077
  if (isOwnRpcServerRecord(record))
31794
32078
  continue;
31795
- const liveness = isPidAlive(record.pid);
32079
+ const liveness = processes ? processes.liveness(record.pid) : isPidAlive(record.pid);
31796
32080
  if (liveness === "dead") {
31797
32081
  staleFiles.push(portFile);
31798
32082
  continue;
31799
32083
  }
31800
- const evidence = readProcessProbeEvidence(record.pid);
32084
+ const evidence = processes ? processes.evidence(record.pid) : readProcessProbeEvidence(record.pid);
31801
32085
  const identity = isPidIdentityPlausible(record, evidence);
31802
32086
  if (identity === "plausible") {
31803
32087
  pids.add(record.pid);
@@ -33106,7 +33390,7 @@ var init_storage_db = __esm(async () => {
33106
33390
  RPC_DISCOVERY_PARSE_GRACE_MS = 10 * 60 * 1000;
33107
33391
  defaultRpcDiscoveryFs = {
33108
33392
  readdirSync: (path, options) => options?.withFileTypes ? readdirSync4(path, { withFileTypes: true }) : readdirSync4(path),
33109
- readFileSync: (path, encoding) => String(readFileSync15(path, encoding)),
33393
+ readFileSync: (path, encoding) => String(readFileSync16(path, encoding)),
33110
33394
  statSync: (path) => ({ mtimeMs: statSync10(path).mtimeMs }),
33111
33395
  unlinkSync: (path) => unlinkSync3(path)
33112
33396
  };
@@ -33119,7 +33403,7 @@ var init_storage_db = __esm(async () => {
33119
33403
  });
33120
33404
 
33121
33405
  // src/lib/database-access.ts
33122
- import { existsSync as existsSync23, writeFileSync as writeFileSync5 } from "node:fs";
33406
+ import { existsSync as existsSync23, writeFileSync as writeFileSync6 } from "node:fs";
33123
33407
  import { pathToFileURL } from "node:url";
33124
33408
  function getPersistedSchemaVersion2(db) {
33125
33409
  return getPersistedSchemaVersion(db);
@@ -33173,7 +33457,7 @@ function openExistingContextDatabaseForMutation(path) {
33173
33457
  async function backupDatabaseSnapshot(db, destination) {
33174
33458
  const serializable = db;
33175
33459
  if (typeof serializable.serialize === "function") {
33176
- writeFileSync5(destination, serializable.serialize(), { flag: "wx" });
33460
+ writeFileSync6(destination, serializable.serialize(), { flag: "wx" });
33177
33461
  return;
33178
33462
  }
33179
33463
  const moduleName = "node:" + "sqlite";
@@ -33455,7 +33739,10 @@ function logRow(db, fromIdentity, toIdentity, tableName, rowId, action, targetRo
33455
33739
  (from_identity, to_identity, table_name, row_id, action, target_row_id, merged_at)
33456
33740
  VALUES (?, ?, ?, ?, ?, ?, ?)`).run(fromIdentity, toIdentity, tableName, rowId, action, targetRowId, mergedAt);
33457
33741
  }
33458
- function mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt) {
33742
+ function normalizedContent(value) {
33743
+ return String(value).toLowerCase().replace(/\s+/g, " ").trim();
33744
+ }
33745
+ function mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt, review) {
33459
33746
  const sourceId = row.id;
33460
33747
  if (typeof sourceId !== "number")
33461
33748
  return false;
@@ -33463,10 +33750,13 @@ function mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt) {
33463
33750
  FROM memories
33464
33751
  WHERE project_path = ? AND category = ? AND normalized_hash = ? AND id <> ?
33465
33752
  LIMIT 1`).get(toIdentity, row.category, row.normalized_hash, sourceId);
33466
- if (collision && typeof collision.id === "number") {
33753
+ if (collision && normalizedContent(collision.content) !== normalizedContent(row.content)) {
33754
+ db.prepare("UPDATE memories SET normalized_hash = ?, metadata_json = json_set(CASE WHEN json_valid(metadata_json) THEN metadata_json ELSE '{}' END, '$.identity_merge_original_hash', ?, '$.identity_merge_review', ?) WHERE id = ?").run(`${row.normalized_hash}:identity-conflict:${sourceId}`, row.normalized_hash, fromIdentity, sourceId);
33755
+ review = true;
33756
+ } else if (collision && typeof collision.id === "number") {
33467
33757
  const targetId = collision.id;
33468
33758
  const targetSeen = Number(collision.seen_count ?? 1);
33469
- const mergedSeen = Math.max(targetSeen, Number(row.seen_count ?? 1));
33759
+ const mergedSeen = targetSeen + Number(row.seen_count ?? 1);
33470
33760
  const sourceClassifiedAt = Number(row.classified_at ?? 0);
33471
33761
  const targetClassifiedAt = Number(collision.classified_at ?? 0);
33472
33762
  if (sourceClassifiedAt > targetClassifiedAt) {
@@ -33499,8 +33789,9 @@ function mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt) {
33499
33789
  END`).run(targetId, sourceId);
33500
33790
  db.prepare("DELETE FROM memory_verifications WHERE memory_id = ?").run(sourceId);
33501
33791
  if (mergedSeen !== targetSeen || collision.status === null || collision.status === undefined) {
33502
- db.prepare("UPDATE memories SET seen_count = ?, status = COALESCE(status, 'active'), updated_at = ? WHERE id = ?").run(mergedSeen, mergedAt, targetId);
33792
+ db.prepare("UPDATE memories SET seen_count = ?, status = CASE WHEN ? = 'active' THEN 'active' ELSE COALESCE(status, 'active') END, updated_at = ? WHERE id = ?").run(mergedSeen, row.status, mergedAt, targetId);
33503
33793
  }
33794
+ db.prepare(`UPDATE memories SET first_seen_at = MIN(first_seen_at, ?), last_seen_at = MAX(last_seen_at, ?), retrieval_count = COALESCE(retrieval_count, 0) + ?, merged_from = CASE WHEN merged_from IS NULL OR merged_from = '' THEN ? ELSE merged_from || ',' || ? END WHERE id = ?`).run(row.first_seen_at, row.last_seen_at, Number(row.retrieval_count ?? 0), String(sourceId), String(sourceId), targetId);
33504
33795
  db.prepare(`UPDATE memories
33505
33796
  SET status = 'archived',
33506
33797
  superseded_by_memory_id = ?,
@@ -33516,6 +33807,9 @@ function mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt) {
33516
33807
  logRow(db, fromIdentity, toIdentity, "memories", String(sourceId), "superseded", String(targetId), mergedAt);
33517
33808
  return true;
33518
33809
  }
33810
+ if (review) {
33811
+ db.prepare("UPDATE memories SET metadata_json = json_set(CASE WHEN json_valid(metadata_json) THEN metadata_json ELSE '{}' END, '$.identity_merge_review', ?) WHERE id = ?").run(fromIdentity, sourceId);
33812
+ }
33519
33813
  const result = db.prepare("UPDATE memories SET project_path = ?, updated_at = ? WHERE id = ? AND project_path = ?").run(toIdentity, mergedAt, sourceId, fromIdentity);
33520
33814
  if ((result.changes ?? 0) === 0)
33521
33815
  return false;
@@ -33530,21 +33824,33 @@ function oldestOpenCycleStart(left, right) {
33530
33824
  const values = [left, right].filter((value) => typeof value === "number" && value > 0);
33531
33825
  return values.length > 0 ? Math.min(...values) : null;
33532
33826
  }
33533
- function reconcileTaskScheduleCollision(db, source, target) {
33534
- const sourceIsNewer = typeof source.last_run_at === "number" && (typeof target.last_run_at !== "number" || source.last_run_at > target.last_run_at);
33827
+ function reconcileTaskScheduleCollision(db, source, target, mergedAt) {
33828
+ const sourceIsNewer = source.last_status === "completed" && (target.last_status !== "completed" || typeof source.last_run_at === "number" && (typeof target.last_run_at !== "number" || source.last_run_at > target.last_run_at));
33535
33829
  const latest = sourceIsNewer ? source : target;
33536
33830
  db.prepare(`UPDATE task_schedule_state
33537
33831
  SET last_run_at = ?, next_due_at = ?, schedule = ?, last_status = ?,
33538
33832
  last_error = ?, retry_count = ?, last_checked_commit = ?,
33539
33833
  last_broad_run_at = ?, retrospective_watermark_ms = ?
33540
- WHERE project_path = ? AND task = ?`).run(nullableMaximum(source.last_run_at, target.last_run_at), latest.next_due_at, latest.schedule, latest.last_status, latest.last_error, latest.retry_count, latest.last_checked_commit, oldestOpenCycleStart(source.last_broad_run_at, target.last_broad_run_at), nullableMaximum(source.retrospective_watermark_ms, target.retrospective_watermark_ms), target.project_path, target.task);
33834
+ WHERE project_path = ? AND task = ?`).run(latest.last_run_at, nextDueAtMs(String(target.schedule ?? ""), Math.max(mergedAt, Number(source.last_run_at ?? 0), Number(target.last_run_at ?? 0))), target.schedule, latest.last_status, latest.last_error, latest.retry_count, latest.last_checked_commit, oldestOpenCycleStart(source.last_broad_run_at, target.last_broad_run_at), nullableMaximum(source.retrospective_watermark_ms, target.retrospective_watermark_ms), target.project_path, target.task);
33541
33835
  }
33542
33836
  function rekeyGenericRow(db, table, row, fromIdentity, toIdentity, mergedAt) {
33543
33837
  const rowId = rowKey(db, table.name, row);
33544
33838
  const collision = findUniqueCollision(db, table, row, fromIdentity, toIdentity);
33545
33839
  if (collision) {
33546
33840
  if (table.name === "task_schedule_state") {
33547
- reconcileTaskScheduleCollision(db, row, collision);
33841
+ reconcileTaskScheduleCollision(db, row, collision, mergedAt);
33842
+ }
33843
+ const maximumColumns = {
33844
+ retrospective_processed_windows: ["processed_at"],
33845
+ embedding_identity_active: ["last_active_at"],
33846
+ memory_embedding_watermarks: ["written_memory_id", "embedded_memory_id", "updated_at"],
33847
+ project_state: ["project_memory_epoch", "project_user_profile_version", "updated_at"],
33848
+ project_key_files_version: ["version"]
33849
+ };
33850
+ const columns = maximumColumns[table.name];
33851
+ if (columns) {
33852
+ const targetPredicate = rowPredicate(db, table.name, collision);
33853
+ db.prepare(`UPDATE ${quoteIdentifier(table.name)} SET ${columns.map((column) => `${quoteIdentifier(column)} = ?`).join(", ")} WHERE ${targetPredicate.sql}`).run(...columns.map((column) => nullableMaximum(row[column], collision[column])), ...targetPredicate.values);
33548
33854
  }
33549
33855
  const sourcePredicate = rowPredicate(db, table.name, row);
33550
33856
  const result = db.prepare(`DELETE FROM ${quoteIdentifier(table.name)} WHERE ${sourcePredicate.sql}`).run(...sourcePredicate.values);
@@ -33553,6 +33859,9 @@ function rekeyGenericRow(db, table, row, fromIdentity, toIdentity, mergedAt) {
33553
33859
  logRow(db, fromIdentity, toIdentity, table.name, rowId, "collision_deleted", rowKey(db, table.name, collision), mergedAt);
33554
33860
  return true;
33555
33861
  }
33862
+ if (table.name === "task_schedule_state") {
33863
+ db.prepare("UPDATE task_schedule_state SET next_due_at = ? WHERE project_path = ? AND task = ?").run(nextDueAtMs(String(row.schedule ?? ""), Math.max(mergedAt, Number(row.last_run_at ?? 0))), fromIdentity, row.task);
33864
+ }
33556
33865
  const predicate = rowPredicate(db, table.name, row);
33557
33866
  const result = db.prepare(`UPDATE ${quoteIdentifier(table.name)}
33558
33867
  SET ${quoteIdentifier(table.identityColumn)} = ?
@@ -33564,21 +33873,37 @@ function rekeyGenericRow(db, table, row, fromIdentity, toIdentity, mergedAt) {
33564
33873
  return true;
33565
33874
  }
33566
33875
  function tableSourceRows(db, table, fromIdentity) {
33567
- return db.prepare(`SELECT rowid, * FROM ${quoteIdentifier(table.name)} WHERE ${quoteIdentifier(table.identityColumn)} = ?`).all(fromIdentity);
33876
+ return db.prepare(`SELECT rowid, * FROM ${quoteIdentifier(table.name)} WHERE ${quoteIdentifier(table.identityColumn)} = ?${table.name === "memories" ? " AND NOT (status = 'archived' AND superseded_by_memory_id IS NOT NULL AND EXISTS (SELECT 1 FROM identity_merge_log l WHERE l.table_name = 'memories' AND l.row_id = CAST(memories.id AS TEXT) AND l.from_identity = memories.project_path AND l.action = 'superseded'))" : ""}`).all(fromIdentity);
33568
33877
  }
33569
33878
  function assertMergeAllowed(db, fromIdentity, toIdentity) {
33879
+ const memberships = db.prepare(`SELECT m.project_path, w.id, w.name FROM workspace_members m JOIN workspaces w ON w.id = m.workspace_id WHERE m.project_path IN (?, ?)`).all(fromIdentity, toIdentity);
33880
+ if (memberships.length === 2 && memberships[0].id !== memberships[1].id) {
33881
+ throw new Error(`Refusing identity merge: different workspaces ${memberships[0].name} and ${memberships[1].name}. Choose membership before merging.`);
33882
+ }
33570
33883
  if (!tableExists3(db, "authority_managed"))
33571
33884
  return;
33572
33885
  const source = db.prepare("SELECT 1 FROM authority_managed WHERE project_path = ? LIMIT 1").get(fromIdentity);
33573
33886
  if (source) {
33574
- throw new Error(`Refusing identity merge: ${fromIdentity} is managed by the Rust module. Drain module authority before re-keying; module-store re-keying is not in scope.`);
33887
+ throw new Error(`Refusing identity merge: ${fromIdentity} is managed by the Rust module: this project runs in Rust mode and its data lives in the module store. Merging will be possible once the single-store migration lands.`);
33575
33888
  }
33576
33889
  const target = db.prepare("SELECT 1 FROM authority_managed WHERE project_path = ? LIMIT 1").get(toIdentity);
33577
33890
  if (target) {
33578
- throw new Error(`Refusing identity merge: ${toIdentity} is managed by the Rust module. Module-owned target pools cannot be re-keyed by this command.`);
33891
+ throw new Error(`Refusing identity merge: ${toIdentity} is managed by the Rust module: this project runs in Rust mode and its data lives in the module store. Merging will be possible once the single-store migration lands.`);
33579
33892
  }
33580
33893
  }
33581
- function auditIdentityMerge(db, fromIdentity, toIdentity) {
33894
+ function memoryDisposition(db, from, to) {
33895
+ const duplicateMemoryIds = [];
33896
+ const reviewMemoryIds = [];
33897
+ for (const row of tableSourceRows(db, { name: "memories", identityColumn: "project_path", derived: false }, from)) {
33898
+ const collision = db.prepare("SELECT content FROM memories WHERE project_path = ? AND category = ? AND normalized_hash = ?").get(to, row.category, row.normalized_hash);
33899
+ if (collision && normalizedContent(collision.content) === normalizedContent(row.content))
33900
+ duplicateMemoryIds.push(Number(row.id));
33901
+ else if (db.prepare("SELECT 1 FROM memories WHERE project_path = ? AND category = ? LIMIT 1").get(to, row.category))
33902
+ reviewMemoryIds.push(Number(row.id));
33903
+ }
33904
+ return { duplicateMemoryIds, reviewMemoryIds };
33905
+ }
33906
+ function auditIdentityMerge(db, fromIdentity, toIdentity, now = Date.now()) {
33582
33907
  const auditedTables = discoverIdentityTables(db).map((table) => ({
33583
33908
  tableName: table.name,
33584
33909
  identityColumn: table.identityColumn,
@@ -33586,12 +33911,31 @@ function auditIdentityMerge(db, fromIdentity, toIdentity) {
33586
33911
  sourceRows: table.derived ? 0 : tableSourceRows(db, table, fromIdentity).length,
33587
33912
  changedRows: 0
33588
33913
  }));
33914
+ const changes = [];
33915
+ for (const table of discoverIdentityTables(db).filter((table) => !table.derived && table.name !== "memories")) {
33916
+ for (const row of tableSourceRows(db, table, fromIdentity)) {
33917
+ const collision = findUniqueCollision(db, table, row, fromIdentity, toIdentity);
33918
+ if (collision)
33919
+ changes.push(`${table.name} ${rowKey(db, table.name, row)}: collapse into ${rowKey(db, table.name, collision)} (target content/configuration retained; progress counters reconciled)`);
33920
+ if (table.name === "workspace_members")
33921
+ changes.push(`workspace ${row.workspace_id}: ${collision ? "collapse aliases" : "transfer source membership"}`);
33922
+ if (table.name === "task_schedule_state") {
33923
+ const schedule = String((collision ?? row).schedule ?? "");
33924
+ changes.push(`task ${row.task}: schedule ${JSON.stringify(row.schedule)} → ${JSON.stringify(schedule)}; next_due_at → ${nextDueAtMs(schedule, Math.max(now, Number(row.last_run_at ?? 0), Number(collision?.last_run_at ?? 0)))}`);
33925
+ }
33926
+ }
33927
+ }
33928
+ const queues = db.prepare("SELECT reason, COUNT(*) AS n FROM dream_queue WHERE project_path IN (?, ?) GROUP BY reason HAVING COUNT(*) > 1").all(fromIdentity, toIdentity);
33929
+ for (const queue of queues)
33930
+ changes.push(`dream_queue ${JSON.stringify(queue.reason)}: ${queue.n} → 1 queued row`);
33589
33931
  return {
33590
33932
  fromIdentity,
33591
33933
  toIdentity,
33934
+ changes,
33592
33935
  auditedTables,
33593
33936
  changedRows: auditedTables.reduce((total, table) => total + table.sourceRows, 0),
33594
- dryRun: true
33937
+ dryRun: true,
33938
+ ...memoryDisposition(db, fromIdentity, toIdentity)
33595
33939
  };
33596
33940
  }
33597
33941
  function mergeProjectIdentities(db, fromIdentity, toIdentity, options = {}) {
@@ -33603,11 +33947,14 @@ function mergeProjectIdentities(db, fromIdentity, toIdentity, options = {}) {
33603
33947
  }
33604
33948
  assertMergeAllowed(db, fromIdentity, toIdentity);
33605
33949
  const tables = discoverIdentityTables(db);
33606
- const report = auditIdentityMerge(db, fromIdentity, toIdentity);
33950
+ const report = auditIdentityMerge(db, fromIdentity, toIdentity, options.now);
33607
33951
  if (options.dryRun)
33608
33952
  return report;
33953
+ if (report.changedRows === 0)
33954
+ return { ...report, dryRun: false };
33609
33955
  const mergedAt = options.now ?? Date.now();
33610
33956
  const run = db.transaction(() => {
33957
+ assertMergeAllowed(db, fromIdentity, toIdentity);
33611
33958
  if (tableExists3(db, "v22_identity_rekey_map")) {
33612
33959
  db.prepare(`INSERT INTO v22_identity_rekey_map (old_project_path, new_project_path, rekeyed_at)
33613
33960
  VALUES (?, ?, ?)
@@ -33622,11 +33969,15 @@ function mergeProjectIdentities(db, fromIdentity, toIdentity, options = {}) {
33622
33969
  const rows = tableSourceRows(db, table, fromIdentity);
33623
33970
  tableReport.sourceRows = rows.length;
33624
33971
  for (const row of rows) {
33625
- const changed = table.name === "memories" ? mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt) : rekeyGenericRow(db, table, row, fromIdentity, toIdentity, mergedAt);
33972
+ const changed = table.name === "memories" ? mergeMemoryRow(db, row, fromIdentity, toIdentity, mergedAt, report.reviewMemoryIds.includes(Number(row.id))) : rekeyGenericRow(db, table, row, fromIdentity, toIdentity, mergedAt);
33626
33973
  if (changed)
33627
33974
  tableReport.changedRows += 1;
33628
33975
  }
33629
33976
  }
33977
+ db.prepare(`DELETE FROM dream_queue WHERE project_path = ? AND id NOT IN (SELECT MIN(id) FROM dream_queue WHERE project_path = ? GROUP BY reason)`).run(toIdentity, toIdentity);
33978
+ db.prepare(`UPDATE dream_queue SET started_at = NULL WHERE project_path = ?`).run(toIdentity);
33979
+ db.prepare(`UPDATE git_sweep_coordinator SET lease_holder = NULL, lease_expires_at = NULL, last_swept_at = NULL WHERE project_path = ?`).run(toIdentity);
33980
+ db.prepare(`UPDATE session_meta SET cached_m0_bytes = NULL, cached_m0_project_identity = NULL WHERE cached_m0_project_identity IN (?, ?)`).run(fromIdentity, toIdentity);
33630
33981
  db.prepare(`INSERT INTO project_state
33631
33982
  (project_path, project_memory_epoch, project_user_profile_version, updated_at)
33632
33983
  VALUES (?, 1, 0, ?)
@@ -33643,6 +33994,7 @@ function mergeProjectIdentities(db, fromIdentity, toIdentity, options = {}) {
33643
33994
  }
33644
33995
  var IDENTITY_COLUMNS, DERIVED_TABLE_SUFFIXES;
33645
33996
  var init_storage_identity_merge = __esm(() => {
33997
+ init_cron();
33646
33998
  IDENTITY_COLUMNS = new Set(["project_path", "project_identity"]);
33647
33999
  DERIVED_TABLE_SUFFIXES = [
33648
34000
  "_fts",
@@ -33654,84 +34006,88 @@ var init_storage_identity_merge = __esm(() => {
33654
34006
  ];
33655
34007
  });
33656
34008
 
33657
- // src/commands/doctor-merge-identity.ts
34009
+ // src/commands/doctor-identity-splits.ts
34010
+ import { realpathSync as realpathSync4 } from "node:fs";
33658
34011
  import { join as join19 } from "node:path";
33659
- function valueAfter(args, flag) {
33660
- const index = args.indexOf(flag);
33661
- if (index < 0)
33662
- return null;
33663
- const value = args[index + 1];
33664
- if (!value || value.startsWith("--")) {
33665
- throw new Error(`${flag} requires a value`);
33666
- }
33667
- return value;
34012
+ function tableExists4(db, table) {
34013
+ return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(table);
33668
34014
  }
33669
- function parseMergeIdentityArgs(args) {
33670
- const from = valueAfter(args, "--from");
33671
- const to = valueAfter(args, "--to");
33672
- if (!from || !to)
33673
- throw new Error("doctor merge-identity requires --from and --to");
33674
- const dbPath = valueAfter(args, "--db");
33675
- const knownFlags = new Set(["--from", "--to", "--db"]);
33676
- for (let index = 0;index < args.length; index += 1) {
33677
- const arg = args[index];
33678
- if (knownFlags.has(arg)) {
33679
- index += 1;
34015
+ function findIdentitySplits(db, host, includeSingles = false) {
34016
+ if (!tableExists4(db, "session_projects"))
34017
+ return [];
34018
+ const directories = new Map;
34019
+ for (const table of ["session", "session_v2"]) {
34020
+ if (!tableExists4(host, table))
33680
34021
  continue;
34022
+ const rows = host.prepare(`SELECT id, directory FROM ${table} WHERE directory IS NOT NULL`).all();
34023
+ for (const row of rows) {
34024
+ if (!row.directory)
34025
+ continue;
34026
+ let canonical = row.directory;
34027
+ try {
34028
+ canonical = realpathSync4.native(canonical);
34029
+ } catch {}
34030
+ const key = `${table === "session" ? "opencode" : "opencode2"}\x00${row.id}`;
34031
+ const roots = directories.get(key) ?? new Set;
34032
+ roots.add(projectDirectoryKey(canonical));
34033
+ directories.set(key, roots);
33681
34034
  }
33682
- if (arg === "--dry-run" || arg === "--yes")
34035
+ }
34036
+ const byDirectory = new Map;
34037
+ const bindings = db.prepare("SELECT session_id, harness, project_path FROM session_projects WHERE harness IN ('opencode', 'opencode2')").all();
34038
+ for (const row of bindings) {
34039
+ if (!/^(git|dir):/.test(row.project_path))
33683
34040
  continue;
33684
- throw new Error(`Unknown doctor merge-identity option: ${arg}`);
34041
+ for (const directory of directories.get(`${row.harness}\x00${row.session_id}`) ?? []) {
34042
+ const identities = byDirectory.get(directory) ?? new Set;
34043
+ identities.add(row.project_path);
34044
+ byDirectory.set(directory, identities);
34045
+ }
33685
34046
  }
33686
- return {
33687
- from,
33688
- to,
33689
- dryRun: args.includes("--dry-run"),
33690
- yes: args.includes("--yes"),
33691
- ...dbPath ? { dbPath } : {}
33692
- };
34047
+ const count = (table, identity) => tableExists4(db, table) ? db.prepare(`SELECT COUNT(*) AS n FROM ${table} WHERE project_path = ?`).get(identity).n : 0;
34048
+ return [...byDirectory].filter(([, identities]) => includeSingles || identities.size > 1).sort(([a], [b]) => a.localeCompare(b)).map(([directory, identities]) => ({
34049
+ directory,
34050
+ identities: [...identities].sort().map((identity) => ({
34051
+ identity,
34052
+ sessions: count("session_projects", identity),
34053
+ memories: count("memories", identity),
34054
+ notes: count("notes", identity),
34055
+ dreamer: [
34056
+ "dream_queue",
34057
+ "dream_runs",
34058
+ "task_schedule_state",
34059
+ "retrospective_processed_windows"
34060
+ ].reduce((n, table) => n + count(table, identity), 0)
34061
+ }))
34062
+ }));
33693
34063
  }
33694
- function printReport(report) {
33695
- console.log(`Identity merge ${report.dryRun ? "preview" : "complete"}:`);
33696
- console.log(` from: ${report.fromIdentity}`);
33697
- console.log(` to: ${report.toIdentity}`);
33698
- console.log(" audited project-scoped tables:");
33699
- for (const table of report.auditedTables) {
33700
- const suffix = table.derived ? " (derived; updated by source triggers)" : "";
33701
- console.log(` ${table.tableName} [${table.identityColumn}]${suffix}: ${report.dryRun ? table.sourceRows : table.changedRows} row(s)`);
33702
- }
33703
- console.log(` rows changed: ${report.changedRows}`);
33704
- if (report.dryRun) {
33705
- console.log(" no database writes performed");
33706
- }
34064
+ function formatIdentitySplits(splits) {
34065
+ return splits.flatMap((split) => [
34066
+ `Project identity split: ${split.directory} (read-only; no merge performed)`,
34067
+ ...split.identities.map((row) => ` ${row.identity}: ${row.sessions} sessions, ${row.memories} memories, ${row.notes} notes, ${row.dreamer} dreamer rows (identity-wide counts)`)
34068
+ ]);
33707
34069
  }
33708
- function openMergeDatabase(path, mutate) {
33709
- const db = mutate ? openExistingContextDatabaseForMutation(path) : openExistingContextDatabase(path, { readonly: true });
34070
+ function diagnoseIdentitySplits() {
34071
+ const db = openExistingContextDatabase(join19(getMagicContextStorageDir(), "context.db"), {
34072
+ readonly: true
34073
+ });
33710
34074
  if (!db)
33711
- throw new Error(`Context database does not exist: ${path}`);
33712
- return db;
33713
- }
33714
- function runMergeIdentityCli(args) {
33715
- const options = parseMergeIdentityArgs(args);
33716
- if (!options.dryRun && !options.yes) {
33717
- console.error("Refusing to mutate context.db without explicit --yes. Re-run with --yes, or use --dry-run to preview.");
33718
- return 2;
33719
- }
33720
- const dbPath = options.dbPath ?? join19(getMagicContextStorageDir(), "context.db");
33721
- const db = openMergeDatabase(dbPath, !options.dryRun);
34075
+ return [];
34076
+ let host = null;
33722
34077
  try {
33723
- const report = mergeProjectIdentities(db, options.from, options.to, {
33724
- dryRun: options.dryRun
33725
- });
33726
- printReport(report);
33727
- return 0;
34078
+ host = openExistingDatabase(resolveOpenCodeDbPath().path, { readonly: true });
34079
+ if (!host)
34080
+ return ["Identity split check skipped: OpenCode session directories unavailable."];
34081
+ return formatIdentitySplits(findIdentitySplits(db, host));
33728
34082
  } finally {
34083
+ host?.close();
33729
34084
  db.close();
33730
34085
  }
33731
34086
  }
33732
- var init_doctor_merge_identity = __esm(async () => {
33733
- init_storage_identity_merge();
34087
+ var init_doctor_identity_splits = __esm(async () => {
34088
+ init_project_identity_cache();
33734
34089
  init_data_path();
34090
+ init_opencode_db_path();
33735
34091
  await init_database_access();
33736
34092
  });
33737
34093
 
@@ -33744,7 +34100,7 @@ import {
33744
34100
  copyFileSync as copyFileSync2,
33745
34101
  existsSync as existsSync24,
33746
34102
  openSync as openSync4,
33747
- renameSync as renameSync4,
34103
+ renameSync as renameSync5,
33748
34104
  rmSync as rmSync3,
33749
34105
  statSync as statSync11
33750
34106
  } from "node:fs";
@@ -33999,15 +34355,15 @@ function activateReplacement(dbPath, replacementPath, originalAsidePath) {
33999
34355
  if (!existsSync24(from))
34000
34356
  continue;
34001
34357
  const to = `${originalAsidePath}${suffix}`;
34002
- renameSync4(from, to);
34358
+ renameSync5(from, to);
34003
34359
  movedOriginals.push({ from, to });
34004
34360
  }
34005
- renameSync4(replacementPath, dbPath);
34361
+ renameSync5(replacementPath, dbPath);
34006
34362
  return movedOriginals.map(({ to }) => to);
34007
34363
  } catch (error) {
34008
34364
  for (const moved of movedOriginals.reverse()) {
34009
34365
  if (existsSync24(moved.to) && !existsSync24(moved.from))
34010
- renameSync4(moved.to, moved.from);
34366
+ renameSync5(moved.to, moved.from);
34011
34367
  }
34012
34368
  throw error;
34013
34369
  }
@@ -34238,6 +34594,232 @@ var init_doctor_repair_db = __esm(async () => {
34238
34594
  ];
34239
34595
  });
34240
34596
 
34597
+ // src/commands/doctor-merge-identity.ts
34598
+ import { execFileSync as execFileSync5 } from "node:child_process";
34599
+ import { createHash as createHash5 } from "node:crypto";
34600
+ import {
34601
+ cpSync as cpSync2,
34602
+ existsSync as existsSync25,
34603
+ mkdirSync as mkdirSync8,
34604
+ mkdtempSync,
34605
+ readdirSync as readdirSync5,
34606
+ readFileSync as readFileSync17,
34607
+ renameSync as renameSync6,
34608
+ writeFileSync as writeFileSync7
34609
+ } from "node:fs";
34610
+ import { dirname as dirname13, join as join21 } from "node:path";
34611
+ function parseMergeIdentityArgs(args) {
34612
+ const values = {};
34613
+ const switches = new Set(["--apply", "--force", "--dry-run", "--yes"]);
34614
+ for (let i = 0;i < args.length; i++) {
34615
+ const flag = args[i];
34616
+ if (["--from", "--to", "--db", "--host-db"].includes(flag)) {
34617
+ const value = args[++i];
34618
+ if (!value || value.startsWith("--"))
34619
+ throw new Error(`${flag} requires a value`);
34620
+ if (values[flag])
34621
+ throw new Error(`Repeated option: ${flag}`);
34622
+ values[flag] = value;
34623
+ } else if (!switches.has(flag))
34624
+ throw new Error(`Unknown doctor merge-identities option: ${flag}`);
34625
+ }
34626
+ if (Boolean(values["--from"]) !== Boolean(values["--to"]))
34627
+ throw new Error("Supply both --from and --to");
34628
+ const apply = args.includes("--apply") || args.includes("--yes");
34629
+ if (apply && (!values["--from"] || args.includes("--dry-run")))
34630
+ throw new Error("--apply requires --from/--to and cannot be combined with --dry-run");
34631
+ return {
34632
+ from: values["--from"],
34633
+ to: values["--to"],
34634
+ dbPath: values["--db"],
34635
+ hostPath: values["--host-db"],
34636
+ dryRun: !apply,
34637
+ yes: apply,
34638
+ force: args.includes("--force")
34639
+ };
34640
+ }
34641
+ function printReport(report) {
34642
+ console.log(`Identity merge ${report.dryRun ? "preview" : "complete"}: ${report.fromIdentity} → ${report.toIdentity}`);
34643
+ for (const table of report.auditedTables) {
34644
+ console.log(` ${table.tableName}: ${report.dryRun ? table.sourceRows : table.changedRows} row(s)${table.derived ? " (derived; maintained by triggers)" : ""}`);
34645
+ }
34646
+ for (const change of report.changes)
34647
+ console.log(` ${change}`);
34648
+ console.log(` identical memories collapsed: ${report.duplicateMemoryIds.join(", ") || "none"}`);
34649
+ console.log(` potential content conflicts kept for review (same category): ${report.reviewMemoryIds.join(", ") || "none"}`);
34650
+ console.log(" Workspace aliases collapse; source-only membership transfers. Target schedules win; next_due_at is recomputed. Queued reasons deduplicate; run history and processed windows remain.");
34651
+ console.log(" Git indexes move; sweep leases reset for rebuilding. Existing memory embeddings remain valid (content unchanged); FTS triggers rekey indexes.");
34652
+ if (!report.changedRows)
34653
+ console.log(" No-op: no source rows remain to move.");
34654
+ if (report.dryRun)
34655
+ console.log(" No writes. Review this preview, then repeat with --apply to confirm.");
34656
+ }
34657
+ function resolveReadOnly(directory) {
34658
+ if (!existsSync25(directory))
34659
+ return;
34660
+ try {
34661
+ const roots = execFileSync5("git", ["rev-list", "--max-parents=0", "HEAD"], {
34662
+ cwd: directory,
34663
+ windowsHide: true,
34664
+ encoding: "utf8",
34665
+ timeout: 5000,
34666
+ stdio: ["ignore", "pipe", "pipe"]
34667
+ }).trim().split(/\s+/).filter((root) => /^[0-9a-f]{40,64}$/.test(root)).sort();
34668
+ if (roots[0])
34669
+ return `git:${roots[0]}`;
34670
+ } catch {}
34671
+ try {
34672
+ const result = execFileSync5("git", ["rev-parse", "--is-inside-work-tree"], {
34673
+ cwd: directory,
34674
+ windowsHide: true,
34675
+ encoding: "utf8",
34676
+ timeout: 5000,
34677
+ stdio: ["ignore", "pipe", "pipe"]
34678
+ });
34679
+ if (result.trim() === "true")
34680
+ return;
34681
+ } catch (error) {
34682
+ if (String(error.stderr).includes("not a git repository"))
34683
+ return `dir:${createHash5("md5").update(projectDirectoryKey(directory)).digest("hex").slice(0, 12)}`;
34684
+ }
34685
+ return;
34686
+ }
34687
+ function observations(db, hostPath, storageDir) {
34688
+ const host = openExistingDatabase(hostPath, { readonly: true });
34689
+ let rows = [];
34690
+ try {
34691
+ rows = host ? findIdentitySplits(db, host, true) : [];
34692
+ } finally {
34693
+ host?.close();
34694
+ }
34695
+ const add = (directory, identity) => {
34696
+ if (!/^(git|dir):/.test(identity))
34697
+ return;
34698
+ directory = projectDirectoryKey(directory);
34699
+ let row = rows.find((item) => item.directory === directory);
34700
+ if (!row) {
34701
+ row = { directory, identities: [] };
34702
+ rows.push(row);
34703
+ }
34704
+ if (!row.identities.some((item) => item.identity === identity))
34705
+ row.identities.push({ identity, sessions: 0, memories: 0, notes: 0, dreamer: 0 });
34706
+ };
34707
+ const sidecars = join21(storageDir, "project-identities");
34708
+ if (existsSync25(sidecars))
34709
+ for (const file of readdirSync5(sidecars)) {
34710
+ if (!file.endsWith(".json"))
34711
+ continue;
34712
+ try {
34713
+ const record = JSON.parse(readFileSync17(join21(sidecars, file), "utf8"));
34714
+ if (typeof record.directory === "string" && typeof record.identity === "string")
34715
+ add(record.directory, record.identity);
34716
+ } catch {}
34717
+ }
34718
+ for (const row of db.prepare("SELECT project_path, display_path FROM workspace_members").all())
34719
+ add(row.display_path, row.project_path);
34720
+ return rows;
34721
+ }
34722
+ function runMergeIdentityCli(args, deps = {}) {
34723
+ const options = parseMergeIdentityArgs(args);
34724
+ const dbPath = options.dbPath ?? join21(getMagicContextStorageDir(), "context.db");
34725
+ const storageDir = dirname13(dbPath);
34726
+ const hostPath = options.hostPath ?? (options.dbPath ? join21(storageDir, "host.db") : resolveOpenCodeDbPath().path);
34727
+ let db = openExistingContextDatabase(dbPath, {
34728
+ readonly: true,
34729
+ minimumSupportedVersion: CLI_SCHEMA_FLOOR_VERSION
34730
+ });
34731
+ if (!db)
34732
+ throw new Error(`Context database does not exist: ${dbPath}`);
34733
+ let directories = [];
34734
+ try {
34735
+ const observed = observations(db, hostPath, storageDir);
34736
+ if (!options.from || !options.to) {
34737
+ for (const split of observed.filter((row) => row.identities.length > 1)) {
34738
+ const target = resolveReadOnly(split.directory) ?? split.identities.map((row) => row.identity).filter((id) => id.startsWith("git:")).sort()[0];
34739
+ console.log(`Project identity split: ${split.directory}`);
34740
+ for (const row of split.identities)
34741
+ if (row.identity !== target)
34742
+ console.log(` ${row.identity} → ${target ?? "unresolved target"}`);
34743
+ }
34744
+ if (!observed.some((row) => row.identities.length > 1))
34745
+ console.log("No detected identity splits.");
34746
+ return 0;
34747
+ }
34748
+ if (options.from === options.to)
34749
+ throw new Error(`Source and target identities must differ: ${options.from}`);
34750
+ for (const identity of [options.from, options.to]) {
34751
+ const known = auditIdentityMerge(db, identity, options.to).changedRows > 0 || db.prepare("SELECT 1 FROM v22_identity_rekey_map WHERE old_project_path = ? OR new_project_path = ?").get(identity, identity);
34752
+ if (!known)
34753
+ throw new Error(`Unknown identity: ${identity}`);
34754
+ }
34755
+ const sourceDirs = observed.filter((row) => row.identities.some((item) => item.identity === options.from)).map((row) => row.directory);
34756
+ const targetDirs = observed.filter((row) => row.identities.some((item) => item.identity === options.to)).map((row) => row.directory);
34757
+ directories = [...new Set([...sourceDirs, ...targetDirs])];
34758
+ const alreadyMerged = auditIdentityMerge(db, options.from, options.to).changedRows === 0 && db.prepare("SELECT 1 FROM v22_identity_rekey_map WHERE old_project_path = ? AND new_project_path = ?").get(options.from, options.to);
34759
+ if (!alreadyMerged && options.from.startsWith("git:") && options.to.startsWith("git:") && (!sourceDirs.length || !targetDirs.length || sourceDirs.some((dir) => !targetDirs.includes(dir)) || targetDirs.some((dir) => !sourceDirs.includes(dir))))
34760
+ throw new Error(`Refusing different directories for ${options.from} (${sourceDirs.join(", ") || "unknown"}) and ${options.to} (${targetDirs.join(", ") || "unknown"})`);
34761
+ if (!options.force && !directories.some((directory) => resolveReadOnly(directory) === options.to))
34762
+ throw new Error(`Target ${options.to} does not resolve for any observed directory on this machine; use --force only after reviewing the identity.`);
34763
+ const preview = mergeProjectIdentities(db, options.from, options.to, { dryRun: true });
34764
+ printReport(preview);
34765
+ if (options.dryRun)
34766
+ return 0;
34767
+ } finally {
34768
+ db.close();
34769
+ }
34770
+ const { from, to } = options;
34771
+ if (!from || !to)
34772
+ return 0;
34773
+ const holders = (deps.inspectHolders ?? defaultInspectHolders)(storageDir);
34774
+ const probe = (deps.probe ?? probeHostProcessesUsing)({
34775
+ files: [dbPath, `${dbPath}-wal`, `${dbPath}-shm`],
34776
+ directories: []
34777
+ });
34778
+ if (!holders.safe || probe.status !== "free")
34779
+ throw new Error(`Refusing merge: close OpenCode, Pi, OpenCode 2 and ck-mc. ${holders.blockers.join(", ")} ${holders.uncertainty ?? ""} ${probe.status === "in_use" ? `context.db holders: ${probe.pids.join(", ")}` : probe.status === "unknown" ? probe.reason : ""}`);
34780
+ const backup = mkdtempSync(join21(storageDir, "identity-merge-backup-"));
34781
+ const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
34782
+ console.log(`Backup directory: ${backup}`);
34783
+ console.log(`Restore with hosts closed: rm -f ${quote(`${dbPath}-wal`)} ${quote(`${dbPath}-shm`)}; cp ${quote(join21(backup, "context.db"))} ${quote(dbPath)}; if test -f ${quote(join21(backup, "context.db-wal"))}; then cp ${quote(join21(backup, "context.db-wal"))} ${quote(`${dbPath}-wal`)}; fi`);
34784
+ const sidecarRoot = join21(storageDir, "project-identities");
34785
+ const sidecarBackup = join21(backup, "project-identities");
34786
+ mkdirSync8(sidecarBackup);
34787
+ if (existsSync25(sidecarRoot))
34788
+ cpSync2(sidecarRoot, sidecarBackup, { recursive: true });
34789
+ console.log(`Restore identity sidecars too: rm -rf ${quote(sidecarRoot)}; cp -R ${quote(sidecarBackup)} ${quote(sidecarRoot)}`);
34790
+ copyDatabaseBundle(dbPath, join21(backup, "context.db"));
34791
+ db = openExistingDatabase(dbPath, { readonly: false });
34792
+ if (!db)
34793
+ throw new Error(`Context database disappeared: ${dbPath}`);
34794
+ try {
34795
+ printReport(mergeProjectIdentities(db, from, to));
34796
+ } finally {
34797
+ db.close();
34798
+ }
34799
+ if (to.startsWith("git:"))
34800
+ for (const directory of directories) {
34801
+ const root = join21(storageDir, "project-identities");
34802
+ mkdirSync8(root, { recursive: true });
34803
+ const path = join21(root, `${createHash5("sha256").update(projectDirectoryKey(directory)).digest("hex")}.json`);
34804
+ const temporary = `${path}.merge.tmp`;
34805
+ writeFileSync7(temporary, JSON.stringify({ directory: projectDirectoryKey(directory), identity: options.to }), { mode: 384 });
34806
+ renameSync6(temporary, path);
34807
+ }
34808
+ return 0;
34809
+ }
34810
+ var init_doctor_merge_identity = __esm(async () => {
34811
+ init_project_identity_cache();
34812
+ init_storage_identity_merge();
34813
+ init_data_path();
34814
+ init_opencode_db_path();
34815
+ init_doctor_opencode2_cache();
34816
+ await __promiseAll([
34817
+ init_database_access(),
34818
+ init_doctor_identity_splits(),
34819
+ init_doctor_repair_db()
34820
+ ]);
34821
+ });
34822
+
34241
34823
  // src/commands/doctor-hidden-sessions.ts
34242
34824
  function parseMetadata(value) {
34243
34825
  if (value && typeof value === "object" && !Array.isArray(value)) {
@@ -34328,20 +34910,20 @@ var init_migration_paths = __esm(() => {
34328
34910
  });
34329
34911
 
34330
34912
  // src/commands/migrate.ts
34331
- import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
34332
- import { existsSync as existsSync25, mkdirSync as mkdirSync7, renameSync as renameSync5, unlinkSync as unlinkSync4 } from "node:fs";
34333
- import { dirname as dirname13, join as join21 } from "node:path";
34913
+ import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
34914
+ import { existsSync as existsSync26, mkdirSync as mkdirSync9, renameSync as renameSync7, unlinkSync as unlinkSync4 } from "node:fs";
34915
+ import { dirname as dirname14, join as join22 } from "node:path";
34334
34916
  function defaultOpenCodeDbPath() {
34335
34917
  return getOpenCodeDatabasePath();
34336
34918
  }
34337
34919
  function defaultCortexkitDbPath() {
34338
- return join21(getMagicContextStorageDir(), "context.db");
34920
+ return join22(getMagicContextStorageDir(), "context.db");
34339
34921
  }
34340
34922
  function defaultPiSessionsRoot() {
34341
34923
  return getPiSessionsRoot();
34342
34924
  }
34343
34925
  function defaultFs() {
34344
- return { writeFileAtomic, unlinkSync: unlinkSync4, existsSync: existsSync25, renameSync: renameSync5, mkdirSync: mkdirSync7 };
34926
+ return { writeFileAtomic, unlinkSync: unlinkSync4, existsSync: existsSync26, renameSync: renameSync7, mkdirSync: mkdirSync9 };
34345
34927
  }
34346
34928
  function stmt(db, sql) {
34347
34929
  return db.prepare(sql);
@@ -34368,7 +34950,7 @@ function shortId() {
34368
34950
  return randomBytes2(4).toString("hex");
34369
34951
  }
34370
34952
  function migrationKeyFor(sourceSessionId, targetHarness) {
34371
- return createHash4("sha256").update(`${sourceSessionId}
34953
+ return createHash6("sha256").update(`${sourceSessionId}
34372
34954
  ${targetHarness}`).digest("hex");
34373
34955
  }
34374
34956
  function moduleManagedProjectForSession(db, sessionId) {
@@ -34407,7 +34989,7 @@ function sweepPendingMigrations(db, fs = defaultFs()) {
34407
34989
  }
34408
34990
  if (row.phase === "db_committed") {
34409
34991
  if (fs.existsSync(row.stage_path)) {
34410
- fs.mkdirSync(dirname13(row.final_path), { recursive: true });
34992
+ fs.mkdirSync(dirname14(row.final_path), { recursive: true });
34411
34993
  fs.renameSync(row.stage_path, row.final_path);
34412
34994
  stmt(db, "DELETE FROM migration_pending WHERE migration_key = ?").run(row.migration_key);
34413
34995
  report.rolledForward += 1;
@@ -34437,7 +35019,7 @@ function claimJournalIdentity(args) {
34437
35019
  migration_key, source_session_id, target_harness, pi_session_id,
34438
35020
  final_path, stage_path, content_sha256, phase, created_at
34439
35021
  ) VALUES (?, ?, ?, ?, ?, ?, '', 'staged', ?)
34440
- ON CONFLICT(migration_key) DO NOTHING`).run(args.migrationKey, args.sourceSessionId, args.targetHarness, piSessionId, args.finalPathFor(piSessionId), join21(args.stageDir, `${args.migrationKey}.jsonl`), args.now);
35022
+ ON CONFLICT(migration_key) DO NOTHING`).run(args.migrationKey, args.sourceSessionId, args.targetHarness, piSessionId, args.finalPathFor(piSessionId), join22(args.stageDir, `${args.migrationKey}.jsonl`), args.now);
34441
35023
  const row = readPendingMigration(args.db, args.migrationKey);
34442
35024
  if (!row) {
34443
35025
  throw new Error("migration journal row disappeared during insert; aborting migration");
@@ -34942,7 +35524,7 @@ function migrateOpenCodeSessionToPi(opts) {
34942
35524
  const provider = opts.provider ?? model.provider;
34943
35525
  const modelId = opts.modelId ?? model.modelId;
34944
35526
  const cwd = session.directory ?? session.path ?? process.cwd();
34945
- const outputDir = join21(piSessionsRoot, projectPathToPiDirSlug(cwd));
35527
+ const outputDir = join22(piSessionsRoot, projectPathToPiDirSlug(cwd));
34946
35528
  const targetHarness = opts.targetHarness ?? "pi";
34947
35529
  const moduleManagedProject = cortexkitDb === null ? null : moduleManagedProjectForSession(cortexkitDb, session.id);
34948
35530
  if (moduleManagedProject) {
@@ -34966,8 +35548,8 @@ function migrateOpenCodeSessionToPi(opts) {
34966
35548
  migrationKey,
34967
35549
  sourceSessionId: session.id,
34968
35550
  targetHarness,
34969
- finalPathFor: (id) => join21(outputDir, `${formatPiFilenameTimestamp(now)}_${id}.jsonl`),
34970
- stageDir: join21(dirname13(piSessionsRoot), MIGRATION_STAGE_DIRNAME),
35551
+ finalPathFor: (id) => join22(outputDir, `${formatPiFilenameTimestamp(now)}_${id}.jsonl`),
35552
+ stageDir: join22(dirname14(piSessionsRoot), MIGRATION_STAGE_DIRNAME),
34971
35553
  now: now.getTime()
34972
35554
  });
34973
35555
  piSessionId = claimed.row.pi_session_id;
@@ -34976,7 +35558,7 @@ function migrateOpenCodeSessionToPi(opts) {
34976
35558
  journalResumed = claimed.resumed;
34977
35559
  } else {
34978
35560
  piSessionId = generateUuidV7(now);
34979
- finalPath = join21(outputDir, `${formatPiFilenameTimestamp(now)}_${piSessionId}.jsonl`);
35561
+ finalPath = join22(outputDir, `${formatPiFilenameTimestamp(now)}_${piSessionId}.jsonl`);
34980
35562
  stagePath = "";
34981
35563
  }
34982
35564
  const buildResult = buildPiEntries({
@@ -35015,11 +35597,11 @@ function migrateOpenCodeSessionToPi(opts) {
35015
35597
  `;
35016
35598
  if (!opts.dryRun) {
35017
35599
  if (journalActive && cortexkitDb !== null && migrationKey !== undefined && plan) {
35018
- const contentSha256 = createHash4("sha256").update(jsonl, "utf8").digest("hex");
35600
+ const contentSha256 = createHash6("sha256").update(jsonl, "utf8").digest("hex");
35019
35601
  commitStagedChecksum(cortexkitDb, migrationKey, contentSha256);
35020
35602
  fs.writeFileAtomic(stagePath, jsonl);
35021
35603
  plan.commit(migrationKey);
35022
- fs.mkdirSync(dirname13(finalPath), { recursive: true });
35604
+ fs.mkdirSync(dirname14(finalPath), { recursive: true });
35023
35605
  fs.renameSync(stagePath, finalPath);
35024
35606
  stmt(cortexkitDb, "DELETE FROM migration_pending WHERE migration_key = ?").run(migrationKey);
35025
35607
  } else {
@@ -35289,9 +35871,9 @@ var init_relocate_memory = __esm(() => {
35289
35871
  });
35290
35872
 
35291
35873
  // src/commands/migrate-session.ts
35292
- import { existsSync as existsSync26, realpathSync as realpathSync4 } from "node:fs";
35293
- import { homedir as homedir14 } from "node:os";
35294
- import path4, { join as join22 } from "node:path";
35874
+ import { existsSync as existsSync27, realpathSync as realpathSync5 } from "node:fs";
35875
+ import { homedir as homedir15 } from "node:os";
35876
+ import path5, { join as join23 } from "node:path";
35295
35877
  function existingSessionColumns(db) {
35296
35878
  const rows = db.prepare("PRAGMA table_info(session)").all();
35297
35879
  return new Set(rows.map((r) => r.name).filter((n) => typeof n === "string"));
@@ -35375,7 +35957,7 @@ function planMigrateSession(sessionId, rawTargetDirectory, deps) {
35375
35957
  ocProjectId = "global";
35376
35958
  ocWorktree = globalRow.worktree || "/";
35377
35959
  }
35378
- const sessionPath = path4.relative(ocWorktree, targetDirectory);
35960
+ const sessionPath = path5.relative(ocWorktree, targetDirectory);
35379
35961
  const ownershipRow = deps.contextDb.prepare("SELECT project_path FROM session_projects WHERE session_id = ? AND harness = 'opencode'").get(sessionId);
35380
35962
  const fromMcIdentity = ownershipRow?.project_path ?? (sessionRow.directory ? deps.resolveIdentity(sessionRow.directory) : "");
35381
35963
  const toMcIdentity = deps.resolveIdentity(targetDirectory);
@@ -35501,18 +36083,18 @@ function defaultOpenCodeDbPath2() {
35501
36083
  return getOpenCodeDatabasePath();
35502
36084
  }
35503
36085
  function defaultContextDbPath() {
35504
- return join22(getMagicContextStorageDir(), "context.db");
36086
+ return join23(getMagicContextStorageDir(), "context.db");
35505
36087
  }
35506
36088
  function realDeps(opencodeDb, contextDb) {
35507
36089
  return {
35508
36090
  opencodeDb,
35509
36091
  contextDb,
35510
36092
  resolveIdentity: resolveProjectIdentity,
35511
- hasGitDir: (dir) => existsSync26(join22(dir, ".git")),
35512
- realpath: (p) => realpathSync4(p)
36093
+ hasGitDir: (dir) => existsSync27(join23(dir, ".git")),
36094
+ realpath: (p) => realpathSync5(p)
35513
36095
  };
35514
36096
  }
35515
- function valueAfter2(args, flag) {
36097
+ function valueAfter(args, flag) {
35516
36098
  const index = args.indexOf(flag);
35517
36099
  if (index === -1)
35518
36100
  return null;
@@ -35566,11 +36148,11 @@ async function runMigrateSessionCli(args) {
35566
36148
  printMigrateSessionHelp();
35567
36149
  return 0;
35568
36150
  }
35569
- const sessionId = valueAfter2(args, "--session");
35570
- const toDir = valueAfter2(args, "--to");
36151
+ const sessionId = valueAfter(args, "--session");
36152
+ const toDir = valueAfter(args, "--to");
35571
36153
  const dryRun = args.includes("--dry-run");
35572
36154
  const skipConfirm = args.includes("--yes");
35573
- const memoriesFlag = valueAfter2(args, "--memories");
36155
+ const memoriesFlag = valueAfter(args, "--memories");
35574
36156
  if (!sessionId) {
35575
36157
  console.error("Missing required flag: --session <id>");
35576
36158
  printMigrateSessionHelp();
@@ -35586,8 +36168,8 @@ async function runMigrateSessionCli(args) {
35586
36168
  printMigrateSessionHelp();
35587
36169
  return 1;
35588
36170
  }
35589
- const expandedTo = toDir.startsWith("~") ? join22(homedir14(), toDir.slice(1).replace(/^[/\\]/, "")) : toDir;
35590
- if (!existsSync26(expandedTo)) {
36171
+ const expandedTo = toDir.startsWith("~") ? join23(homedir15(), toDir.slice(1).replace(/^[/\\]/, "")) : toDir;
36172
+ if (!existsSync27(expandedTo)) {
35591
36173
  console.error(`Target directory does not exist: ${expandedTo}`);
35592
36174
  return 1;
35593
36175
  }
@@ -35739,9 +36321,9 @@ var init_migrate_session = __esm(async () => {
35739
36321
  });
35740
36322
 
35741
36323
  // ../plugin/src/features/magic-context/v22-deferred-backfill.ts
35742
- import { createHash as createHash5 } from "node:crypto";
35743
- import { realpathSync as realpathSync5 } from "node:fs";
35744
- import path5 from "node:path";
36324
+ import { createHash as createHash7 } from "node:crypto";
36325
+ import { realpathSync as realpathSync6 } from "node:fs";
36326
+ import path6 from "node:path";
35745
36327
  function readMeta(db, key) {
35746
36328
  const row = db.prepare("SELECT value FROM schema_migrations_meta WHERE key = ?").get(key);
35747
36329
  return row?.value ?? null;
@@ -35762,7 +36344,7 @@ function countFailures(db) {
35762
36344
  function classifyBackfillError(error) {
35763
36345
  if (error instanceof ProjectIdentityError) {
35764
36346
  return {
35765
- errorClass: error.errorClass === "dubious_ownership" ? "unknown" : error.errorClass,
36347
+ errorClass: error.errorClass === "dubious_ownership" || error.errorClass === "home_project_disabled" || error.errorClass === "git_identity_unavailable" ? "unknown" : error.errorClass,
35766
36348
  errorMessage: error.message
35767
36349
  };
35768
36350
  }
@@ -35784,11 +36366,11 @@ function resolveBackfillIdentity(rawProjectPath) {
35784
36366
  function computeLegacyRustDirIdentity(rawProjectPath) {
35785
36367
  let canonical;
35786
36368
  try {
35787
- canonical = realpathSync5(rawProjectPath);
36369
+ canonical = realpathSync6(rawProjectPath);
35788
36370
  } catch {
35789
- canonical = path5.isAbsolute(rawProjectPath) ? rawProjectPath : path5.join(process.cwd(), rawProjectPath);
36371
+ canonical = path6.isAbsolute(rawProjectPath) ? rawProjectPath : path6.join(process.cwd(), rawProjectPath);
35790
36372
  }
35791
- return `dir:${createHash5("sha256").update(canonical, "utf8").digest("hex")}`;
36373
+ return `dir:${createHash7("sha256").update(canonical, "utf8").digest("hex")}`;
35792
36374
  }
35793
36375
  function upsertRekeyMap(db, oldProjectPath, newProjectPath, rekeyedAt) {
35794
36376
  db.prepare(`INSERT INTO v22_identity_rekey_map (old_project_path, new_project_path, rekeyed_at)
@@ -36009,7 +36591,7 @@ var init_tick_failure = __esm(() => {
36009
36591
  });
36010
36592
 
36011
36593
  // ../pi-plugin/src/config/index.ts
36012
- import { existsSync as existsSync27 } from "node:fs";
36594
+ import { existsSync as existsSync28 } from "node:fs";
36013
36595
  function getProjectConfigPaths(cwd) {
36014
36596
  const basePath = cortexKitProjectConfigBasePath(cwd);
36015
36597
  return [`${basePath}.jsonc`, `${basePath}.json`];
@@ -36019,10 +36601,10 @@ function getUserConfigPaths() {
36019
36601
  return [`${basePath}.jsonc`, `${basePath}.json`];
36020
36602
  }
36021
36603
  function resolveFirstExisting(paths) {
36022
- return paths.find((path) => existsSync27(path));
36604
+ return paths.find((path) => existsSync28(path));
36023
36605
  }
36024
36606
  function resolvePiLegacyFallback(sources) {
36025
- return sources.find((source) => existsSync27(source.path)) ?? null;
36607
+ return sources.find((source) => existsSync28(source.path)) ?? null;
36026
36608
  }
36027
36609
  function loadConfigFile(path, scope) {
36028
36610
  let raw;
@@ -36317,7 +36899,7 @@ function loadPiConfigDetailed(opts = {}, applyRuntimeGlobals = true) {
36317
36899
  if (loaded)
36318
36900
  loadedFiles.push(loaded);
36319
36901
  }
36320
- const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) => existsSync27(source.path));
36902
+ const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) => existsSync28(source.path));
36321
36903
  const userPath = resolveFirstExisting(getUserConfigPaths());
36322
36904
  const userLegacyFallback = userPath ? null : resolvePiLegacyFallback(harnessLegacy.user);
36323
36905
  const userReadPath = userPath ?? userLegacyFallback?.path;
@@ -36326,7 +36908,7 @@ function loadPiConfigDetailed(opts = {}, applyRuntimeGlobals = true) {
36326
36908
  if (loaded)
36327
36909
  loadedFiles.push(loaded);
36328
36910
  }
36329
- const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) => existsSync27(source.path));
36911
+ const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) => existsSync28(source.path));
36330
36912
  if (userLegacyFallback) {
36331
36913
  warnings.push(`[user config] reading legacy config from ${userLegacyFallback.path} until migration completes; run \`npx @cortexkit/magic-context doctor\` to consolidate into the shared CortexKit location.`);
36332
36914
  } else if (legacyUserUnmigrated) {
@@ -36448,10 +37030,11 @@ var DATABASE_REPAIR_COMMAND = "bunx @cortexkit/magic-context@latest doctor repai
36448
37030
 
36449
37031
  // src/lib/github-issue.ts
36450
37032
  import { spawnSync as spawnSync5 } from "node:child_process";
36451
- import { readFileSync as readFileSync16 } from "node:fs";
37033
+ import { readFileSync as readFileSync18 } from "node:fs";
36452
37034
  function runGhCommand(args) {
36453
37035
  try {
36454
37036
  const result = spawnSync5("gh", args, {
37037
+ windowsHide: true,
36455
37038
  encoding: "utf-8",
36456
37039
  stdio: ["ignore", "pipe", "pipe"]
36457
37040
  });
@@ -36474,7 +37057,7 @@ function failed(result) {
36474
37057
  function submitGithubIssue(title, bodyPath, runner = runGhCommand) {
36475
37058
  let body;
36476
37059
  try {
36477
- body = readFileSync16(bodyPath, "utf-8");
37060
+ body = readFileSync18(bodyPath, "utf-8");
36478
37061
  } catch (error) {
36479
37062
  return {
36480
37063
  ok: false,
@@ -36532,9 +37115,9 @@ var MAX_GITHUB_ISSUE_BODY_CHARS = 65536;
36532
37115
  var init_github_issue = () => {};
36533
37116
 
36534
37117
  // src/lib/log-lines.ts
36535
- import { existsSync as existsSync28, readFileSync as readFileSync17, statSync as statSync12 } from "node:fs";
37118
+ import { existsSync as existsSync29, readFileSync as readFileSync19, statSync as statSync12 } from "node:fs";
36536
37119
  import { tmpdir as tmpdir2 } from "node:os";
36537
- import { join as join23 } from "node:path";
37120
+ import { join as join24 } from "node:path";
36538
37121
  function tokenize(input, limit = Number.POSITIVE_INFINITY) {
36539
37122
  const tokens = [];
36540
37123
  let index = 0;
@@ -36820,20 +37403,20 @@ function parseLogLine(line) {
36820
37403
  }
36821
37404
  function getMagicContextLogPaths(harness, options = {}) {
36822
37405
  const override = options.override === undefined ? process.env.MAGIC_CONTEXT_LOG_PATH?.trim() : options.override?.trim();
36823
- const storageLogs = join23(options.storageDir ?? getMagicContextStorageDir(), "logs");
37406
+ const storageLogs = join24(options.storageDir ?? getMagicContextStorageDir(), "logs");
36824
37407
  return [
36825
37408
  ...override ? [override] : [],
36826
- join23(options.tempDir ?? tmpdir2(), harness, "magic-context", "magic-context.log"),
36827
- join23(storageLogs, `magic-context.${harness}.log`),
36828
- join23(storageLogs, "magic-context.log")
37409
+ join24(options.tempDir ?? tmpdir2(), harness, "magic-context", "magic-context.log"),
37410
+ join24(storageLogs, `magic-context.${harness}.log`),
37411
+ join24(storageLogs, "magic-context.log")
36829
37412
  ].filter((path, index, paths) => paths.indexOf(path) === index);
36830
37413
  }
36831
37414
  function inspectLogFile(path) {
36832
- if (!existsSync28(path)) {
37415
+ if (!existsSync29(path)) {
36833
37416
  return { path, exists: false, sizeKb: 0, lineCount: 0, grammar: "unknown" };
36834
37417
  }
36835
37418
  try {
36836
- const content = readFileSync17(path, "utf8");
37419
+ const content = readFileSync19(path, "utf8");
36837
37420
  const lines = content.split(/\r?\n/).filter((line) => line.length > 0);
36838
37421
  const grammars = new Set(lines.flatMap((line) => {
36839
37422
  const parsed = parseLogLine(line);
@@ -36860,7 +37443,7 @@ function readLogLines(files) {
36860
37443
  return [];
36861
37444
  try {
36862
37445
  let precedingTimestamp = "";
36863
- return readFileSync17(file.path, "utf8").split(/\r?\n/).filter((line) => line.length > 0).map((line, lineIndex) => {
37446
+ return readFileSync19(file.path, "utf8").split(/\r?\n/).filter((line) => line.length > 0).map((line, lineIndex) => {
36864
37447
  precedingTimestamp = parseLogLine(line)?.ts ?? precedingTimestamp;
36865
37448
  return { line, timestamp: precedingTimestamp, fileIndex, lineIndex };
36866
37449
  });
@@ -36909,10 +37492,10 @@ var init_redaction2 = __esm(() => {
36909
37492
  });
36910
37493
 
36911
37494
  // src/commands/doctor-omp.ts
36912
- import { execFileSync as execFileSync5, spawnSync as spawnSync6 } from "node:child_process";
36913
- import { existsSync as existsSync29, mkdirSync as mkdirSync8, readFileSync as readFileSync18 } from "node:fs";
37495
+ import { execFileSync as execFileSync6, spawnSync as spawnSync6 } from "node:child_process";
37496
+ import { existsSync as existsSync30, mkdirSync as mkdirSync10, readFileSync as readFileSync20 } from "node:fs";
36914
37497
  import { createRequire as createRequire3 } from "node:module";
36915
- import { dirname as dirname14, join as join24, resolve as resolve8 } from "node:path";
37498
+ import { dirname as dirname15, join as join25, resolve as resolve8 } from "node:path";
36916
37499
  function parseSemver(value) {
36917
37500
  const match = value?.match(/(\d+)\.(\d+)\.(\d+)/);
36918
37501
  return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
@@ -36954,10 +37537,10 @@ function selfVersion() {
36954
37537
  return "unknown";
36955
37538
  }
36956
37539
  function readConfig2(path) {
36957
- if (!existsSync29(path))
37540
+ if (!existsSync30(path))
36958
37541
  return {};
36959
37542
  try {
36960
- const parsed = import_comment_json7.parse(readFileSync18(path, "utf-8"));
37543
+ const parsed = import_comment_json7.parse(readFileSync20(path, "utf-8"));
36961
37544
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? {} : { error: "top level is not an object" };
36962
37545
  } catch (error) {
36963
37546
  return { error: error instanceof Error ? error.message : String(error) };
@@ -36967,7 +37550,7 @@ function pluginDeclaresOmp(path) {
36967
37550
  if (!path)
36968
37551
  return null;
36969
37552
  try {
36970
- const pkg = JSON.parse(readFileSync18(join24(path, "package.json"), "utf-8"));
37553
+ const pkg = JSON.parse(readFileSync20(join25(path, "package.json"), "utf-8"));
36971
37554
  return Array.isArray(pkg.omp?.extensions) || Array.isArray(pkg.pi?.extensions);
36972
37555
  } catch {
36973
37556
  return null;
@@ -37048,7 +37631,7 @@ async function runHealthChecks(options) {
37048
37631
  }
37049
37632
  }
37050
37633
  const userConfigPath = getOmpUserConfigPath();
37051
- if (!existsSync29(userConfigPath)) {
37634
+ if (!existsSync30(userConfigPath)) {
37052
37635
  add(results, "warn", `No Magic Context user config at ${userConfigPath}`);
37053
37636
  repairPlan.writeUserConfig = true;
37054
37637
  } else {
@@ -37065,9 +37648,9 @@ async function runHealthChecks(options) {
37065
37648
  for (const warning of loaded.warnings.slice(0, 5))
37066
37649
  add(results, "warn", warning);
37067
37650
  const storage = getMagicContextStorageResolution();
37068
- const dbPath = join24(storage.path, "context.db");
37651
+ const dbPath = join25(storage.path, "context.db");
37069
37652
  add(results, "info", `Shared storage: ${storage.path} (source: ${storage.source})`);
37070
- if (!existsSync29(dbPath))
37653
+ if (!existsSync30(dbPath))
37071
37654
  add(results, "info", `Shared context DB will be created at ${dbPath}`);
37072
37655
  else {
37073
37656
  let db = null;
@@ -37121,7 +37704,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
37121
37704
  };
37122
37705
  }
37123
37706
  function writeDefaultConfig(path) {
37124
- mkdirSync8(dirname14(path), { recursive: true });
37707
+ mkdirSync10(dirname15(path), { recursive: true });
37125
37708
  const config = {
37126
37709
  $schema: "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json",
37127
37710
  ...MagicContextConfigSchema.parse({})
@@ -37131,7 +37714,7 @@ function writeDefaultConfig(path) {
37131
37714
  }
37132
37715
  async function repair(plan, deps, prompts, cwd) {
37133
37716
  let fixed = 0;
37134
- if (plan.writeUserConfig && !existsSync29(getOmpUserConfigPath())) {
37717
+ if (plan.writeUserConfig && !existsSync30(getOmpUserConfigPath())) {
37135
37718
  writeDefaultConfig(getOmpUserConfigPath());
37136
37719
  prompts.log.success(`Wrote default Magic Context config to ${getOmpUserConfigPath()}`);
37137
37720
  fixed += 1;
@@ -37173,6 +37756,7 @@ function timestamp2(date) {
37173
37756
  function runGhCommandWithDeps(deps, args) {
37174
37757
  if (args[0] === "issue") {
37175
37758
  const result = deps.spawnSync("gh", args, {
37759
+ windowsHide: true,
37176
37760
  encoding: "utf-8",
37177
37761
  stdio: ["ignore", "pipe", "pipe"]
37178
37762
  });
@@ -37184,6 +37768,7 @@ function runGhCommandWithDeps(deps, args) {
37184
37768
  }
37185
37769
  try {
37186
37770
  const output = deps.execFileSync("gh", args, {
37771
+ windowsHide: true,
37187
37772
  encoding: "utf-8",
37188
37773
  stdio: ["ignore", "pipe", "pipe"]
37189
37774
  });
@@ -37216,7 +37801,7 @@ async function runIssueFlow(options) {
37216
37801
  ...report.results.map((result) => `- ${result.status.toUpperCase()}: ${sanitizeDiagnosticText(result.message)}`)
37217
37802
  ].join(`
37218
37803
  `);
37219
- const path = join24(options.cwd, `magic-context-omp-issue-${timestamp2(options.deps.now())}.md`);
37804
+ const path = join25(options.cwd, `magic-context-omp-issue-${timestamp2(options.deps.now())}.md`);
37220
37805
  writeFileAtomic(path, `${body}
37221
37806
  `);
37222
37807
  options.prompts.log.success(`Sanitized report written to ${path}`);
@@ -37287,7 +37872,7 @@ var init_doctor_omp = __esm(async () => {
37287
37872
  openExistingContextDatabase,
37288
37873
  now: () => new Date,
37289
37874
  inspectMagicContextLogs,
37290
- execFileSync: execFileSync5,
37875
+ execFileSync: execFileSync6,
37291
37876
  spawnSync: spawnSync6
37292
37877
  };
37293
37878
  });
@@ -37404,7 +37989,7 @@ function truncate(text) {
37404
37989
  var DEFAULT_TIMEOUT_MS = 1e4, MAX_PREVIEW_CHARS = 240;
37405
37990
 
37406
37991
  // ../plugin/src/features/magic-context/memory/embedding-synapse.ts
37407
- import { createHash as createHash6 } from "node:crypto";
37992
+ import { createHash as createHash8 } from "node:crypto";
37408
37993
  function toSynapseLaneDescriptor(metadata) {
37409
37994
  const tokenBudget = metadata.recommended_token_budget;
37410
37995
  return {
@@ -37544,7 +38129,7 @@ function wait(ms) {
37544
38129
  return new Promise((resolve) => setTimeout(resolve, ms));
37545
38130
  }
37546
38131
  function sha256(value) {
37547
- return createHash6("sha256").update(value).digest("hex");
38132
+ return createHash8("sha256").update(value).digest("hex");
37548
38133
  }
37549
38134
  function stableJson2(value) {
37550
38135
  if (Array.isArray(value))
@@ -38188,12 +38773,12 @@ function listShadowBackfillStalls(db, projectIdentity) {
38188
38773
  var SHADOW_BACKFILL_PROVENANCE_KEY = "_magicContextShadowBackfill";
38189
38774
 
38190
38775
  // ../plugin/src/shared/auto-update-provenance.ts
38191
- import { existsSync as existsSync30, readFileSync as readFileSync19 } from "node:fs";
38776
+ import { existsSync as existsSync31, readFileSync as readFileSync21 } from "node:fs";
38192
38777
  function readAutoUpdateCheckState(path) {
38193
- if (!existsSync30(path))
38778
+ if (!existsSync31(path))
38194
38779
  return null;
38195
38780
  try {
38196
- const value = JSON.parse(readFileSync19(path, "utf-8"));
38781
+ const value = JSON.parse(readFileSync21(path, "utf-8"));
38197
38782
  if (!value || typeof value !== "object" || Array.isArray(value))
38198
38783
  return null;
38199
38784
  const raw = value;
@@ -38221,14 +38806,14 @@ var init_auto_update_provenance = () => {};
38221
38806
  // ../plugin/src/shared/tui-config.ts
38222
38807
  import {
38223
38808
  chmodSync as chmodSync5,
38224
- existsSync as existsSync31,
38225
- mkdirSync as mkdirSync9,
38226
- readFileSync as readFileSync20,
38227
- renameSync as renameSync6,
38809
+ existsSync as existsSync32,
38810
+ mkdirSync as mkdirSync11,
38811
+ readFileSync as readFileSync22,
38812
+ renameSync as renameSync8,
38228
38813
  statSync as statSync13,
38229
- writeFileSync as writeFileSync6
38814
+ writeFileSync as writeFileSync8
38230
38815
  } from "node:fs";
38231
- import { dirname as dirname15, join as join25 } from "node:path";
38816
+ import { dirname as dirname16, join as join26 } from "node:path";
38232
38817
  function pluginEntryId(entry) {
38233
38818
  if (typeof entry === "string")
38234
38819
  return entry;
@@ -38259,21 +38844,21 @@ function writeTuiConfigAtomic(configPath, config) {
38259
38844
  const body = `${import_comment_json8.stringify(config, null, 2)}
38260
38845
  `;
38261
38846
  const tmpPath = `${configPath}.tmp`;
38262
- writeFileSync6(tmpPath, body);
38847
+ writeFileSync8(tmpPath, body);
38263
38848
  try {
38264
38849
  if (statSync13(configPath, { throwIfNoEntry: false })?.isFile()) {
38265
38850
  chmodSync5(tmpPath, statSync13(configPath).mode & 511);
38266
38851
  }
38267
38852
  } catch {}
38268
- renameSync6(tmpPath, configPath);
38853
+ renameSync8(tmpPath, configPath);
38269
38854
  }
38270
38855
  function resolveTuiConfigPath(configDirOverride) {
38271
38856
  const configDir = configDirOverride ?? getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
38272
- const jsoncPath = join25(configDir, "tui.jsonc");
38273
- const jsonPath = join25(configDir, "tui.json");
38274
- if (existsSync31(jsoncPath))
38857
+ const jsoncPath = join26(configDir, "tui.jsonc");
38858
+ const jsonPath = join26(configDir, "tui.json");
38859
+ if (existsSync32(jsoncPath))
38275
38860
  return jsoncPath;
38276
- if (existsSync31(jsonPath))
38861
+ if (existsSync32(jsonPath))
38277
38862
  return jsonPath;
38278
38863
  return jsoncPath;
38279
38864
  }
@@ -38281,8 +38866,8 @@ function ensureTuiPluginEntry(options = {}) {
38281
38866
  try {
38282
38867
  const configPath = resolveTuiConfigPath(options.configDir);
38283
38868
  let config = {};
38284
- if (existsSync31(configPath)) {
38285
- const raw = readFileSync20(configPath, "utf-8");
38869
+ if (existsSync32(configPath)) {
38870
+ const raw = readFileSync22(configPath, "utf-8");
38286
38871
  config = import_comment_json8.parse(raw) ?? {};
38287
38872
  }
38288
38873
  const plugins = Array.isArray(config.plugin) ? [...config.plugin] : [];
@@ -38316,7 +38901,7 @@ function ensureTuiPluginEntry(options = {}) {
38316
38901
  }
38317
38902
  }
38318
38903
  config.plugin = plugins;
38319
- mkdirSync9(dirname15(configPath), { recursive: true });
38904
+ mkdirSync11(dirname16(configPath), { recursive: true });
38320
38905
  writeTuiConfigAtomic(configPath, config);
38321
38906
  log3(`[magic-context] updated TUI plugin entry in ${configPath}`);
38322
38907
  return true;
@@ -38334,9 +38919,9 @@ var init_tui_config = __esm(() => {
38334
38919
  });
38335
38920
 
38336
38921
  // src/lib/diagnostics-opencode.ts
38337
- import { existsSync as existsSync32, readdirSync as readdirSync5, readFileSync as readFileSync21, statSync as statSync14 } from "node:fs";
38922
+ import { existsSync as existsSync33, readdirSync as readdirSync6, readFileSync as readFileSync23, statSync as statSync14 } from "node:fs";
38338
38923
  import { createRequire as createRequire4 } from "node:module";
38339
- import { join as join26 } from "node:path";
38924
+ import { join as join27 } from "node:path";
38340
38925
  function getSelfVersion() {
38341
38926
  const require2 = createRequire4(import.meta.url);
38342
38927
  for (const relPath of ["../../package.json", "../package.json"]) {
@@ -38351,7 +38936,7 @@ function getSelfVersion() {
38351
38936
  }
38352
38937
  function readLegacyCachedVersion(root) {
38353
38938
  try {
38354
- const pkg = JSON.parse(readFileSync21(getOpenCodePluginPackageJsonPath(root), "utf-8"));
38939
+ const pkg = JSON.parse(readFileSync23(getOpenCodePluginPackageJsonPath(root), "utf-8"));
38355
38940
  return typeof pkg.version === "string" ? pkg.version : undefined;
38356
38941
  } catch {
38357
38942
  return;
@@ -38360,7 +38945,7 @@ function readLegacyCachedVersion(root) {
38360
38945
  function collectOpenCodePluginCacheReport(hostGeneration, opencodeConfig) {
38361
38946
  const installs = [];
38362
38947
  for (const root of getOpenCodePluginCacheRoots()) {
38363
- if (!existsSync32(root))
38948
+ if (!existsSync33(root))
38364
38949
  continue;
38365
38950
  const spec = root.endsWith("@latest") ? "latest" : "(bare)";
38366
38951
  installs.push({
@@ -38394,7 +38979,7 @@ function getStorageResolution() {
38394
38979
  }
38395
38980
  function fileSize(path) {
38396
38981
  try {
38397
- return existsSync32(path) ? statSync14(path).size : 0;
38982
+ return existsSync33(path) ? statSync14(path).size : 0;
38398
38983
  } catch {
38399
38984
  return 0;
38400
38985
  }
@@ -38406,10 +38991,10 @@ function sanitizeValue(value) {
38406
38991
  return sanitizeConfigValue(value);
38407
38992
  }
38408
38993
  function readConfig3(path) {
38409
- if (!existsSync32(path))
38994
+ if (!existsSync33(path))
38410
38995
  return { value: null };
38411
38996
  try {
38412
- const raw = readFileSync21(path, "utf-8");
38997
+ const raw = readFileSync23(path, "utf-8");
38413
38998
  const value = import_comment_json9.parse(raw);
38414
38999
  return { value };
38415
39000
  } catch (error) {
@@ -38434,7 +39019,7 @@ function configHasPluginEntry(config) {
38434
39019
  }
38435
39020
  function parseHistorianDumpMeta(path) {
38436
39021
  try {
38437
- const xml = readFileSync21(path, "utf-8");
39022
+ const xml = readFileSync23(path, "utf-8");
38438
39023
  const parsed = parseCompartmentOutput(xml);
38439
39024
  const factCountByCategory = {};
38440
39025
  for (const fact of parsed.facts) {
@@ -38467,11 +39052,11 @@ function parseHistorianDumpMeta(path) {
38467
39052
  }
38468
39053
  }
38469
39054
  function listDumpsInDir(dir, limit) {
38470
- if (!existsSync32(dir))
39055
+ if (!existsSync33(dir))
38471
39056
  return { count: 0, recent: [] };
38472
39057
  try {
38473
- const entries = readdirSync5(dir).filter((name) => name.endsWith(".xml")).map((name) => {
38474
- const stat = statSync14(join26(dir, name));
39058
+ const entries = readdirSync6(dir).filter((name) => name.endsWith(".xml")).map((name) => {
39059
+ const stat = statSync14(join27(dir, name));
38475
39060
  return {
38476
39061
  name,
38477
39062
  mtime: stat.mtimeMs,
@@ -38480,7 +39065,7 @@ function listDumpsInDir(dir, limit) {
38480
39065
  }).sort((a, b) => b.mtime - a.mtime);
38481
39066
  const now = Date.now();
38482
39067
  const recent = entries.slice(0, limit).map((entry) => {
38483
- const meta = parseHistorianDumpMeta(join26(dir, entry.name));
39068
+ const meta = parseHistorianDumpMeta(join27(dir, entry.name));
38484
39069
  const summary = {
38485
39070
  name: entry.name,
38486
39071
  ageMinutes: Math.round((now - entry.mtime) / 60000),
@@ -38618,8 +39203,8 @@ async function collectRecentSessions(resolution, hostGeneration) {
38618
39203
  }
38619
39204
  }
38620
39205
  async function collectHistorianFailures(storageDirPath) {
38621
- const contextDbPath = join26(storageDirPath, "context.db");
38622
- if (!existsSync32(contextDbPath))
39206
+ const contextDbPath = join27(storageDirPath, "context.db");
39207
+ if (!existsSync33(contextDbPath))
38623
39208
  return [];
38624
39209
  if (typeof globalThis.Bun === "undefined") {
38625
39210
  return [];
@@ -38652,8 +39237,8 @@ async function collectHistorianFailures(storageDirPath) {
38652
39237
  }
38653
39238
  }
38654
39239
  async function collectHistorianRuns(storageDirPath) {
38655
- const contextDbPath = join26(storageDirPath, "context.db");
38656
- if (!existsSync32(contextDbPath))
39240
+ const contextDbPath = join27(storageDirPath, "context.db");
39241
+ if (!existsSync33(contextDbPath))
38657
39242
  return [];
38658
39243
  if (typeof globalThis.Bun === "undefined")
38659
39244
  return [];
@@ -38726,7 +39311,7 @@ async function collectDiagnostics() {
38726
39311
  const magicContextConfig = readConfig3(configPaths.magicContextConfig);
38727
39312
  const storageResolution = getStorageResolution();
38728
39313
  const storageDirPath = storageResolution.path;
38729
- const contextDbPath = join26(storageDirPath, "context.db");
39314
+ const contextDbPath = join27(storageDirPath, "context.db");
38730
39315
  let compactionEnabled = false;
38731
39316
  try {
38732
39317
  compactionEnabled = isCompactionEnabled(loadPluginConfig(process.cwd()));
@@ -38758,7 +39343,7 @@ async function collectDiagnostics() {
38758
39343
  opencodeConfigHasPlugin: configHasPluginEntry(opencodeConfig.value),
38759
39344
  tuiConfigHasPlugin: configHasPluginEntry(tuiConfig.value),
38760
39345
  magicContextConfig: {
38761
- exists: existsSync32(configPaths.magicContextConfig),
39346
+ exists: existsSync33(configPaths.magicContextConfig),
38762
39347
  ...magicContextConfig.error ? { parseError: magicContextConfig.error } : {},
38763
39348
  flags: sanitizeValue(magicContextConfig.value ?? {}) ?? {}
38764
39349
  },
@@ -38766,7 +39351,7 @@ async function collectDiagnostics() {
38766
39351
  storageDir: {
38767
39352
  path: storageDirPath,
38768
39353
  source: storageResolution.source,
38769
- exists: existsSync32(storageDirPath),
39354
+ exists: existsSync33(storageDirPath),
38770
39355
  contextDbSizeBytes: fileSize(contextDbPath)
38771
39356
  },
38772
39357
  openCodeDatabase: {
@@ -38999,9 +39584,9 @@ var init_embedding_local = __esm(() => {
38999
39584
 
39000
39585
  // src/lib/embedding-runtime.ts
39001
39586
  import { spawnSync as spawnSync7 } from "node:child_process";
39002
- import { existsSync as existsSync33, readFileSync as readFileSync22 } from "node:fs";
39587
+ import { existsSync as existsSync34, readFileSync as readFileSync24 } from "node:fs";
39003
39588
  import { createRequire as createRequire5 } from "node:module";
39004
- import { dirname as dirname16, join as join27, sep } from "node:path";
39589
+ import { dirname as dirname17, join as join28, sep } from "node:path";
39005
39590
  function describeError(error) {
39006
39591
  const message = error instanceof Error ? error.message : String(error ?? "unknown error");
39007
39592
  const code = error?.code;
@@ -39009,6 +39594,7 @@ function describeError(error) {
39009
39594
  }
39010
39595
  function runOnnxRuntimeNodeLoadProbeChild(packageDir) {
39011
39596
  return spawnSync7(process.execPath, ["-e", ONNX_RUNTIME_NODE_LOAD_PROBE_SCRIPT], {
39597
+ windowsHide: true,
39012
39598
  encoding: "utf8",
39013
39599
  env: { ...process.env, [ONNX_LOAD_PROBE_PACKAGE_DIR_ENV]: packageDir },
39014
39600
  stdio: ["ignore", "pipe", "pipe"],
@@ -39142,11 +39728,11 @@ function expectedBinaryRelPath(platform, arch) {
39142
39728
  const supportedArch = arch === "x64" || arch === "arm64";
39143
39729
  if (!supportedPlatform || !supportedArch)
39144
39730
  return null;
39145
- return join27("bin", "napi-v6", platform, arch, "onnxruntime_binding.node");
39731
+ return join28("bin", "napi-v6", platform, arch, "onnxruntime_binding.node");
39146
39732
  }
39147
39733
  function onnxRuntimeNodeVersion(packageDir) {
39148
39734
  try {
39149
- const parsed = JSON.parse(readFileSync22(join27(packageDir, "package.json"), "utf8"));
39735
+ const parsed = JSON.parse(readFileSync24(join28(packageDir, "package.json"), "utf8"));
39150
39736
  return typeof parsed.version === "string" ? parsed.version : undefined;
39151
39737
  } catch {
39152
39738
  return;
@@ -39195,7 +39781,7 @@ function formatLocalEmbeddingRuntimeWasmSelected(status) {
39195
39781
  function requireNodeWasmBundle(probe, candidates) {
39196
39782
  if (probe.state === "failed")
39197
39783
  return probe;
39198
- const bundlePath = candidates.find((candidate) => existsSync33(candidate));
39784
+ const bundlePath = candidates.find((candidate) => existsSync34(candidate));
39199
39785
  return bundlePath ? probe : {
39200
39786
  state: "failed",
39201
39787
  reason: `functional Node WASM Transformers bundle is missing (checked ${candidates.join(", ")})`
@@ -39203,13 +39789,13 @@ function requireNodeWasmBundle(probe, candidates) {
39203
39789
  }
39204
39790
  function nodeWasmBundleCandidates(installRoot) {
39205
39791
  return [
39206
- join27(installRoot, "dist", "transformers-node-wasm.js"),
39207
- join27(installRoot, "node_modules", "@cortexkit", "opencode-magic-context", "dist", "transformers-node-wasm.js"),
39208
- join27(installRoot, "node_modules", "@cortexkit", "pi-magic-context", "dist", "transformers-node-wasm.js")
39792
+ join28(installRoot, "dist", "transformers-node-wasm.js"),
39793
+ join28(installRoot, "node_modules", "@cortexkit", "opencode-magic-context", "dist", "transformers-node-wasm.js"),
39794
+ join28(installRoot, "node_modules", "@cortexkit", "pi-magic-context", "dist", "transformers-node-wasm.js")
39209
39795
  ];
39210
39796
  }
39211
39797
  function probeWasmRuntimeAt(installRoot) {
39212
- const requireFn = createRequire5(join27(installRoot, "package.json"));
39798
+ const requireFn = createRequire5(join28(installRoot, "package.json"));
39213
39799
  return requireNodeWasmBundle(probeWasmRuntimeFromRequire(requireFn), nodeWasmBundleCandidates(installRoot));
39214
39800
  }
39215
39801
  function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform, arch = process.arch, runtimePreference = "auto", host) {
@@ -39218,9 +39804,9 @@ function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform,
39218
39804
  const wasm = probeWasmRuntimeAt(installRoot);
39219
39805
  return wasm.state === "ok" ? { state: "wasm-selected", wasmPath: wasm.wasmPath } : { state: "wasm-broken", wasmReason: wasm.reason };
39220
39806
  }
39221
- const packageDir = join27(installRoot, "node_modules", "onnxruntime-node");
39807
+ const packageDir = join28(installRoot, "node_modules", "onnxruntime-node");
39222
39808
  let native;
39223
- if (!existsSync33(join27(packageDir, "package.json"))) {
39809
+ if (!existsSync34(join28(packageDir, "package.json"))) {
39224
39810
  native = { state: "package-missing", packageDir };
39225
39811
  } else {
39226
39812
  const rel = expectedBinaryRelPath(platform, arch);
@@ -39230,8 +39816,8 @@ function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform,
39230
39816
  binaryPath: packageDir
39231
39817
  };
39232
39818
  } else {
39233
- const binaryPath = join27(packageDir, rel);
39234
- native = existsSync33(binaryPath) ? probeOnnxRuntimeNodeLoad(packageDir) ?? { state: "ok", binaryPath } : {
39819
+ const binaryPath = join28(packageDir, rel);
39820
+ native = existsSync34(binaryPath) ? probeOnnxRuntimeNodeLoad(packageDir) ?? { state: "ok", binaryPath } : {
39235
39821
  state: "binary-missing",
39236
39822
  packageDir,
39237
39823
  expectedBinary: binaryPath,
@@ -39242,7 +39828,7 @@ function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform,
39242
39828
  return withWasmFallback(native, () => probeWasmRuntimeAt(installRoot));
39243
39829
  }
39244
39830
  function checkLocalEmbeddingRuntime(installRoots, platform = process.platform, arch = process.arch, runtimePreference = "auto", host) {
39245
- const existing = installRoots.filter((root) => existsSync33(root));
39831
+ const existing = installRoots.filter((root) => existsSync34(root));
39246
39832
  if (existing.length === 0) {
39247
39833
  return {
39248
39834
  state: "unknown",
@@ -39268,19 +39854,19 @@ function checkLocalEmbeddingRuntime(installRoots, platform = process.platform, a
39268
39854
  function packageDirFromResolved(resolvedPath, packageName) {
39269
39855
  const marker = `node_modules${sep}${packageName.split("/").join(sep)}`;
39270
39856
  const idx = resolvedPath.indexOf(marker);
39271
- return idx >= 0 ? resolvedPath.slice(0, idx + marker.length) : dirname16(resolvedPath);
39857
+ return idx >= 0 ? resolvedPath.slice(0, idx + marker.length) : dirname17(resolvedPath);
39272
39858
  }
39273
39859
  function probeWasmRuntimeByResolution(pluginDir) {
39274
39860
  try {
39275
- const reqPlugin = createRequire5(join27(pluginDir, "package.json"));
39861
+ const reqPlugin = createRequire5(join28(pluginDir, "package.json"));
39276
39862
  const direct = probeWasmRuntimeFromRequire(reqPlugin);
39277
39863
  let probe = direct;
39278
39864
  if (direct.state === "failed" && direct.reason.startsWith("onnxruntime-web is not resolvable")) {
39279
39865
  const tfResolved = reqPlugin.resolve("@huggingface/transformers");
39280
39866
  const tfDir = packageDirFromResolved(tfResolved, "@huggingface/transformers");
39281
- probe = probeWasmRuntimeFromRequire(createRequire5(join27(tfDir, "package.json")));
39867
+ probe = probeWasmRuntimeFromRequire(createRequire5(join28(tfDir, "package.json")));
39282
39868
  }
39283
- return requireNodeWasmBundle(probe, [join27(pluginDir, "dist", "transformers-node-wasm.js")]);
39869
+ return requireNodeWasmBundle(probe, [join28(pluginDir, "dist", "transformers-node-wasm.js")]);
39284
39870
  } catch (error) {
39285
39871
  return {
39286
39872
  state: "failed",
@@ -39289,7 +39875,7 @@ function probeWasmRuntimeByResolution(pluginDir) {
39289
39875
  }
39290
39876
  }
39291
39877
  function checkLocalEmbeddingRuntimeByResolution(pluginDir, platform = process.platform, arch = process.arch, runtimePreference = "auto", host) {
39292
- if (!existsSync33(join27(pluginDir, "package.json"))) {
39878
+ if (!existsSync34(join28(pluginDir, "package.json"))) {
39293
39879
  return { state: "unknown", reason: "plugin package dir not found" };
39294
39880
  }
39295
39881
  const selectedRuntime = resolveLocalEmbeddingRuntime(runtimePreference, host);
@@ -39300,13 +39886,13 @@ function checkLocalEmbeddingRuntimeByResolution(pluginDir, platform = process.pl
39300
39886
  let onnxDir = null;
39301
39887
  let resolveError;
39302
39888
  try {
39303
- const reqPlugin = createRequire5(join27(pluginDir, "package.json"));
39889
+ const reqPlugin = createRequire5(join28(pluginDir, "package.json"));
39304
39890
  try {
39305
39891
  onnxDir = packageDirFromResolved(reqPlugin.resolve("onnxruntime-node"), "onnxruntime-node");
39306
39892
  } catch {
39307
39893
  const tfResolved = reqPlugin.resolve("@huggingface/transformers");
39308
39894
  const tfDir = packageDirFromResolved(tfResolved, "@huggingface/transformers");
39309
- const reqTf = createRequire5(join27(tfDir, "package.json"));
39895
+ const reqTf = createRequire5(join28(tfDir, "package.json"));
39310
39896
  onnxDir = packageDirFromResolved(reqTf.resolve("onnxruntime-node"), "onnxruntime-node");
39311
39897
  }
39312
39898
  } catch (error) {
@@ -39314,7 +39900,7 @@ function checkLocalEmbeddingRuntimeByResolution(pluginDir, platform = process.pl
39314
39900
  if (resolveError === "ERR_MODULE_NOT_FOUND" || resolveError === "MODULE_NOT_FOUND") {
39315
39901
  return withWasmFallback({
39316
39902
  state: "package-missing",
39317
- packageDir: join27(pluginDir, "node_modules", "onnxruntime-node")
39903
+ packageDir: join28(pluginDir, "node_modules", "onnxruntime-node")
39318
39904
  }, () => probeWasmRuntimeByResolution(pluginDir));
39319
39905
  }
39320
39906
  return {
@@ -39330,8 +39916,8 @@ function checkLocalEmbeddingRuntimeByResolution(pluginDir, platform = process.pl
39330
39916
  if (rel === null) {
39331
39917
  native = probeOnnxRuntimeNodeLoad(onnxDir) ?? { state: "ok", binaryPath: onnxDir };
39332
39918
  } else {
39333
- const binaryPath = join27(onnxDir, rel);
39334
- native = existsSync33(binaryPath) ? probeOnnxRuntimeNodeLoad(onnxDir) ?? { state: "ok", binaryPath } : {
39919
+ const binaryPath = join28(onnxDir, rel);
39920
+ native = existsSync34(binaryPath) ? probeOnnxRuntimeNodeLoad(onnxDir) ?? { state: "ok", binaryPath } : {
39335
39921
  state: "binary-missing",
39336
39922
  packageDir: onnxDir,
39337
39923
  expectedBinary: binaryPath,
@@ -39473,8 +40059,8 @@ var init_issue_body = __esm(() => {
39473
40059
  });
39474
40060
 
39475
40061
  // src/lib/logs-opencode.ts
39476
- import { writeFileSync as writeFileSync7 } from "node:fs";
39477
- import { join as join28 } from "node:path";
40062
+ import { writeFileSync as writeFileSync9 } from "node:fs";
40063
+ import { join as join29 } from "node:path";
39478
40064
  function sanitizeLogContent(content) {
39479
40065
  return sanitizeDiagnosticText(content);
39480
40066
  }
@@ -39581,12 +40167,12 @@ async function bundleIssueReport(report, description, title, sessionFilter = nul
39581
40167
  const bodyMarkdown = capBodyToGithubLimit(rawBodyMarkdown);
39582
40168
  const truncated = bodyMarkdown !== rawBodyMarkdown;
39583
40169
  const timestamp = formatTimestamp(new Date);
39584
- const path = join28(process.cwd(), `magic-context-issue-${timestamp}.md`);
39585
- writeFileSync7(path, `${bodyMarkdown}
40170
+ const path = join29(process.cwd(), `magic-context-issue-${timestamp}.md`);
40171
+ writeFileSync9(path, `${bodyMarkdown}
39586
40172
  `);
39587
- const fullPath = truncated ? join28(process.cwd(), `magic-context-issue-${timestamp}-full.md`) : undefined;
40173
+ const fullPath = truncated ? join29(process.cwd(), `magic-context-issue-${timestamp}-full.md`) : undefined;
39588
40174
  if (fullPath)
39589
- writeFileSync7(fullPath, `${rawBodyMarkdown}
40175
+ writeFileSync9(fullPath, `${rawBodyMarkdown}
39590
40176
  `);
39591
40177
  return { path, bodyMarkdown, ...fullPath ? { fullPath } : {}, truncated };
39592
40178
  }
@@ -39850,25 +40436,25 @@ function migrateExperimentalPinKeyFilesForDoctor(mcConfig) {
39850
40436
  }
39851
40437
 
39852
40438
  // ../plugin/src/hooks/auto-update-checker/constants.ts
39853
- import { homedir as homedir15, platform } from "node:os";
39854
- import { join as join29 } from "node:path";
40439
+ import { homedir as homedir16, platform } from "node:os";
40440
+ import { join as join30 } from "node:path";
39855
40441
  function getOpenCodeCacheRoot() {
39856
40442
  if (platform() === "win32") {
39857
- return join29(process.env.LOCALAPPDATA ?? homedir15(), "opencode");
40443
+ return join30(process.env.LOCALAPPDATA ?? homedir16(), "opencode");
39858
40444
  }
39859
- return join29(homedir15(), ".cache", "opencode");
40445
+ return join30(homedir16(), ".cache", "opencode");
39860
40446
  }
39861
40447
  function getOpenCodeConfigRoot() {
39862
40448
  if (platform() === "win32") {
39863
- return join29(process.env.APPDATA ?? join29(homedir15(), "AppData", "Roaming"), "opencode");
40449
+ return join30(process.env.APPDATA ?? join30(homedir16(), "AppData", "Roaming"), "opencode");
39864
40450
  }
39865
- return join29(process.env.XDG_CONFIG_HOME ?? join29(homedir15(), ".config"), "opencode");
40451
+ return join30(process.env.XDG_CONFIG_HOME ?? join30(homedir16(), ".config"), "opencode");
39866
40452
  }
39867
40453
  var PACKAGE_NAME = "@cortexkit/opencode-magic-context", CACHE_DIR, USER_OPENCODE_CONFIG, USER_OPENCODE_CONFIG_JSONC;
39868
40454
  var init_constants2 = __esm(() => {
39869
- CACHE_DIR = join29(getOpenCodeCacheRoot(), "packages");
39870
- USER_OPENCODE_CONFIG = join29(getOpenCodeConfigRoot(), "opencode.json");
39871
- USER_OPENCODE_CONFIG_JSONC = join29(getOpenCodeConfigRoot(), "opencode.jsonc");
40455
+ CACHE_DIR = join30(getOpenCodeCacheRoot(), "packages");
40456
+ USER_OPENCODE_CONFIG = join30(getOpenCodeConfigRoot(), "opencode.json");
40457
+ USER_OPENCODE_CONFIG_JSONC = join30(getOpenCodeConfigRoot(), "opencode.jsonc");
39872
40458
  });
39873
40459
 
39874
40460
  // ../plugin/src/hooks/auto-update-checker/types.ts
@@ -39892,14 +40478,14 @@ var init_types = __esm(() => {
39892
40478
  // ../plugin/src/hooks/auto-update-checker/checker.ts
39893
40479
  import {
39894
40480
  accessSync as accessSync2,
39895
- existsSync as existsSync34,
40481
+ existsSync as existsSync35,
39896
40482
  constants as fsConstants,
39897
- readFileSync as readFileSync23,
39898
- renameSync as renameSync7,
40483
+ readFileSync as readFileSync25,
40484
+ renameSync as renameSync9,
39899
40485
  statSync as statSync15,
39900
- writeFileSync as writeFileSync8
40486
+ writeFileSync as writeFileSync10
39901
40487
  } from "node:fs";
39902
- import { dirname as dirname17, isAbsolute as isAbsolute7, join as join30, resolve as resolve9 } from "node:path";
40488
+ import { dirname as dirname18, isAbsolute as isAbsolute7, join as join31, resolve as resolve9 } from "node:path";
39903
40489
  function warn(message) {
39904
40490
  log3(`WARN: ${message}`);
39905
40491
  }
@@ -39925,17 +40511,17 @@ function parseJsonConfig(content) {
39925
40511
  }
39926
40512
  function getSurfaceConfigPaths(directory, surface) {
39927
40513
  const name = surface === "server" ? "opencode" : "tui";
39928
- const globalDir = dirname17(USER_OPENCODE_CONFIG);
40514
+ const globalDir = dirname18(USER_OPENCODE_CONFIG);
39929
40515
  const dirs = [
39930
40516
  globalDir,
39931
40517
  directory,
39932
- join30(directory, ".opencode"),
40518
+ join31(directory, ".opencode"),
39933
40519
  process.env.OPENCODE_CONFIG_DIR
39934
40520
  ].filter((value) => Boolean(value));
39935
40521
  const paths = [];
39936
40522
  for (const dir of dirs) {
39937
40523
  for (const extension of ["json", "jsonc"]) {
39938
- const path = join30(dir, `${name}.${extension}`);
40524
+ const path = join31(dir, `${name}.${extension}`);
39939
40525
  if (!paths.includes(path))
39940
40526
  paths.push(path);
39941
40527
  }
@@ -39946,9 +40532,9 @@ function getWinningConfig(directory, surface) {
39946
40532
  let winner = null;
39947
40533
  for (const path of getSurfaceConfigPaths(directory, surface)) {
39948
40534
  try {
39949
- if (!existsSync34(path))
40535
+ if (!existsSync35(path))
39950
40536
  continue;
39951
- const parsed = parseJsonConfig(readFileSync23(path, "utf-8"));
40537
+ const parsed = parseJsonConfig(readFileSync25(path, "utf-8"));
39952
40538
  const entries = getPluginEntries(parsed);
39953
40539
  if (entries.some((entry) => entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))) {
39954
40540
  if (winner) {
@@ -39984,14 +40570,14 @@ var init_checker = __esm(() => {
39984
40570
  });
39985
40571
 
39986
40572
  // src/lib/opencode-plugin-schema-fence.ts
39987
- import { existsSync as existsSync35, readdirSync as readdirSync6, readFileSync as readFileSync24 } from "node:fs";
39988
- import { join as join31 } from "node:path";
40573
+ import { existsSync as existsSync36, readdirSync as readdirSync7, readFileSync as readFileSync26 } from "node:fs";
40574
+ import { join as join32 } from "node:path";
39989
40575
  import { gunzipSync } from "node:zlib";
39990
40576
  function readPackageVersion(packageJsonPath) {
39991
40577
  try {
39992
- if (!existsSync35(packageJsonPath))
40578
+ if (!existsSync36(packageJsonPath))
39993
40579
  return null;
39994
- const packageJson = JSON.parse(readFileSync24(packageJsonPath, "utf-8"));
40580
+ const packageJson = JSON.parse(readFileSync26(packageJsonPath, "utf-8"));
39995
40581
  if (packageJson.name !== OPENCODE_PLUGIN_NAME || typeof packageJson.version !== "string") {
39996
40582
  return null;
39997
40583
  }
@@ -40010,8 +40596,8 @@ function readFenceValue(text) {
40010
40596
  return values.size === 1 ? [...values][0] ?? null : null;
40011
40597
  }
40012
40598
  function readFenceFromDist(packageDirectory) {
40013
- const distDirectory = join31(packageDirectory, "dist");
40014
- if (!existsSync35(distDirectory))
40599
+ const distDirectory = join32(packageDirectory, "dist");
40600
+ if (!existsSync36(distDirectory))
40015
40601
  return null;
40016
40602
  const pending = [distDirectory];
40017
40603
  const fenceValues = new Set;
@@ -40020,15 +40606,15 @@ function readFenceFromDist(packageDirectory) {
40020
40606
  if (!directory)
40021
40607
  continue;
40022
40608
  try {
40023
- for (const entry of readdirSync6(directory, { withFileTypes: true })) {
40024
- const path = join31(directory, entry.name);
40609
+ for (const entry of readdirSync7(directory, { withFileTypes: true })) {
40610
+ const path = join32(directory, entry.name);
40025
40611
  if (entry.isDirectory()) {
40026
40612
  pending.push(path);
40027
40613
  continue;
40028
40614
  }
40029
40615
  if (!entry.isFile() || !/\.(?:[cm]?js|ts)$/.test(entry.name))
40030
40616
  continue;
40031
- const fence = readFenceValue(readFileSync24(path, "utf-8"));
40617
+ const fence = readFenceValue(readFileSync26(path, "utf-8"));
40032
40618
  if (fence !== null)
40033
40619
  fenceValues.add(fence);
40034
40620
  }
@@ -40039,20 +40625,20 @@ function readFenceFromDist(packageDirectory) {
40039
40625
  return fenceValues.size === 1 ? [...fenceValues][0] ?? null : null;
40040
40626
  }
40041
40627
  function findInstalledPluginPackage(cacheDirectory, specifier, requestedVersion) {
40042
- const directCacheRoot = join31(cacheDirectory, specifier);
40628
+ const directCacheRoot = join32(cacheDirectory, specifier);
40043
40629
  const candidates = [directCacheRoot];
40044
40630
  try {
40045
- for (const entry of readdirSync6(cacheDirectory, { withFileTypes: true })) {
40631
+ for (const entry of readdirSync7(cacheDirectory, { withFileTypes: true })) {
40046
40632
  if (!entry.isDirectory())
40047
40633
  continue;
40048
- const candidate = join31(cacheDirectory, entry.name);
40634
+ const candidate = join32(cacheDirectory, entry.name);
40049
40635
  if (!candidates.includes(candidate))
40050
40636
  candidates.push(candidate);
40051
40637
  }
40052
40638
  } catch {}
40053
40639
  for (const cacheRoot of candidates) {
40054
- const directory = join31(cacheRoot, "node_modules", ...PACKAGE_PATH_SEGMENTS);
40055
- const version = readPackageVersion(join31(directory, "package.json"));
40640
+ const directory = join32(cacheRoot, "node_modules", ...PACKAGE_PATH_SEGMENTS);
40641
+ const version = readPackageVersion(join32(directory, "package.json"));
40056
40642
  if (!version)
40057
40643
  continue;
40058
40644
  if (cacheRoot === directCacheRoot || EXACT_VERSION_PATTERN.test(requestedVersion) && version === requestedVersion) {
@@ -40246,13 +40832,13 @@ var init_storage_versions = __esm(async () => {
40246
40832
  });
40247
40833
 
40248
40834
  // src/commands/doctor-opencode-cache.ts
40249
- import { existsSync as existsSync36, readFileSync as readFileSync25, rmSync as rmSync4 } from "node:fs";
40835
+ import { existsSync as existsSync37, readFileSync as readFileSync27, rmSync as rmSync4 } from "node:fs";
40250
40836
  function readCachedPluginVersion(pluginCacheDir) {
40251
40837
  try {
40252
40838
  const installedPkgPath = getOpenCodePluginPackageJsonPath(pluginCacheDir);
40253
- if (!existsSync36(installedPkgPath))
40839
+ if (!existsSync37(installedPkgPath))
40254
40840
  return;
40255
- const pkg = JSON.parse(readFileSync25(installedPkgPath, "utf-8"));
40841
+ const pkg = JSON.parse(readFileSync27(installedPkgPath, "utf-8"));
40256
40842
  return typeof pkg.version === "string" ? pkg.version : undefined;
40257
40843
  } catch {
40258
40844
  return;
@@ -40261,7 +40847,7 @@ function readCachedPluginVersion(pluginCacheDir) {
40261
40847
  async function clearPluginCache(options = {}, deps = {}) {
40262
40848
  const remove = deps.remove ?? ((path) => rmSync4(path, { recursive: true, force: true }));
40263
40849
  const pluginCacheRoots = getOpenCodePluginCacheRoots();
40264
- const existingRoots = pluginCacheRoots.filter((root) => existsSync36(root));
40850
+ const existingRoots = pluginCacheRoots.filter((root) => existsSync37(root));
40265
40851
  if (existingRoots.length === 0) {
40266
40852
  return { action: "not_found", path: pluginCacheRoots[0] ?? "" };
40267
40853
  }
@@ -40328,19 +40914,19 @@ var init_doctor_opencode_cache = __esm(() => {
40328
40914
  });
40329
40915
 
40330
40916
  // src/commands/doctor-cached-plugin-fence.ts
40331
- import { existsSync as existsSync37 } from "node:fs";
40332
- import { dirname as dirname18 } from "node:path";
40917
+ import { existsSync as existsSync38 } from "node:fs";
40918
+ import { dirname as dirname19 } from "node:path";
40333
40919
  function listCachedOpenCodePluginFences(sources = {}) {
40334
40920
  const fences = [];
40335
40921
  for (const root of sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots()) {
40336
40922
  const packageJson = getOpenCodePluginPackageJsonPath(root);
40337
- if (!existsSync37(packageJson))
40923
+ if (!existsSync38(packageJson))
40338
40924
  continue;
40339
40925
  fences.push({
40340
40926
  host: "opencode1",
40341
40927
  directory: root,
40342
40928
  version: readCachedPluginVersion(root),
40343
- supportedVersion: readFenceFromDist(dirname18(packageJson))
40929
+ supportedVersion: readFenceFromDist(dirname19(packageJson))
40344
40930
  });
40345
40931
  }
40346
40932
  for (const slot of listOpenCodeV2PluginCacheSlots(sources.openCodeV2NpmCacheDir)) {
@@ -40354,7 +40940,7 @@ function listCachedOpenCodePluginFences(sources = {}) {
40354
40940
  directory: slot.slot,
40355
40941
  spec: slot.spec,
40356
40942
  version: slot.version,
40357
- supportedVersion: readFenceFromDist(dirname18(getOpenCodePluginPackageJsonPath(generation)))
40943
+ supportedVersion: readFenceFromDist(dirname19(getOpenCodePluginPackageJsonPath(generation)))
40358
40944
  });
40359
40945
  }
40360
40946
  return fences;
@@ -40429,11 +41015,11 @@ function mcMarkerPredicate(column = "data") {
40429
41015
  AND json_extract(${data}, '$.summary') = 1
40430
41016
  AND json_extract(${data}, '$.providerID') = 'magic-context'`;
40431
41017
  }
40432
- function tableExists4(db, table) {
41018
+ function tableExists5(db, table) {
40433
41019
  return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1").get(table) != null;
40434
41020
  }
40435
41021
  function countMissingCompleted(db) {
40436
- if (!tableExists4(db, "message"))
41022
+ if (!tableExists5(db, "message"))
40437
41023
  return 0;
40438
41024
  const row = db.prepare(`SELECT COUNT(*) AS count
40439
41025
  FROM message
@@ -40442,7 +41028,7 @@ function countMissingCompleted(db) {
40442
41028
  return Number(row?.count ?? 0);
40443
41029
  }
40444
41030
  function migrationV1ToV2Completed(db) {
40445
- if (!tableExists4(db, "kv"))
41031
+ if (!tableExists5(db, "kv"))
40446
41032
  return false;
40447
41033
  const columns = db.prepare("PRAGMA table_info(kv)").all();
40448
41034
  const names = new Set(columns.flatMap((column) => typeof column.name === "string" ? [column.name] : []));
@@ -40459,7 +41045,7 @@ function migrationV1ToV2Completed(db) {
40459
41045
  }
40460
41046
  }
40461
41047
  function countUnmatchedConvertedMarkers(db, migratedV2Schema) {
40462
- if (!migratedV2Schema || !tableExists4(db, "message"))
41048
+ if (!migratedV2Schema || !tableExists5(db, "message"))
40463
41049
  return 0;
40464
41050
  const row = db.prepare(`SELECT COUNT(DISTINCT json_extract(${safeData("marker.data")}, '$.parentID')) AS count
40465
41051
  FROM message marker
@@ -40474,7 +41060,7 @@ function countUnmatchedConvertedMarkers(db, migratedV2Schema) {
40474
41060
  return Number(row?.count ?? 0);
40475
41061
  }
40476
41062
  function countPostConversionMessages(db, migratedV2Schema) {
40477
- if (!migratedV2Schema || !tableExists4(db, "message") || !tableExists4(db, "session")) {
41063
+ if (!migratedV2Schema || !tableExists5(db, "message") || !tableExists5(db, "session")) {
40478
41064
  return { messages: 0, sessions: 0 };
40479
41065
  }
40480
41066
  const row = db.prepare(`SELECT COUNT(*) AS messages, COUNT(DISTINCT converted.session_id) AS sessions
@@ -40491,7 +41077,7 @@ function countPostConversionMessages(db, migratedV2Schema) {
40491
41077
  function checkOpenCodeCompactionMarkerConversion(db, options = {}) {
40492
41078
  const missingBefore = countMissingCompleted(db);
40493
41079
  let repaired = 0;
40494
- if (options.fix && missingBefore > 0 && tableExists4(db, "message")) {
41080
+ if (options.fix && missingBefore > 0 && tableExists5(db, "message")) {
40495
41081
  repaired = db.transaction(() => {
40496
41082
  const result = db.prepare(`UPDATE message
40497
41083
  SET data = json_set(
@@ -40505,7 +41091,7 @@ function checkOpenCodeCompactionMarkerConversion(db, options = {}) {
40505
41091
  })();
40506
41092
  }
40507
41093
  const missingAfter = countMissingCompleted(db);
40508
- const migratedV2Schema = tableExists4(db, "session_v2") && tableExists4(db, "session_message");
41094
+ const migratedV2Schema = tableExists5(db, "session_v2") && tableExists5(db, "session_message");
40509
41095
  const migrationCompleted = migrationV1ToV2Completed(db);
40510
41096
  const unmatchedConvertedMarkers = countUnmatchedConvertedMarkers(db, migratedV2Schema);
40511
41097
  const postConversion = countPostConversionMessages(db, migratedV2Schema);
@@ -40616,6 +41202,182 @@ var init_doctor_harness_relabel = __esm(async () => {
40616
41202
  await init_opencode2_relabel();
40617
41203
  });
40618
41204
 
41205
+ // src/commands/doctor-hidden-children.ts
41206
+ import { spawnSync as spawnSync8 } from "node:child_process";
41207
+ import { existsSync as existsSync39, realpathSync as realpathSync7 } from "node:fs";
41208
+ import { dirname as dirname20 } from "node:path";
41209
+ async function assertHiddenChildStoresClosed(contextDbPath, hostDbPath, platform = process.platform, processProbe = () => inspectProcessesAsync(true)) {
41210
+ if (platform === "win32") {
41211
+ const processes = await processProbe();
41212
+ if (processes.pi.state !== "known" || processes.pi.processIds.length > 0 || (processes.pi.inconclusivePids?.length ?? 0) > 0 || processes.processSnapshot?.source !== "cim") {
41213
+ throw new Error("Windows process probe could not rule out OpenCode, Pi or ck-mc holders");
41214
+ }
41215
+ const unknown = processes.processSnapshot.facts.filter(({ pid, imageName, commandLine }) => pid !== process.pid && (!imageName && !commandLine || /^(?:bun|node|deno)(?:\.exe)?$/i.test(imageName ?? "") && !commandLine));
41216
+ if (unknown.length)
41217
+ throw new Error(`Windows process identity is unavailable (PID ${unknown.map(({ pid }) => pid).join(", ")})`);
41218
+ const blockers = processes.processSnapshot.facts.filter(({ pid, imageName, commandLine }) => {
41219
+ if (pid === process.pid)
41220
+ return false;
41221
+ const image = (imageName ?? "").toLowerCase().replaceAll("\\", "/").split("/").at(-1) ?? "";
41222
+ return /^(?:opencode|opencode2|pi|omp|ck-mc)(?:\.exe)?$/.test(image) || /(?:^|[\\/\s"'])(?:ck-mc|opencode2?)(?:\.exe|\.js|\.mjs)?(?:$|[\s"'])/i.test(commandLine ?? "");
41223
+ });
41224
+ if (blockers.length)
41225
+ throw new Error(`OpenCode, Pi or ck-mc process is running (PID ${blockers.map(({ pid }) => pid).join(", ")})`);
41226
+ return;
41227
+ }
41228
+ const inspection = defaultInspectHolders(dirname20(contextDbPath));
41229
+ if (!inspection.safe) {
41230
+ throw new Error(`database holders cannot be ruled out: ${[...inspection.blockers, inspection.uncertainty].filter(Boolean).join("; ")}`);
41231
+ }
41232
+ const paths = [contextDbPath, hostDbPath].flatMap((path) => [path, `${path}-wal`, `${path}-shm`].filter(existsSync39).map((file) => realpathSync7(file)));
41233
+ const result = spawnSync8("lsof", ["-Fn", "--", ...paths], {
41234
+ encoding: "utf8",
41235
+ timeout: 30000,
41236
+ maxBuffer: 32 * 1024 * 1024,
41237
+ windowsHide: true
41238
+ });
41239
+ if (result.error || result.stderr?.trim() || result.status !== 0 && !(result.status === 1 && !result.stdout?.trim())) {
41240
+ throw new Error(`lsof could not inspect database holders: ${result.error?.message ?? result.stderr ?? result.status}`);
41241
+ }
41242
+ const opened = result.stdout.split(`
41243
+ `).filter((line) => line.startsWith("n"));
41244
+ if (opened.length)
41245
+ throw new Error(`database holder is present: ${opened.map((line) => line.slice(1)).join(", ")}`);
41246
+ if (result.status === 0)
41247
+ throw new Error("lsof returned success without any file evidence");
41248
+ }
41249
+ function assertVerifiedV2Schema(db) {
41250
+ const table = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all();
41251
+ const names = new Set(table.map((row) => row.name));
41252
+ if (!names.has("session_v2") || !names.has("session_message") || names.has("message") || names.has("part")) {
41253
+ throw new Error("OpenCode store is not the verified OpenCode 2 session_v2 schema");
41254
+ }
41255
+ const fields = db.prepare("PRAGMA table_info('session_v2')").all();
41256
+ if (!fields.some((field) => field.name === "id" && field.pk === 1) || !fields.some((field) => field.name === "metadata") || !fields.some((field) => field.name === "directory")) {
41257
+ throw new Error("OpenCode session_v2 columns differ from the verified schema");
41258
+ }
41259
+ const dependents = table.flatMap(({ name }) => {
41260
+ const foreignKeys = db.prepare(`PRAGMA foreign_key_list('${name.replaceAll("'", "''")}')`).all();
41261
+ return foreignKeys.filter((key) => key.table === "session_v2").map((key) => ({ name, ...key }));
41262
+ });
41263
+ if (dependents.length !== CASCADE_TABLES.length || dependents.some((key) => !CASCADE_TABLES.includes(key.name) || key.from !== "session_id" || key.to !== "id" || key.on_delete !== "CASCADE")) {
41264
+ throw new Error("OpenCode session_v2 cascade schema differs from the verified 2.0.18 host");
41265
+ }
41266
+ }
41267
+ async function cleanupRetiredHiddenChildren(options) {
41268
+ const { contextDbPath, hostDbPath, fix = false } = options;
41269
+ if (!existsSync39(contextDbPath) || !existsSync39(hostDbPath))
41270
+ return { waiting: 0, deleted: 0 };
41271
+ const context = new Database(contextDbPath, { readonly: true, fileMustExist: true });
41272
+ const host = new Database(hostDbPath, { readonly: true, fileMustExist: true });
41273
+ let states;
41274
+ let candidates;
41275
+ let waiting;
41276
+ try {
41277
+ assertVerifiedV2Schema(host);
41278
+ const rows = context.prepare("SELECT key, value FROM schema_migrations_meta WHERE substr(key, 1, ?) = ?").all(PREFIX.length, PREFIX);
41279
+ states = rows.map(({ key, value }) => {
41280
+ const state = JSON.parse(value);
41281
+ if (state.version !== 1 || !Array.isArray(state.retired_children) || state.retired_children.some((child) => typeof child.id !== "string")) {
41282
+ throw new Error(`Invalid hidden-child state at ${key}`);
41283
+ }
41284
+ return { key, state };
41285
+ });
41286
+ const ids = [
41287
+ ...new Set(states.flatMap(({ state }) => state.retired_children.map((child) => child.id)))
41288
+ ];
41289
+ waiting = ids.length;
41290
+ candidates = ids.filter((id) => {
41291
+ const row = host.prepare("SELECT metadata FROM session_v2 WHERE id = ?").get(id);
41292
+ if (!row)
41293
+ return false;
41294
+ let marker;
41295
+ try {
41296
+ marker = JSON.parse(row.metadata ?? "null");
41297
+ } catch {
41298
+ return false;
41299
+ }
41300
+ return marker?.magic_context === "hidden-run";
41301
+ });
41302
+ } finally {
41303
+ context.close();
41304
+ host.close();
41305
+ }
41306
+ options.report?.(`${waiting} retired hidden sessions are waiting for deletion${fix ? "" : "; run `doctor --fix` with OpenCode closed"}`);
41307
+ if (!fix || candidates.length === 0)
41308
+ return { waiting, deleted: 0 };
41309
+ const inspect = options.inspectHolders ?? ((context, host) => assertHiddenChildStoresClosed(context, host, options.platform, options.processProbe));
41310
+ await inspect(contextDbPath, hostDbPath);
41311
+ const backup = `${hostDbPath}.hidden-child-backup-${new Date().toISOString().replace(/[:.]/g, "-")}-${process.pid}`;
41312
+ copyDatabaseBundle(hostDbPath, backup);
41313
+ options.report?.(`OpenCode store backup: ${backup}`);
41314
+ await inspect(contextDbPath, hostDbPath);
41315
+ const writableHost = new Database(hostDbPath);
41316
+ let writableContext;
41317
+ const deletedIds = [];
41318
+ let hostLocked = false;
41319
+ let contextLocked = false;
41320
+ try {
41321
+ writableHost.exec("PRAGMA busy_timeout = 250");
41322
+ writableHost.exec("PRAGMA foreign_keys = ON");
41323
+ writableContext = new Database(contextDbPath);
41324
+ writableContext.exec("PRAGMA busy_timeout = 250");
41325
+ try {
41326
+ writableHost.exec("BEGIN EXCLUSIVE");
41327
+ hostLocked = true;
41328
+ writableContext.exec("BEGIN EXCLUSIVE");
41329
+ contextLocked = true;
41330
+ } catch (error) {
41331
+ throw new Error(`Cannot acquire exclusive locks on ${hostDbPath} and ${contextDbPath}: ${error instanceof Error ? error.message : String(error)}`);
41332
+ }
41333
+ assertVerifiedV2Schema(writableHost);
41334
+ for (const id of candidates) {
41335
+ const result = writableHost.prepare("DELETE FROM session_v2 WHERE id = ? AND json_valid(metadata) AND json_extract(metadata, '$.magic_context') = 'hidden-run'").run(id);
41336
+ if (result.changes > 0)
41337
+ deletedIds.push(id);
41338
+ }
41339
+ for (const { key } of states) {
41340
+ const current = writableContext.prepare("SELECT value FROM schema_migrations_meta WHERE key = ?").get(key);
41341
+ if (!current)
41342
+ throw new Error(`Hidden-child state ${key} disappeared during repair`);
41343
+ const state = JSON.parse(current.value);
41344
+ if (state.version !== 1 || !Array.isArray(state.retired_children)) {
41345
+ throw new Error(`Hidden-child state ${key} changed during repair`);
41346
+ }
41347
+ state.retired_children = state.retired_children.filter((child) => !deletedIds.includes(child.id));
41348
+ writableContext.prepare("UPDATE schema_migrations_meta SET value = ? WHERE key = ?").run(JSON.stringify(state), key);
41349
+ }
41350
+ writableHost.exec("COMMIT");
41351
+ hostLocked = false;
41352
+ writableContext.exec("COMMIT");
41353
+ contextLocked = false;
41354
+ } finally {
41355
+ if (contextLocked)
41356
+ writableContext?.exec("ROLLBACK");
41357
+ if (hostLocked)
41358
+ writableHost.exec("ROLLBACK");
41359
+ writableContext?.close();
41360
+ writableHost.close();
41361
+ }
41362
+ options.report?.(`Deleted ${deletedIds.length} marked, recorded hidden sessions`);
41363
+ return { waiting, deleted: deletedIds.length, backup };
41364
+ }
41365
+ var PREFIX = "opencode2_hidden_children:", CASCADE_TABLES;
41366
+ var init_doctor_hidden_children = __esm(async () => {
41367
+ init_rpc_utils();
41368
+ await __promiseAll([
41369
+ init_sqlite(),
41370
+ init_doctor_repair_db()
41371
+ ]);
41372
+ CASCADE_TABLES = [
41373
+ "instruction_entry",
41374
+ "instruction_state",
41375
+ "session_inbox",
41376
+ "session_message",
41377
+ "session_pending"
41378
+ ];
41379
+ });
41380
+
40619
41381
  // src/commands/doctor-opencode-plugin-duplicates.ts
40620
41382
  import { isDeepStrictEqual } from "node:util";
40621
41383
  function entryOptions(entry) {
@@ -40842,11 +41604,11 @@ function formatUnresolvedCompartmentSession(session) {
40842
41604
  }
40843
41605
 
40844
41606
  // src/commands/doctor-opencode.ts
40845
- import { spawnSync as spawnSync8 } from "node:child_process";
40846
- import { existsSync as existsSync38, mkdtempSync, readFileSync as readFileSync26, rmSync as rmSync5 } from "node:fs";
41607
+ import { spawnSync as spawnSync9 } from "node:child_process";
41608
+ import { existsSync as existsSync40, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync5 } from "node:fs";
40847
41609
  import { createRequire as createRequire6 } from "node:module";
40848
- import { homedir as homedir16, tmpdir as tmpdir3 } from "node:os";
40849
- import { join as join32 } from "node:path";
41610
+ import { homedir as homedir17, tmpdir as tmpdir3 } from "node:os";
41611
+ import { join as join33 } from "node:path";
40850
41612
  function formatSharedDbRowCounts(db) {
40851
41613
  return SHARED_DB_ROW_COUNT_TABLES.map((table) => {
40852
41614
  try {
@@ -40877,7 +41639,8 @@ function findUnknownConfiguredModels(configured, catalog) {
40877
41639
  return [];
40878
41640
  });
40879
41641
  }
40880
- function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args, cwd) => spawnSync8("opencode", args, {
41642
+ function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args, cwd) => spawnSync9("opencode", args, {
41643
+ windowsHide: true,
40881
41644
  cwd,
40882
41645
  encoding: "utf8",
40883
41646
  timeout: hostGeneration === "v2" ? 90000 : 45000,
@@ -40914,7 +41677,7 @@ function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args
40914
41677
  }
40915
41678
  if (models.length === 0)
40916
41679
  return;
40917
- const tempRoot = mkdtempSync(join32(tmpdir3(), "magic-context-opencode-catalog-"));
41680
+ const tempRoot = mkdtempSync2(join33(tmpdir3(), "magic-context-opencode-catalog-"));
40918
41681
  try {
40919
41682
  const command = hostGeneration === "v2" ? ["api", "model.list", "--param", `directory=${projectDir}`] : ["models", "--verbose"];
40920
41683
  const guidance = `opencode ${command.join(" ")}`;
@@ -41090,7 +41853,7 @@ function getSelfVersion2() {
41090
41853
  function describeAutoUpdateStall(specifier, autoUpdateEnabled, storageDir = getMagicContextStorageDir()) {
41091
41854
  if (!autoUpdateEnabled || !isPinnedOpenCodePluginSpecifier(specifier))
41092
41855
  return null;
41093
- const state = readAutoUpdateCheckState(join32(storageDir, AUTO_UPDATE_CHECK_STATE_FILENAME));
41856
+ const state = readAutoUpdateCheckState(join33(storageDir, AUTO_UPDATE_CHECK_STATE_FILENAME));
41094
41857
  const owner = isUpdaterPinnedSpec(state, specifier) ? "updater" : "you";
41095
41858
  return `auto-update: stalled — config pinned to ${specifier} (by ${owner})`;
41096
41859
  }
@@ -41099,15 +41862,15 @@ function getUserNpmrcPath() {
41099
41862
  if (custom)
41100
41863
  return custom;
41101
41864
  const home = process.env.HOME?.trim();
41102
- return join32(home || homedir16(), ".npmrc");
41865
+ return join33(home || homedir17(), ".npmrc");
41103
41866
  }
41104
41867
  function collectNpmReleaseAgeWarnings() {
41105
41868
  const ageWarnings = [];
41106
41869
  const npmrcPath = getUserNpmrcPath();
41107
- if (!existsSync38(npmrcPath))
41870
+ if (!existsSync40(npmrcPath))
41108
41871
  return ageWarnings;
41109
41872
  try {
41110
- const npmrc = readFileSync26(npmrcPath, "utf-8");
41873
+ const npmrc = readFileSync28(npmrcPath, "utf-8");
41111
41874
  for (const line of npmrc.split(`
41112
41875
  `)) {
41113
41876
  const trimmed = line.trim();
@@ -41140,15 +41903,18 @@ function compareVersions(a, b) {
41140
41903
  function openBrowser(url) {
41141
41904
  try {
41142
41905
  if (process.platform === "darwin") {
41143
- const child = spawnSync8("open", [url], { stdio: "ignore" });
41906
+ const child = spawnSync9("open", [url], { windowsHide: true, stdio: "ignore" });
41144
41907
  if (child.status === 0)
41145
41908
  return;
41146
41909
  } else if (process.platform === "linux") {
41147
- const child = spawnSync8("xdg-open", [url], { stdio: "ignore" });
41910
+ const child = spawnSync9("xdg-open", [url], { windowsHide: true, stdio: "ignore" });
41148
41911
  if (child.status === 0)
41149
41912
  return;
41150
41913
  } else if (process.platform === "win32") {
41151
- const child = spawnSync8("cmd", ["/c", "start", "", url], { stdio: "ignore" });
41914
+ const child = spawnSync9("cmd", ["/c", "start", "", url], {
41915
+ windowsHide: true,
41916
+ stdio: "ignore"
41917
+ });
41152
41918
  if (child.status === 0)
41153
41919
  return;
41154
41920
  }
@@ -41241,7 +42007,7 @@ function checkLocalEmbeddingRuntimeForDoctor(runtimePreference = "auto") {
41241
42007
  return { issues: 0 };
41242
42008
  }
41243
42009
  async function checkEmbeddingConfig(magicContextConfigPath) {
41244
- if (!existsSync38(magicContextConfigPath)) {
42010
+ if (!existsSync40(magicContextConfigPath)) {
41245
42011
  return checkLocalEmbeddingRuntimeForDoctor();
41246
42012
  }
41247
42013
  let rawText;
@@ -41398,7 +42164,7 @@ async function runDoctor2(options = {}) {
41398
42164
  const v22Result = await runV22BackfillCommands({
41399
42165
  name: "OpenCode",
41400
42166
  openDatabase: (readonly = true) => {
41401
- const dbPath = join32(getMagicContextStorageDir(), "context.db");
42167
+ const dbPath = join33(getMagicContextStorageDir(), "context.db");
41402
42168
  v22Db = readonly ? openExistingContextDatabase(dbPath, { readonly: true }) : openExistingContextDatabaseForMutation(dbPath);
41403
42169
  return v22Db;
41404
42170
  },
@@ -41430,7 +42196,7 @@ async function runDoctor2(options = {}) {
41430
42196
  failCount++;
41431
42197
  issues++;
41432
42198
  };
41433
- const authorityDbPath = join32(getMagicContextStorageDir(), "context.db");
42199
+ const authorityDbPath = join33(getMagicContextStorageDir(), "context.db");
41434
42200
  let authorityDb = null;
41435
42201
  try {
41436
42202
  authorityDb = openExistingContextDatabase(authorityDbPath, { readonly: true });
@@ -41484,6 +42250,19 @@ async function runDoctor2(options = {}) {
41484
42250
  else
41485
42251
  fail(openCodeDbCheck.message);
41486
42252
  if (openCodeDbCheck.ok) {
42253
+ if (storeGeneration === "v2") {
42254
+ try {
42255
+ const cleanup = await cleanupRetiredHiddenChildren({
42256
+ contextDbPath: authorityDbPath,
42257
+ hostDbPath: openCodeDbResolution.path,
42258
+ fix: options.fix,
42259
+ report: (line) => log2.info(line)
42260
+ });
42261
+ fixed += cleanup.deleted;
42262
+ } catch (error) {
42263
+ fail(`Retired hidden-child cleanup refused: ${error instanceof Error ? error.message : String(error)}`);
42264
+ }
42265
+ }
41487
42266
  let markerDb = null;
41488
42267
  try {
41489
42268
  markerDb = new Database(openCodeDbResolution.path, {
@@ -41591,7 +42370,7 @@ async function runDoctor2(options = {}) {
41591
42370
  pass(`OpenCode config: ${paths.opencodeConfig}`);
41592
42371
  }
41593
42372
  let autoUpdateEnabled = true;
41594
- if (existsSync38(paths.magicContextConfig)) {
42373
+ if (existsSync40(paths.magicContextConfig)) {
41595
42374
  pass(`Magic Context config: ${paths.magicContextConfig}`);
41596
42375
  try {
41597
42376
  const raw = loadRawConfigFile({ configPath: paths.magicContextConfig, tier: "user" });
@@ -41627,7 +42406,7 @@ async function runDoctor2(options = {}) {
41627
42406
  warn(`No magic-context.jsonc found — using defaults`);
41628
42407
  log2.info(" Run 'setup' to create one with model recommendations");
41629
42408
  }
41630
- if (existsSync38(paths.magicContextConfig)) {
42409
+ if (existsSync40(paths.magicContextConfig)) {
41631
42410
  try {
41632
42411
  const raw = loadRawConfigFile({ configPath: paths.magicContextConfig, tier: "user" });
41633
42412
  if (!raw)
@@ -41757,7 +42536,7 @@ async function runDoctor2(options = {}) {
41757
42536
  };
41758
42537
  if (paths.opencodeConfigFormat !== "none") {
41759
42538
  try {
41760
- const raw = readFileSync26(paths.opencodeConfig, "utf-8");
42539
+ const raw = readFileSync28(paths.opencodeConfig, "utf-8");
41761
42540
  const config = import_comment_json11.parse(raw);
41762
42541
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
41763
42542
  if (checkPluginDuplicates(config, configName, options, {
@@ -41826,9 +42605,9 @@ async function runDoctor2(options = {}) {
41826
42605
  pass("Added TUI sidebar plugin to tui.json");
41827
42606
  warn("Restart OpenCode to see the sidebar");
41828
42607
  fixed++;
41829
- } else if (existsSync38(paths.tuiConfig)) {
42608
+ } else if (existsSync40(paths.tuiConfig)) {
41830
42609
  try {
41831
- const tuiRaw = readFileSync26(paths.tuiConfig, "utf-8");
42610
+ const tuiRaw = readFileSync28(paths.tuiConfig, "utf-8");
41832
42611
  const tuiConfig = import_comment_json11.parse(tuiRaw);
41833
42612
  const tuiRawPlugins = Array.isArray(tuiConfig?.plugin) ? tuiConfig.plugin : [];
41834
42613
  const tuiIdx = tuiRawPlugins.findIndex((entry) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry));
@@ -41866,7 +42645,7 @@ async function runDoctor2(options = {}) {
41866
42645
  fail("Could not create or verify the TUI sidebar config");
41867
42646
  }
41868
42647
  }
41869
- if (existsSync38(paths.magicContextConfig)) {
42648
+ if (existsSync40(paths.magicContextConfig)) {
41870
42649
  try {
41871
42650
  const raw = loadRawConfigFile({ configPath: paths.magicContextConfig, tier: "user" });
41872
42651
  if (!raw)
@@ -41888,9 +42667,9 @@ async function runDoctor2(options = {}) {
41888
42667
  else
41889
42668
  passCount++;
41890
42669
  const storage = getMagicContextStorageResolution();
41891
- const dbPath = join32(storage.path, "context.db");
42670
+ const dbPath = join33(storage.path, "context.db");
41892
42671
  log2.info(`Shared storage: ${storage.path} (source: ${storage.source})`);
41893
- if (!existsSync38(dbPath)) {
42672
+ if (!existsSync40(dbPath)) {
41894
42673
  log2.info(`Shared context DB not yet created at ${dbPath} (will be created on first run)`);
41895
42674
  } else {
41896
42675
  log2.info(`Shared context DB exists at ${dbPath}`);
@@ -41992,7 +42771,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
41992
42771
  let v2DistTag;
41993
42772
  if (paths.opencodeConfigFormat !== "none") {
41994
42773
  try {
41995
- v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync26(paths.opencodeConfig, "utf-8")));
42774
+ v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")));
41996
42775
  } catch {}
41997
42776
  }
41998
42777
  const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
@@ -42119,16 +42898,17 @@ var init_doctor_opencode = __esm(async () => {
42119
42898
  init_v22_backfill_commands(),
42120
42899
  init_doctor_authority(),
42121
42900
  init_doctor_cached_plugin_fence(),
42122
- init_doctor_harness_relabel()
42901
+ init_doctor_harness_relabel(),
42902
+ init_doctor_hidden_children()
42123
42903
  ]);
42124
42904
  import_comment_json11 = __toESM(require_src3(), 1);
42125
42905
  SHARED_DB_ROW_COUNT_TABLES = ["tags", "compartments", "memories", "notes", "dream_runs"];
42126
42906
  });
42127
42907
 
42128
42908
  // src/lib/diagnostics-pi.ts
42129
- import { existsSync as existsSync39, readdirSync as readdirSync7, readFileSync as readFileSync27, statSync as statSync16 } from "node:fs";
42909
+ import { existsSync as existsSync41, readdirSync as readdirSync8, readFileSync as readFileSync29, statSync as statSync16 } from "node:fs";
42130
42910
  import { createRequire as createRequire7 } from "node:module";
42131
- import { join as join33 } from "node:path";
42911
+ import { join as join34 } from "node:path";
42132
42912
  function getSelfVersion3() {
42133
42913
  const req = createRequire7(import.meta.url);
42134
42914
  for (const relPath of ["../../package.json", "../package.json"]) {
@@ -42143,7 +42923,7 @@ function getSelfVersion3() {
42143
42923
  }
42144
42924
  function fileSize2(path) {
42145
42925
  try {
42146
- return existsSync39(path) ? statSync16(path).size : 0;
42926
+ return existsSync41(path) ? statSync16(path).size : 0;
42147
42927
  } catch {
42148
42928
  return 0;
42149
42929
  }
@@ -42155,11 +42935,11 @@ function sanitizeValue2(value) {
42155
42935
  return sanitizeConfigValue(value);
42156
42936
  }
42157
42937
  function readJsonc(path) {
42158
- if (!existsSync39(path))
42938
+ if (!existsSync41(path))
42159
42939
  return { value: {} };
42160
42940
  try {
42161
42941
  return {
42162
- value: import_comment_json12.parse(readFileSync27(path, "utf-8"))
42942
+ value: import_comment_json12.parse(readFileSync29(path, "utf-8"))
42163
42943
  };
42164
42944
  } catch (error) {
42165
42945
  return {
@@ -42175,7 +42955,7 @@ function readConfigDiagnostic(path) {
42175
42955
  const parsed = readJsonc(path);
42176
42956
  return {
42177
42957
  path,
42178
- exists: existsSync39(path),
42958
+ exists: existsSync41(path),
42179
42959
  ...parsed.parseError ? { parseError: parsed.parseError } : {},
42180
42960
  flags: sanitizeValue2(parsed.value)
42181
42961
  };
@@ -42193,25 +42973,25 @@ function reverseSlugToDirectory(slug) {
42193
42973
  }
42194
42974
  function collectPiRecentSessions() {
42195
42975
  const sessionsRoot = getPiSessionsRoot();
42196
- if (!existsSync39(sessionsRoot))
42976
+ if (!existsSync41(sessionsRoot))
42197
42977
  return [];
42198
42978
  try {
42199
- const slugs = readdirSync7(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
42979
+ const slugs = readdirSync8(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
42200
42980
  const candidates = [];
42201
42981
  for (const slug of slugs) {
42202
42982
  const directory = reverseSlugToDirectory(slug);
42203
42983
  if (!directory)
42204
42984
  continue;
42205
- const slugDir = join33(sessionsRoot, slug);
42985
+ const slugDir = join34(sessionsRoot, slug);
42206
42986
  let files;
42207
42987
  try {
42208
- files = readdirSync7(slugDir).filter((name) => name.endsWith(".jsonl"));
42988
+ files = readdirSync8(slugDir).filter((name) => name.endsWith(".jsonl"));
42209
42989
  } catch {
42210
42990
  continue;
42211
42991
  }
42212
42992
  for (const file of files) {
42213
42993
  try {
42214
- const mtime = statSync16(join33(slugDir, file)).mtimeMs;
42994
+ const mtime = statSync16(join34(slugDir, file)).mtimeMs;
42215
42995
  const sessionId = file.replace(/\.jsonl$/, "");
42216
42996
  candidates.push({ sessionId, directory, mtime });
42217
42997
  } catch {}
@@ -42229,7 +43009,7 @@ function collectPiRecentSessions() {
42229
43009
  }
42230
43010
  function parseHistorianDumpMeta2(path) {
42231
43011
  try {
42232
- const xml = readFileSync27(path, "utf-8");
43012
+ const xml = readFileSync29(path, "utf-8");
42233
43013
  const parsed = parseCompartmentOutput(xml);
42234
43014
  const factCountByCategory = {};
42235
43015
  for (const fact of parsed.facts) {
@@ -42262,11 +43042,11 @@ function parseHistorianDumpMeta2(path) {
42262
43042
  }
42263
43043
  }
42264
43044
  function listDumpsInDir2(dir, limit) {
42265
- if (!existsSync39(dir))
43045
+ if (!existsSync41(dir))
42266
43046
  return { count: 0, recent: [] };
42267
43047
  try {
42268
- const entries = readdirSync7(dir).filter((name) => name.endsWith(".xml")).map((name) => {
42269
- const stat = statSync16(join33(dir, name));
43048
+ const entries = readdirSync8(dir).filter((name) => name.endsWith(".xml")).map((name) => {
43049
+ const stat = statSync16(join34(dir, name));
42270
43050
  return {
42271
43051
  name,
42272
43052
  mtime: stat.mtimeMs,
@@ -42275,7 +43055,7 @@ function listDumpsInDir2(dir, limit) {
42275
43055
  }).sort((a, b) => b.mtime - a.mtime);
42276
43056
  const now = Date.now();
42277
43057
  const recent = entries.slice(0, limit).map((entry) => {
42278
- const meta = parseHistorianDumpMeta2(join33(dir, entry.name));
43058
+ const meta = parseHistorianDumpMeta2(join34(dir, entry.name));
42279
43059
  const summary = {
42280
43060
  name: entry.name,
42281
43061
  ageMinutes: Math.round((now - entry.mtime) / 60000),
@@ -42339,7 +43119,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
42339
43119
  const loaded = loadPiConfig({ cwd });
42340
43120
  const storageResolution = getMagicContextStorageResolution();
42341
43121
  const storageDirPath = storageResolution.path;
42342
- const dbPath = join33(storageDirPath, "context.db");
43122
+ const dbPath = join34(storageDirPath, "context.db");
42343
43123
  const logFiles = inspectMagicContextLogs("pi");
42344
43124
  const primaryLog = logFiles.find((file) => file.exists) ?? logFiles[0];
42345
43125
  const otherPiExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
@@ -42356,7 +43136,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
42356
43136
  piVersion: pi ? getPiVersion(pi.path) : null,
42357
43137
  settings: {
42358
43138
  path: settingsPath,
42359
- exists: existsSync39(settingsPath),
43139
+ exists: existsSync41(settingsPath),
42360
43140
  ...settingsParsed.parseError ? { parseError: settingsParsed.parseError } : {},
42361
43141
  hasMagicContextPackage: hasPiMagicContextPackage(packages),
42362
43142
  packages: sanitizeValue2(packages)
@@ -42373,7 +43153,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
42373
43153
  storageDir: {
42374
43154
  path: storageDirPath,
42375
43155
  source: storageResolution.source,
42376
- exists: existsSync39(storageDirPath),
43156
+ exists: existsSync41(storageDirPath),
42377
43157
  contextDbSizeBytes: fileSize2(dbPath)
42378
43158
  },
42379
43159
  conflicts: {
@@ -42478,8 +43258,8 @@ var init_diagnostics_pi = __esm(() => {
42478
43258
  });
42479
43259
 
42480
43260
  // src/lib/logs-pi.ts
42481
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
42482
- import { dirname as dirname19, join as join34 } from "node:path";
43261
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "node:fs";
43262
+ import { dirname as dirname21, join as join35 } from "node:path";
42483
43263
  function sanitizeLogContent2(content) {
42484
43264
  return sanitizeString2(content);
42485
43265
  }
@@ -42549,13 +43329,13 @@ async function bundleIssueReport2(report, description, title, options = {}) {
42549
43329
  const truncated = bodyMarkdown !== rawBodyMarkdown;
42550
43330
  const cwd = options.cwd ?? process.cwd();
42551
43331
  const timestamp = formatTimestamp2(options.now ?? new Date);
42552
- const path = options.outputPath ?? join34(cwd, `magic-context-pi-issue-${timestamp}.md`);
42553
- mkdirSync10(dirname19(path), { recursive: true });
42554
- writeFileSync9(path, `${bodyMarkdown}
43332
+ const path = options.outputPath ?? join35(cwd, `magic-context-pi-issue-${timestamp}.md`);
43333
+ mkdirSync12(dirname21(path), { recursive: true });
43334
+ writeFileSync11(path, `${bodyMarkdown}
42555
43335
  `);
42556
- const fullPath = truncated ? options.outputPath ? `${options.outputPath}.full.md` : join34(cwd, `magic-context-pi-issue-${timestamp}-full.md`) : undefined;
43336
+ const fullPath = truncated ? options.outputPath ? `${options.outputPath}.full.md` : join35(cwd, `magic-context-pi-issue-${timestamp}-full.md`) : undefined;
42557
43337
  if (fullPath)
42558
- writeFileSync9(fullPath, `${rawBodyMarkdown}
43338
+ writeFileSync11(fullPath, `${rawBodyMarkdown}
42559
43339
  `);
42560
43340
  return { path, bodyMarkdown, ...fullPath ? { fullPath } : {}, truncated };
42561
43341
  }
@@ -42566,11 +43346,11 @@ var init_logs_pi = __esm(() => {
42566
43346
  });
42567
43347
 
42568
43348
  // src/commands/doctor-pi.ts
42569
- import { execFileSync as execFileSync6, spawnSync as spawnSync9 } from "node:child_process";
42570
- import { existsSync as existsSync40, mkdirSync as mkdirSync11, readdirSync as readdirSync8, readFileSync as readFileSync28, rmSync as rmSync6, statSync as statSync17 } from "node:fs";
43349
+ import { execFileSync as execFileSync7, spawnSync as spawnSync10 } from "node:child_process";
43350
+ import { existsSync as existsSync42, mkdirSync as mkdirSync13, readdirSync as readdirSync9, readFileSync as readFileSync30, rmSync as rmSync6, statSync as statSync17 } from "node:fs";
42571
43351
  import { createRequire as createRequire8 } from "node:module";
42572
- import { homedir as homedir17 } from "node:os";
42573
- import { dirname as dirname20, isAbsolute as isAbsolute8, join as join35 } from "node:path";
43352
+ import { homedir as homedir18 } from "node:os";
43353
+ import { dirname as dirname22, isAbsolute as isAbsolute8, join as join36 } from "node:path";
42574
43354
  function depsFrom(options) {
42575
43355
  return {
42576
43356
  ...DEFAULT_DEPS5,
@@ -42607,7 +43387,8 @@ function selfVersion2() {
42607
43387
  }
42608
43388
  function getLatestNpmVersion(packageName) {
42609
43389
  try {
42610
- return execFileSync6("npm", ["view", packageName, "version"], {
43390
+ return execFileSync7("npm", ["view", packageName, "version"], {
43391
+ windowsHide: true,
42611
43392
  encoding: "utf-8",
42612
43393
  stdio: ["ignore", "pipe", "ignore"],
42613
43394
  timeout: 1e4
@@ -42661,7 +43442,7 @@ function summarize(results) {
42661
43442
  function readJsonc2(path) {
42662
43443
  try {
42663
43444
  return {
42664
- value: import_comment_json13.parse(readFileSync28(path, "utf-8"))
43445
+ value: import_comment_json13.parse(readFileSync30(path, "utf-8"))
42665
43446
  };
42666
43447
  } catch (error) {
42667
43448
  return {
@@ -42711,15 +43492,15 @@ function piPluginDirCandidates(packages, cwd) {
42711
43492
  if (resolved)
42712
43493
  dirs.push(resolved);
42713
43494
  }
42714
- dirs.push(join35(agentDir, "npm", "node_modules", PACKAGE_NAME2));
42715
- dirs.push(join35(cwd, ".pi", "npm", "node_modules", PACKAGE_NAME2));
42716
- return dirs.filter((dir) => existsSync40(join35(dir, "package.json")));
43495
+ dirs.push(join36(agentDir, "npm", "node_modules", PACKAGE_NAME2));
43496
+ dirs.push(join36(cwd, ".pi", "npm", "node_modules", PACKAGE_NAME2));
43497
+ return dirs.filter((dir) => existsSync42(join36(dir, "package.json")));
42717
43498
  }
42718
43499
  function projectConfigPath(cwd) {
42719
43500
  return resolveCortexKitProjectConfigPath(cwd);
42720
43501
  }
42721
43502
  function readConfigForEmbedding(path, isProjectConfig) {
42722
- if (!existsSync40(path))
43503
+ if (!existsSync42(path))
42723
43504
  return null;
42724
43505
  try {
42725
43506
  const raw = loadRawConfigFile({
@@ -42805,22 +43586,22 @@ function pinnedVersionFromPackageSpecifier(specifier) {
42805
43586
  }
42806
43587
  function cacheRoots(cwd) {
42807
43588
  const home = process.env.HOME?.trim();
42808
- const cacheHome = process.env.XDG_CACHE_HOME || join35(home || homedir17(), ".cache");
43589
+ const cacheHome = process.env.XDG_CACHE_HOME || join36(home || homedir18(), ".cache");
42809
43590
  const piCacheRoot = getPiCacheRoot();
42810
43591
  return [
42811
- join35(piCacheRoot, "extensions"),
42812
- join35(piCacheRoot, "packages"),
42813
- join35(cacheHome, "pi", "extensions"),
42814
- join35(cacheHome, "pi", "packages"),
42815
- join35(getPiAgentConfigDir(), "cache", "extensions"),
42816
- join35(getPiAgentConfigDir(), "npm", "node_modules", PACKAGE_NAME2),
42817
- join35(cwd, ".pi", "npm", "node_modules", PACKAGE_NAME2)
43592
+ join36(piCacheRoot, "extensions"),
43593
+ join36(piCacheRoot, "packages"),
43594
+ join36(cacheHome, "pi", "extensions"),
43595
+ join36(cacheHome, "pi", "packages"),
43596
+ join36(getPiAgentConfigDir(), "cache", "extensions"),
43597
+ join36(getPiAgentConfigDir(), "npm", "node_modules", PACKAGE_NAME2),
43598
+ join36(cwd, ".pi", "npm", "node_modules", PACKAGE_NAME2)
42818
43599
  ];
42819
43600
  }
42820
43601
  function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
42821
43602
  const found = new Map;
42822
43603
  const visit = (path, depth) => {
42823
- if (depth < 0 || !existsSync40(path))
43604
+ if (depth < 0 || !existsSync42(path))
42824
43605
  return;
42825
43606
  let stat;
42826
43607
  try {
@@ -42830,10 +43611,10 @@ function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
42830
43611
  }
42831
43612
  if (!stat.isDirectory())
42832
43613
  return;
42833
- const packageJson = join35(path, "package.json");
42834
- if (existsSync40(packageJson)) {
43614
+ const packageJson = join36(path, "package.json");
43615
+ if (existsSync42(packageJson)) {
42835
43616
  try {
42836
- const pkg = JSON.parse(readFileSync28(packageJson, "utf-8"));
43617
+ const pkg = JSON.parse(readFileSync30(packageJson, "utf-8"));
42837
43618
  const name = typeof pkg.name === "string" ? pkg.name : "";
42838
43619
  const version = typeof pkg.version === "string" ? pkg.version : undefined;
42839
43620
  if (name === PACKAGE_NAME2) {
@@ -42848,12 +43629,12 @@ function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
42848
43629
  }
42849
43630
  let entries = [];
42850
43631
  try {
42851
- entries = readdirSync8(path);
43632
+ entries = readdirSync9(path);
42852
43633
  } catch {
42853
43634
  return;
42854
43635
  }
42855
43636
  for (const entry of entries)
42856
- visit(join35(path, entry), depth - 1);
43637
+ visit(join36(path, entry), depth - 1);
42857
43638
  };
42858
43639
  for (const root of cacheRoots(cwd))
42859
43640
  visit(root, 5);
@@ -42898,7 +43679,7 @@ async function runHealthChecks2(options) {
42898
43679
  }
42899
43680
  const settingsPath = getPiUserExtensionsPath();
42900
43681
  let packages = [];
42901
- if (!existsSync40(settingsPath)) {
43682
+ if (!existsSync42(settingsPath)) {
42902
43683
  add2(results, "fail", `Pi settings not found at ${settingsPath}`);
42903
43684
  repairPlan.addPackageEntry = true;
42904
43685
  } else {
@@ -42922,7 +43703,7 @@ async function runHealthChecks2(options) {
42922
43703
  ["user", userConfigPath, true],
42923
43704
  ["project", projectPath, false]
42924
43705
  ]) {
42925
- if (!existsSync40(path)) {
43706
+ if (!existsSync42(path)) {
42926
43707
  if (required) {
42927
43708
  add2(results, "warn", `No ${label} magic-context.jsonc found at ${path}`);
42928
43709
  if (userConfigMigrationRefused) {
@@ -42962,9 +43743,9 @@ async function runHealthChecks2(options) {
42962
43743
  }
42963
43744
  const storage = getMagicContextStorageResolution();
42964
43745
  const storageDir = storage.path;
42965
- const dbPath = join35(storageDir, "context.db");
43746
+ const dbPath = join36(storageDir, "context.db");
42966
43747
  add2(results, "info", `Shared storage: ${storageDir} (source: ${storage.source})`);
42967
- const existedBeforeOpen = existsSync40(dbPath);
43748
+ const existedBeforeOpen = existsSync42(dbPath);
42968
43749
  if (existedBeforeOpen)
42969
43750
  add2(results, "pass", `Shared context DB exists at ${dbPath}`);
42970
43751
  else
@@ -43182,7 +43963,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
43182
43963
  return { results, repairPlan, ...summarize(results) };
43183
43964
  }
43184
43965
  function writeDefaultMagicContextConfig(path) {
43185
- mkdirSync11(dirname20(path), { recursive: true });
43966
+ mkdirSync13(dirname22(path), { recursive: true });
43186
43967
  const config = {
43187
43968
  $schema: "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json",
43188
43969
  ...MagicContextConfigSchema.parse({})
@@ -43204,7 +43985,7 @@ function repair2(plan, prompts) {
43204
43985
  }
43205
43986
  if (plan.writeUserConfig) {
43206
43987
  const configPath = getPiUserConfigPath();
43207
- if (!existsSync40(configPath)) {
43988
+ if (!existsSync42(configPath)) {
43208
43989
  try {
43209
43990
  writeDefaultMagicContextConfig(configPath);
43210
43991
  prompts.log.success(`Wrote default Magic Context config to ${configPath}`);
@@ -43228,6 +44009,7 @@ function repair2(plan, prompts) {
43228
44009
  function runGhCommandWithDeps2(deps, args) {
43229
44010
  if (args[0] === "issue") {
43230
44011
  const result = deps.spawnSync("gh", args, {
44012
+ windowsHide: true,
43231
44013
  encoding: "utf-8",
43232
44014
  stdio: ["ignore", "pipe", "pipe"]
43233
44015
  });
@@ -43239,6 +44021,7 @@ function runGhCommandWithDeps2(deps, args) {
43239
44021
  }
43240
44022
  try {
43241
44023
  const output = deps.execFileSync("gh", args, {
44024
+ windowsHide: true,
43242
44025
  encoding: "utf-8",
43243
44026
  stdio: ["ignore", "pipe", "pipe"]
43244
44027
  });
@@ -43256,7 +44039,7 @@ async function runIssueFlow3(options) {
43256
44039
  if (options.reportPath) {
43257
44040
  try {
43258
44041
  const report = await options.deps.collectDiagnostics(options.cwd);
43259
- const outputPath = isAbsolute8(options.reportPath) ? options.reportPath : join35(options.cwd, options.reportPath);
44042
+ const outputPath = isAbsolute8(options.reportPath) ? options.reportPath : join36(options.cwd, options.reportPath);
43260
44043
  const bundled = await bundleIssueReport2(report, "Generated non-interactively by `doctor --issue --report`.", "Magic Context diagnostic report", { cwd: options.cwd, now: options.deps.now(), outputPath });
43261
44044
  options.prompts.log.info(`Report written to ${bundled.path}`);
43262
44045
  return 0;
@@ -43340,7 +44123,7 @@ async function runDoctor3(options = {}) {
43340
44123
  const v22Result = await runV22BackfillCommands({
43341
44124
  name: "Pi",
43342
44125
  openDatabase: (readonly = true) => {
43343
- const dbPath = join35(getMagicContextStorageDir(), "context.db");
44126
+ const dbPath = join36(getMagicContextStorageDir(), "context.db");
43344
44127
  v22Db = readonly ? openExistingContextDatabase(dbPath, { readonly: true }) : openExistingContextDatabaseForMutation(dbPath);
43345
44128
  return v22Db;
43346
44129
  },
@@ -43382,7 +44165,7 @@ async function runDoctor3(options = {}) {
43382
44165
  prompts.outro(first.fail > 0 ? "Doctor found failures" : "Doctor complete");
43383
44166
  return first.fail > 0 ? 1 : 0;
43384
44167
  }
43385
- function valueAfter3(args, flag) {
44168
+ function valueAfter2(args, flag) {
43386
44169
  const index = args.indexOf(flag);
43387
44170
  if (index === -1)
43388
44171
  return null;
@@ -43392,8 +44175,8 @@ function valueAfter3(args, flag) {
43392
44175
  return next;
43393
44176
  }
43394
44177
  function parseDoctorArgs(args) {
43395
- const rekeyV22DirIdentity = valueAfter3(args, "--rekey-v22-dir-identity");
43396
- const report = valueAfter3(args, "--report");
44178
+ const rekeyV22DirIdentity = valueAfter2(args, "--rekey-v22-dir-identity");
44179
+ const report = valueAfter2(args, "--report");
43397
44180
  return {
43398
44181
  force: args.includes("--force"),
43399
44182
  issue: args.includes("--issue") || report !== null,
@@ -43450,14 +44233,14 @@ var init_doctor_pi = __esm(async () => {
43450
44233
  probeEmbeddingEndpoint,
43451
44234
  openExistingContextDatabase,
43452
44235
  now: () => new Date,
43453
- execFileSync: execFileSync6,
43454
- spawnSync: spawnSync9
44236
+ execFileSync: execFileSync7,
44237
+ spawnSync: spawnSync10
43455
44238
  };
43456
44239
  });
43457
44240
 
43458
44241
  // src/commands/doctor.ts
43459
- import { existsSync as existsSync41, rmSync as rmSync7 } from "node:fs";
43460
- import { join as join36 } from "node:path";
44242
+ import { existsSync as existsSync43, rmSync as rmSync7 } from "node:fs";
44243
+ import { join as join37 } from "node:path";
43461
44244
  async function runDoctor4(options) {
43462
44245
  if (options.clear)
43463
44246
  return runClear();
@@ -43475,7 +44258,7 @@ async function runDoctor4(options) {
43475
44258
  const result = await runV22BackfillCommands({
43476
44259
  name: "Magic Context",
43477
44260
  openDatabase: (readonly = true) => {
43478
- const dbPath = join36(getMagicContextStorageDir(), "context.db");
44261
+ const dbPath = join37(getMagicContextStorageDir(), "context.db");
43479
44262
  v22Db = readonly ? openExistingContextDatabase(dbPath, { readonly: true }) : openExistingContextDatabaseForMutation(dbPath);
43480
44263
  return v22Db;
43481
44264
  },
@@ -43488,8 +44271,14 @@ async function runDoctor4(options) {
43488
44271
  if (result.handled)
43489
44272
  return result.exitCode;
43490
44273
  }
44274
+ try {
44275
+ for (const line of diagnoseIdentitySplits())
44276
+ log2.warn(line);
44277
+ } catch (error) {
44278
+ log2.warn(`Identity split check unavailable: ${error instanceof Error ? error.message : String(error)}`);
44279
+ }
43491
44280
  await init_migrate();
43492
- const sweepDbPath = join36(getMagicContextStorageDir(), "context.db");
44281
+ const sweepDbPath = join37(getMagicContextStorageDir(), "context.db");
43493
44282
  let sweepDb = null;
43494
44283
  try {
43495
44284
  sweepDb = openExistingContextDatabaseForMutation(sweepDbPath);
@@ -43603,7 +44392,7 @@ async function runClear() {
43603
44392
  failed += 1;
43604
44393
  continue;
43605
44394
  }
43606
- } else if (existsSync41(item.path)) {
44395
+ } else if (existsSync43(item.path)) {
43607
44396
  rmSync7(item.path, { recursive: true, force: true });
43608
44397
  }
43609
44398
  s.stop(`Cleared ${item.path}`);
@@ -43633,6 +44422,7 @@ var init_doctor = __esm(async () => {
43633
44422
  await __promiseAll([
43634
44423
  init_database_access(),
43635
44424
  init_v22_backfill_commands(),
44425
+ init_doctor_identity_splits(),
43636
44426
  init_doctor_omp(),
43637
44427
  init_doctor_opencode(),
43638
44428
  init_doctor_pi()
@@ -43694,7 +44484,7 @@ var DOCTOR_HELP = [
43694
44484
  " drain-authority <project> Drain module memory/note authority back to TypeScript",
43695
44485
  " migrate Migrate an OpenCode session to Pi or OMP JSONL",
43696
44486
  " migrate-session Re-home an OpenCode session to another directory",
43697
- " merge-identity Merge project rows between identities",
44487
+ " merge-identities Preview or explicitly merge split identities",
43698
44488
  " repair-db Back up and salvage a corrupted shared database",
43699
44489
  " list-hidden-sessions List Magic Context OpenCode 2 roots",
43700
44490
  ""
@@ -43710,16 +44500,19 @@ var DRAIN_AUTHORITY_HELP = [
43710
44500
  `);
43711
44501
  var MERGE_IDENTITY_HELP = [
43712
44502
  "",
43713
- " Usage: magic-context doctor merge-identity --from <identity> --to <identity> [--dry-run] [--yes] [--db <path>]",
44503
+ " Usage: magic-context doctor merge-identities [--from <identity> --to <identity>] [--apply] [--force] [--db <path>]",
43714
44504
  "",
43715
44505
  " Merge every project-scoped row from one project identity into another.",
43716
44506
  "",
43717
44507
  " Options:",
43718
44508
  " --from <identity> Identity whose rows are moved",
43719
44509
  " --to <identity> Identity that receives them",
43720
- " --dry-run Report what would change without writing",
43721
- " --yes Confirm the write (required unless --dry-run)",
44510
+ " (no flags) List splits; --from/--to previews without writing",
44511
+ " --apply Confirm the reviewed preview; hosts must be closed",
44512
+ " --force Permit a target not resolving on this machine",
43722
44513
  " --db <path> Use this context.db instead of the shared one",
44514
+ " --host-db <path> Read session directories from this host database",
44515
+ " Legacy alias: doctor merge-identity; --dry-run previews, --yes applies",
43723
44516
  ""
43724
44517
  ].join(`
43725
44518
  `);
@@ -43743,7 +44536,7 @@ function subcommandHelp(argv) {
43743
44536
  const subcommand = rest[0];
43744
44537
  if (subcommand === "drain-authority")
43745
44538
  return DRAIN_AUTHORITY_HELP;
43746
- if (subcommand === "merge-identity")
44539
+ if (subcommand === "merge-identity" || subcommand === "merge-identities")
43747
44540
  return MERGE_IDENTITY_HELP;
43748
44541
  if (subcommand === "list-hidden-sessions")
43749
44542
  return LIST_HIDDEN_SESSIONS_HELP;
@@ -43792,7 +44585,7 @@ function getVersion() {
43792
44585
  }
43793
44586
  return "0.0.0";
43794
44587
  }
43795
- function valueAfter4(args, flag) {
44588
+ function valueAfter3(args, flag) {
43796
44589
  const index = args.indexOf(flag);
43797
44590
  if (index === -1)
43798
44591
  return null;
@@ -43877,7 +44670,7 @@ async function main(argv = process.argv.slice(2)) {
43877
44670
  ]);
43878
44671
  return runDoctorDrainAuthority(projectRoot, join(getMagicContextStorageDir(), "context.db"));
43879
44672
  }
43880
- if (rest[0] === "merge-identity") {
44673
+ if (rest[0] === "merge-identities" || rest[0] === "merge-identity") {
43881
44674
  await init_doctor_merge_identity();
43882
44675
  return runMergeIdentityCli(rest.slice(1));
43883
44676
  }
@@ -43898,8 +44691,8 @@ async function main(argv = process.argv.slice(2)) {
43898
44691
  return runMigrateSessionCli(rest.slice(1));
43899
44692
  }
43900
44693
  await init_doctor();
43901
- const rekeyV22DirIdentity = valueAfter4(rest, "--rekey-v22-dir-identity");
43902
- const report = valueAfter4(rest, "--report");
44694
+ const rekeyV22DirIdentity = valueAfter3(rest, "--rekey-v22-dir-identity");
44695
+ const report = valueAfter3(rest, "--report");
43903
44696
  return runDoctor4({
43904
44697
  force: rest.includes("--force"),
43905
44698
  fix: rest.includes("--fix"),