@cortexkit/magic-context 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +966 -613
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -29680,7 +29680,7 @@ function expandConfigPath(value) {
29680
29680
  return `${homedir7()}/${trimmed.slice(2)}`;
29681
29681
  return trimmed;
29682
29682
  }
29683
- 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, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", PiThinkingLevelSchema, PiConfigSchema, PromptSurfacePresetSchema, PromptSurfaceModelKeySchema, PromptSurfaceToolKeySchema, PromptSurfaceConfigSchema, PER_HARNESS_MIGRATION_INVENTORY, OcEntryObjectSchema, OcEntrySchema, PiEntryObjectSchema, PiEntrySchema, OpenCodeHarnessBlockSchema, PiHarnessBlockSchema, OpenCodeTaskExecutionSchema, PiTaskExecutionSchema, DreamerOpenCodeHarnessBlockSchema, DreamerPiHarnessBlockSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, DEFAULT_TASK_SCHEDULES2, DreamTasksSchema, AgentMetadataSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, EmbeddingFallbackProviderSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
29683
+ 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, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", PiThinkingLevelSchema, PiConfigSchema, PromptSurfacePresetSchema, PromptSurfaceModelKeySchema, PromptSurfaceToolKeySchema, PromptSurfaceConfigSchema, PER_HARNESS_MIGRATION_INVENTORY, OcEntryObjectSchema, OcEntrySchema, PiEntryObjectSchema, PiEntrySchema, OpenCodeHarnessBlockSchema, PiHarnessBlockSchema, OpenCodeTaskExecutionSchema, PiTaskExecutionSchema, DreamerOpenCodeHarnessBlockSchema, DreamerPiHarnessBlockSchema, ProfileOpenCodeModelBlockSchema, ProfilePiModelBlockSchema, ProfileHistorianSchema, ProfileDreamerSchema, ProfileSidekickSchema, ConfigProfileSchema, ConfigProfilesSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, DEFAULT_TASK_SCHEDULES2, DreamTasksSchema, AgentMetadataSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, EmbeddingFallbackProviderSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
29684
29684
  var init_magic_context = __esm(() => {
29685
29685
  init_zod();
29686
29686
  init_language_directive();
@@ -29799,6 +29799,37 @@ var init_magic_context = __esm(() => {
29799
29799
  thinking_level: PiThinkingLevelSchema.describe("Pi thinking level for the primary entry when it declares none. Fallback entries declare thinking levels per-entry."),
29800
29800
  tasks: exports_external.record(exports_external.string(), PiTaskExecutionSchema).optional().describe("Pi task execution overrides. Each named task accepts only model, fallback_models, thinking_level, and timeout_minutes.")
29801
29801
  }).strict().describe("Strict Pi dreamer model-resolution block. It accepts no OpenCode vocabulary.");
29802
+ ProfileOpenCodeModelBlockSchema = exports_external.object({
29803
+ model: OcEntrySchema.optional().describe("Primary OpenCode model entry."),
29804
+ fallback_models: exports_external.array(OcEntrySchema).optional().describe("Ordered fallback OpenCode model entries."),
29805
+ variant: exports_external.string().optional().describe("OpenCode reasoning variant for the primary model entry.")
29806
+ }).strict().describe("Strict profile-only OpenCode model-selection block.");
29807
+ ProfilePiModelBlockSchema = exports_external.object({
29808
+ model: PiEntrySchema.optional().describe("Primary Pi model entry."),
29809
+ fallback_models: exports_external.array(PiEntrySchema).optional().describe("Ordered fallback Pi model entries."),
29810
+ thinking_level: PiThinkingLevelSchema.describe("Pi thinking level for the primary model entry.")
29811
+ }).strict().describe("Strict profile-only Pi model-selection block.");
29812
+ ProfileHistorianSchema = exports_external.object({
29813
+ opencode: ProfileOpenCodeModelBlockSchema.optional(),
29814
+ pi: ProfilePiModelBlockSchema.optional()
29815
+ }).strict();
29816
+ ProfileDreamerSchema = exports_external.object({
29817
+ opencode: ProfileOpenCodeModelBlockSchema.optional(),
29818
+ pi: ProfilePiModelBlockSchema.optional()
29819
+ }).strict();
29820
+ ProfileSidekickSchema = AgentOverrideConfigSchema.pick({
29821
+ model: true,
29822
+ fallback_models: true,
29823
+ variant: true
29824
+ }).extend({
29825
+ thinking_level: PiThinkingLevelSchema.describe("Pi thinking level for the sidekick model selection.")
29826
+ }).strict();
29827
+ ConfigProfileSchema = exports_external.object({
29828
+ historian: ProfileHistorianSchema.optional(),
29829
+ dreamer: ProfileDreamerSchema.optional(),
29830
+ sidekick: ProfileSidekickSchema.optional()
29831
+ }).strict().describe("User-owned model-selection overlay. Only historian/dreamer harness model blocks and sidekick model-selection fields are allowed.");
29832
+ ConfigProfilesSchema = exports_external.record(exports_external.string().trim().min(1, "Profile names must not be empty or whitespace-only."), ConfigProfileSchema);
29802
29833
  CronScheduleSchema = exports_external.string().refine((s) => s.trim() === "" || isValidCron(s), {
29803
29834
  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.'
29804
29835
  }).describe('5-field cron schedule (e.g. "0 3 * * *"), or "" to disable this task.');
@@ -29973,6 +30004,8 @@ var init_magic_context = __esm(() => {
29973
30004
  transform_mode: exports_external.enum(["ts", "rust"]).default("ts").describe('Experimental: routes the entire Magic Context runtime for the project through the ck-mc Rust module over subc (requires user-level `subc` config); "ts" is the current TypeScript pipeline.'),
29974
30005
  auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
29975
30006
  language: exports_external.string().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian, dreamer, sidekick, and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
30007
+ profile: exports_external.string().trim().min(1).optional().describe("Select a named user-owned model profile. A valid project name overrides this user default; an empty string, null, or other non-string project value is ignored with a warning so the user selection still applies. Unknown names warn and use the base configuration."),
30008
+ profiles: ConfigProfilesSchema.optional().describe("User-level named model profiles. A profile may contain only historian/dreamer model, fallback_models, OpenCode variant, and Pi thinking_level fields plus sidekick model-selection fields; task execution policy (including timeout_minutes) is excluded. Project configs may select a name but cannot define profiles."),
29976
30009
  historian: HistorianConfigSchema.describe("Historian metadata plus independent strict OpenCode and Pi execution blocks. Retained metadata stays at historian; model, fallback_models, variant, and thinking_level belong only in historian.opencode or historian.pi."),
29977
30010
  dreamer: DreamerConfigSchema.optional().describe("Dreamer metadata and scheduling plus independent strict OpenCode and Pi execution blocks. schedule and promotion_threshold stay at dreamer.tasks; model, fallback_models, variant, thinking_level, and timeout_minutes belong only in the matching harness block."),
29978
30011
  smart_notes: exports_external.object({
@@ -30075,6 +30108,72 @@ var init_magic_context = __esm(() => {
30075
30108
  });
30076
30109
  });
30077
30110
 
30111
+ // ../plugin/src/config/profiles.ts
30112
+ function withoutProfileFields(raw) {
30113
+ const copy = { ...raw };
30114
+ delete copy.profile;
30115
+ delete copy.profiles;
30116
+ return copy;
30117
+ }
30118
+ function readProfileSelection(raw) {
30119
+ if (!Object.hasOwn(raw, "profile"))
30120
+ return { declared: false };
30121
+ const value = raw.profile;
30122
+ if (typeof value !== "string")
30123
+ return { declared: true };
30124
+ const name = value.trim();
30125
+ return name.length > 0 ? { declared: true, name } : { declared: true };
30126
+ }
30127
+ function resolveConfigProfile(args) {
30128
+ const warnings = [];
30129
+ const userSelection = readProfileSelection(args.userRaw);
30130
+ const projectSelection = readProfileSelection(args.projectRaw);
30131
+ const selection = projectSelection.name ? { name: projectSelection.name, source: "project" } : userSelection.name ? { name: userSelection.name, source: "user" } : undefined;
30132
+ if (projectSelection.declared && !projectSelection.name) {
30133
+ warnings.push("Ignoring invalid profile selection from project config; expected a non-empty string.");
30134
+ }
30135
+ if (!projectSelection.declared && userSelection.declared && !userSelection.name) {
30136
+ warnings.push("Ignoring invalid profile selection from user config; expected a non-empty string.");
30137
+ }
30138
+ let profiles = {};
30139
+ if (Object.hasOwn(args.userRaw, "profiles")) {
30140
+ const parsed = ConfigProfilesSchema.safeParse(args.userRaw.profiles);
30141
+ if (parsed.success) {
30142
+ profiles = parsed.data;
30143
+ } else {
30144
+ warnings.push("Ignoring profiles from user config: invalid profile configuration; profiles may contain only historian/dreamer harness model blocks and sidekick model-selection fields.");
30145
+ }
30146
+ }
30147
+ if (!selection) {
30148
+ return {
30149
+ userBase: withoutProfileFields(args.userRaw),
30150
+ projectBase: withoutProfileFields(args.projectRaw),
30151
+ overlay: {},
30152
+ warnings
30153
+ };
30154
+ }
30155
+ if (!Object.hasOwn(profiles, selection.name)) {
30156
+ warnings.push(`Unknown profile "${selection.name}" selected by ${selection.source} config; using base config without a profile.`);
30157
+ return {
30158
+ userBase: withoutProfileFields(args.userRaw),
30159
+ projectBase: withoutProfileFields(args.projectRaw),
30160
+ overlay: {},
30161
+ warnings
30162
+ };
30163
+ }
30164
+ const overlay = profiles[selection.name];
30165
+ return {
30166
+ userBase: withoutProfileFields(args.userRaw),
30167
+ projectBase: withoutProfileFields(args.projectRaw),
30168
+ overlay,
30169
+ activeProfile: selection.name,
30170
+ warnings
30171
+ };
30172
+ }
30173
+ var init_profiles = __esm(() => {
30174
+ init_magic_context();
30175
+ });
30176
+
30078
30177
  // ../plugin/src/config/project-security.ts
30079
30178
  function isPlainObject2(value) {
30080
30179
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -30204,6 +30303,10 @@ function makeProjectThresholdWarning(field, reason) {
30204
30303
  }
30205
30304
  function stripUnsafeProjectConfigFields(projectRaw) {
30206
30305
  const warnings = [];
30306
+ if ("profiles" in projectRaw) {
30307
+ delete projectRaw.profiles;
30308
+ warnings.push("Ignoring profiles from project config (security: profile definitions are user-level only; a repository may select a named user profile with profile).");
30309
+ }
30207
30310
  if ("auto_update" in projectRaw) {
30208
30311
  delete projectRaw.auto_update;
30209
30312
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
@@ -30923,8 +31026,6 @@ function loadPluginConfigDetailed(directory) {
30923
31026
  const userLoaded = userDetected.format !== "none" ? loadConfigFileDetailed(userDetected.path, "user") : userLegacyFallback.source ? loadConfigFileDetailed(userLegacyFallback.source.path, "user") : null;
30924
31027
  const projectLoaded = projectDetected.format !== "none" ? loadConfigFileDetailed(projectDetected.path, "project") : projectLegacyFallback.source ? loadConfigFileDetailed(projectLegacyFallback.source.path, "project") : null;
30925
31028
  const allWarnings = [];
30926
- let mergedRaw = {};
30927
- const trustedBaseConfig = parsePluginConfig(userLoaded?.config ?? {});
30928
31029
  if (userLegacyFallback.source) {
30929
31030
  allWarnings.push(`[user config] reading legacy config from ${userLegacyFallback.source.path} until migration completes; run \`npx @cortexkit/magic-context doctor\` to consolidate into the shared CortexKit location.`);
30930
31031
  } else if (legacyUserUnmigrated) {
@@ -30937,21 +31038,31 @@ function loadPluginConfigDetailed(directory) {
30937
31038
  }
30938
31039
  if (userLoaded) {
30939
31040
  allWarnings.push(...userLoaded.warnings.map((w) => `[user config] ${w}`));
30940
- mergedRaw = deepMergeRawConfig(mergedRaw, userLoaded.config);
30941
31041
  }
31042
+ let projectRaw = {};
30942
31043
  if (projectLoaded) {
30943
31044
  allWarnings.push(...projectLoaded.warnings.map((w) => `[project config] ${w}`));
30944
- const projectRaw = { ...projectLoaded.config };
31045
+ projectRaw = { ...projectLoaded.config };
30945
31046
  for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
30946
31047
  allWarnings.push(`[project config] ${warning}`);
30947
31048
  }
30948
- mergedRaw = deepMergeRawConfig(mergedRaw, projectRaw);
30949
- for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userLoaded?.config)) {
31049
+ }
31050
+ const profileResolution = resolveConfigProfile({
31051
+ userRaw: userLoaded?.config ?? {},
31052
+ projectRaw
31053
+ });
31054
+ allWarnings.push(...profileResolution.warnings.map((warning) => `[config] ${warning}`));
31055
+ const trustedProfiledRaw = deepMergeRawConfig(profileResolution.userBase, profileResolution.overlay);
31056
+ let mergedRaw = trustedProfiledRaw;
31057
+ const trustedBaseConfig = parsePluginConfig(trustedProfiledRaw);
31058
+ if (projectLoaded) {
31059
+ mergedRaw = deepMergeRawConfig(mergedRaw, profileResolution.projectBase);
31060
+ for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, profileResolution.userBase)) {
30950
31061
  allWarnings.push(`[project config] ${warning}`);
30951
31062
  }
30952
31063
  for (const warning of constrainProjectThresholdOverrides({
30953
31064
  mergedRaw,
30954
- projectRaw,
31065
+ projectRaw: profileResolution.projectBase,
30955
31066
  trustedBaseConfig
30956
31067
  })) {
30957
31068
  allWarnings.push(`[project config] ${warning}`);
@@ -30959,6 +31070,8 @@ function loadPluginConfigDetailed(directory) {
30959
31070
  }
30960
31071
  const recoveredTopLevelKeys = [];
30961
31072
  const config2 = parsePluginConfig(mergedRaw, recoveredTopLevelKeys);
31073
+ if (profileResolution.activeProfile)
31074
+ config2.profile = profileResolution.activeProfile;
30962
31075
  setOutputReserveConfig(config2.output_reserve);
30963
31076
  setWindowOverlayPath(config2.models?.window_overlay_path);
30964
31077
  if (config2.configWarnings?.length) {
@@ -31006,6 +31119,7 @@ var init_config = __esm(() => {
31006
31119
  init_agent_disable();
31007
31120
  init_migrate_config_location();
31008
31121
  init_migrate_dreamer_v2();
31122
+ init_profiles();
31009
31123
  init_project_security();
31010
31124
  init_raw_loader();
31011
31125
  init_magic_context();
@@ -35166,6 +35280,17 @@ function readPsProcessStartTime(pid) {
35166
35280
  return null;
35167
35281
  }
35168
35282
  }
35283
+ function readProcessStartTime(pid) {
35284
+ if (!Number.isInteger(pid) || pid <= 0)
35285
+ return null;
35286
+ return rpcIdentityPlatform === "linux" ? readLinuxProcessStartTime(pid) : rpcIdentityPlatform === "win32" ? null : readPsProcessStartTime(pid);
35287
+ }
35288
+ function readProcessProbeEvidence(pid) {
35289
+ return {
35290
+ startTime: readProcessStartTime(pid),
35291
+ commandLine: readProcessCommand(pid)
35292
+ };
35293
+ }
35169
35294
  function parseCsvLine(line) {
35170
35295
  const fields = [];
35171
35296
  let field = "";
@@ -35298,16 +35423,16 @@ function commandLooksLikeOpenCode(command) {
35298
35423
  const normalized = command.toLowerCase();
35299
35424
  return OPEN_CODE_COMMAND_MARKERS.some((marker) => normalized.includes(marker));
35300
35425
  }
35301
- function isPidIdentityPlausible(record2) {
35426
+ function isPidIdentityPlausible(record2, evidence) {
35302
35427
  if (!Number.isInteger(record2.pid) || record2.pid <= 0)
35303
35428
  return "implausible";
35304
35429
  if (Number.isFinite(record2.started_at) && record2.started_at > 0) {
35305
- const processStartTime = rpcIdentityPlatform === "linux" ? readLinuxProcessStartTime(record2.pid) : rpcIdentityPlatform === "win32" ? null : readPsProcessStartTime(record2.pid);
35430
+ const processStartTime = evidence ? evidence.startTime : readProcessStartTime(record2.pid);
35306
35431
  if (processStartTime === null)
35307
35432
  return "inconclusive";
35308
35433
  return processStartTime <= record2.started_at + RPC_IDENTITY_SKEW_TOLERANCE_MS ? "plausible" : "implausible";
35309
35434
  }
35310
- const command = rpcIdentityPlatform === "linux" ? readLinuxProcessCommand(record2.pid) : rpcIdentityPlatform === "win32" ? readWindowsProcess(record2.pid).command ?? null : readPsProcessCommand(record2.pid);
35435
+ const command = evidence ? evidence.commandLine : rpcIdentityPlatform === "linux" ? readLinuxProcessCommand(record2.pid) : rpcIdentityPlatform === "win32" ? readWindowsProcess(record2.pid).command ?? null : readPsProcessCommand(record2.pid);
35311
35436
  if (command === null)
35312
35437
  return "inconclusive";
35313
35438
  return commandLooksLikeOpenCode(command) ? "plausible" : "implausible";
@@ -35371,9 +35496,6 @@ function inspectLivePiProcesses() {
35371
35496
  };
35372
35497
  }
35373
35498
  }
35374
- function discoverLivePiProcessIds() {
35375
- return inspectLivePiProcesses().processIds;
35376
- }
35377
35499
  function parseRpcPortFile(content, fallbackPid = 0) {
35378
35500
  const trimmed = content.trim();
35379
35501
  if (!trimmed)
@@ -35417,177 +35539,500 @@ var init_rpc_utils = __esm(() => {
35417
35539
  rpcProcessListExecFileSync = execFileSync4;
35418
35540
  rpcIdentityPlatform = process.platform;
35419
35541
  });
35420
- // ../plugin/src/hooks/magic-context/compartment-parser.ts
35421
- function makeTierOpenRegex(n2) {
35422
- return new RegExp(`<p${n2}\\s*(/?)>`);
35542
+ // ../plugin/src/shared/redaction.ts
35543
+ import { homedir as homedir12, userInfo } from "node:os";
35544
+ function escapeRegex2(value) {
35545
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35423
35546
  }
35424
- function extractTier(inner, index) {
35425
- const openMatch = TIER_OPEN_REGEXES[index].exec(inner);
35426
- if (!openMatch)
35427
- return;
35428
- if (openMatch[1] === "/")
35429
- return "";
35430
- const rest = inner.slice(openMatch.index + openMatch[0].length);
35431
- const closeAt = rest.search(TIER_CLOSE_ANY_REGEX);
35432
- let body = closeAt === -1 ? rest : rest.slice(0, closeAt);
35433
- const openInside = body.search(TIER_OPEN_ANY_REGEX);
35434
- if (openInside !== -1)
35435
- body = body.slice(0, openInside);
35436
- return unescapeXml(body.trim());
35547
+ function redactionTypeForKey(key) {
35548
+ const normalized = key.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "_");
35549
+ const suffix = normalized.split(".").filter(Boolean).at(-1) ?? normalized;
35550
+ return suffix || "secret";
35437
35551
  }
35438
- function extractTiersFromInner(inner) {
35439
- return {
35440
- p1: extractTier(inner, 0),
35441
- p2: extractTier(inner, 1),
35442
- p3: extractTier(inner, 2),
35443
- p4: extractTier(inner, 3)
35444
- };
35552
+ function isNonSecretScalarValue(value) {
35553
+ const v = value.trim();
35554
+ if (v === "true" || v === "false" || v === "null" || v === "undefined")
35555
+ return true;
35556
+ return /^[+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(v);
35445
35557
  }
35446
- function parseCompartmentOutput(text3) {
35447
- const compartments = [];
35448
- const facts = [];
35449
- for (const match of text3.matchAll(COMPARTMENT_REGEX)) {
35450
- const attrs = match[1];
35451
- const inner = match[2];
35452
- const startMatch = attrs.match(ATTR_START_REGEX);
35453
- const endMatch = attrs.match(ATTR_END_REGEX);
35454
- const titleMatch = attrs.match(ATTR_TITLE_REGEX);
35455
- if (!startMatch || !endMatch || !titleMatch)
35456
- continue;
35457
- const startMessage = parseInt(startMatch[1], 10);
35458
- const endMessage = parseInt(endMatch[1], 10);
35459
- const title = unescapeXml(titleMatch[1]);
35460
- if (Number.isNaN(startMessage) || Number.isNaN(endMessage) || !title)
35461
- continue;
35462
- const episodeMatch = attrs.match(ATTR_EPISODE_REGEX);
35463
- const importanceMatch = attrs.match(ATTR_IMPORTANCE_REGEX);
35464
- const episodeType = episodeMatch ? unescapeXml(episodeMatch[1]) : undefined;
35465
- const importance = importanceMatch ? parseInt(importanceMatch[1], 10) : undefined;
35466
- const p1 = extractTier(inner, 0);
35467
- if (typeof p1 === "string" && p1.length > 0) {
35468
- const p2 = extractTier(inner, 1);
35469
- const p3 = extractTier(inner, 2);
35470
- const p4 = extractTier(inner, 3);
35471
- compartments.push({
35472
- startMessage,
35473
- endMessage,
35474
- title,
35475
- content: p1,
35476
- p1,
35477
- p2: typeof p2 === "string" ? p2 : p1,
35478
- p3: typeof p3 === "string" ? p3 : typeof p2 === "string" ? p2 : p1,
35479
- p4: typeof p4 === "string" ? p4 : "",
35480
- importance,
35481
- episodeType
35482
- });
35558
+ function isSecretKey(key) {
35559
+ const segments = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[._-]+/).filter(Boolean);
35560
+ if (segments.length === 0)
35561
+ return false;
35562
+ if (segments.length === 1) {
35563
+ const first = segments[0];
35564
+ return Boolean(first && SECRET_SEGMENT_PATTERN.test(first));
35565
+ }
35566
+ for (let i2 = 0;i2 < segments.length; i2++) {
35567
+ const seg = segments[i2];
35568
+ if (!seg || !SECRET_SEGMENT_PATTERN.test(seg))
35483
35569
  continue;
35570
+ let trailingOk = true;
35571
+ for (let j = i2 + 1;j < segments.length; j++) {
35572
+ const tail = segments[j];
35573
+ if (!tail)
35574
+ continue;
35575
+ if (TRAILING_DESCRIPTORS.has(tail))
35576
+ continue;
35577
+ if (SECRET_SEGMENT_PATTERN.test(tail))
35578
+ continue;
35579
+ trailingOk = false;
35580
+ break;
35484
35581
  }
35485
- const content = unescapeXml(inner.trim());
35486
- if (content) {
35487
- compartments.push({
35488
- startMessage,
35489
- endMessage,
35490
- title,
35491
- content,
35492
- importance,
35493
- episodeType
35494
- });
35495
- }
35496
- }
35497
- const factsBlockMatch = text3.match(FACTS_BLOCK_REGEX);
35498
- const factsScope = factsBlockMatch ? factsBlockMatch[1] : text3.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
35499
- for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
35500
- const category = categoryMatch[1];
35501
- const blockContent = categoryMatch[2];
35502
- for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
35503
- const content = unescapeXml(itemMatch[1].trim());
35504
- if (content) {
35505
- facts.push({ category, content });
35506
- }
35582
+ if (!trailingOk)
35583
+ continue;
35584
+ for (let k = i2 - 1;k >= 0; k--) {
35585
+ const lead = segments[k];
35586
+ if (lead && SECRET_QUALIFIERS.has(lead))
35587
+ return true;
35507
35588
  }
35508
35589
  }
35509
- const unprocessedMatch = text3.match(UNPROCESSED_REGEX);
35510
- const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
35511
- const userObservations = [];
35512
- const userObsMatch = text3.match(USER_OBSERVATIONS_REGEX);
35513
- if (userObsMatch) {
35514
- for (const itemMatch of userObsMatch[1].matchAll(USER_OBS_ITEM_REGEX)) {
35515
- const obs = unescapeXml(itemMatch[1].trim());
35516
- if (obs)
35517
- userObservations.push(obs);
35518
- }
35590
+ return false;
35591
+ }
35592
+ function sanitizePathString(value) {
35593
+ const home = homedir12();
35594
+ const username = userInfo().username;
35595
+ let sanitized = value;
35596
+ if (home) {
35597
+ sanitized = sanitized.replace(new RegExp(escapeRegex2(home), "g"), "~");
35519
35598
  }
35520
- const primerCandidates = [];
35521
- const primerMatch = text3.match(PRIMER_CANDIDATES_REGEX);
35522
- if (primerMatch) {
35523
- const block2 = primerMatch[1];
35524
- let sawElement = false;
35525
- for (const el of block2.matchAll(PRIMER_ELEMENT_REGEX)) {
35526
- sawElement = true;
35527
- const question = unescapeXml(el[2].trim());
35528
- if (question) {
35529
- primerCandidates.push({
35530
- question,
35531
- originCompartmentIndex: Number.parseInt(el[1], 10)
35532
- });
35533
- }
35534
- }
35535
- if (!sawElement) {
35536
- for (const itemMatch of block2.matchAll(PRIMER_ITEM_REGEX)) {
35537
- const question = unescapeXml(itemMatch[1].trim());
35538
- if (question)
35539
- primerCandidates.push({ question });
35540
- }
35541
- }
35599
+ sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
35600
+ sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
35601
+ sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
35602
+ if (username) {
35603
+ sanitized = sanitized.replace(new RegExp(escapeRegex2(username), "g"), "<USER>");
35542
35604
  }
35543
- const events = parseEvents(text3);
35544
- compartments.sort((a3, b) => a3.startMessage - b.startMessage);
35545
- return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
35605
+ return sanitized;
35546
35606
  }
35547
- function parseEvents(text3) {
35548
- const blockMatch = text3.match(EVENTS_BLOCK_REGEX);
35549
- if (!blockMatch)
35550
- return [];
35551
- const block2 = blockMatch[1];
35552
- const events = [];
35553
- for (const elMatch of block2.matchAll(EVENT_ELEMENT_REGEX)) {
35554
- const kind = elMatch[1];
35555
- const atRaw = parseInt(elMatch[2], 10);
35556
- const atCompartment = Number.isNaN(atRaw) ? null : atRaw;
35557
- const fields = {};
35558
- for (const fieldMatch of elMatch[3].matchAll(EVENT_FIELD_REGEX)) {
35559
- const name = fieldMatch[1];
35560
- const value = unescapeXml(fieldMatch[2].trim());
35561
- if (value)
35562
- fields[name] = value;
35607
+ function redactSecretText(value) {
35608
+ let redacted = value;
35609
+ for (const { pattern, replacement } of SECRET_TEXT_PATTERNS) {
35610
+ if (typeof replacement === "string") {
35611
+ redacted = redacted.replace(pattern, replacement);
35612
+ } else {
35613
+ redacted = redacted.replace(pattern, replacement);
35563
35614
  }
35564
- events.push({ kind, atCompartment, fields });
35565
35615
  }
35566
- return events;
35616
+ return redacted;
35567
35617
  }
35568
- function unescapeXml(s) {
35569
- return s.replace(/&amp;/g, "&").replace(/&apos;/g, "'").replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">");
35618
+ function sanitizeDiagnosticText(value) {
35619
+ return redactSecretText(sanitizePathString(value));
35570
35620
  }
35571
- var COMPARTMENT_REGEX, ATTR_START_REGEX, ATTR_END_REGEX, ATTR_TITLE_REGEX, ATTR_EPISODE_REGEX, ATTR_IMPORTANCE_REGEX, TIER_OPEN_REGEXES, TIER_CLOSE_ANY_REGEX, TIER_OPEN_ANY_REGEX, CATEGORY_BLOCK_REGEX, FACT_ITEM_REGEX, UNPROCESSED_REGEX, USER_OBSERVATIONS_REGEX, USER_OBS_ITEM_REGEX, PRIMER_CANDIDATES_REGEX, PRIMER_ELEMENT_REGEX, PRIMER_ITEM_REGEX, FACTS_BLOCK_REGEX, EVENTS_BLOCK_REGEX, EVENT_ELEMENT_REGEX, EVENT_FIELD_REGEX;
35572
- var init_compartment_parser = __esm(() => {
35573
- COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
35574
- ATTR_START_REGEX = /\bstart="(\d+)"/;
35575
- ATTR_END_REGEX = /\bend="(\d+)"/;
35576
- ATTR_TITLE_REGEX = /\btitle="([^"]*)"/;
35577
- ATTR_EPISODE_REGEX = /\bepisode_type="([^"]*)"/;
35578
- ATTR_IMPORTANCE_REGEX = /\bimportance="(\d+)"/;
35579
- TIER_OPEN_REGEXES = [
35580
- makeTierOpenRegex(1),
35581
- makeTierOpenRegex(2),
35582
- makeTierOpenRegex(3),
35583
- makeTierOpenRegex(4)
35584
- ];
35585
- TIER_CLOSE_ANY_REGEX = /<\/p\d/;
35586
- TIER_OPEN_ANY_REGEX = /<p\d/;
35587
- CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
35588
- FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
35589
- UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
35590
- USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
35621
+ function sanitizeConfigValue(value, keyPath = []) {
35622
+ if (value === null || typeof value === "number" || typeof value === "boolean")
35623
+ return value;
35624
+ const key = keyPath.at(-1) ?? "";
35625
+ if (key && isSecretKey(key)) {
35626
+ return `<REDACTED:${redactionTypeForKey(key)}>`;
35627
+ }
35628
+ if (typeof value === "string")
35629
+ return sanitizeDiagnosticText(value);
35630
+ if (Array.isArray(value)) {
35631
+ return value.map((entry, index) => sanitizeConfigValue(entry, [...keyPath, String(index)]));
35632
+ }
35633
+ if (value && typeof value === "object") {
35634
+ return Object.fromEntries(Object.entries(value).map(([entryKey, entry]) => [
35635
+ entryKey,
35636
+ sanitizeConfigValue(entry, [...keyPath, entryKey])
35637
+ ]));
35638
+ }
35639
+ return value;
35640
+ }
35641
+ var SECRET_WORDS, SECRET_SEGMENT_PATTERN, TRAILING_DESCRIPTORS, SECRET_QUALIFIERS, SECRET_TEXT_PATTERNS;
35642
+ var init_redaction = __esm(() => {
35643
+ SECRET_WORDS = [
35644
+ "key",
35645
+ "token",
35646
+ "secret",
35647
+ "password",
35648
+ "auth",
35649
+ "authorization",
35650
+ "bearer",
35651
+ "credential"
35652
+ ];
35653
+ SECRET_SEGMENT_PATTERN = new RegExp(`^(?:${SECRET_WORDS.map((w) => `${w}s?`).join("|")})$`, "i");
35654
+ TRAILING_DESCRIPTORS = new Set(["id", "ids", "value", "values", "header", "headers"]);
35655
+ SECRET_QUALIFIERS = new Set([
35656
+ "api",
35657
+ "access",
35658
+ "private",
35659
+ "client",
35660
+ "auth",
35661
+ "authorization",
35662
+ "secret",
35663
+ "bearer",
35664
+ "session",
35665
+ "refresh",
35666
+ "service",
35667
+ "x",
35668
+ "openai",
35669
+ "anthropic",
35670
+ "google",
35671
+ "github",
35672
+ "huggingface",
35673
+ "aws",
35674
+ "azure"
35675
+ ]);
35676
+ SECRET_TEXT_PATTERNS = [
35677
+ {
35678
+ pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
35679
+ replacement: "<ANTHROPIC_API_KEY_REDACTED>"
35680
+ },
35681
+ {
35682
+ pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/g,
35683
+ replacement: "<OPENAI_API_KEY_REDACTED>"
35684
+ },
35685
+ {
35686
+ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
35687
+ replacement: "<GITHUB_PAT_REDACTED>"
35688
+ },
35689
+ {
35690
+ pattern: /\b(?:gh[opsu]|ghr)_[A-Za-z0-9]{30,}/g,
35691
+ replacement: "<GITHUB_TOKEN_REDACTED>"
35692
+ },
35693
+ {
35694
+ pattern: /\bhf_[A-Za-z0-9]{30,}/g,
35695
+ replacement: "<HUGGINGFACE_TOKEN_REDACTED>"
35696
+ },
35697
+ {
35698
+ pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
35699
+ replacement: "<AWS_ACCESS_KEY_ID_REDACTED>"
35700
+ },
35701
+ {
35702
+ pattern: /\bxox[abprsuvc]-[A-Za-z0-9-]{10,}/g,
35703
+ replacement: "<SLACK_TOKEN_REDACTED>"
35704
+ },
35705
+ {
35706
+ pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g,
35707
+ replacement: "<GOOGLE_API_KEY_REDACTED>"
35708
+ },
35709
+ {
35710
+ pattern: /\b(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]{8,})/gi,
35711
+ replacement: (_full, prefix) => `${prefix}<REDACTED:bearer>`
35712
+ },
35713
+ {
35714
+ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
35715
+ replacement: "<JWT_REDACTED>"
35716
+ },
35717
+ {
35718
+ pattern: /(["'])([^"']*(?:key|token|secret|password|auth|bearer|credential)[^"']*)\1(\s*:\s*)(["'])([^"']*)\4/gi,
35719
+ replacement: (full, quote, key, separator, valueQuote, value) => isNonSecretScalarValue(value) ? full : `${quote}${key}${quote}${separator}${valueQuote}<REDACTED:${redactionTypeForKey(key)}>${valueQuote}`
35720
+ },
35721
+ {
35722
+ pattern: /\b([A-Za-z0-9_.-]*(?:key|token|secret|password|auth|bearer|credential)[A-Za-z0-9_.-]*)\s*=\s*([^\s'"`]+)/gi,
35723
+ replacement: (full, key, value) => isNonSecretScalarValue(value) ? full : `${key}=<REDACTED:${redactionTypeForKey(key)}>`
35724
+ }
35725
+ ];
35726
+ });
35727
+
35728
+ // ../plugin/src/features/magic-context/fail-closed-block.ts
35729
+ function attachFailClosedBlockingProcessEvidence(process3, evidence) {
35730
+ Object.defineProperties(process3, {
35731
+ startTime: { configurable: true, value: evidence.startTime },
35732
+ commandLine: { configurable: true, value: evidence.commandLine }
35733
+ });
35734
+ return process3;
35735
+ }
35736
+ function normalizeFailClosedProcessKind(process3) {
35737
+ switch (process3.kind) {
35738
+ case "OpenCode server":
35739
+ case "OpenCode instance (TUI/CLI)":
35740
+ case "Pi":
35741
+ case "process":
35742
+ return process3.kind;
35743
+ }
35744
+ switch (process3.harness?.trim().toLowerCase()) {
35745
+ case "opencode server":
35746
+ return "OpenCode server";
35747
+ case "opencode instance (tui/cli)":
35748
+ case "opencode instance":
35749
+ return "OpenCode instance (TUI/CLI)";
35750
+ case "pi":
35751
+ case "pi harness":
35752
+ case "omp":
35753
+ return "Pi";
35754
+ default:
35755
+ return "process";
35756
+ }
35757
+ }
35758
+ function formatFailClosedProcessStartTime(startTime) {
35759
+ if (!Number.isFinite(startTime) || startTime <= 0)
35760
+ return "unverified";
35761
+ try {
35762
+ const parts = new Intl.DateTimeFormat("en-US", {
35763
+ day: "numeric",
35764
+ hour: "2-digit",
35765
+ minute: "2-digit",
35766
+ month: "short",
35767
+ hour12: false
35768
+ }).formatToParts(new Date(startTime));
35769
+ const values = Object.fromEntries(parts.map(({ type, value }) => [type, value]));
35770
+ if (!values.month || !values.day || !values.hour || !values.minute)
35771
+ return "unverified";
35772
+ return `${values.month} ${values.day} ${values.hour}:${values.minute}`;
35773
+ } catch {
35774
+ return "unverified";
35775
+ }
35776
+ }
35777
+ function formatFailClosedCommandLine(commandLine) {
35778
+ if (typeof commandLine !== "string" || commandLine.trim().length === 0)
35779
+ return "unverified";
35780
+ try {
35781
+ const normalized = commandLine.replaceAll("\x00", " ").replace(/\s+/g, " ").trim();
35782
+ if (normalized.length === 0)
35783
+ return "unverified";
35784
+ const sanitized = sanitizeDiagnosticText(normalized).trim();
35785
+ if (sanitized.length === 0)
35786
+ return "unverified";
35787
+ return sanitized.length > MAX_FORMATTED_COMMAND_LINE_LENGTH ? `${sanitized.slice(0, MAX_FORMATTED_COMMAND_LINE_LENGTH - 3)}...` : sanitized;
35788
+ } catch {
35789
+ return "unverified";
35790
+ }
35791
+ }
35792
+ function formatFailClosedBlockingProcesses(processes) {
35793
+ const uniqueProcesses = new Map;
35794
+ for (const process3 of processes) {
35795
+ if (!Number.isInteger(process3.pid) || process3.pid <= 0)
35796
+ continue;
35797
+ const candidate = {
35798
+ kind: normalizeFailClosedProcessKind(process3),
35799
+ pid: process3.pid,
35800
+ startTime: process3.startTime,
35801
+ commandLine: process3.commandLine
35802
+ };
35803
+ const previous = uniqueProcesses.get(process3.pid);
35804
+ if (!previous || previous.kind === "process" && candidate.kind !== "process") {
35805
+ uniqueProcesses.set(process3.pid, candidate);
35806
+ } else {
35807
+ uniqueProcesses.set(process3.pid, {
35808
+ ...previous,
35809
+ startTime: previous.startTime ?? candidate.startTime,
35810
+ commandLine: previous.commandLine ?? candidate.commandLine
35811
+ });
35812
+ }
35813
+ }
35814
+ const entries = [...uniqueProcesses.values()];
35815
+ const visible = entries.slice(0, MAX_FORMATTED_BLOCKING_PROCESSES);
35816
+ const rendered = visible.map(({ kind, pid }) => `${kind} (PID ${pid})`);
35817
+ const omitted = entries.length - visible.length;
35818
+ if (omitted > 0)
35819
+ rendered.push(`${omitted} more blocking process(es)`);
35820
+ if (rendered.length === 0)
35821
+ return "a live process";
35822
+ const summary = rendered.length === 1 ? rendered[0] : `${rendered.slice(0, -1).join(", ")}, and ${rendered.at(-1)}`;
35823
+ const evidence = entries.map(({ kind, pid, startTime, commandLine }) => `- PID ${pid}: ${kind}, started ${formatFailClosedProcessStartTime(startTime)}, cmd: ${formatFailClosedCommandLine(commandLine)}`).join(`
35824
+ `);
35825
+ return `${summary}
35826
+ Blocking process evidence:
35827
+ ${evidence}`;
35828
+ }
35829
+ function formatFailClosedBlockingMessage(reason) {
35830
+ if (reason.kind === "migration_guard") {
35831
+ if (reason.unreadableFile) {
35832
+ const arm = reason.unreadableArm ?? "io";
35833
+ const recovery = arm === "io" ? `If none of these processes are running, it is safe to delete ${reason.unreadableFile} and retry.` : `The file may be a recent incomplete write; retry after the file is older than the ten-minute grace window, or stop the relevant process before deleting it.`;
35834
+ return [
35835
+ `Magic Context cannot migrate the shared database because RPC discovery file ${reason.unreadableFile} is uncertain (${arm} arm), so the absence of a live process cannot be proven.`,
35836
+ recovery,
35837
+ `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
35838
+ ].join(" ");
35839
+ }
35840
+ return [
35841
+ `Magic Context cannot migrate the shared database because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
35842
+ "Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
35843
+ `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
35844
+ ].join(" ");
35845
+ }
35846
+ if (reason.kind === "schema_fence") {
35847
+ return [
35848
+ `Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (upstream migration lane v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
35849
+ `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
35850
+ ].join(" ");
35851
+ }
35852
+ const cause = reason.cause.trim().length > 0 ? reason.cause.trim() : "unknown storage error";
35853
+ return [
35854
+ `Magic Context cannot operate: persistent storage failed (${cause}).`,
35855
+ "The plugin will not silently degrade to native compaction while enabled.",
35856
+ `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
35857
+ ].join(" ");
35858
+ }
35859
+ var FAIL_CLOSED_DOCTOR_COMMAND = "npx @cortexkit/magic-context@latest doctor", OPENCODE_INTERNAL_AGENT_NAMES, MAX_FORMATTED_BLOCKING_PROCESSES = 8, MAX_FORMATTED_COMMAND_LINE_LENGTH = 80;
35860
+ var init_fail_closed_block = __esm(() => {
35861
+ init_redaction();
35862
+ OPENCODE_INTERNAL_AGENT_NAMES = new Set(["title", "summary", "compaction"]);
35863
+ });
35864
+
35865
+ // ../plugin/src/hooks/magic-context/compartment-parser.ts
35866
+ function makeTierOpenRegex(n2) {
35867
+ return new RegExp(`<p${n2}\\s*(/?)>`);
35868
+ }
35869
+ function extractTier(inner, index) {
35870
+ const openMatch = TIER_OPEN_REGEXES[index].exec(inner);
35871
+ if (!openMatch)
35872
+ return;
35873
+ if (openMatch[1] === "/")
35874
+ return "";
35875
+ const rest = inner.slice(openMatch.index + openMatch[0].length);
35876
+ const closeAt = rest.search(TIER_CLOSE_ANY_REGEX);
35877
+ let body = closeAt === -1 ? rest : rest.slice(0, closeAt);
35878
+ const openInside = body.search(TIER_OPEN_ANY_REGEX);
35879
+ if (openInside !== -1)
35880
+ body = body.slice(0, openInside);
35881
+ return unescapeXml(body.trim());
35882
+ }
35883
+ function extractTiersFromInner(inner) {
35884
+ return {
35885
+ p1: extractTier(inner, 0),
35886
+ p2: extractTier(inner, 1),
35887
+ p3: extractTier(inner, 2),
35888
+ p4: extractTier(inner, 3)
35889
+ };
35890
+ }
35891
+ function parseCompartmentOutput(text3) {
35892
+ const compartments = [];
35893
+ const facts = [];
35894
+ for (const match of text3.matchAll(COMPARTMENT_REGEX)) {
35895
+ const attrs = match[1];
35896
+ const inner = match[2];
35897
+ const startMatch = attrs.match(ATTR_START_REGEX);
35898
+ const endMatch = attrs.match(ATTR_END_REGEX);
35899
+ const titleMatch = attrs.match(ATTR_TITLE_REGEX);
35900
+ if (!startMatch || !endMatch || !titleMatch)
35901
+ continue;
35902
+ const startMessage = parseInt(startMatch[1], 10);
35903
+ const endMessage = parseInt(endMatch[1], 10);
35904
+ const title = unescapeXml(titleMatch[1]);
35905
+ if (Number.isNaN(startMessage) || Number.isNaN(endMessage) || !title)
35906
+ continue;
35907
+ const episodeMatch = attrs.match(ATTR_EPISODE_REGEX);
35908
+ const importanceMatch = attrs.match(ATTR_IMPORTANCE_REGEX);
35909
+ const episodeType = episodeMatch ? unescapeXml(episodeMatch[1]) : undefined;
35910
+ const importance = importanceMatch ? parseInt(importanceMatch[1], 10) : undefined;
35911
+ const p1 = extractTier(inner, 0);
35912
+ if (typeof p1 === "string" && p1.length > 0) {
35913
+ const p2 = extractTier(inner, 1);
35914
+ const p3 = extractTier(inner, 2);
35915
+ const p4 = extractTier(inner, 3);
35916
+ compartments.push({
35917
+ startMessage,
35918
+ endMessage,
35919
+ title,
35920
+ content: p1,
35921
+ p1,
35922
+ p2: typeof p2 === "string" ? p2 : p1,
35923
+ p3: typeof p3 === "string" ? p3 : typeof p2 === "string" ? p2 : p1,
35924
+ p4: typeof p4 === "string" ? p4 : "",
35925
+ importance,
35926
+ episodeType
35927
+ });
35928
+ continue;
35929
+ }
35930
+ const content = unescapeXml(inner.trim());
35931
+ if (content) {
35932
+ compartments.push({
35933
+ startMessage,
35934
+ endMessage,
35935
+ title,
35936
+ content,
35937
+ importance,
35938
+ episodeType
35939
+ });
35940
+ }
35941
+ }
35942
+ const factsBlockMatch = text3.match(FACTS_BLOCK_REGEX);
35943
+ const factsScope = factsBlockMatch ? factsBlockMatch[1] : text3.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
35944
+ for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
35945
+ const category = categoryMatch[1];
35946
+ const blockContent = categoryMatch[2];
35947
+ for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
35948
+ const content = unescapeXml(itemMatch[1].trim());
35949
+ if (content) {
35950
+ facts.push({ category, content });
35951
+ }
35952
+ }
35953
+ }
35954
+ const unprocessedMatch = text3.match(UNPROCESSED_REGEX);
35955
+ const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
35956
+ const userObservations = [];
35957
+ const userObsMatch = text3.match(USER_OBSERVATIONS_REGEX);
35958
+ if (userObsMatch) {
35959
+ for (const itemMatch of userObsMatch[1].matchAll(USER_OBS_ITEM_REGEX)) {
35960
+ const obs = unescapeXml(itemMatch[1].trim());
35961
+ if (obs)
35962
+ userObservations.push(obs);
35963
+ }
35964
+ }
35965
+ const primerCandidates = [];
35966
+ const primerMatch = text3.match(PRIMER_CANDIDATES_REGEX);
35967
+ if (primerMatch) {
35968
+ const block2 = primerMatch[1];
35969
+ let sawElement = false;
35970
+ for (const el of block2.matchAll(PRIMER_ELEMENT_REGEX)) {
35971
+ sawElement = true;
35972
+ const question = unescapeXml(el[2].trim());
35973
+ if (question) {
35974
+ primerCandidates.push({
35975
+ question,
35976
+ originCompartmentIndex: Number.parseInt(el[1], 10)
35977
+ });
35978
+ }
35979
+ }
35980
+ if (!sawElement) {
35981
+ for (const itemMatch of block2.matchAll(PRIMER_ITEM_REGEX)) {
35982
+ const question = unescapeXml(itemMatch[1].trim());
35983
+ if (question)
35984
+ primerCandidates.push({ question });
35985
+ }
35986
+ }
35987
+ }
35988
+ const events = parseEvents(text3);
35989
+ compartments.sort((a3, b) => a3.startMessage - b.startMessage);
35990
+ return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
35991
+ }
35992
+ function parseEvents(text3) {
35993
+ const blockMatch = text3.match(EVENTS_BLOCK_REGEX);
35994
+ if (!blockMatch)
35995
+ return [];
35996
+ const block2 = blockMatch[1];
35997
+ const events = [];
35998
+ for (const elMatch of block2.matchAll(EVENT_ELEMENT_REGEX)) {
35999
+ const kind = elMatch[1];
36000
+ const atRaw = parseInt(elMatch[2], 10);
36001
+ const atCompartment = Number.isNaN(atRaw) ? null : atRaw;
36002
+ const fields = {};
36003
+ for (const fieldMatch of elMatch[3].matchAll(EVENT_FIELD_REGEX)) {
36004
+ const name = fieldMatch[1];
36005
+ const value = unescapeXml(fieldMatch[2].trim());
36006
+ if (value)
36007
+ fields[name] = value;
36008
+ }
36009
+ events.push({ kind, atCompartment, fields });
36010
+ }
36011
+ return events;
36012
+ }
36013
+ function unescapeXml(s) {
36014
+ return s.replace(/&amp;/g, "&").replace(/&apos;/g, "'").replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">");
36015
+ }
36016
+ var COMPARTMENT_REGEX, ATTR_START_REGEX, ATTR_END_REGEX, ATTR_TITLE_REGEX, ATTR_EPISODE_REGEX, ATTR_IMPORTANCE_REGEX, TIER_OPEN_REGEXES, TIER_CLOSE_ANY_REGEX, TIER_OPEN_ANY_REGEX, CATEGORY_BLOCK_REGEX, FACT_ITEM_REGEX, UNPROCESSED_REGEX, USER_OBSERVATIONS_REGEX, USER_OBS_ITEM_REGEX, PRIMER_CANDIDATES_REGEX, PRIMER_ELEMENT_REGEX, PRIMER_ITEM_REGEX, FACTS_BLOCK_REGEX, EVENTS_BLOCK_REGEX, EVENT_ELEMENT_REGEX, EVENT_FIELD_REGEX;
36017
+ var init_compartment_parser = __esm(() => {
36018
+ COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
36019
+ ATTR_START_REGEX = /\bstart="(\d+)"/;
36020
+ ATTR_END_REGEX = /\bend="(\d+)"/;
36021
+ ATTR_TITLE_REGEX = /\btitle="([^"]*)"/;
36022
+ ATTR_EPISODE_REGEX = /\bepisode_type="([^"]*)"/;
36023
+ ATTR_IMPORTANCE_REGEX = /\bimportance="(\d+)"/;
36024
+ TIER_OPEN_REGEXES = [
36025
+ makeTierOpenRegex(1),
36026
+ makeTierOpenRegex(2),
36027
+ makeTierOpenRegex(3),
36028
+ makeTierOpenRegex(4)
36029
+ ];
36030
+ TIER_CLOSE_ANY_REGEX = /<\/p\d/;
36031
+ TIER_OPEN_ANY_REGEX = /<p\d/;
36032
+ CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
36033
+ FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
36034
+ UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
36035
+ USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
35591
36036
  USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
35592
36037
  PRIMER_CANDIDATES_REGEX = /<primer_candidates>(.*?)<\/primer_candidates>/s;
35593
36038
  PRIMER_ELEMENT_REGEX = /<primer\s+at_compartment="(\d+)"\s*>(.*?)<\/primer>/gs;
@@ -38100,8 +38545,8 @@ function classifyDiscoveryRecordKind(record2) {
38100
38545
  }
38101
38546
  return null;
38102
38547
  }
38103
- function classifyRpcProcess(record2) {
38104
- return classifyDiscoveryRecordKind(record2) ?? classifyProcessKind(readProcessCommand(record2.pid));
38548
+ function classifyRpcProcess(record2, commandLine) {
38549
+ return classifyDiscoveryRecordKind(record2) ?? classifyProcessKind(commandLine === undefined ? readProcessProbeEvidence(record2.pid).commandLine : commandLine);
38105
38550
  }
38106
38551
  function classifyJunkDiscovery(portFile, raw, staleFiles) {
38107
38552
  let mtimeMs;
@@ -38178,18 +38623,23 @@ function inspectRpcServerDiscovery(storageDir) {
38178
38623
  continue;
38179
38624
  }
38180
38625
  const liveness = isPidAlive(record2.pid);
38181
- const identity = liveness === "dead" ? "implausible" : isPidIdentityPlausible(record2);
38182
- if (liveness === "alive" && identity === "plausible") {
38626
+ if (liveness === "dead") {
38627
+ staleFiles.push(portFile);
38628
+ continue;
38629
+ }
38630
+ const evidence = readProcessProbeEvidence(record2.pid);
38631
+ const identity = isPidIdentityPlausible(record2, evidence);
38632
+ if (identity === "plausible") {
38183
38633
  pids.add(record2.pid);
38184
- const detected = {
38185
- kind: classifyRpcProcess(record2),
38634
+ const detected = attachFailClosedBlockingProcessEvidence({
38635
+ kind: classifyRpcProcess(record2, evidence.commandLine),
38186
38636
  pid: record2.pid
38187
- };
38637
+ }, evidence);
38188
38638
  const previous = processByPid.get(record2.pid);
38189
38639
  if (!previous || previous.kind === "process" && detected.kind !== "process") {
38190
38640
  processByPid.set(record2.pid, detected);
38191
38641
  }
38192
- } else if (liveness === "dead" || identity === "implausible") {
38642
+ } else if (identity === "implausible") {
38193
38643
  staleFiles.push(portFile);
38194
38644
  } else {
38195
38645
  inconclusivePids.add(record2.pid);
@@ -38222,10 +38672,14 @@ function inspectRpcServerDiscovery(storageDir) {
38222
38672
  }
38223
38673
  return { state: "stale", serverPids: [], staleFiles };
38224
38674
  }
38675
+ function createPiBlockingProcess(pid) {
38676
+ return attachFailClosedBlockingProcessEvidence({ kind: "Pi", pid }, readProcessProbeEvidence(pid));
38677
+ }
38225
38678
  function getLiveMigrationBlockingProcesses(storageDir) {
38226
38679
  const discovery = inspectRpcServerDiscovery(storageDir);
38227
38680
  const openCode = discovery.state === "live" ? discovery.serverProcesses ?? [] : [];
38228
- const pi = discoverLivePiProcessIds().map((pid) => ({ kind: "Pi", pid }));
38681
+ const piDiscovery = inspectLivePiProcesses();
38682
+ const pi = piDiscovery.processIds.map(createPiBlockingProcess);
38229
38683
  return [...openCode, ...pi];
38230
38684
  }
38231
38685
  function applySqliteTuningPragmas(db) {
@@ -39359,6 +39813,7 @@ var init_storage_db = __esm(async () => {
39359
39813
  init_data_path();
39360
39814
  init_logger();
39361
39815
  init_rpc_utils();
39816
+ init_fail_closed_block();
39362
39817
  init_migrations();
39363
39818
  init_tool_definition_tokens();
39364
39819
  init_tool_owner_backfill();
@@ -40508,11 +40963,11 @@ var init_doctor_repair_db = __esm(async () => {
40508
40963
  });
40509
40964
 
40510
40965
  // src/lib/migration-paths.ts
40511
- import { homedir as homedir12 } from "node:os";
40966
+ import { homedir as homedir13 } from "node:os";
40512
40967
  import { join as join18 } from "node:path";
40513
40968
  function getOpenCodeDatabasePath() {
40514
40969
  const dataHome = process.env.XDG_DATA_HOME?.trim();
40515
- return join18(dataHome || join18(homedir12(), ".local", "share"), "opencode", "opencode.db");
40970
+ return join18(dataHome || join18(homedir13(), ".local", "share"), "opencode", "opencode.db");
40516
40971
  }
40517
40972
  function projectPathToPiSessionSlug(projectPath, platform = process.platform) {
40518
40973
  const separators = platform === "win32" ? /[:\\/]+/g : /\/+/g;
@@ -41487,7 +41942,7 @@ __export(exports_migrate_session, {
41487
41942
  runMigrateSessionCli: () => runMigrateSessionCli
41488
41943
  });
41489
41944
  import { existsSync as existsSync23, realpathSync as realpathSync4 } from "node:fs";
41490
- import { homedir as homedir13 } from "node:os";
41945
+ import { homedir as homedir14 } from "node:os";
41491
41946
  import path4, { join as join20 } from "node:path";
41492
41947
  function existingSessionColumns(db) {
41493
41948
  const rows = db.prepare("PRAGMA table_info(session)").all();
@@ -41783,7 +42238,7 @@ async function runMigrateSessionCli(args) {
41783
42238
  printMigrateSessionHelp();
41784
42239
  return 1;
41785
42240
  }
41786
- const expandedTo = toDir.startsWith("~") ? join20(homedir13(), toDir.slice(1).replace(/^[/\\]/, "")) : toDir;
42241
+ const expandedTo = toDir.startsWith("~") ? join20(homedir14(), toDir.slice(1).replace(/^[/\\]/, "")) : toDir;
41787
42242
  if (!existsSync23(expandedTo)) {
41788
42243
  console.error(`Target directory does not exist: ${expandedTo}`);
41789
42244
  return 1;
@@ -42370,38 +42825,45 @@ function loadPiConfig(opts = {}) {
42370
42825
  } else if (legacyProjectUnmigrated) {
42371
42826
  warnings.push("[project config] legacy Magic Context config exists but the shared CortexKit config is absent; embedding registration is paused until config migration completes.");
42372
42827
  }
42373
- let rawConfig = {};
42374
42828
  const mergeFiles = [...loadedFiles].sort((a3, b) => {
42375
42829
  if (a3.scope === b.scope)
42376
42830
  return 0;
42377
42831
  return a3.scope === "user" ? -1 : 1;
42378
42832
  });
42379
- const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
42380
- const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
42833
+ const userRaw = mergeFiles.find((f) => f.scope === "user")?.config ?? {};
42834
+ const projectLoaded = mergeFiles.find((f) => f.scope === "project");
42835
+ let projectRaw = {};
42381
42836
  for (const loaded of mergeFiles) {
42382
42837
  const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
42383
42838
  warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
42384
- if (loaded.scope === "project") {
42385
- const projectRaw = { ...loaded.config };
42386
- for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
42387
- warnings.push(`${prefix} ${warning}`);
42388
- }
42389
- rawConfig = mergeRawConfigs(rawConfig, projectRaw);
42390
- for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
42391
- warnings.push(`${prefix} ${warning}`);
42392
- }
42393
- for (const warning of constrainProjectThresholdOverrides({
42394
- mergedRaw: rawConfig,
42395
- projectRaw,
42396
- trustedBaseConfig
42397
- })) {
42398
- warnings.push(`${prefix} ${warning}`);
42399
- }
42400
- } else {
42401
- rawConfig = mergeRawConfigs(rawConfig, loaded.config);
42839
+ if (loaded.scope !== "project")
42840
+ continue;
42841
+ projectRaw = { ...loaded.config };
42842
+ for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
42843
+ warnings.push(`${prefix} ${warning}`);
42844
+ }
42845
+ }
42846
+ const profileResolution = resolveConfigProfile({ userRaw, projectRaw });
42847
+ warnings.push(...profileResolution.warnings.map((warning) => `[config] ${warning}`));
42848
+ const trustedProfiledRaw = mergeRawConfigs(profileResolution.userBase, profileResolution.overlay);
42849
+ let rawConfig = trustedProfiledRaw;
42850
+ const trustedBaseConfig = parsePiConfig(trustedProfiledRaw).config;
42851
+ if (projectLoaded) {
42852
+ rawConfig = mergeRawConfigs(rawConfig, profileResolution.projectBase);
42853
+ for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, profileResolution.userBase)) {
42854
+ warnings.push(`[project config] ${warning}`);
42855
+ }
42856
+ for (const warning of constrainProjectThresholdOverrides({
42857
+ mergedRaw: rawConfig,
42858
+ projectRaw: profileResolution.projectBase,
42859
+ trustedBaseConfig
42860
+ })) {
42861
+ warnings.push(`[project config] ${warning}`);
42402
42862
  }
42403
42863
  }
42404
42864
  const parsed = parsePiConfig(rawConfig);
42865
+ if (profileResolution.activeProfile)
42866
+ parsed.config.profile = profileResolution.activeProfile;
42405
42867
  setOutputReserveConfig(parsed.config.output_reserve);
42406
42868
  setWindowOverlayPath(parsed.config.models?.window_overlay_path);
42407
42869
  warnings.push(...parsed.warnings.map((warning) => `[merged config] ${warning}`));
@@ -42417,6 +42879,7 @@ var init_config2 = __esm(() => {
42417
42879
  init_migrate_config_location();
42418
42880
  init_agent_disable();
42419
42881
  init_migrate_dreamer_v2();
42882
+ init_profiles();
42420
42883
  init_project_security();
42421
42884
  init_raw_loader();
42422
42885
  init_magic_context();
@@ -42433,191 +42896,90 @@ function formatDatabaseRepairGuidance(dbPath) {
42433
42896
  }
42434
42897
  var DATABASE_REPAIR_COMMAND = "bunx @cortexkit/magic-context@latest doctor repair-db";
42435
42898
 
42436
- // ../plugin/src/shared/redaction.ts
42437
- import { homedir as homedir14, userInfo } from "node:os";
42438
- function escapeRegex2(value) {
42439
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
42440
- }
42441
- function redactionTypeForKey(key) {
42442
- const normalized = key.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "_");
42443
- const suffix = normalized.split(".").filter(Boolean).at(-1) ?? normalized;
42444
- return suffix || "secret";
42445
- }
42446
- function isNonSecretScalarValue(value) {
42447
- const v = value.trim();
42448
- if (v === "true" || v === "false" || v === "null" || v === "undefined")
42449
- return true;
42450
- return /^[+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(v);
42451
- }
42452
- function isSecretKey(key) {
42453
- const segments = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[._-]+/).filter(Boolean);
42454
- if (segments.length === 0)
42455
- return false;
42456
- if (segments.length === 1) {
42457
- const first = segments[0];
42458
- return Boolean(first && SECRET_SEGMENT_PATTERN.test(first));
42459
- }
42460
- for (let i2 = 0;i2 < segments.length; i2++) {
42461
- const seg = segments[i2];
42462
- if (!seg || !SECRET_SEGMENT_PATTERN.test(seg))
42463
- continue;
42464
- let trailingOk = true;
42465
- for (let j = i2 + 1;j < segments.length; j++) {
42466
- const tail = segments[j];
42467
- if (!tail)
42468
- continue;
42469
- if (TRAILING_DESCRIPTORS.has(tail))
42470
- continue;
42471
- if (SECRET_SEGMENT_PATTERN.test(tail))
42472
- continue;
42473
- trailingOk = false;
42474
- break;
42475
- }
42476
- if (!trailingOk)
42477
- continue;
42478
- for (let k = i2 - 1;k >= 0; k--) {
42479
- const lead = segments[k];
42480
- if (lead && SECRET_QUALIFIERS.has(lead))
42481
- return true;
42482
- }
42899
+ // src/lib/github-issue.ts
42900
+ import { spawnSync as spawnSync4 } from "node:child_process";
42901
+ import { readFileSync as readFileSync14 } from "node:fs";
42902
+ function runGhCommand(args) {
42903
+ try {
42904
+ const result = spawnSync4("gh", args, {
42905
+ encoding: "utf-8",
42906
+ stdio: ["ignore", "pipe", "pipe"]
42907
+ });
42908
+ return {
42909
+ status: result.status,
42910
+ stdout: String(result.stdout ?? ""),
42911
+ stderr: String(result.stderr ?? "")
42912
+ };
42913
+ } catch (error51) {
42914
+ return {
42915
+ status: null,
42916
+ stdout: "",
42917
+ stderr: error51 instanceof Error ? error51.message : String(error51)
42918
+ };
42483
42919
  }
42484
- return false;
42485
42920
  }
42486
- function sanitizePathString(value) {
42487
- const home = homedir14();
42488
- const username = userInfo().username;
42489
- let sanitized = value;
42490
- if (home) {
42491
- sanitized = sanitized.replace(new RegExp(escapeRegex2(home), "g"), "~");
42492
- }
42493
- sanitized = sanitized.replace(/\/Users\/[^/]+\//g, "/Users/<USER>/");
42494
- sanitized = sanitized.replace(/\/home\/[^/]+\//g, "/home/<USER>/");
42495
- sanitized = sanitized.replace(/C:\\Users\\[^\\]+\\/g, "C:\\Users\\<USER>\\");
42496
- if (username) {
42497
- sanitized = sanitized.replace(new RegExp(escapeRegex2(username), "g"), "<USER>");
42498
- }
42499
- return sanitized;
42921
+ function failed(result) {
42922
+ return result.status !== 0;
42500
42923
  }
42501
- function redactSecretText(value) {
42502
- let redacted = value;
42503
- for (const { pattern, replacement } of SECRET_TEXT_PATTERNS) {
42504
- if (typeof replacement === "string") {
42505
- redacted = redacted.replace(pattern, replacement);
42506
- } else {
42507
- redacted = redacted.replace(pattern, replacement);
42508
- }
42924
+ function submitGithubIssue(title, bodyPath, runner = runGhCommand) {
42925
+ let body;
42926
+ try {
42927
+ body = readFileSync14(bodyPath, "utf-8");
42928
+ } catch (error51) {
42929
+ return {
42930
+ ok: false,
42931
+ reason: "body-unreadable",
42932
+ message: `The diagnostics file could not be read (${error51 instanceof Error ? error51.message : String(error51)}).`
42933
+ };
42509
42934
  }
42510
- return redacted;
42511
- }
42512
- function sanitizeDiagnosticText(value) {
42513
- return redactSecretText(sanitizePathString(value));
42514
- }
42515
- function sanitizeConfigValue(value, keyPath = []) {
42516
- if (value === null || typeof value === "number" || typeof value === "boolean")
42517
- return value;
42518
- const key = keyPath.at(-1) ?? "";
42519
- if (key && isSecretKey(key)) {
42520
- return `<REDACTED:${redactionTypeForKey(key)}>`;
42935
+ if (Array.from(body).length > MAX_GITHUB_ISSUE_BODY_CHARS || Buffer.byteLength(body, "utf8") > MAX_GITHUB_ISSUE_BODY_CHARS) {
42936
+ return {
42937
+ ok: false,
42938
+ reason: "body-too-large",
42939
+ message: `The GitHub issue body is larger than GitHub's ${MAX_GITHUB_ISSUE_BODY_CHARS.toLocaleString()}-character limit.`
42940
+ };
42521
42941
  }
42522
- if (typeof value === "string")
42523
- return sanitizeDiagnosticText(value);
42524
- if (Array.isArray(value)) {
42525
- return value.map((entry, index) => sanitizeConfigValue(entry, [...keyPath, String(index)]));
42942
+ const installed = runner(["--version"]);
42943
+ if (failed(installed)) {
42944
+ return {
42945
+ ok: false,
42946
+ reason: "gh-not-installed",
42947
+ message: "The GitHub CLI (gh) is not installed or could not be started."
42948
+ };
42526
42949
  }
42527
- if (value && typeof value === "object") {
42528
- return Object.fromEntries(Object.entries(value).map(([entryKey, entry]) => [
42529
- entryKey,
42530
- sanitizeConfigValue(entry, [...keyPath, entryKey])
42531
- ]));
42950
+ const auth = runner(["auth", "status", "--hostname", "github.com"]);
42951
+ if (failed(auth)) {
42952
+ return {
42953
+ ok: false,
42954
+ reason: "not-authenticated",
42955
+ message: "gh is not authenticated. Run `gh auth login` before submitting an issue."
42956
+ };
42532
42957
  }
42533
- return value;
42534
- }
42535
- var SECRET_WORDS, SECRET_SEGMENT_PATTERN, TRAILING_DESCRIPTORS, SECRET_QUALIFIERS, SECRET_TEXT_PATTERNS;
42536
- var init_redaction = __esm(() => {
42537
- SECRET_WORDS = [
42538
- "key",
42539
- "token",
42540
- "secret",
42541
- "password",
42542
- "auth",
42543
- "authorization",
42544
- "bearer",
42545
- "credential"
42546
- ];
42547
- SECRET_SEGMENT_PATTERN = new RegExp(`^(?:${SECRET_WORDS.map((w) => `${w}s?`).join("|")})$`, "i");
42548
- TRAILING_DESCRIPTORS = new Set(["id", "ids", "value", "values", "header", "headers"]);
42549
- SECRET_QUALIFIERS = new Set([
42550
- "api",
42551
- "access",
42552
- "private",
42553
- "client",
42554
- "auth",
42555
- "authorization",
42556
- "secret",
42557
- "bearer",
42558
- "session",
42559
- "refresh",
42560
- "service",
42561
- "x",
42562
- "openai",
42563
- "anthropic",
42564
- "google",
42565
- "github",
42566
- "huggingface",
42567
- "aws",
42568
- "azure"
42958
+ const result = runner([
42959
+ "issue",
42960
+ "create",
42961
+ "-R",
42962
+ "cortexkit/magic-context",
42963
+ "--title",
42964
+ title,
42965
+ "--body-file",
42966
+ bodyPath
42569
42967
  ]);
42570
- SECRET_TEXT_PATTERNS = [
42571
- {
42572
- pattern: /\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{32,}/g,
42573
- replacement: "<ANTHROPIC_API_KEY_REDACTED>"
42574
- },
42575
- {
42576
- pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{32,}/g,
42577
- replacement: "<OPENAI_API_KEY_REDACTED>"
42578
- },
42579
- {
42580
- pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
42581
- replacement: "<GITHUB_PAT_REDACTED>"
42582
- },
42583
- {
42584
- pattern: /\b(?:gh[opsu]|ghr)_[A-Za-z0-9]{30,}/g,
42585
- replacement: "<GITHUB_TOKEN_REDACTED>"
42586
- },
42587
- {
42588
- pattern: /\bhf_[A-Za-z0-9]{30,}/g,
42589
- replacement: "<HUGGINGFACE_TOKEN_REDACTED>"
42590
- },
42591
- {
42592
- pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
42593
- replacement: "<AWS_ACCESS_KEY_ID_REDACTED>"
42594
- },
42595
- {
42596
- pattern: /\bxox[abprsuvc]-[A-Za-z0-9-]{10,}/g,
42597
- replacement: "<SLACK_TOKEN_REDACTED>"
42598
- },
42599
- {
42600
- pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g,
42601
- replacement: "<GOOGLE_API_KEY_REDACTED>"
42602
- },
42603
- {
42604
- pattern: /\b(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]{8,})/gi,
42605
- replacement: (_full, prefix) => `${prefix}<REDACTED:bearer>`
42606
- },
42607
- {
42608
- pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
42609
- replacement: "<JWT_REDACTED>"
42610
- },
42611
- {
42612
- pattern: /(["'])([^"']*(?:key|token|secret|password|auth|bearer|credential)[^"']*)\1(\s*:\s*)(["'])([^"']*)\4/gi,
42613
- replacement: (full, quote, key, separator, valueQuote, value) => isNonSecretScalarValue(value) ? full : `${quote}${key}${quote}${separator}${valueQuote}<REDACTED:${redactionTypeForKey(key)}>${valueQuote}`
42614
- },
42615
- {
42616
- pattern: /\b([A-Za-z0-9_.-]*(?:key|token|secret|password|auth|bearer|credential)[A-Za-z0-9_.-]*)\s*=\s*([^\s'"`]+)/gi,
42617
- replacement: (full, key, value) => isNonSecretScalarValue(value) ? full : `${key}=<REDACTED:${redactionTypeForKey(key)}>`
42618
- }
42619
- ];
42620
- });
42968
+ if (result.status === 0) {
42969
+ return { ok: true, output: result.stdout.trim() };
42970
+ }
42971
+ const details = result.stderr.trim() || result.stdout.trim();
42972
+ return {
42973
+ ok: false,
42974
+ reason: "create-failed",
42975
+ message: details ? `GitHub issue creation failed: ${details}` : "GitHub issue creation failed: gh returned a non-zero status."
42976
+ };
42977
+ }
42978
+ function formatGithubIssueFallback(submission, fallbackPath) {
42979
+ return `${submission.message} The sanitized diagnostics are saved at ${fallbackPath}; open https://github.com/cortexkit/magic-context/issues/new and drag that file into the issue before submitting manually.`;
42980
+ }
42981
+ var MAX_GITHUB_ISSUE_BODY_CHARS = 65536;
42982
+ var init_github_issue = () => {};
42621
42983
 
42622
42984
  // src/lib/redaction.ts
42623
42985
  function sanitizeDiagnosticEndpoint(value) {
@@ -42642,8 +43004,8 @@ var init_redaction2 = __esm(() => {
42642
43004
  });
42643
43005
 
42644
43006
  // src/commands/doctor-omp.ts
42645
- import { execFileSync as execFileSync5, spawnSync as spawnSync4 } from "node:child_process";
42646
- import { existsSync as existsSync25, mkdirSync as mkdirSync9, readFileSync as readFileSync14 } from "node:fs";
43007
+ import { execFileSync as execFileSync5, spawnSync as spawnSync5 } from "node:child_process";
43008
+ import { existsSync as existsSync25, mkdirSync as mkdirSync9, readFileSync as readFileSync15 } from "node:fs";
42647
43009
  import { createRequire as createRequire2 } from "node:module";
42648
43010
  import { dirname as dirname14, join as join21, resolve as resolve6 } from "node:path";
42649
43011
  function parseSemver(value) {
@@ -42690,7 +43052,7 @@ function readConfig2(path6) {
42690
43052
  if (!existsSync25(path6))
42691
43053
  return {};
42692
43054
  try {
42693
- const parsed = import_comment_json8.parse(readFileSync14(path6, "utf-8"));
43055
+ const parsed = import_comment_json8.parse(readFileSync15(path6, "utf-8"));
42694
43056
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? {} : { error: "top level is not an object" };
42695
43057
  } catch (error51) {
42696
43058
  return { error: error51 instanceof Error ? error51.message : String(error51) };
@@ -42700,7 +43062,7 @@ function pluginDeclaresOmp(path6) {
42700
43062
  if (!path6)
42701
43063
  return null;
42702
43064
  try {
42703
- const pkg = JSON.parse(readFileSync14(join21(path6, "package.json"), "utf-8"));
43065
+ const pkg = JSON.parse(readFileSync15(join21(path6, "package.json"), "utf-8"));
42704
43066
  return Array.isArray(pkg.omp?.extensions) || Array.isArray(pkg.pi?.extensions);
42705
43067
  } catch {
42706
43068
  return null;
@@ -42889,6 +43251,33 @@ async function repair(plan, deps, prompts, cwd) {
42889
43251
  function timestamp2(date5) {
42890
43252
  return date5.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
42891
43253
  }
43254
+ function runGhCommandWithDeps(deps, args) {
43255
+ if (args[0] === "issue") {
43256
+ const result = deps.spawnSync("gh", args, {
43257
+ encoding: "utf-8",
43258
+ stdio: ["ignore", "pipe", "pipe"]
43259
+ });
43260
+ return {
43261
+ status: result.status,
43262
+ stdout: String(result.stdout ?? ""),
43263
+ stderr: String(result.stderr ?? "")
43264
+ };
43265
+ }
43266
+ try {
43267
+ const output = deps.execFileSync("gh", args, {
43268
+ encoding: "utf-8",
43269
+ stdio: ["ignore", "pipe", "pipe"]
43270
+ });
43271
+ return { status: 0, stdout: String(output ?? ""), stderr: "" };
43272
+ } catch (error51) {
43273
+ const result = error51;
43274
+ return {
43275
+ status: typeof result.status === "number" ? result.status : 1,
43276
+ stdout: String(result.stdout ?? ""),
43277
+ stderr: String(result.stderr ?? "")
43278
+ };
43279
+ }
43280
+ }
42892
43281
  async function runIssueFlow(options) {
42893
43282
  const title = await options.prompts.text("Issue title", {
42894
43283
  placeholder: "Short summary of the OMP problem",
@@ -42912,34 +43301,15 @@ async function runIssueFlow(options) {
42912
43301
  writeFileAtomic(path6, `${body}
42913
43302
  `);
42914
43303
  options.prompts.log.success(`Sanitized report written to ${path6}`);
42915
- try {
42916
- options.deps.execFileSync("gh", ["--version"], { stdio: "ignore" });
42917
- } catch {
42918
- options.prompts.log.info("gh CLI unavailable; submit the generated report manually");
42919
- return 0;
42920
- }
42921
- try {
42922
- options.deps.execFileSync("gh", ["auth", "status"], { stdio: "ignore" });
42923
- } catch {
42924
- options.prompts.log.info("gh CLI is installed but not authenticated; submit the generated report manually");
42925
- return 0;
42926
- }
42927
43304
  if (await options.prompts.confirm("Submit this issue on GitHub now?", false)) {
42928
- const result = options.deps.spawnSync("gh", [
42929
- "issue",
42930
- "create",
42931
- "-R",
42932
- "cortexkit/magic-context",
42933
- "--title",
42934
- `[omp] ${title}`,
42935
- "--body-file",
42936
- path6
42937
- ], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
42938
- if (result.status === 0)
42939
- options.prompts.log.success(String(result.stdout).trim());
42940
- else
42941
- options.prompts.log.warn(String(result.stderr).trim());
43305
+ const result = submitGithubIssue(`[omp] ${title}`, path6, (args) => runGhCommandWithDeps(options.deps, args));
43306
+ if (result.ok) {
43307
+ options.prompts.log.success(result.output);
43308
+ } else {
43309
+ options.prompts.log.warn(formatGithubIssueFallback(result, path6));
43310
+ }
42942
43311
  }
43312
+ options.prompts.log.info(`Open https://github.com/cortexkit/magic-context/issues/new and drag ${path6} into the issue`);
42943
43313
  return 0;
42944
43314
  }
42945
43315
  async function runDoctor(options = {}) {
@@ -42979,6 +43349,7 @@ var init_doctor_omp = __esm(async () => {
42979
43349
  init_omp();
42980
43350
  init_atomic_write();
42981
43351
  init_config_location_migration();
43352
+ init_github_issue();
42982
43353
  init_omp_helpers();
42983
43354
  init_paths();
42984
43355
  init_prompts();
@@ -42995,7 +43366,7 @@ var init_doctor_omp = __esm(async () => {
42995
43366
  openExistingContextDatabase,
42996
43367
  now: () => new Date,
42997
43368
  execFileSync: execFileSync5,
42998
- spawnSync: spawnSync4
43369
+ spawnSync: spawnSync5
42999
43370
  };
43000
43371
  });
43001
43372
 
@@ -43115,7 +43486,7 @@ import {
43115
43486
  chmodSync as chmodSync4,
43116
43487
  existsSync as existsSync26,
43117
43488
  mkdirSync as mkdirSync10,
43118
- readFileSync as readFileSync15,
43489
+ readFileSync as readFileSync16,
43119
43490
  renameSync as renameSync6,
43120
43491
  statSync as statSync10,
43121
43492
  writeFileSync as writeFileSync7
@@ -43174,7 +43545,7 @@ function ensureTuiPluginEntry(options = {}) {
43174
43545
  const configPath = resolveTuiConfigPath(options.configDir);
43175
43546
  let config2 = {};
43176
43547
  if (existsSync26(configPath)) {
43177
- const raw = readFileSync15(configPath, "utf-8");
43548
+ const raw = readFileSync16(configPath, "utf-8");
43178
43549
  config2 = import_comment_json9.parse(raw) ?? {};
43179
43550
  }
43180
43551
  const plugins = Array.isArray(config2.plugin) ? [...config2.plugin] : [];
@@ -43226,7 +43597,7 @@ var init_tui_config = __esm(() => {
43226
43597
  });
43227
43598
 
43228
43599
  // src/lib/diagnostics-opencode.ts
43229
- import { existsSync as existsSync27, readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync11 } from "node:fs";
43600
+ import { existsSync as existsSync27, readdirSync as readdirSync3, readFileSync as readFileSync17, statSync as statSync11 } from "node:fs";
43230
43601
  import { createRequire as createRequire3 } from "node:module";
43231
43602
  import { homedir as homedir15 } from "node:os";
43232
43603
  import { join as join23 } from "node:path";
@@ -43248,7 +43619,7 @@ function getPluginCacheInfo() {
43248
43619
  for (const installedPkgPath of getOpenCodePluginPackageJsonPaths()) {
43249
43620
  try {
43250
43621
  if (existsSync27(installedPkgPath)) {
43251
- const pkg = JSON.parse(readFileSync16(installedPkgPath, "utf-8"));
43622
+ const pkg = JSON.parse(readFileSync17(installedPkgPath, "utf-8"));
43252
43623
  cached2 = typeof pkg.version === "string" ? pkg.version : undefined;
43253
43624
  if (cached2)
43254
43625
  break;
@@ -43280,7 +43651,7 @@ function readConfig3(path6) {
43280
43651
  if (!existsSync27(path6))
43281
43652
  return { value: null };
43282
43653
  try {
43283
- const raw = readFileSync16(path6, "utf-8");
43654
+ const raw = readFileSync17(path6, "utf-8");
43284
43655
  const value = import_comment_json10.parse(raw);
43285
43656
  return { value };
43286
43657
  } catch (error51) {
@@ -43305,7 +43676,7 @@ function configHasPluginEntry(config2) {
43305
43676
  }
43306
43677
  function parseHistorianDumpMeta(path6) {
43307
43678
  try {
43308
- const xml = readFileSync16(path6, "utf-8");
43679
+ const xml = readFileSync17(path6, "utf-8");
43309
43680
  const parsed = parseCompartmentOutput(xml);
43310
43681
  const factCountByCategory = {};
43311
43682
  for (const fact of parsed.facts) {
@@ -43405,6 +43776,62 @@ function collectHistorianDumps(recentSessions) {
43405
43776
  }
43406
43777
  };
43407
43778
  }
43779
+ function collectRecentSessionsFromDatabase(database) {
43780
+ const rows = database.prepare(`WITH active AS (
43781
+ SELECT id, directory, title, time_updated, parent_id
43782
+ FROM session
43783
+ WHERE time_archived IS NULL
43784
+ ),
43785
+ root_activity AS (
43786
+ SELECT p.id AS root_id,
43787
+ MAX(
43788
+ CASE
43789
+ WHEN c.time_updated > p.time_updated THEN c.time_updated
43790
+ ELSE p.time_updated
43791
+ END
43792
+ ) AS latest_activity
43793
+ FROM active p
43794
+ LEFT JOIN active c ON c.parent_id = p.id
43795
+ WHERE p.parent_id IS NULL
43796
+ GROUP BY p.id
43797
+ ),
43798
+ selected_roots AS (
43799
+ SELECT p.id, p.directory, p.title, p.time_updated, r.latest_activity
43800
+ FROM active p
43801
+ JOIN root_activity r ON r.root_id = p.id
43802
+ ORDER BY r.latest_activity DESC
43803
+ LIMIT ${RECENT_SESSION_GROUP_LIMIT}
43804
+ ),
43805
+ ranked_children AS (
43806
+ SELECT c.id, c.directory, c.title, c.time_updated, c.parent_id,
43807
+ r.latest_activity,
43808
+ ROW_NUMBER() OVER (
43809
+ PARTITION BY c.parent_id
43810
+ ORDER BY c.time_updated DESC
43811
+ ) AS child_rank
43812
+ FROM active c
43813
+ JOIN selected_roots r ON c.parent_id = r.id
43814
+ )
43815
+ SELECT id, directory, title, time_updated, NULL AS parent_id,
43816
+ latest_activity, 0 AS row_kind, 0 AS child_rank
43817
+ FROM selected_roots
43818
+ UNION ALL
43819
+ SELECT id, directory, title, time_updated, parent_id,
43820
+ latest_activity, 1 AS row_kind, child_rank
43821
+ FROM ranked_children
43822
+ WHERE child_rank <= ${CHILD_SESSIONS_PER_PARENT_LIMIT}
43823
+ ORDER BY latest_activity DESC, row_kind ASC, child_rank ASC, time_updated DESC`).all();
43824
+ return rows.flatMap((row) => {
43825
+ const sessionId = typeof row.id === "string" ? row.id : null;
43826
+ const directory = typeof row.directory === "string" ? row.directory : null;
43827
+ if (!sessionId || !directory)
43828
+ return [];
43829
+ const title = typeof row.title === "string" ? row.title : "";
43830
+ const lastActiveAt = typeof row.time_updated === "number" ? new Date(row.time_updated).toISOString() : "";
43831
+ const parentSessionId = typeof row.parent_id === "string" && row.parent_id.length > 0 ? row.parent_id : null;
43832
+ return [{ sessionId, title, directory, lastActiveAt, parentSessionId }];
43833
+ });
43834
+ }
43408
43835
  async function collectRecentSessions() {
43409
43836
  const dataHome = process.env.XDG_DATA_HOME || join23(process.env.HOME || homedir15(), ".local", "share");
43410
43837
  const opencodeDbPath = join23(dataHome, "opencode", "opencode.db");
@@ -43423,16 +43850,7 @@ async function collectRecentSessions() {
43423
43850
  let db = null;
43424
43851
  try {
43425
43852
  db = new DatabaseClass(opencodeDbPath, { readonly: true });
43426
- const rows = db.prepare("SELECT id, directory, title, time_updated FROM session " + "WHERE time_archived IS NULL AND parent_id IS NULL " + "ORDER BY time_updated DESC LIMIT 5").all();
43427
- return rows.flatMap((row) => {
43428
- const sessionId = typeof row.id === "string" ? row.id : null;
43429
- const directory = typeof row.directory === "string" ? row.directory : null;
43430
- if (!sessionId || !directory)
43431
- return [];
43432
- const title = typeof row.title === "string" ? row.title : "";
43433
- const lastActiveAt = typeof row.time_updated === "number" ? new Date(row.time_updated).toISOString() : "";
43434
- return [{ sessionId, title, directory, lastActiveAt }];
43435
- });
43853
+ return collectRecentSessionsFromDatabase(db);
43436
43854
  } catch {
43437
43855
  return [];
43438
43856
  } finally {
@@ -43658,7 +44076,8 @@ function renderDiagnosticsMarkdown(report) {
43658
44076
  sessionId: session.sessionId,
43659
44077
  title: sanitizeDiagnosticText(session.title),
43660
44078
  directory: sanitizeString(session.directory),
43661
- lastActiveAt: session.lastActiveAt
44079
+ lastActiveAt: session.lastActiveAt,
44080
+ parentSessionId: session.parentSessionId ?? null
43662
44081
  }));
43663
44082
  return [
43664
44083
  `- Timestamp: ${report.timestamp}`,
@@ -43730,7 +44149,7 @@ function renderDiagnosticsMarkdown(report) {
43730
44149
  ].join(`
43731
44150
  `);
43732
44151
  }
43733
- var import_comment_json10;
44152
+ var import_comment_json10, RECENT_SESSION_GROUP_LIMIT = 5, CHILD_SESSIONS_PER_PARENT_LIMIT = 3;
43734
44153
  var init_diagnostics_opencode = __esm(() => {
43735
44154
  init_config();
43736
44155
  init_agent_disable();
@@ -43746,7 +44165,7 @@ var init_diagnostics_opencode = __esm(() => {
43746
44165
  });
43747
44166
 
43748
44167
  // src/lib/embedding-runtime.ts
43749
- import { spawnSync as spawnSync5 } from "node:child_process";
44168
+ import { spawnSync as spawnSync6 } from "node:child_process";
43750
44169
  import { existsSync as existsSync28 } from "node:fs";
43751
44170
  import { createRequire as createRequire4 } from "node:module";
43752
44171
  import { dirname as dirname16, join as join24, sep } from "node:path";
@@ -43756,7 +44175,7 @@ function describeError(error51) {
43756
44175
  return typeof code === "string" && code.length > 0 ? `${code}: ${message}` : message;
43757
44176
  }
43758
44177
  function runOnnxRuntimeNodeLoadProbeChild(packageDir) {
43759
- return spawnSync5(process.execPath, ["-e", ONNX_RUNTIME_NODE_LOAD_PROBE_SCRIPT], {
44178
+ return spawnSync6(process.execPath, ["-e", ONNX_RUNTIME_NODE_LOAD_PROBE_SCRIPT], {
43760
44179
  encoding: "utf8",
43761
44180
  env: { ...process.env, [ONNX_LOAD_PROBE_PACKAGE_DIR_ENV]: packageDir },
43762
44181
  stdio: ["ignore", "pipe", "pipe"],
@@ -44064,7 +44483,7 @@ var init_issue_body = __esm(() => {
44064
44483
  });
44065
44484
 
44066
44485
  // src/lib/logs-opencode.ts
44067
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "node:fs";
44486
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "node:fs";
44068
44487
  import { join as join25 } from "node:path";
44069
44488
  function sanitizeLogContent(content) {
44070
44489
  return sanitizeDiagnosticText(content);
@@ -44107,7 +44526,7 @@ function filterLogLinesBySession(lines, sessionId) {
44107
44526
  }
44108
44527
  async function bundleIssueReport(report, description, title, sessionFilter = null) {
44109
44528
  const LOG_TAIL_LINES = 400;
44110
- const allLogLines = report.logFile.exists ? readFileSync17(report.logFile.path, "utf-8").split(/\r?\n/) : [];
44529
+ const allLogLines = report.logFile.exists ? readFileSync18(report.logFile.path, "utf-8").split(/\r?\n/) : [];
44111
44530
  const logLines = filterLogLinesBySession(allLogLines, sessionFilter);
44112
44531
  const recentLog = sanitizeLogContent(logLines.slice(-LOG_TAIL_LINES).join(`
44113
44532
  `)).trim();
@@ -44121,8 +44540,16 @@ async function bundleIssueReport(report, description, title, sessionFilter = nul
44121
44540
  const sanitizedConfigPath = sanitizeDiagnosticText(report.configPaths.magicContextConfig);
44122
44541
  const sanitizedDescription = sanitizeDiagnosticText(description);
44123
44542
  const sanitizedTitle = sanitizeDiagnosticText(title).trim();
44543
+ const selectedSession = sessionFilter ? report.recentSessions.find((session) => session.sessionId === sessionFilter) : undefined;
44544
+ const sessionContext = selectedSession ? [
44545
+ "## Session context",
44546
+ `- Selected session: ${sanitizeDiagnosticText(selectedSession.sessionId)}`,
44547
+ `- Parent session: ${sanitizeDiagnosticText(selectedSession.parentSessionId ?? "none")}`,
44548
+ ""
44549
+ ] : [];
44124
44550
  const rawBodyMarkdown = [
44125
44551
  ...sanitizedTitle ? ["## Title", sanitizedTitle, ""] : [],
44552
+ ...sessionContext,
44126
44553
  "## Description",
44127
44554
  sanitizedDescription,
44128
44555
  "",
@@ -44158,10 +44585,16 @@ async function bundleIssueReport(report, description, title, sessionFilter = nul
44158
44585
  ].join(`
44159
44586
  `);
44160
44587
  const bodyMarkdown = capBodyToGithubLimit(rawBodyMarkdown);
44161
- const path6 = join25(process.cwd(), `magic-context-issue-${formatTimestamp(new Date)}.md`);
44588
+ const truncated = bodyMarkdown !== rawBodyMarkdown;
44589
+ const timestamp3 = formatTimestamp(new Date);
44590
+ const path6 = join25(process.cwd(), `magic-context-issue-${timestamp3}.md`);
44162
44591
  writeFileSync8(path6, `${bodyMarkdown}
44163
44592
  `);
44164
- return { path: path6, bodyMarkdown };
44593
+ const fullPath = truncated ? join25(process.cwd(), `magic-context-issue-${timestamp3}-full.md`) : undefined;
44594
+ if (fullPath)
44595
+ writeFileSync8(fullPath, `${rawBodyMarkdown}
44596
+ `);
44597
+ return { path: path6, bodyMarkdown, ...fullPath ? { fullPath } : {}, truncated };
44165
44598
  }
44166
44599
  var HISTORIAN_LOG_PATTERNS;
44167
44600
  var init_logs_opencode = __esm(() => {
@@ -44465,7 +44898,7 @@ import {
44465
44898
  accessSync as accessSync2,
44466
44899
  existsSync as existsSync29,
44467
44900
  constants as fsConstants,
44468
- readFileSync as readFileSync18,
44901
+ readFileSync as readFileSync19,
44469
44902
  renameSync as renameSync7,
44470
44903
  statSync as statSync12,
44471
44904
  writeFileSync as writeFileSync9
@@ -44519,7 +44952,7 @@ function getWinningConfig(directory, surface) {
44519
44952
  try {
44520
44953
  if (!existsSync29(path6))
44521
44954
  continue;
44522
- const parsed = parseJsonConfig(readFileSync18(path6, "utf-8"));
44955
+ const parsed = parseJsonConfig(readFileSync19(path6, "utf-8"));
44523
44956
  const entries = getPluginEntries(parsed);
44524
44957
  if (entries.some((entry) => entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))) {
44525
44958
  if (winner) {
@@ -44555,14 +44988,14 @@ var init_checker = __esm(() => {
44555
44988
  });
44556
44989
 
44557
44990
  // src/lib/opencode-plugin-schema-fence.ts
44558
- import { existsSync as existsSync30, readdirSync as readdirSync4, readFileSync as readFileSync19 } from "node:fs";
44991
+ import { existsSync as existsSync30, readdirSync as readdirSync4, readFileSync as readFileSync20 } from "node:fs";
44559
44992
  import { join as join28 } from "node:path";
44560
44993
  import { gunzipSync } from "node:zlib";
44561
44994
  function readPackageVersion(packageJsonPath) {
44562
44995
  try {
44563
44996
  if (!existsSync30(packageJsonPath))
44564
44997
  return null;
44565
- const packageJson = JSON.parse(readFileSync19(packageJsonPath, "utf-8"));
44998
+ const packageJson = JSON.parse(readFileSync20(packageJsonPath, "utf-8"));
44566
44999
  if (packageJson.name !== OPENCODE_PLUGIN_NAME || typeof packageJson.version !== "string") {
44567
45000
  return null;
44568
45001
  }
@@ -44599,7 +45032,7 @@ function readFenceFromDist(packageDirectory) {
44599
45032
  }
44600
45033
  if (!entry.isFile() || !/\.(?:[cm]?js|ts)$/.test(entry.name))
44601
45034
  continue;
44602
- const fence = readFenceValue(readFileSync19(path6, "utf-8"));
45035
+ const fence = readFenceValue(readFileSync20(path6, "utf-8"));
44603
45036
  if (fence !== null)
44604
45037
  fenceValues.add(fence);
44605
45038
  }
@@ -44767,85 +45200,6 @@ var init_opencode_plugin_schema_fence = __esm(() => {
44767
45200
  EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
44768
45201
  });
44769
45202
 
44770
- // ../plugin/src/features/magic-context/fail-closed-block.ts
44771
- function normalizeFailClosedProcessKind(process3) {
44772
- switch (process3.kind) {
44773
- case "OpenCode server":
44774
- case "OpenCode instance (TUI/CLI)":
44775
- case "Pi":
44776
- case "process":
44777
- return process3.kind;
44778
- }
44779
- switch (process3.harness?.trim().toLowerCase()) {
44780
- case "opencode server":
44781
- return "OpenCode server";
44782
- case "opencode instance (tui/cli)":
44783
- case "opencode instance":
44784
- return "OpenCode instance (TUI/CLI)";
44785
- case "pi":
44786
- case "pi harness":
44787
- case "omp":
44788
- return "Pi";
44789
- default:
44790
- return "process";
44791
- }
44792
- }
44793
- function formatFailClosedBlockingProcesses(processes) {
44794
- const uniqueProcesses = new Map;
44795
- for (const process3 of processes) {
44796
- if (!Number.isInteger(process3.pid) || process3.pid <= 0)
44797
- continue;
44798
- const kind = normalizeFailClosedProcessKind(process3);
44799
- uniqueProcesses.set(`${kind}\x00${process3.pid}`, { kind, pid: process3.pid });
44800
- }
44801
- const entries = [...uniqueProcesses.values()];
44802
- const visible = entries.slice(0, MAX_FORMATTED_BLOCKING_PROCESSES);
44803
- const rendered = visible.map(({ kind, pid }) => `${kind} (PID ${pid})`);
44804
- const omitted = entries.length - visible.length;
44805
- if (omitted > 0)
44806
- rendered.push(`${omitted} more blocking process(es)`);
44807
- if (rendered.length === 0)
44808
- return "a live process";
44809
- if (rendered.length === 1)
44810
- return rendered[0];
44811
- const last = rendered.pop();
44812
- return `${rendered.join(", ")}, and ${last}`;
44813
- }
44814
- function formatFailClosedBlockingMessage(reason) {
44815
- if (reason.kind === "migration_guard") {
44816
- if (reason.unreadableFile) {
44817
- const arm = reason.unreadableArm ?? "io";
44818
- const recovery = arm === "io" ? `If none of these processes are running, it is safe to delete ${reason.unreadableFile} and retry.` : `The file may be a recent incomplete write; retry after the file is older than the ten-minute grace window, or stop the relevant process before deleting it.`;
44819
- return [
44820
- `Magic Context cannot migrate the shared database because RPC discovery file ${reason.unreadableFile} is uncertain (${arm} arm), so the absence of a live process cannot be proven.`,
44821
- recovery,
44822
- `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
44823
- ].join(" ");
44824
- }
44825
- return [
44826
- `Magic Context cannot migrate the shared database because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
44827
- "Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
44828
- `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
44829
- ].join(" ");
44830
- }
44831
- if (reason.kind === "schema_fence") {
44832
- return [
44833
- `Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (upstream migration lane v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
44834
- `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
44835
- ].join(" ");
44836
- }
44837
- const cause = reason.cause.trim().length > 0 ? reason.cause.trim() : "unknown storage error";
44838
- return [
44839
- `Magic Context cannot operate: persistent storage failed (${cause}).`,
44840
- "The plugin will not silently degrade to native compaction while enabled.",
44841
- `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
44842
- ].join(" ");
44843
- }
44844
- var FAIL_CLOSED_DOCTOR_COMMAND = "npx @cortexkit/magic-context@latest doctor", OPENCODE_INTERNAL_AGENT_NAMES, MAX_FORMATTED_BLOCKING_PROCESSES = 8;
44845
- var init_fail_closed_block = __esm(() => {
44846
- OPENCODE_INTERNAL_AGENT_NAMES = new Set(["title", "summary", "compaction"]);
44847
- });
44848
-
44849
45203
  // src/lib/storage-versions.ts
44850
45204
  function checkStorageVersionFence(versions2, options = {}) {
44851
45205
  const {
@@ -44896,13 +45250,13 @@ var init_storage_versions = __esm(async () => {
44896
45250
  });
44897
45251
 
44898
45252
  // src/commands/doctor-opencode-cache.ts
44899
- import { existsSync as existsSync31, readFileSync as readFileSync20, rmSync as rmSync3 } from "node:fs";
45253
+ import { existsSync as existsSync31, readFileSync as readFileSync21, rmSync as rmSync3 } from "node:fs";
44900
45254
  function readCachedPluginVersion(pluginCacheDir) {
44901
45255
  try {
44902
45256
  const installedPkgPath = getOpenCodePluginPackageJsonPath(pluginCacheDir);
44903
45257
  if (!existsSync31(installedPkgPath))
44904
45258
  return;
44905
- const pkg = JSON.parse(readFileSync20(installedPkgPath, "utf-8"));
45259
+ const pkg = JSON.parse(readFileSync21(installedPkgPath, "utf-8"));
44906
45260
  return typeof pkg.version === "string" ? pkg.version : undefined;
44907
45261
  } catch {
44908
45262
  return;
@@ -44941,26 +45295,26 @@ async function clearPluginCache(options = {}, deps = {}) {
44941
45295
  };
44942
45296
  }
44943
45297
  const cleared = [];
44944
- const failed = [];
45298
+ const failed2 = [];
44945
45299
  for (const entry of clearTargets) {
44946
45300
  try {
44947
45301
  remove(entry.path);
44948
45302
  cleared.push(entry);
44949
45303
  } catch (err) {
44950
- failed.push({
45304
+ failed2.push({
44951
45305
  path: entry.path,
44952
45306
  error: err instanceof Error ? err.message : String(err)
44953
45307
  });
44954
45308
  }
44955
45309
  }
44956
- if (failed.length > 0) {
44957
- const firstFailure = failed[0];
45310
+ if (failed2.length > 0) {
45311
+ const firstFailure = failed2[0];
44958
45312
  return {
44959
45313
  action: "error",
44960
45314
  path: firstFailure?.path ?? clearTargets[0]?.path ?? existingRoots[0] ?? "",
44961
- paths: failed.map((entry) => entry.path),
45315
+ paths: failed2.map((entry) => entry.path),
44962
45316
  clearedPaths: cleared.map((entry) => entry.path),
44963
- failedPaths: failed.map((entry) => entry.path),
45317
+ failedPaths: failed2.map((entry) => entry.path),
44964
45318
  error: firstFailure?.error
44965
45319
  };
44966
45320
  }
@@ -44978,8 +45332,8 @@ var init_doctor_opencode_cache = __esm(() => {
44978
45332
  });
44979
45333
 
44980
45334
  // src/commands/doctor-opencode.ts
44981
- import { execSync as execSync2, spawnSync as spawnSync6 } from "node:child_process";
44982
- import { existsSync as existsSync32, readFileSync as readFileSync21, statSync as statSync13 } from "node:fs";
45335
+ import { spawnSync as spawnSync7 } from "node:child_process";
45336
+ import { existsSync as existsSync32, readFileSync as readFileSync22, statSync as statSync13 } from "node:fs";
44983
45337
  import { createRequire as createRequire5 } from "node:module";
44984
45338
  import { homedir as homedir17 } from "node:os";
44985
45339
  import { join as join29 } from "node:path";
@@ -45089,7 +45443,7 @@ function collectNpmReleaseAgeWarnings() {
45089
45443
  if (!existsSync32(npmrcPath))
45090
45444
  return ageWarnings;
45091
45445
  try {
45092
- const npmrc = readFileSync21(npmrcPath, "utf-8");
45446
+ const npmrc = readFileSync22(npmrcPath, "utf-8");
45093
45447
  for (const line of npmrc.split(`
45094
45448
  `)) {
45095
45449
  const trimmed = line.trim();
@@ -45119,26 +45473,18 @@ function compareVersions(a3, b) {
45119
45473
  }
45120
45474
  return 0;
45121
45475
  }
45122
- function isGhInstalled() {
45123
- try {
45124
- execSync2("gh --version", { stdio: "pipe" });
45125
- return true;
45126
- } catch {
45127
- return false;
45128
- }
45129
- }
45130
45476
  function openBrowser(url2) {
45131
45477
  try {
45132
45478
  if (process.platform === "darwin") {
45133
- const child = spawnSync6("open", [url2], { stdio: "ignore" });
45479
+ const child = spawnSync7("open", [url2], { stdio: "ignore" });
45134
45480
  if (child.status === 0)
45135
45481
  return;
45136
45482
  } else if (process.platform === "linux") {
45137
- const child = spawnSync6("xdg-open", [url2], { stdio: "ignore" });
45483
+ const child = spawnSync7("xdg-open", [url2], { stdio: "ignore" });
45138
45484
  if (child.status === 0)
45139
45485
  return;
45140
45486
  } else if (process.platform === "win32") {
45141
- const child = spawnSync6("cmd", ["/c", "start", "", url2], { stdio: "ignore" });
45487
+ const child = spawnSync7("cmd", ["/c", "start", "", url2], { stdio: "ignore" });
45142
45488
  if (child.status === 0)
45143
45489
  return;
45144
45490
  }
@@ -45165,8 +45511,10 @@ async function runIssueFlow2() {
45165
45511
  ...report.recentSessions.map((session, index) => {
45166
45512
  const displayTitle = session.title.trim() || "(no title)";
45167
45513
  const truncatedTitle = displayTitle.length > 50 ? `${displayTitle.slice(0, 47)}...` : displayTitle;
45514
+ const childPrefix = session.parentSessionId ? " ↳ " : "";
45515
+ const parentSuffix = session.parentSessionId ? ` (child of ${session.parentSessionId})` : "";
45168
45516
  return {
45169
- label: `${truncatedTitle} — ${session.sessionId}${index === 0 ? " (most recent)" : ""}`,
45517
+ label: `${childPrefix}${truncatedTitle} — ${session.sessionId}${parentSuffix}${index === 0 ? " (most recent)" : ""}`,
45170
45518
  value: session.sessionId
45171
45519
  };
45172
45520
  }),
@@ -45179,30 +45527,23 @@ async function runIssueFlow2() {
45179
45527
  }
45180
45528
  s.start("Bundling issue report");
45181
45529
  const bundled = await bundleIssueReport(report, description, title, sessionFilter);
45182
- s.stop(`Report written to ${bundled.path}`);
45530
+ s.stop(bundled.fullPath ? `Report written to ${bundled.path}; full bundle at ${bundled.fullPath}` : `Report written to ${bundled.path}`);
45183
45531
  const shouldSubmit = await confirm2("Submit this issue on GitHub now?", true);
45184
- if (shouldSubmit && isGhInstalled()) {
45185
- const result = spawnSync6("gh", [
45186
- "issue",
45187
- "create",
45188
- "-R",
45189
- "cortexkit/magic-context",
45190
- "--title",
45191
- title,
45192
- "--body-file",
45193
- bundled.path
45194
- ], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
45195
- if (result.status === 0) {
45196
- log2.success(result.stdout.trim());
45532
+ if (shouldSubmit) {
45533
+ const result = submitGithubIssue(title, bundled.path);
45534
+ if (result.ok) {
45535
+ log2.success(result.output);
45536
+ if (bundled.fullPath) {
45537
+ log2.info(`Full diagnostics bundle available to drag onto the issue: ${bundled.fullPath}`);
45538
+ }
45197
45539
  outro2("Issue submitted — thanks for the report!");
45198
45540
  return 0;
45199
45541
  }
45200
- log2.warn(result.stderr.trim() || "gh issue create failed");
45201
- } else if (shouldSubmit && !isGhInstalled()) {
45202
- log2.warn("gh CLI not found — falling back to browser");
45542
+ const fallbackPath = bundled.fullPath ?? bundled.path;
45543
+ log2.warn(formatGithubIssueFallback(result, fallbackPath));
45203
45544
  }
45204
45545
  const url2 = `https://github.com/cortexkit/magic-context/issues/new?title=${encodeURIComponent(title)}&template=bug_report.yml`;
45205
- log2.info(`Open this URL and paste the contents of ${bundled.path} into the Diagnostics field:`);
45546
+ log2.info(`Open this URL and drag ${bundled.fullPath ?? bundled.path} into the Diagnostics field:`);
45206
45547
  log2.info(url2);
45207
45548
  openBrowser(url2);
45208
45549
  outro2("Issue report ready");
@@ -45594,7 +45935,7 @@ async function runDoctor2(options = {}) {
45594
45935
  }
45595
45936
  if (paths.opencodeConfigFormat !== "none") {
45596
45937
  try {
45597
- const raw = readFileSync21(paths.opencodeConfig, "utf-8");
45938
+ const raw = readFileSync22(paths.opencodeConfig, "utf-8");
45598
45939
  const config2 = import_comment_json12.parse(raw);
45599
45940
  const rawPlugins = Array.isArray(config2?.plugin) ? config2.plugin : [];
45600
45941
  const existingIdx = rawPlugins.findIndex((entry) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry));
@@ -45681,7 +46022,7 @@ async function runDoctor2(options = {}) {
45681
46022
  fixed++;
45682
46023
  } else if (existsSync32(paths.tuiConfig)) {
45683
46024
  try {
45684
- const tuiRaw = readFileSync21(paths.tuiConfig, "utf-8");
46025
+ const tuiRaw = readFileSync22(paths.tuiConfig, "utf-8");
45685
46026
  const tuiConfig = import_comment_json12.parse(tuiRaw);
45686
46027
  const tuiRawPlugins = Array.isArray(tuiConfig?.plugin) ? tuiConfig.plugin : [];
45687
46028
  const tuiIdx = tuiRawPlugins.findIndex((entry) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry));
@@ -45935,6 +46276,7 @@ var init_doctor_opencode = __esm(async () => {
45935
46276
  init_config_location_migration();
45936
46277
  init_diagnostics_opencode();
45937
46278
  init_embedding_runtime();
46279
+ init_github_issue();
45938
46280
  init_logs_opencode();
45939
46281
  init_migrate_dreamer_v2_doctor();
45940
46282
  init_opencode_detect();
@@ -45957,7 +46299,7 @@ var init_doctor_opencode = __esm(async () => {
45957
46299
 
45958
46300
  // src/lib/diagnostics-pi.ts
45959
46301
  import { createHash as createHash5 } from "node:crypto";
45960
- import { existsSync as existsSync33, readdirSync as readdirSync5, readFileSync as readFileSync22, statSync as statSync14 } from "node:fs";
46302
+ import { existsSync as existsSync33, readdirSync as readdirSync5, readFileSync as readFileSync23, statSync as statSync14 } from "node:fs";
45961
46303
  import { createRequire as createRequire6 } from "node:module";
45962
46304
  import { homedir as homedir18, userInfo as userInfo2 } from "node:os";
45963
46305
  import { join as join30 } from "node:path";
@@ -46031,7 +46373,7 @@ function readJsonc(path6) {
46031
46373
  return { value: {} };
46032
46374
  try {
46033
46375
  return {
46034
- value: import_comment_json13.parse(readFileSync22(path6, "utf-8"))
46376
+ value: import_comment_json13.parse(readFileSync23(path6, "utf-8"))
46035
46377
  };
46036
46378
  } catch (error51) {
46037
46379
  return {
@@ -46101,7 +46443,7 @@ function collectPiRecentSessions() {
46101
46443
  }
46102
46444
  function parseHistorianDumpMeta2(path6) {
46103
46445
  try {
46104
- const xml = readFileSync22(path6, "utf-8");
46446
+ const xml = readFileSync23(path6, "utf-8");
46105
46447
  const parsed = parseCompartmentOutput(xml);
46106
46448
  const factCountByCategory = {};
46107
46449
  for (const fact of parsed.facts) {
@@ -46346,7 +46688,7 @@ var init_diagnostics_pi = __esm(() => {
46346
46688
  });
46347
46689
 
46348
46690
  // src/lib/logs-pi.ts
46349
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync10 } from "node:fs";
46691
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync10 } from "node:fs";
46350
46692
  import { join as join31 } from "node:path";
46351
46693
  function sanitizeLogContent2(content) {
46352
46694
  return sanitizeString2(content);
@@ -46376,7 +46718,7 @@ function filterLogLinesBySession2(lines, sessionId) {
46376
46718
  }
46377
46719
  async function bundleIssueReport2(report, description, title, options = {}) {
46378
46720
  const LOG_TAIL_LINES = 400;
46379
- const allLogLines = report.logFile.exists ? readFileSync23(report.logFile.path, "utf-8").split(/\r?\n/) : [];
46721
+ const allLogLines = report.logFile.exists ? readFileSync24(report.logFile.path, "utf-8").split(/\r?\n/) : [];
46380
46722
  const logLines = filterLogLinesBySession2(allLogLines, options.sessionFilter ?? null);
46381
46723
  const recentLog = sanitizeLogContent2(logLines.slice(-LOG_TAIL_LINES).join(`
46382
46724
  `)).trim();
@@ -46411,11 +46753,17 @@ async function bundleIssueReport2(report, description, title, options = {}) {
46411
46753
  ].join(`
46412
46754
  `);
46413
46755
  const bodyMarkdown = capBodyToGithubLimit(rawBodyMarkdown);
46756
+ const truncated = bodyMarkdown !== rawBodyMarkdown;
46414
46757
  const cwd = options.cwd ?? process.cwd();
46415
- const path6 = join31(cwd, `magic-context-pi-issue-${formatTimestamp2(options.now ?? new Date)}.md`);
46758
+ const timestamp3 = formatTimestamp2(options.now ?? new Date);
46759
+ const path6 = join31(cwd, `magic-context-pi-issue-${timestamp3}.md`);
46416
46760
  writeFileSync10(path6, `${bodyMarkdown}
46417
46761
  `);
46418
- return { path: path6, bodyMarkdown };
46762
+ const fullPath = truncated ? join31(cwd, `magic-context-pi-issue-${timestamp3}-full.md`) : undefined;
46763
+ if (fullPath)
46764
+ writeFileSync10(fullPath, `${rawBodyMarkdown}
46765
+ `);
46766
+ return { path: path6, bodyMarkdown, ...fullPath ? { fullPath } : {}, truncated };
46419
46767
  }
46420
46768
  var init_logs_pi = __esm(() => {
46421
46769
  init_diagnostics_pi();
@@ -46423,8 +46771,8 @@ var init_logs_pi = __esm(() => {
46423
46771
  });
46424
46772
 
46425
46773
  // src/commands/doctor-pi.ts
46426
- import { execFileSync as execFileSync6, spawnSync as spawnSync7 } from "node:child_process";
46427
- import { existsSync as existsSync34, mkdirSync as mkdirSync11, readdirSync as readdirSync6, readFileSync as readFileSync24, rmSync as rmSync4, statSync as statSync15 } from "node:fs";
46774
+ import { execFileSync as execFileSync6, spawnSync as spawnSync8 } from "node:child_process";
46775
+ import { existsSync as existsSync34, mkdirSync as mkdirSync11, readdirSync as readdirSync6, readFileSync as readFileSync25, rmSync as rmSync4, statSync as statSync15 } from "node:fs";
46428
46776
  import { createRequire as createRequire7 } from "node:module";
46429
46777
  import { homedir as homedir19 } from "node:os";
46430
46778
  import { dirname as dirname18, isAbsolute as isAbsolute5, join as join32 } from "node:path";
@@ -46517,7 +46865,7 @@ function summarize(results) {
46517
46865
  function readJsonc2(path6) {
46518
46866
  try {
46519
46867
  return {
46520
- value: import_comment_json14.parse(readFileSync24(path6, "utf-8"))
46868
+ value: import_comment_json14.parse(readFileSync25(path6, "utf-8"))
46521
46869
  };
46522
46870
  } catch (error51) {
46523
46871
  return {
@@ -46674,7 +47022,7 @@ function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
46674
47022
  const packageJson = join32(path6, "package.json");
46675
47023
  if (existsSync34(packageJson)) {
46676
47024
  try {
46677
- const pkg = JSON.parse(readFileSync24(packageJson, "utf-8"));
47025
+ const pkg = JSON.parse(readFileSync25(packageJson, "utf-8"));
46678
47026
  const name = typeof pkg.name === "string" ? pkg.name : "";
46679
47027
  const version2 = typeof pkg.version === "string" ? pkg.version : undefined;
46680
47028
  if (name === PACKAGE_NAME2) {
@@ -46943,7 +47291,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
46943
47291
  if (existsSync34(logPath)) {
46944
47292
  const stat = statSync15(logPath);
46945
47293
  const sizeKb = (stat.size / 1024).toFixed(0);
46946
- const lines = readFileSync24(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
47294
+ const lines = readFileSync25(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
46947
47295
  add2(results, "info", `Log file: ${logPath} (${sizeKb} KB)`);
46948
47296
  const lastLine = lines.at(-1);
46949
47297
  add2(results, "info", `Last plugin log line: ${lastLine ? sanitizeDiagnosticText(lastLine) : "<empty log>"}`);
@@ -47021,17 +47369,31 @@ function repair2(plan, prompts) {
47021
47369
  }
47022
47370
  return fixed;
47023
47371
  }
47024
- function ghAvailableAndAuthed(deps) {
47025
- try {
47026
- deps.execFileSync("gh", ["--version"], {
47027
- stdio: ["ignore", "pipe", "ignore"]
47372
+ function runGhCommandWithDeps2(deps, args) {
47373
+ if (args[0] === "issue") {
47374
+ const result = deps.spawnSync("gh", args, {
47375
+ encoding: "utf-8",
47376
+ stdio: ["ignore", "pipe", "pipe"]
47028
47377
  });
47029
- deps.execFileSync("gh", ["auth", "status"], {
47030
- stdio: ["ignore", "pipe", "ignore"]
47378
+ return {
47379
+ status: result.status,
47380
+ stdout: String(result.stdout ?? ""),
47381
+ stderr: String(result.stderr ?? "")
47382
+ };
47383
+ }
47384
+ try {
47385
+ const output = deps.execFileSync("gh", args, {
47386
+ encoding: "utf-8",
47387
+ stdio: ["ignore", "pipe", "pipe"]
47031
47388
  });
47032
- return true;
47033
- } catch {
47034
- return false;
47389
+ return { status: 0, stdout: String(output ?? ""), stderr: "" };
47390
+ } catch (error51) {
47391
+ const result = error51;
47392
+ return {
47393
+ status: typeof result.status === "number" ? result.status : 1,
47394
+ stdout: String(result.stdout ?? ""),
47395
+ stderr: String(result.stderr ?? "")
47396
+ };
47035
47397
  }
47036
47398
  }
47037
47399
  async function runIssueFlow3(options) {
@@ -47069,32 +47431,22 @@ async function runIssueFlow3(options) {
47069
47431
  now: options.deps.now(),
47070
47432
  sessionFilter
47071
47433
  });
47072
- spinner3.stop(`Report written to ${bundled.path}`);
47073
- if (ghAvailableAndAuthed(options.deps)) {
47074
- const shouldSubmit = await options.prompts.confirm("Submit this issue on GitHub now?", false);
47075
- if (shouldSubmit) {
47076
- const result = options.deps.spawnSync("gh", [
47077
- "issue",
47078
- "create",
47079
- "-R",
47080
- "cortexkit/magic-context",
47081
- "--title",
47082
- `[pi] ${title}`,
47083
- "--body-file",
47084
- bundled.path
47085
- ], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
47086
- if (result.status === 0) {
47087
- options.prompts.log.success(String(result.stdout).trim());
47088
- options.prompts.outro("Issue submitted — thanks for the report!");
47089
- return 0;
47090
- }
47091
- options.prompts.log.warn(String(result.stderr).trim() || "gh issue create failed");
47434
+ spinner3.stop(bundled.fullPath ? `Report written to ${bundled.path}; full bundle at ${bundled.fullPath}` : `Report written to ${bundled.path}`);
47435
+ const shouldSubmit = await options.prompts.confirm("Submit this issue on GitHub now?", false);
47436
+ if (shouldSubmit) {
47437
+ const result = submitGithubIssue(`[pi] ${title}`, bundled.path, (args) => runGhCommandWithDeps2(options.deps, args));
47438
+ if (result.ok) {
47439
+ options.prompts.log.success(result.output);
47440
+ if (bundled.fullPath) {
47441
+ options.prompts.log.info(`Full diagnostics bundle available to drag onto the issue: ${bundled.fullPath}`);
47442
+ }
47443
+ options.prompts.outro("Issue submitted — thanks for the report!");
47444
+ return 0;
47092
47445
  }
47093
- } else {
47094
- options.prompts.log.warn("gh CLI is unavailable or not authenticated; printing report for manual issue creation");
47446
+ options.prompts.log.warn(formatGithubIssueFallback(result, bundled.fullPath ?? bundled.path));
47095
47447
  }
47096
47448
  console.log(bundled.bodyMarkdown);
47097
- options.prompts.log.info(`Open https://github.com/cortexkit/magic-context/issues/new and attach ${bundled.path}`);
47449
+ options.prompts.log.info(`Open https://github.com/cortexkit/magic-context/issues/new and drag ${bundled.fullPath ?? bundled.path} into the issue`);
47098
47450
  options.prompts.outro("Issue report ready");
47099
47451
  return 0;
47100
47452
  } catch (error51) {
@@ -47199,6 +47551,7 @@ var init_doctor_pi = __esm(async () => {
47199
47551
  init_config_location_migration();
47200
47552
  init_diagnostics_pi();
47201
47553
  init_embedding_runtime();
47554
+ init_github_issue();
47202
47555
  init_logs_pi();
47203
47556
  init_paths();
47204
47557
  init_pi_helpers();
@@ -47224,7 +47577,7 @@ var init_doctor_pi = __esm(async () => {
47224
47577
  openExistingContextDatabase,
47225
47578
  now: () => new Date,
47226
47579
  execFileSync: execFileSync6,
47227
- spawnSync: spawnSync7
47580
+ spawnSync: spawnSync8
47228
47581
  };
47229
47582
  });
47230
47583
 
@@ -47353,7 +47706,7 @@ async function runClear() {
47353
47706
  outro2("Done.");
47354
47707
  return 0;
47355
47708
  }
47356
- let failed = 0;
47709
+ let failed2 = 0;
47357
47710
  for (const idxStr of picks) {
47358
47711
  const idx = Number.parseInt(idxStr, 10);
47359
47712
  const item = items[idx];
@@ -47369,11 +47722,11 @@ async function runClear() {
47369
47722
  } catch (err) {
47370
47723
  s.stop(`Failed: ${item.path}`);
47371
47724
  log2.error(err.message);
47372
- failed += 1;
47725
+ failed2 += 1;
47373
47726
  }
47374
47727
  }
47375
- outro2(failed === 0 ? "Done." : `Done with ${failed} failure(s).`);
47376
- return failed === 0 ? 0 : 1;
47728
+ outro2(failed2 === 0 ? "Done." : `Done with ${failed2} failure(s).`);
47729
+ return failed2 === 0 ? 0 : 1;
47377
47730
  }
47378
47731
  function formatSize(bytes) {
47379
47732
  if (bytes < 1024)