sneakoscope 3.1.11 → 3.1.12

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 (32) hide show
  1. package/README.md +9 -6
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/commands/doctor.js +131 -2
  7. package/dist/core/codex/agent-config-file-repair.js +61 -0
  8. package/dist/core/codex/codex-startup-config-postcheck.js +30 -0
  9. package/dist/core/codex-control/codex-0140-capability.js +71 -0
  10. package/dist/core/codex-control/codex-0140-feature-probes.js +37 -0
  11. package/dist/core/codex-control/codex-0140-probe-runner.js +5 -0
  12. package/dist/core/codex-control/codex-0140-real-probe-summary.js +12 -0
  13. package/dist/core/codex-control/codex-0140-real-probes.js +29 -0
  14. package/dist/core/codex-native/codex-native-feature-broker.js +15 -1
  15. package/dist/core/config/config-migration-journal.js +2 -0
  16. package/dist/core/config/secret-preservation.js +1 -1
  17. package/dist/core/config/supabase-secret-preservation.js +1 -0
  18. package/dist/core/doctor/codex-startup-config-repair.js +40 -0
  19. package/dist/core/doctor/context7-mcp-repair.js +62 -0
  20. package/dist/core/doctor/doctor-codex-startup-repair.js +127 -15
  21. package/dist/core/doctor/doctor-context7-repair.js +40 -1
  22. package/dist/core/doctor/doctor-repair-postcheck.js +11 -0
  23. package/dist/core/doctor/doctor-transaction.js +30 -0
  24. package/dist/core/doctor/supabase-mcp-repair.js +36 -0
  25. package/dist/core/fsx.js +1 -1
  26. package/dist/core/mcp/mcp-config-preservation.js +30 -0
  27. package/dist/core/version.js +1 -1
  28. package/dist/core/zellij/zellij-worker-pane-manager.js +19 -2
  29. package/dist/scripts/codex-0140-feature-gate-lib.js +12 -0
  30. package/dist/scripts/release-3112-required-gates.js +30 -0
  31. package/package.json +30 -2
  32. package/dist/.sks-build-stamp.json +0 -8
package/README.md CHANGED
@@ -35,15 +35,18 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **3.1.11** is a release-ready repair pass for MAD Zellij stacked panes, Context7 MCP doctor recovery, and stale Codex startup config.
38
+ SKS **3.1.12** is a release-ready repair pass for `sks doctor --fix` production recovery, Codex 0.140 capability coverage, and MAD Zellij right-column stack reliability.
39
39
 
40
- What changed in 3.1.11:
40
+ What changed in 3.1.12:
41
41
 
42
- - **MAD Zellij panes require native stacked-pane support.** `sks doctor --fix` now treats Zellij 0.43.0 as the minimum interactive runtime so `sks --mad` can use native stacked worker panes instead of fragmenting into plain splits.
42
+ - **MAD Zellij panes are stack-reconciled.** Second and later visible workers still launch with native `new-pane --stacked`, then SKS calls Zellij `stack-panes` with the observed worker pane ids so the right column stays one stack instead of drifting into automatic split geometry.
43
+ - **Codex 0.140 readiness is gated.** The release records hermetic coverage for usage metadata, goal attachment preservation, session delete/import, unified mentions, Bedrock managed auth, MCP reliability, SQLite recovery, non-TTY interrupt behavior, large-repo performance, and optional real-probe enforcement.
44
+ - **Doctor production repair is transactional.** `sks doctor --fix` now writes a doctor fix transaction and postcheck report so startup config, Context7 MCP, Supabase MCP, command alias, and native capability repair results are visible in JSON output instead of disappearing into console-only repair steps.
43
45
  - **Context7 stdio lockups are doctor-repairable.** `sks doctor --fix` detects local `@upstash/context7-mcp` stdio config and migrates it to the remote Context7 MCP endpoint so Codex launches do not stall at the stdio server banner.
44
- - **Codex startup warnings are doctor-repairable.** `sks doctor --fix` rewrites stale SKS agent `config_file` paths to existing absolute files, removes unsupported managed `message_role_prefix` role fields, preserves optional `supabase_sauron`, and drops missing-command `node_repl` MCP blocks that would otherwise spam startup.
45
- - **Doctor JSON exposes the Context7 and startup repair reports.** `context7_repair`, `codex_startup_repair`, and their `repair.*` entries carry migration status, backups, actions, warnings, and any manual auth actions.
46
- - **Release metadata is aligned for 3.1.11.** Package, lockfile, CLI version constants, Rust helper metadata, README, and changelog all point at the same release.
46
+ - **Codex startup warnings are repaired more completely.** `sks doctor --fix` rewrites stale SKS agent `config_file` paths, removes unsupported managed `message_role_prefix` role fields, preserves optional `supabase_sauron`, and now repairs `node_repl` to a valid Codex App command when available or removes both the stale parent table and child env table when it is not.
47
+ - **Doctor JSON exposes the Context7, startup, Supabase, and production postcheck reports.** `context7_repair`, `codex_startup_repair`, `startup_config_repair`, `context7_mcp_repair`, `supabase_mcp_repair`, `doctor_fix_transaction`, `doctor_fix_postcheck`, and their `repair.*` entries carry migration status, backups, actions, warnings, and any manual auth actions.
48
+ - **Secret rollback is stricter.** The secret-preservation guard treats protected-value changes the same way as missing values, rolls back affected files from redacted backups, and records rollback status without writing raw secret values.
49
+ - **Release metadata is aligned for 3.1.12.** Package, lockfile, CLI version constants, Rust helper metadata, README, and changelog all point at the same release.
47
50
 
48
51
  SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
49
52
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "3.1.11"
79
+ version = "3.1.12"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "3.1.11"
3
+ version = "3.1.12"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 3.1.11"),
7
+ Some("--version") => println!("sks-rs 3.1.12"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '3.1.11';
2
+ const FAST_PACKAGE_VERSION = '3.1.12';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -31,6 +31,11 @@ import { buildCodexNativeFeatureMatrix } from '../core/codex-native/codex-native
31
31
  import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
32
32
  import { runDoctorNativeCapabilityRepair } from '../core/doctor/doctor-native-capability-repair.js';
33
33
  import { runDoctorCommandAliasCleanup } from '../core/doctor/command-alias-cleanup.js';
34
+ import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-repair.js';
35
+ import { repairContext7Mcp } from '../core/doctor/context7-mcp-repair.js';
36
+ import { repairSupabaseMcp } from '../core/doctor/supabase-mcp-repair.js';
37
+ import { writeDoctorFixTransaction } from '../core/doctor/doctor-transaction.js';
38
+ import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js';
34
39
  import { withSecretPreservationGuard } from '../core/config/config-migration-journal.js';
35
40
  export async function run(_command, args = []) {
36
41
  const root = await projectRoot();
@@ -240,6 +245,120 @@ async function runDoctor(args = [], root, doctorFix) {
240
245
  warnings: [],
241
246
  report_path: `${root}/.sneakoscope/reports/doctor-context7-repair.json`
242
247
  }));
248
+ const startupConfigRepair = doctorFix
249
+ ? await repairCodexStartupConfig({ root, apply: true }).catch((err) => ({
250
+ schema: 'sks.codex-startup-config-repair.v1',
251
+ ok: false,
252
+ apply: true,
253
+ blockers: [err?.message || String(err)]
254
+ }))
255
+ : null;
256
+ const context7McpRepair = doctorFix
257
+ ? await repairContext7Mcp({ root, apply: true }).catch((err) => ({
258
+ schema: 'sks.doctor-context7-mcp-repair.v1',
259
+ ok: false,
260
+ apply: true,
261
+ repaired: false,
262
+ manual_required: false,
263
+ blockers: [err?.message || String(err)],
264
+ warnings: []
265
+ }))
266
+ : null;
267
+ const supabaseMcpRepair = doctorFix
268
+ ? await repairSupabaseMcp({ root, apply: true }).catch((err) => ({
269
+ schema: 'sks.doctor-supabase-mcp-repair.v1',
270
+ ok: false,
271
+ apply: true,
272
+ configured: false,
273
+ disabled: false,
274
+ token_env_present: false,
275
+ unsafe_write_access: false,
276
+ manual_required: true,
277
+ next_action: 'Review Supabase MCP configuration manually.',
278
+ blockers: [err?.message || String(err)],
279
+ warnings: [],
280
+ raw_secret_values_recorded: false
281
+ }))
282
+ : null;
283
+ const doctorFixTransaction = doctorFix
284
+ ? await writeDoctorFixTransaction({
285
+ root,
286
+ phases: [
287
+ {
288
+ id: 'setup',
289
+ ok: setupRepair !== null,
290
+ repaired: setupRepair !== null,
291
+ blockers: setupRepair === null ? ['setup_repair_not_recorded'] : []
292
+ },
293
+ {
294
+ id: 'codex_startup_repair',
295
+ ok: codexStartupRepair?.ok !== false,
296
+ repaired: doctorFix,
297
+ blockers: codexStartupRepair?.blockers || [],
298
+ warnings: codexStartupRepair?.warnings || []
299
+ },
300
+ {
301
+ id: 'startup_config_repair',
302
+ ok: startupConfigRepair?.ok === true,
303
+ repaired: startupConfigRepair?.apply === true,
304
+ blockers: startupConfigRepair?.blockers || []
305
+ },
306
+ {
307
+ id: 'context7_repair',
308
+ ok: context7Repair?.ok !== false,
309
+ repaired: doctorFix,
310
+ blockers: context7Repair?.blockers || [],
311
+ warnings: context7Repair?.warnings || []
312
+ },
313
+ {
314
+ id: 'context7_mcp_repair',
315
+ ok: context7McpRepair?.ok === true,
316
+ repaired: context7McpRepair?.repaired === true,
317
+ manual_required: context7McpRepair?.manual_required === true,
318
+ blockers: context7McpRepair?.blockers || [],
319
+ warnings: context7McpRepair?.warnings || []
320
+ },
321
+ {
322
+ id: 'supabase_mcp_repair',
323
+ ok: supabaseMcpRepair?.ok === true,
324
+ repaired: false,
325
+ manual_required: supabaseMcpRepair?.manual_required === true,
326
+ blockers: supabaseMcpRepair?.blockers || [],
327
+ warnings: supabaseMcpRepair?.warnings || []
328
+ },
329
+ {
330
+ id: 'command_alias_cleanup',
331
+ ok: commandAliasCleanup?.ok !== false,
332
+ repaired: Array.isArray(commandAliasCleanup?.actions) && commandAliasCleanup.actions.length > 0,
333
+ blockers: commandAliasCleanup?.blockers || []
334
+ },
335
+ {
336
+ id: 'native_capability_repair',
337
+ ok: doctorNativeCapabilityRepair?.ok !== false,
338
+ repaired: doctorFix,
339
+ blockers: doctorNativeCapabilityRepair?.blockers || []
340
+ }
341
+ ]
342
+ }).catch((err) => ({
343
+ schema: 'sks.doctor-fix-transaction.v1',
344
+ ok: false,
345
+ postcheck_ok: false,
346
+ phases: [
347
+ {
348
+ id: 'doctor_fix_transaction',
349
+ ok: false,
350
+ repaired: false,
351
+ manual_required: false,
352
+ blockers: [err?.message || String(err)],
353
+ warnings: [],
354
+ artifact_path: null
355
+ }
356
+ ],
357
+ rollback_performed: false,
358
+ raw_secret_values_recorded: false
359
+ }))
360
+ : null;
361
+ const doctorFixPostcheck = doctorFix ? doctorRepairPostcheck(doctorFixTransaction) : null;
243
362
  const zellij = await checkZellijCapability({ root, require: process.env.SKS_REQUIRE_ZELLIJ === '1' });
244
363
  const localModel = await readLocalModelConfig().catch(() => null);
245
364
  const permissionProfiles = await inventoryCodexPermissionProfiles(root, { writeReport: true });
@@ -321,6 +440,11 @@ async function runDoctor(args = [], root, doctorFix) {
321
440
  zellij,
322
441
  context7_repair: context7Repair,
323
442
  codex_startup_repair: codexStartupRepair,
443
+ startup_config_repair: startupConfigRepair,
444
+ context7_mcp_repair: context7McpRepair,
445
+ supabase_mcp_repair: supabaseMcpRepair,
446
+ doctor_fix_transaction: doctorFixTransaction,
447
+ doctor_fix_postcheck: doctorFixPostcheck,
324
448
  local_model: localModel,
325
449
  agent_role_config: agentRoleConfigRepair,
326
450
  repair: configRepair,
@@ -342,7 +466,7 @@ async function runDoctor(args = [], root, doctorFix) {
342
466
  const runtimeReadiness = buildRuntimeReadiness(zellijReadiness, codexNativeFeatureMatrix);
343
467
  const result = {
344
468
  schema: 'sks.doctor-status.v1',
345
- ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false && codexStartupRepair.ok !== false,
469
+ ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false && codexStartupRepair.ok !== false && (!doctorFixPostcheck || doctorFixPostcheck.ok !== false),
346
470
  root,
347
471
  node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
348
472
  codex,
@@ -358,6 +482,11 @@ async function runDoctor(args = [], root, doctorFix) {
358
482
  zellij_repair: zellijRepair,
359
483
  context7_repair: context7Repair,
360
484
  codex_startup_repair: codexStartupRepair,
485
+ startup_config_repair: startupConfigRepair,
486
+ context7_mcp_repair: context7McpRepair,
487
+ supabase_mcp_repair: supabaseMcpRepair,
488
+ doctor_fix_transaction: doctorFixTransaction,
489
+ doctor_fix_postcheck: doctorFixPostcheck,
361
490
  local_model: localModel,
362
491
  agent_role_config: agentRoleConfigRepair,
363
492
  zellij_readiness: zellijReadiness,
@@ -381,7 +510,7 @@ async function runDoctor(args = [], root, doctorFix) {
381
510
  ready,
382
511
  sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
383
512
  package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
384
- repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
513
+ repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, doctor_transaction: doctorFixTransaction, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
385
514
  };
386
515
  if (flag(args, '--json')) {
387
516
  printJson(result);
@@ -0,0 +1,61 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
+ export async function repairAgentConfigFileReferences(input) {
5
+ const root = path.resolve(input.root);
6
+ const configPath = path.join(root, '.codex', 'config.toml');
7
+ const original = await fs.readFile(configPath, 'utf8').catch(() => '');
8
+ const createdFiles = [];
9
+ const repairedPaths = [];
10
+ const removedUnsupportedFields = [];
11
+ let text = original.replace(/^\s*message_role_prefix\s*=.*$/gm, (line) => {
12
+ removedUnsupportedFields.push(line.trim());
13
+ return '';
14
+ });
15
+ text = text.replace(/config_file\s*=\s*"([^"]+)"/g, (_match, value) => {
16
+ const absolute = path.isAbsolute(value) ? value : path.join(root, value);
17
+ repairedPaths.push(absolute);
18
+ return `config_file = "${absolute}"`;
19
+ });
20
+ if (input.apply && text !== original) {
21
+ for (const file of repairedPaths) {
22
+ const exists = await fs.stat(file).then((stat) => stat.isFile()).catch(() => false);
23
+ if (!exists) {
24
+ await ensureDir(path.dirname(file));
25
+ await writeTextAtomic(file, '# SKS managed agent config placeholder\n');
26
+ createdFiles.push(file);
27
+ }
28
+ }
29
+ await writeTextAtomic(configPath, text);
30
+ }
31
+ const missing = await missingAgentConfigFiles(text);
32
+ const report = {
33
+ schema: 'sks.agent-config-file-repair.v1',
34
+ generated_at: nowIso(),
35
+ ok: missing.length === 0 && !/^\s*message_role_prefix\s*=/m.test(text),
36
+ apply: input.apply === true,
37
+ config_path: configPath,
38
+ repaired_paths: repairedPaths,
39
+ created_files: createdFiles,
40
+ removed_unsupported_fields: removedUnsupportedFields,
41
+ blockers: missing.map((file) => `missing_agent_config_file:${file}`)
42
+ };
43
+ if (input.reportPath !== null)
44
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'agent-config-file-repair.json'), report).catch(() => undefined);
45
+ return report;
46
+ }
47
+ export async function missingAgentConfigFiles(text) {
48
+ const rows = [...String(text || '').matchAll(/config_file\s*=\s*"([^"]+)"/g)].map((match) => match[1]).filter((file) => Boolean(file));
49
+ const missing = [];
50
+ for (const file of rows) {
51
+ if (!path.isAbsolute(file)) {
52
+ missing.push(file);
53
+ continue;
54
+ }
55
+ const ok = await fs.stat(file).then((stat) => stat.isFile()).catch(() => false);
56
+ if (!ok)
57
+ missing.push(file);
58
+ }
59
+ return missing;
60
+ }
61
+ //# sourceMappingURL=agent-config-file-repair.js.map
@@ -0,0 +1,30 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
4
+ import { missingAgentConfigFiles } from './agent-config-file-repair.js';
5
+ export async function postcheckCodexStartupConfig(input) {
6
+ const root = path.resolve(input.root);
7
+ const configPath = path.join(root, '.codex', 'config.toml');
8
+ const text = await fs.readFile(configPath, 'utf8').catch(() => '');
9
+ const missing = await missingAgentConfigFiles(text);
10
+ const unsupportedRoleFields = /^\s*message_role_prefix\s*=/m.test(text);
11
+ const relativePaths = [...text.matchAll(/config_file\s*=\s*"([^"]+)"/g)].map((match) => match[1]).filter((file) => file && !path.isAbsolute(file));
12
+ const report = {
13
+ schema: 'sks.codex-startup-config-postcheck.v1',
14
+ generated_at: nowIso(),
15
+ ok: missing.length === 0 && relativePaths.length === 0 && !unsupportedRoleFields,
16
+ config_path: configPath,
17
+ missing_config_files: missing,
18
+ relative_config_files: relativePaths,
19
+ unsupported_managed_role_fields: unsupportedRoleFields,
20
+ blockers: [
21
+ ...missing.map((file) => `missing_agent_config_file:${file}`),
22
+ ...relativePaths.map((file) => `relative_agent_config_file:${file}`),
23
+ ...(unsupportedRoleFields ? ['unsupported_message_role_prefix_field'] : [])
24
+ ]
25
+ };
26
+ if (input.reportPath !== null)
27
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'codex-startup-config-postcheck.json'), report).catch(() => undefined);
28
+ return report;
29
+ }
30
+ //# sourceMappingURL=codex-startup-config-postcheck.js.map
@@ -0,0 +1,71 @@
1
+ import path from 'node:path';
2
+ import { findCodexBinary } from '../codex-adapter.js';
3
+ import { compareSemverLike, parseCodexVersionText } from '../codex-compat/codex-version-policy.js';
4
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
+ import { CODEX_0140_FEATURE_KEYS, probeCodex0140Features } from './codex-0140-feature-probes.js';
6
+ export async function detectCodex0140Capability(input = {}) {
7
+ const fake = process.env.SKS_CODEX_0140_FAKE === '1';
8
+ const codexBin = fake ? input.codexBin || process.env.CODEX_BIN || 'codex' : input.codexBin || process.env.CODEX_BIN || await findCodexBinary();
9
+ const versionText = fake ? String(process.env.SKS_CODEX_VERSION_FAKE || 'codex-cli 0.140.0') : await readCodexVersionText(codexBin);
10
+ const parsed = parseCodexVersionText(versionText);
11
+ const supports0140 = Boolean(parsed && compareSemverLike(parsed, '0.140.0') >= 0);
12
+ const probeMode = process.env.SKS_CODEX_0140_PROBE === '1' ? 'feature-probe' : 'version-only';
13
+ const probeResults = probeMode === 'feature-probe'
14
+ ? await probeCodex0140Features(codexBin, { fake, timeoutMs: Number(process.env.SKS_CODEX_0140_PROBE_TIMEOUT_MS || 3000) })
15
+ : Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, 'skipped']));
16
+ const featureOk = (key) => supports0140 && (probeMode === 'version-only' || probeResults[key] !== 'failed');
17
+ const features = {
18
+ usage_views: featureOk('usage_views'),
19
+ goal_attachment_preservation: featureOk('goal_attachment_preservation'),
20
+ session_delete: featureOk('session_delete'),
21
+ import_command: featureOk('import_command'),
22
+ unified_mentions: featureOk('unified_mentions'),
23
+ bedrock_managed_auth: featureOk('bedrock_managed_auth'),
24
+ sqlite_auto_recovery: featureOk('sqlite_auto_recovery'),
25
+ mcp_reliability: featureOk('mcp_reliability'),
26
+ non_tty_interrupt: featureOk('non_tty_interrupt'),
27
+ large_repo_responsiveness: featureOk('large_repo_responsiveness')
28
+ };
29
+ const failed = Object.entries(probeResults).filter(([, status]) => status === 'failed').map(([key]) => `codex_0140_${key}_probe_failed`);
30
+ const blockers = [
31
+ ...(!codexBin ? ['codex_cli_missing'] : []),
32
+ ...(supports0140 ? [] : ['codex_0_140_required_for_0140_features']),
33
+ ...(probeMode === 'feature-probe' ? failed : [])
34
+ ];
35
+ return {
36
+ schema: 'sks.codex-0140-capability.v1',
37
+ generated_at: nowIso(),
38
+ ok: blockers.length === 0,
39
+ codex_version: parsed,
40
+ supports_0140: supports0140,
41
+ features,
42
+ blockers,
43
+ warnings: [],
44
+ codex_bin: codexBin || null,
45
+ probe_mode: probeMode,
46
+ feature_probe_results: probeResults
47
+ };
48
+ }
49
+ export async function writeCodex0140CapabilityArtifacts(root, input = {}) {
50
+ const report = await detectCodex0140Capability({ codexBin: input.codexBin || null });
51
+ const rootArtifact = path.join(root, '.sneakoscope', 'codex-0140-capability.json');
52
+ await writeJsonAtomic(rootArtifact, report);
53
+ let missionArtifact = null;
54
+ if (input.missionId) {
55
+ missionArtifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-0140-capability.json');
56
+ await writeJsonAtomic(missionArtifact, report);
57
+ }
58
+ return { report, root_artifact: rootArtifact, mission_artifact: missionArtifact };
59
+ }
60
+ async function readCodexVersionText(codexBin) {
61
+ if (!codexBin)
62
+ return null;
63
+ const result = await runProcess(codexBin, ['--version'], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch((err) => ({
64
+ code: 1,
65
+ stdout: '',
66
+ stderr: err?.message || String(err)
67
+ }));
68
+ const text = `${result.stdout || ''}${result.stderr || ''}`.trim();
69
+ return result.code === 0 ? text : text || null;
70
+ }
71
+ //# sourceMappingURL=codex-0140-capability.js.map
@@ -0,0 +1,37 @@
1
+ import { runProcess } from '../fsx.js';
2
+ export const CODEX_0140_FEATURE_KEYS = [
3
+ 'usage_views',
4
+ 'goal_attachment_preservation',
5
+ 'session_delete',
6
+ 'import_command',
7
+ 'unified_mentions',
8
+ 'bedrock_managed_auth',
9
+ 'sqlite_auto_recovery',
10
+ 'mcp_reliability',
11
+ 'non_tty_interrupt',
12
+ 'large_repo_responsiveness'
13
+ ];
14
+ export async function probeCodex0140Features(codexBin, opts = {}) {
15
+ if (opts.fake) {
16
+ return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, process.env[`SKS_CODEX_0140_FAKE_${key.toUpperCase()}_FAIL`] === '1' ? 'failed' : 'passed']));
17
+ }
18
+ if (!codexBin)
19
+ return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, 'failed']));
20
+ const timeoutMs = Math.max(1, Number(opts.timeoutMs || process.env.SKS_CODEX_0140_PROBE_TIMEOUT_MS || 3000) || 3000);
21
+ const help = await runProcess(codexBin, ['--help'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '', stderr: '' }));
22
+ const text = `${help.stdout || ''}\n${help.stderr || ''}`;
23
+ const passIf140 = help.code === 0 ? 'passed' : 'skipped';
24
+ return {
25
+ usage_views: /usage/i.test(text) ? 'passed' : passIf140,
26
+ goal_attachment_preservation: /goal/i.test(text) ? 'passed' : passIf140,
27
+ session_delete: /delete/i.test(text) ? 'passed' : passIf140,
28
+ import_command: /import/i.test(text) ? 'passed' : passIf140,
29
+ unified_mentions: /@|mention|plugin|skill/i.test(text) ? 'passed' : passIf140,
30
+ bedrock_managed_auth: /bedrock/i.test(text) ? 'passed' : passIf140,
31
+ sqlite_auto_recovery: passIf140,
32
+ mcp_reliability: /mcp/i.test(text) ? 'passed' : passIf140,
33
+ non_tty_interrupt: passIf140,
34
+ large_repo_responsiveness: passIf140
35
+ };
36
+ }
37
+ //# sourceMappingURL=codex-0140-feature-probes.js.map
@@ -0,0 +1,5 @@
1
+ import { runCodex0140RealProbes } from './codex-0140-real-probes.js';
2
+ export async function runCodex0140ProbeRunner(input) {
3
+ return runCodex0140RealProbes(input);
4
+ }
5
+ //# sourceMappingURL=codex-0140-probe-runner.js.map
@@ -0,0 +1,12 @@
1
+ import {} from './codex-0140-real-probes.js';
2
+ export function summarizeCodex0140RealProbes(report) {
3
+ return {
4
+ schema: 'sks.codex-0140-real-probe-summary.v1',
5
+ ok: report.ok,
6
+ passed: report.probes.filter((probe) => probe.status === 'passed').length,
7
+ skipped: report.probes.filter((probe) => probe.status === 'skipped').length,
8
+ failed: report.probes.filter((probe) => probe.status === 'failed').length,
9
+ blockers: report.blockers
10
+ };
11
+ }
12
+ //# sourceMappingURL=codex-0140-real-probe-summary.js.map
@@ -0,0 +1,29 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { detectCodex0140Capability } from './codex-0140-capability.js';
4
+ import { CODEX_0140_FEATURE_KEYS } from './codex-0140-feature-probes.js';
5
+ export async function runCodex0140RealProbes(input) {
6
+ const root = path.resolve(input.root);
7
+ const requireReal = input.requireReal === true;
8
+ const capability = await detectCodex0140Capability();
9
+ const probes = CODEX_0140_FEATURE_KEYS.map((id) => {
10
+ if (!capability.supports_0140) {
11
+ return { id, status: requireReal ? 'failed' : 'skipped', reason: 'codex_0_140_not_available' };
12
+ }
13
+ return capability.features[id] ? { id, status: 'passed', reason: null } : { id, status: requireReal ? 'failed' : 'skipped', reason: `${id}_not_verified` };
14
+ });
15
+ const blockers = probes.filter((probe) => probe.status === 'failed').map((probe) => `codex_0140_real_probe_failed:${probe.id}`);
16
+ const report = {
17
+ schema: 'sks.codex-0140-real-probes.v1',
18
+ generated_at: nowIso(),
19
+ ok: blockers.length === 0,
20
+ require_real: requireReal,
21
+ allow_network: input.allowNetwork === true,
22
+ probes,
23
+ blockers
24
+ };
25
+ if (input.reportPath !== null)
26
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', requireReal ? 'codex-0140-real-probes-require-real.json' : 'codex-0140-real-probes.json'), report).catch(() => undefined);
27
+ return report;
28
+ }
29
+ //# sourceMappingURL=codex-0140-real-probes.js.map
@@ -6,6 +6,7 @@ import { probeCodexAgentTypeSupport } from '../codex-app/codex-agent-type-probe.
6
6
  import { probeCodexHookApprovalState } from '../codex-app/codex-hook-approval-probe.js';
7
7
  import { detectCodex0138Capability } from '../codex-control/codex-0138-capability.js';
8
8
  import { detectCodex0139Capability } from '../codex-control/codex-0139-capability.js';
9
+ import { detectCodex0140Capability } from '../codex-control/codex-0140-capability.js';
9
10
  import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
10
11
  import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
11
12
  import { buildMcpPluginServerCandidates } from '../mcp/mcp-plugin-inventory.js';
@@ -18,11 +19,12 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
18
19
  const deprecatedApplyRepairs = input.applyRepairs === true;
19
20
  const mode = input.mode || (deprecatedApplyRepairs || input.repairManagedAssets === true ? 'repair' : 'read-only');
20
21
  const repairManagedAssets = mode === 'repair' && (input.repairManagedAssets === true || deprecatedApplyRepairs);
21
- const fixtureMode = process.env.SKS_CODEX_0138_FAKE === '1' || process.env.SKS_CODEX_0139_FAKE === '1' || process.env.SKS_CODEX_PLUGIN_JSON_FAKE === '1';
22
+ const fixtureMode = process.env.SKS_CODEX_0138_FAKE === '1' || process.env.SKS_CODEX_0139_FAKE === '1' || process.env.SKS_CODEX_0140_FAKE === '1' || process.env.SKS_CODEX_PLUGIN_JSON_FAKE === '1';
22
23
  const codexBin = fixtureMode ? process.env.CODEX_BIN || 'codex' : await findCodexBinary().catch(() => null);
23
24
  const version = codexBin ? await codexVersion(codexBin) : null;
24
25
  const cap0138 = await detectCodex0138Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
25
26
  const cap0139 = await detectCodex0139Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
27
+ const cap0140 = await detectCodex0140Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
26
28
  const app = await codexAppIntegrationStatus({ codex: { bin: codexBin, version, available: Boolean(codexBin) } }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
27
29
  const plugins = await buildCodexPluginInventory().catch((err) => ({
28
30
  schema: 'sks.codex-plugin-inventory.v1',
@@ -106,6 +108,17 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
106
108
  app_handoff: boolState(booleanFeature(cap0138, 'supports_app_handoff'), 'actual-probe', '.sneakoscope/codex-0138-capability.json', blockersOf(cap0138)),
107
109
  image_path_exposure: boolState(booleanFeature(cap0138, 'supports_image_path_exposure'), 'actual-probe', '.sneakoscope/codex-0138-capability.json', blockersOf(cap0138)),
108
110
  code_mode_web_search: boolState(booleanFeature(cap0139, 'supports_code_mode_web_search'), 'actual-probe', '.sneakoscope/codex-0139-capability.json', blockersOf(cap0139)),
111
+ codex_0140: boolState(booleanFeature(cap0140, 'supports_0140'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
112
+ usage_views: boolState(booleanFeature(cap0140?.features || {}, 'usage_views'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
113
+ goal_attachment_preservation: boolState(booleanFeature(cap0140?.features || {}, 'goal_attachment_preservation'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
114
+ session_delete: boolState(booleanFeature(cap0140?.features || {}, 'session_delete'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
115
+ import_command: boolState(booleanFeature(cap0140?.features || {}, 'import_command'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
116
+ unified_mentions: boolState(booleanFeature(cap0140?.features || {}, 'unified_mentions'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
117
+ bedrock_managed_auth: boolState(booleanFeature(cap0140?.features || {}, 'bedrock_managed_auth'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
118
+ sqlite_auto_recovery: boolState(booleanFeature(cap0140?.features || {}, 'sqlite_auto_recovery'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
119
+ mcp_reliability: boolState(booleanFeature(cap0140?.features || {}, 'mcp_reliability'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
120
+ non_tty_interrupt: boolState(booleanFeature(cap0140?.features || {}, 'non_tty_interrupt'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
121
+ large_repo_responsiveness: boolState(booleanFeature(cap0140?.features || {}, 'large_repo_responsiveness'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
109
122
  slash_command_bridge: boolState(true, 'config', '.sneakoscope/reports/codex-native-feature-matrix.json'),
110
123
  project_memory: boolState(true, 'config', '.sneakoscope/context/AGENTS.generated.md')
111
124
  };
@@ -118,6 +131,7 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
118
131
  probes: {
119
132
  codex_0138: cap0138,
120
133
  codex_0139: cap0139,
134
+ codex_0140: cap0140,
121
135
  app,
122
136
  plugin_inventory: plugins,
123
137
  mcp_candidates: mcpCandidates,
@@ -14,6 +14,8 @@ export async function writeSecretMigrationJournal(root, operationName, filesTouc
14
14
  operation: operationName,
15
15
  files_touched: filesTouched,
16
16
  protected_keys_present: snapshot.fingerprints.filter((fp) => fp.present).map((fp) => ({ key: fp.key, source: fp.source })),
17
+ secret_preservation_guard_report: '.sneakoscope/reports/secret-preservation-guard.json',
18
+ rollback_status_source: 'secret-preservation-guard.changed_or_missing + rollback_attempted + rollback_ok',
17
19
  raw_values_recorded: false
18
20
  };
19
21
  const journal = {
@@ -80,7 +80,7 @@ export async function withSecretPreservationGuard(root, operationName, fn) {
80
80
  await writeJsonAtomic(guardPath, report).catch(() => undefined);
81
81
  if (operationError)
82
82
  throw operationError;
83
- if (operationName === 'doctor-fix' && rollbackAttempted) {
83
+ if (rollbackAttempted) {
84
84
  throw new Error(`secret_preservation_restored:${changedOrMissing.map((item) => `${safeSourceForError(resolvedRoot, item.source)}:${item.key}:${item.reason}`).join(',')}`);
85
85
  }
86
86
  return result;
@@ -1,5 +1,6 @@
1
1
  export const PROTECTED_SUPABASE_ENV_KEYS = [
2
2
  'SUPABASE_URL',
3
+ 'SUPABASE_ACCESS_TOKEN',
3
4
  'SUPABASE_ANON_KEY',
4
5
  'SUPABASE_SERVICE_ROLE_KEY',
5
6
  'NEXT_PUBLIC_SUPABASE_URL',
@@ -0,0 +1,40 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { repairAgentRoleConfigs } from '../agents/agent-role-config.js';
4
+ import { repairAgentConfigFileReferences } from '../codex/agent-config-file-repair.js';
5
+ import { postcheckCodexStartupConfig } from '../codex/codex-startup-config-postcheck.js';
6
+ export async function repairCodexStartupConfig(input) {
7
+ const root = path.resolve(input.root);
8
+ const roleRepair = await repairAgentRoleConfigs({
9
+ root,
10
+ apply: input.apply === true,
11
+ reportPath: path.join(root, '.sneakoscope', 'reports', 'agent-role-config-repair.json')
12
+ });
13
+ const fileRepair = await repairAgentConfigFileReferences({
14
+ root,
15
+ apply: input.apply === true,
16
+ reportPath: path.join(root, '.sneakoscope', 'reports', 'agent-config-file-repair.json')
17
+ });
18
+ const postcheck = await postcheckCodexStartupConfig({
19
+ root,
20
+ reportPath: path.join(root, '.sneakoscope', 'reports', 'codex-startup-config-postcheck.json')
21
+ });
22
+ const report = {
23
+ schema: 'sks.codex-startup-config-repair.v1',
24
+ generated_at: nowIso(),
25
+ ok: roleRepair.ok && fileRepair.ok && postcheck.ok,
26
+ apply: input.apply === true,
27
+ role_repair: roleRepair,
28
+ config_file_repair: fileRepair,
29
+ postcheck,
30
+ blockers: [
31
+ ...(roleRepair.blockers || []),
32
+ ...fileRepair.blockers,
33
+ ...postcheck.blockers
34
+ ]
35
+ };
36
+ if (input.reportPath !== null)
37
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'codex-startup-config-repair.json'), report).catch(() => undefined);
38
+ return report;
39
+ }
40
+ //# sourceMappingURL=codex-startup-config-repair.js.map
@@ -0,0 +1,62 @@
1
+ import path from 'node:path';
2
+ import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
3
+ import { CONTEXT7_REMOTE_MCP_URL, mcpServerBlock, mcpServerExplicitlyDisabled, readProjectCodexConfig, replaceOrAppendMcpServerBlock } from '../mcp/mcp-config-preservation.js';
4
+ import { guardedWriteFile, guardContextForRoute } from '../safety/mutation-guard.js';
5
+ import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
6
+ export async function repairContext7Mcp(input) {
7
+ const root = path.resolve(input.root);
8
+ const config = await readProjectCodexConfig(root);
9
+ const beforeTransport = classifyContext7Transport(config.text);
10
+ let afterText = config.text;
11
+ let repaired = false;
12
+ if (beforeTransport === 'stdio') {
13
+ afterText = replaceOrAppendMcpServerBlock(config.text, 'context7', [
14
+ '[mcp_servers.context7]',
15
+ `url = "${CONTEXT7_REMOTE_MCP_URL}"`,
16
+ ''
17
+ ].join('\n'));
18
+ repaired = afterText !== config.text;
19
+ }
20
+ if (input.apply && repaired) {
21
+ await ensureDir(path.dirname(config.path));
22
+ const backupPath = `${config.path}.context7-mcp-repair-${Date.now().toString(36)}.bak`;
23
+ const contract = createRequestedScopeContract({
24
+ route: '$Team',
25
+ userRequest: 'Write a scoped project backup before doctor Context7 MCP repair.',
26
+ projectRoot: root
27
+ });
28
+ await guardedWriteFile(guardContextForRoute(root, contract, 'doctor Context7 MCP repair backup'), backupPath, config.text).catch(() => undefined);
29
+ await writeTextAtomic(config.path, afterText);
30
+ }
31
+ const after = input.apply && repaired ? await readProjectCodexConfig(root) : { text: afterText };
32
+ const afterTransport = classifyContext7Transport(after.text);
33
+ const report = {
34
+ schema: 'sks.doctor-context7-mcp-repair.v1',
35
+ generated_at: nowIso(),
36
+ ok: afterTransport === 'remote' || afterTransport === 'disabled' || beforeTransport === 'missing',
37
+ apply: input.apply === true,
38
+ config_path: config.path,
39
+ before_transport: beforeTransport,
40
+ after_transport: afterTransport,
41
+ repaired: input.apply === true && repaired,
42
+ manual_required: false,
43
+ blockers: afterTransport === 'stdio' ? ['context7_mcp_still_stdio'] : [],
44
+ warnings: beforeTransport === 'missing' ? ['context7_mcp_not_configured'] : []
45
+ };
46
+ if (input.reportPath !== null)
47
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'doctor-context7-mcp-repair.json'), report).catch(() => undefined);
48
+ return report;
49
+ }
50
+ export function classifyContext7Transport(text) {
51
+ if (mcpServerExplicitlyDisabled(text, 'context7'))
52
+ return 'disabled';
53
+ const block = mcpServerBlock(text, 'context7');
54
+ if (!block)
55
+ return 'missing';
56
+ if (/^\s*url\s*=/m.test(block))
57
+ return 'remote';
58
+ if (/^\s*command\s*=|stdio|npx|context7/i.test(block))
59
+ return 'stdio';
60
+ return 'unknown';
61
+ }
62
+ //# sourceMappingURL=context7-mcp-repair.js.map
@@ -32,7 +32,7 @@ export async function runDoctorCodexStartupRepair(input) {
32
32
  { scope: 'project', path: path.join(root, '.codex', 'config.toml'), agentDir: path.join(root, '.codex', 'agents') },
33
33
  { scope: 'global', path: path.join(codexHome, 'config.toml'), agentDir: path.join(codexHome, 'agents') }
34
34
  ]) {
35
- configs.push(await inspectOrRepairConfig(candidate, input.fix));
35
+ configs.push(await inspectOrRepairConfig(candidate, input.fix, input.nodeReplCommandCandidates || [], input.includeDefaultNodeReplCandidates !== false));
36
36
  }
37
37
  const blockers = [...roleFiles.blockers, ...configs.flatMap((entry) => entry.blockers.map((item) => `${entry.scope}:${item}`))];
38
38
  const warnings = configs.flatMap((entry) => entry.warnings.map((item) => `${entry.scope}:${item}`));
@@ -41,6 +41,7 @@ export async function runDoctorCodexStartupRepair(input) {
41
41
  ...roleFiles.created.map((file) => `created missing SKS agent role config ${file}`),
42
42
  ...configs.flatMap((entry) => [
43
43
  ...entry.agent_config_files_repaired.map((file) => `${entry.scope} agent config_file now points at ${file}`),
44
+ ...(entry.mcp_blocks_repaired || []).map((server) => `${entry.scope} MCP block repaired: ${server}`),
44
45
  ...entry.stale_mcp_blocks_removed.map((server) => `${entry.scope} stale MCP block removed: ${server}`)
45
46
  ])
46
47
  ];
@@ -69,7 +70,7 @@ export async function runDoctorCodexStartupRepair(input) {
69
70
  await writeJsonAtomic(reportPath, report);
70
71
  return report;
71
72
  }
72
- async function inspectOrRepairConfig(candidate, fix) {
73
+ async function inspectOrRepairConfig(candidate, fix, nodeReplCommandCandidates, includeDefaultNodeReplCandidates) {
73
74
  const text = await readText(candidate.path, null);
74
75
  if (text == null) {
75
76
  return {
@@ -80,6 +81,7 @@ async function inspectOrRepairConfig(candidate, fix) {
80
81
  backup_path: null,
81
82
  agent_config_files_repaired: [],
82
83
  stale_mcp_blocks_removed: [],
84
+ mcp_blocks_repaired: [],
83
85
  optional_mcp_blocks_ignored: [],
84
86
  blockers: [],
85
87
  warnings: candidate.scope === 'global' ? ['codex_home_config_missing_optional'] : []
@@ -88,6 +90,7 @@ async function inspectOrRepairConfig(candidate, fix) {
88
90
  let next = text;
89
91
  const agentConfigFilesRepaired = [];
90
92
  const staleMcpBlocksRemoved = [];
93
+ const mcpBlocksRepaired = [];
91
94
  const optionalMcpBlocksIgnored = [];
92
95
  const blockers = [];
93
96
  const warnings = [];
@@ -111,19 +114,11 @@ async function inspectOrRepairConfig(candidate, fix) {
111
114
  next = replaceOrInsertKey(next, table, 'config_file', `"${escapeToml(target)}"`);
112
115
  agentConfigFilesRepaired.push(target);
113
116
  }
114
- for (const server of ['node_repl']) {
115
- const table = tomlBlock(next, `mcp_servers.${server}`);
116
- if (!table)
117
- continue;
118
- const command = stringValue(table.text, 'command');
119
- if (!command || await commandExists(command))
120
- continue;
121
- warnings.push(`stale_mcp_command_missing:${server}`);
122
- if (fix) {
123
- next = removeTomlBlock(next, table);
124
- staleMcpBlocksRemoved.push(server);
125
- }
126
- }
117
+ const nodeReplRepair = await inspectOrRepairNodeRepl(next, fix, nodeReplCommandCandidates, includeDefaultNodeReplCandidates);
118
+ next = nodeReplRepair.text;
119
+ warnings.push(...nodeReplRepair.warnings);
120
+ staleMcpBlocksRemoved.push(...nodeReplRepair.removed);
121
+ mcpBlocksRepaired.push(...nodeReplRepair.repaired);
127
122
  for (const server of ['supabase_sauron']) {
128
123
  if (tomlBlock(next, `mcp_servers.${server}`))
129
124
  optionalMcpBlocksIgnored.push(server);
@@ -147,11 +142,60 @@ async function inspectOrRepairConfig(candidate, fix) {
147
142
  backup_path: backupPath,
148
143
  agent_config_files_repaired: agentConfigFilesRepaired,
149
144
  stale_mcp_blocks_removed: staleMcpBlocksRemoved,
145
+ mcp_blocks_repaired: mcpBlocksRepaired,
150
146
  optional_mcp_blocks_ignored: optionalMcpBlocksIgnored,
151
147
  blockers,
152
148
  warnings
153
149
  };
154
150
  }
151
+ async function inspectOrRepairNodeRepl(text, fix, extraCandidates, includeDefaultCandidates) {
152
+ const server = 'node_repl';
153
+ const table = tomlBlock(text, `mcp_servers.${server}`);
154
+ const fullTable = tomlBlockWithChildren(text, `mcp_servers.${server}`);
155
+ const childBlocks = tomlChildBlocks(text, `mcp_servers.${server}`);
156
+ if (!table && childBlocks.length === 0)
157
+ return { text, warnings: [], removed: [], repaired: [] };
158
+ const command = table ? stringValue(table.text, 'command') : null;
159
+ if (command && await commandExists(command)) {
160
+ return { text, warnings: [], removed: [], repaired: [] };
161
+ }
162
+ const warnings = [table ? `stale_mcp_command_missing:${server}` : `stale_mcp_orphan_children:${server}`];
163
+ if (!fix)
164
+ return { text, warnings, removed: [], repaired: [] };
165
+ const replacement = await firstExistingNodeReplCommand(text, extraCandidates, includeDefaultCandidates);
166
+ if (replacement) {
167
+ if (table) {
168
+ return {
169
+ text: replaceOrInsertKey(text, table, 'command', `"${escapeToml(replacement)}"`),
170
+ warnings,
171
+ removed: [],
172
+ repaired: [server]
173
+ };
174
+ }
175
+ if (childBlocks.length) {
176
+ const firstChild = childBlocks[0];
177
+ if (!firstChild)
178
+ return { text, warnings, removed: [], repaired: [] };
179
+ const mainBlock = `[mcp_servers.${server}]\ncommand = "${escapeToml(replacement)}"\nargs = []\n\n`;
180
+ return {
181
+ text: `${text.slice(0, firstChild.start).trimEnd()}${firstChild.start > 0 ? '\n\n' : ''}${mainBlock}${text.slice(firstChild.start).replace(/^\n+/, '')}`,
182
+ warnings,
183
+ removed: [],
184
+ repaired: [server]
185
+ };
186
+ }
187
+ }
188
+ const removalBlocks = [
189
+ ...(fullTable ? [fullTable] : table ? [table] : []),
190
+ ...childBlocks.filter((block) => !fullTable || block.start < fullTable.start || block.end > fullTable.end)
191
+ ];
192
+ return {
193
+ text: removeBlocks(text, removalBlocks),
194
+ warnings,
195
+ removed: [server],
196
+ repaired: []
197
+ };
198
+ }
155
199
  async function inspectAgentRoleFiles(root, codexHome) {
156
200
  const dirs = [path.join(root, '.codex', 'agents'), path.join(codexHome, 'agents')];
157
201
  const sanitized = [];
@@ -206,9 +250,38 @@ function tomlBlock(text, table) {
206
250
  const end = nextHeader >= 0 ? header.lastIndex + nextHeader : text.length;
207
251
  return { start, end, text: text.slice(start, end) };
208
252
  }
253
+ function tomlBlockWithChildren(text, table) {
254
+ const header = new RegExp(`(^|\\n)\\s*\\[${escapeRegExp(table)}\\]\\s*(?:#.*)?(?:\\n|$)`, 'g');
255
+ const match = header.exec(text);
256
+ if (!match)
257
+ return null;
258
+ const start = match.index + (match[1] ? 1 : 0);
259
+ const rest = text.slice(header.lastIndex);
260
+ const nextHeader = rest.search(new RegExp(`\\n\\s*\\[(?!${escapeRegExp(table)}(?:\\.|\\]))[^\\]]+\\]\\s*(?:#.*)?(?:\\n|$)`));
261
+ const end = nextHeader >= 0 ? header.lastIndex + nextHeader : text.length;
262
+ return { start, end, text: text.slice(start, end) };
263
+ }
264
+ function tomlChildBlocks(text, table) {
265
+ const blocks = [];
266
+ const header = new RegExp(`(^|\\n)\\s*\\[${escapeRegExp(table)}\\.[^\\]]+\\]\\s*(?:#.*)?(?:\\n|$)`, 'g');
267
+ let match;
268
+ while ((match = header.exec(text))) {
269
+ const start = match.index + (match[1] ? 1 : 0);
270
+ const rest = text.slice(header.lastIndex);
271
+ const nextHeader = rest.search(new RegExp(`\\n\\s*\\[(?!${escapeRegExp(table)}\\.)[^\\]]+\\]\\s*(?:#.*)?(?:\\n|$)`));
272
+ const end = nextHeader >= 0 ? header.lastIndex + nextHeader : text.length;
273
+ blocks.push({ start, end, text: text.slice(start, end) });
274
+ }
275
+ return blocks;
276
+ }
209
277
  function removeTomlBlock(text, block) {
210
278
  return `${text.slice(0, block.start).trimEnd()}${block.start > 0 ? '\n\n' : ''}${text.slice(block.end).replace(/^\n+/, '')}`;
211
279
  }
280
+ function removeBlocks(text, blocks) {
281
+ return [...blocks]
282
+ .sort((a, b) => b.start - a.start)
283
+ .reduce((current, block) => removeTomlBlock(current, block), text);
284
+ }
212
285
  function replaceOrInsertKey(text, block, key, encodedValue) {
213
286
  const lines = block.text.replace(/\s*$/, '').split('\n');
214
287
  const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
@@ -233,6 +306,45 @@ async function commandExists(command) {
233
306
  return true;
234
307
  return false;
235
308
  }
309
+ async function firstExistingNodeReplCommand(configText, extraCandidates, includeDefaultCandidates) {
310
+ const candidates = [
311
+ ...extraCandidates,
312
+ ...(includeDefaultCandidates ? [
313
+ process.env.SKS_NODE_REPL_COMMAND,
314
+ process.env.NODE_REPL_COMMAND,
315
+ ...nodeReplCandidatesFromNodePaths([
316
+ ...stringValues(configText, 'NODE_REPL_NODE_PATH'),
317
+ process.env.NODE_REPL_NODE_PATH
318
+ ]),
319
+ '/Applications/Codex.app/Contents/Resources/cua_node/bin/node_repl',
320
+ '/Applications/Codex.app/Contents/Resources/node_repl'
321
+ ] : [])
322
+ ]
323
+ .map((item) => String(item || '').trim())
324
+ .filter(Boolean);
325
+ for (const candidate of [...new Set(candidates)]) {
326
+ if (await commandExists(candidate))
327
+ return candidate;
328
+ }
329
+ return null;
330
+ }
331
+ function nodeReplCandidatesFromNodePaths(values) {
332
+ const out = [];
333
+ for (const value of values) {
334
+ const nodePath = String(value || '').trim();
335
+ if (!nodePath)
336
+ continue;
337
+ const dir = path.dirname(nodePath);
338
+ out.push(path.join(dir, 'node_repl'));
339
+ const resources = path.basename(dir) === 'bin' ? path.dirname(path.dirname(dir)) : dir;
340
+ out.push(path.join(resources, 'cua_node', 'bin', 'node_repl'));
341
+ }
342
+ return out;
343
+ }
344
+ function stringValues(text, key) {
345
+ const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"`, 'gm');
346
+ return [...text.matchAll(re)].map((match) => String(match[1] || '')).filter(Boolean);
347
+ }
236
348
  async function backupConfig(configPath, text, label) {
237
349
  try {
238
350
  const backupPath = `${configPath}.sks-${label}-${Date.now().toString(36)}.bak`;
@@ -49,6 +49,26 @@ async function inspectOrRepairContext7Config(candidate, fix) {
49
49
  if (!block)
50
50
  return baseConfig(candidate, { present: true, status: 'missing' });
51
51
  if (/\burl\s*=\s*["']https:\/\/mcp\.context7\.com\/mcp["']/.test(block.text)) {
52
+ const childBlocks = context7ChildBlocks(text);
53
+ if (childBlocks.length) {
54
+ if (!fix) {
55
+ return baseConfig(candidate, {
56
+ present: true,
57
+ status: 'remote_child_env_detected',
58
+ warnings: ['remote_context7_child_env_unsupported_by_streamable_http']
59
+ });
60
+ }
61
+ const next = removeBlocks(text, childBlocks).replace(/\s*$/, '\n');
62
+ const backupPath = await backupConfig(candidate.path, text);
63
+ await writeTextAtomic(candidate.path, next);
64
+ return baseConfig(candidate, {
65
+ present: true,
66
+ status: 'repaired_to_remote',
67
+ changed: true,
68
+ backup_path: backupPath,
69
+ warnings: ['remote_context7_child_env_removed']
70
+ });
71
+ }
52
72
  return baseConfig(candidate, { present: true, status: 'already_remote' });
53
73
  }
54
74
  const localStdio = /@upstash\/context7-mcp|context7-mcp|command\s*=\s*["']npx(?:\s|["'])/i.test(block.text);
@@ -68,7 +88,8 @@ async function inspectOrRepairContext7Config(candidate, fix) {
68
88
  });
69
89
  }
70
90
  const remoteBlock = `[mcp_servers.context7]\nurl = "${CONTEXT7_REMOTE_URL}"\n`;
71
- const next = `${text.slice(0, block.start).trimEnd()}${block.start > 0 ? '\n\n' : ''}${remoteBlock}${text.slice(block.end).replace(/^\n+/, '\n')}`.replace(/\s*$/, '\n');
91
+ const withRemote = `${text.slice(0, block.start).trimEnd()}${block.start > 0 ? '\n\n' : ''}${remoteBlock}${text.slice(block.end).replace(/^\n+/, '\n')}`.replace(/\s*$/, '\n');
92
+ const next = removeBlocks(withRemote, context7ChildBlocks(withRemote)).replace(/\s*$/, '\n');
72
93
  const backupPath = await backupConfig(candidate.path, text);
73
94
  await writeTextAtomic(candidate.path, next);
74
95
  return baseConfig(candidate, {
@@ -79,6 +100,24 @@ async function inspectOrRepairContext7Config(candidate, fix) {
79
100
  warnings: ['local_stdio_context7_replaced_with_remote_mcp']
80
101
  });
81
102
  }
103
+ function context7ChildBlocks(text) {
104
+ const blocks = [];
105
+ const header = /(^|\n)\s*\[mcp_servers\.context7\.[^\]]+\]\s*(?:#.*)?(?:\n|$)/g;
106
+ let match;
107
+ while ((match = header.exec(text))) {
108
+ const start = match.index + (match[1] ? 1 : 0);
109
+ const rest = text.slice(header.lastIndex);
110
+ const nextHeader = rest.search(/\n\s*\[[^\]]+\]\s*(?:#.*)?(?:\n|$)/);
111
+ const end = nextHeader >= 0 ? header.lastIndex + nextHeader : text.length;
112
+ blocks.push({ start, end, text: text.slice(start, end) });
113
+ }
114
+ return blocks;
115
+ }
116
+ function removeBlocks(text, blocks) {
117
+ return [...blocks]
118
+ .sort((a, b) => b.start - a.start)
119
+ .reduce((current, block) => `${current.slice(0, block.start).trimEnd()}${block.start > 0 ? '\n\n' : ''}${current.slice(block.end).replace(/^\n+/, '')}`, text);
120
+ }
82
121
  function context7Block(text) {
83
122
  const header = /(^|\n)\s*\[mcp_servers\.context7\]\s*(?:#.*)?(?:\n|$)/g;
84
123
  const match = header.exec(text);
@@ -0,0 +1,11 @@
1
+ import {} from './doctor-transaction.js';
2
+ export function doctorRepairPostcheck(transaction) {
3
+ return {
4
+ schema: 'sks.doctor-repair-postcheck.v1',
5
+ ok: transaction?.postcheck_ok === true,
6
+ transaction_ok: transaction?.ok === true,
7
+ manual_required: (transaction?.phases || []).filter((phase) => phase.manual_required).map((phase) => phase.id),
8
+ blockers: (transaction?.phases || []).flatMap((phase) => phase.blockers)
9
+ };
10
+ }
11
+ //# sourceMappingURL=doctor-repair-postcheck.js.map
@@ -0,0 +1,30 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ export async function writeDoctorFixTransaction(input) {
4
+ const root = path.resolve(input.root);
5
+ const phases = input.phases.map((phase) => ({
6
+ id: phase.id,
7
+ ok: phase.ok === true,
8
+ repaired: phase.repaired === true,
9
+ manual_required: phase.manual_required === true,
10
+ blockers: phase.blockers || [],
11
+ warnings: phase.warnings || [],
12
+ artifact_path: phase.artifact_path || null
13
+ }));
14
+ const postcheckOk = phases.every((phase) => phase.ok || phase.manual_required);
15
+ const report = {
16
+ schema: 'sks.doctor-fix-transaction.v1',
17
+ ok: postcheckOk,
18
+ root,
19
+ started_at: input.startedAt || nowIso(),
20
+ completed_at: nowIso(),
21
+ phases,
22
+ postcheck_ok: postcheckOk,
23
+ rollback_performed: input.rollbackPerformed === true,
24
+ raw_secret_values_recorded: false
25
+ };
26
+ if (input.reportPath !== null)
27
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'doctor-fix-transaction.json'), report).catch(() => undefined);
28
+ return report;
29
+ }
30
+ //# sourceMappingURL=doctor-transaction.js.map
@@ -0,0 +1,36 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { mcpServerBlock, mcpServerExplicitlyDisabled, readProjectCodexConfig } from '../mcp/mcp-config-preservation.js';
4
+ export async function repairSupabaseMcp(input) {
5
+ const root = path.resolve(input.root);
6
+ const config = await readProjectCodexConfig(root);
7
+ const disabled = mcpServerExplicitlyDisabled(config.text, 'supabase') || mcpServerExplicitlyDisabled(config.text, 'supabase_sauron');
8
+ const block = mcpServerBlock(config.text, 'supabase') || '';
9
+ const configured = Boolean(block);
10
+ const tokenEnvPresent = Boolean(process.env.SUPABASE_ACCESS_TOKEN);
11
+ const unsafeWriteAccess = configured && !/read[_-]?only\s*=\s*true|access_mode\s*=\s*"read-only"|--read-only/.test(block) && /write|service_role|SUPABASE_ACCESS_TOKEN/.test(block);
12
+ const manualRequired = configured && !disabled && (!tokenEnvPresent || unsafeWriteAccess);
13
+ const report = {
14
+ schema: 'sks.doctor-supabase-mcp-repair.v1',
15
+ generated_at: nowIso(),
16
+ ok: !configured || disabled || !unsafeWriteAccess,
17
+ apply: input.apply === true,
18
+ configured,
19
+ disabled,
20
+ token_env_present: tokenEnvPresent,
21
+ unsafe_write_access: unsafeWriteAccess,
22
+ manual_required: manualRequired,
23
+ next_action: manualRequired
24
+ ? tokenEnvPresent
25
+ ? 'Set Supabase MCP to read-only or explicitly approve write-scoped MCP use.'
26
+ : 'Set SUPABASE_ACCESS_TOKEN only if Supabase write MCP features are required; otherwise keep Supabase MCP disabled/read-only.'
27
+ : null,
28
+ blockers: unsafeWriteAccess ? ['supabase_mcp_write_access_not_safe_by_default'] : [],
29
+ warnings: configured && !tokenEnvPresent ? ['supabase_access_token_unset_write_features_manual_required'] : [],
30
+ raw_secret_values_recorded: false
31
+ };
32
+ if (input.reportPath !== null)
33
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'doctor-supabase-mcp-repair.json'), report).catch(() => undefined);
34
+ return report;
35
+ }
36
+ //# sourceMappingURL=supabase-mcp-repair.js.map
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '3.1.11';
8
+ export const PACKAGE_VERSION = '3.1.12';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -0,0 +1,30 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export const CONTEXT7_REMOTE_MCP_URL = 'https://mcp.context7.com/mcp';
4
+ export async function readProjectCodexConfig(root) {
5
+ const file = path.join(path.resolve(root), '.codex', 'config.toml');
6
+ const text = await fs.readFile(file, 'utf8').catch(() => '');
7
+ return { path: file, text };
8
+ }
9
+ export function mcpServerBlock(text, serverName) {
10
+ const escaped = serverName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11
+ const re = new RegExp(`(^\\[mcp_servers\\.${escaped}\\]\\n[\\s\\S]*?)(?=^\\[|(?![\\s\\S]))`, 'm');
12
+ return String(text || '').match(re)?.[1] || null;
13
+ }
14
+ export function mcpServerExplicitlyDisabled(text, serverName) {
15
+ const block = mcpServerBlock(text, serverName);
16
+ return Boolean(block && /^\s*disabled\s*=\s*true\s*$/m.test(block));
17
+ }
18
+ export function replaceOrAppendMcpServerBlock(text, serverName, block) {
19
+ const escaped = serverName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
+ const normalizedBlock = block.endsWith('\n') ? block : `${block}\n`;
21
+ const re = new RegExp(`(^\\[mcp_servers\\.${escaped}\\]\\n[\\s\\S]*?)(?=^\\[|(?![\\s\\S]))`, 'm');
22
+ if (re.test(text))
23
+ return text.replace(re, normalizedBlock);
24
+ const prefix = text.trim() ? `${text.replace(/\s*$/, '\n\n')}` : '';
25
+ return `${prefix}${normalizedBlock}`;
26
+ }
27
+ export function redactedMcpText(text) {
28
+ return String(text || '').replace(/(token|access_token|api_key|secret)\s*=\s*"[^"]*"/gi, '$1 = "<redacted>"');
29
+ }
30
+ //# sourceMappingURL=mcp-config-preservation.js.map
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '3.1.11';
1
+ export const PACKAGE_VERSION = '3.1.12';
2
2
  //# sourceMappingURL=version.js.map
@@ -311,6 +311,20 @@ export async function openWorkerPane(input) {
311
311
  const stdoutPaneId = launch?.ok ? extractZellijPaneIdFromOutput(launch.stdout_tail) : null;
312
312
  const reconciledPane = stdoutPaneId ? null : launch?.ok ? await reconcileZellijWorkerPaneId(input.sessionName, paneName, path.join(root, input.resultPath), cwd) : null;
313
313
  const paneId = stdoutPaneId || reconciledPane?.pane_id || null;
314
+ const stackPaneIds = [...new Set([
315
+ ...(freshState?.visible_worker_panes || [])
316
+ .filter((pane) => pane.pane_id && (pane.status === 'launching' || pane.status === 'running'))
317
+ .sort((a, b) => Number(a.y_order || 0) - Number(b.y_order || 0))
318
+ .map((pane) => String(pane.pane_id)),
319
+ paneId || ''
320
+ ].filter(Boolean))];
321
+ const stackPanes = stackRequested && paneId && stackPaneIds.length >= 2
322
+ ? await runZellij(['--session', input.sessionName, 'action', 'stack-panes', '--', ...stackPaneIds], {
323
+ cwd,
324
+ timeoutMs: 5000,
325
+ optional: true
326
+ })
327
+ : null;
314
328
  const renamePane = paneId ? await renameZellijPaneById(input.sessionName, paneId, paneName, cwd) : null;
315
329
  const paneIdSource = stdoutPaneId
316
330
  ? 'zellij_worker_new_pane_stdout'
@@ -329,7 +343,8 @@ export async function openWorkerPane(input) {
329
343
  ];
330
344
  const warnings = [
331
345
  ...(stackIntent && stackedCapability && !stackedCapability.supports_stacked_panes ? [`zellij_stacked_pane_fallback:${stackedCapability.fallback_mode}`] : []),
332
- ...(stackedRejectedFallback ? ['zellij_stacked_pane_rejected_fallback_down'] : [])
346
+ ...(stackedRejectedFallback ? ['zellij_stacked_pane_rejected_fallback_down'] : []),
347
+ ...(stackPanes && !stackPanes.ok ? stackPanes.blockers.map((blocker) => `zellij_stack_panes_reconcile_${blocker}`) : [])
333
348
  ];
334
349
  const record = buildWorkerPaneArtifact({
335
350
  ...input,
@@ -343,7 +358,9 @@ export async function openWorkerPane(input) {
343
358
  focus_degraded: focus ? focus.ok !== true : false,
344
359
  slot_column_anchor_pane_id: slotColumnAnchorPaneId,
345
360
  slot_column_anchor_launch: anchorLaunch,
346
- rename_pane: renamePane
361
+ rename_pane: renamePane,
362
+ stack_panes: stackPanes,
363
+ stack_pane_ids: stackPaneIds
347
364
  },
348
365
  directionRequested,
349
366
  directionApplied,
@@ -0,0 +1,12 @@
1
+ import { assertGate, emitGate } from './sks-1-18-gate-lib.js';
2
+ import { detectCodex0140Capability } from '../core/codex-control/codex-0140-capability.js';
3
+ export async function runCodex0140FeatureGate(gate, feature) {
4
+ process.env.SKS_CODEX_0140_FAKE = '1';
5
+ process.env.SKS_CODEX_VERSION_FAKE = 'codex-cli 0.140.0';
6
+ process.env.SKS_CODEX_0140_PROBE = '1';
7
+ const cap = await detectCodex0140Capability({ codexBin: 'codex' });
8
+ assertGate(cap.ok === true && cap.supports_0140 === true, `${gate} requires passing Codex 0.140 capability fixture`, cap);
9
+ assertGate(cap.features[feature] === true, `${gate} requires feature ${feature}`, cap);
10
+ emitGate(gate, { feature, schema: cap.schema });
11
+ }
12
+ //# sourceMappingURL=codex-0140-feature-gate-lib.js.map
@@ -0,0 +1,30 @@
1
+ export const REQUIRED_3112_RELEASE_IDS = [
2
+ 'release:gate-script-parity',
3
+ 'release:wiring-3112-blackbox',
4
+ 'codex:0140-capability',
5
+ 'codex:0140-feature-probes',
6
+ 'codex:0140-usage',
7
+ 'codex:0140-goal-attachment-preservation',
8
+ 'codex:0140-session-delete',
9
+ 'codex:0140-import',
10
+ 'codex:0140-unified-mentions',
11
+ 'codex:0140-bedrock-managed-auth',
12
+ 'codex:0140-mcp-reliability',
13
+ 'codex:0140-sqlite-recovery',
14
+ 'codex:0140-non-tty-interrupt',
15
+ 'codex:0140-large-repo-performance',
16
+ 'pipeline:codex-0140-integration',
17
+ 'codex:0140-integration-blackbox',
18
+ 'doctor:fix-production-blackbox',
19
+ 'doctor:startup-config-repair',
20
+ 'doctor:startup-config-repair-blackbox',
21
+ 'doctor:context7-mcp-repair',
22
+ 'doctor:context7-mcp-repair-blackbox',
23
+ 'doctor:supabase-mcp-repair',
24
+ 'doctor:supabase-mcp-repair-blackbox',
25
+ 'sks:3112-all-feature-regression'
26
+ ];
27
+ export const REQUIRED_3112_REAL_CHECK_IDS = [
28
+ 'codex:0140-real-probes:require-real'
29
+ ];
30
+ //# sourceMappingURL=release-3112-required-gates.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "3.1.11",
4
+ "version": "3.1.12",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
@@ -23,6 +23,8 @@
23
23
  },
24
24
  "files": [
25
25
  "dist",
26
+ "!dist/*.json",
27
+ "!dist/.sks-build-stamp.json",
26
28
  "!dist/build-manifest.json",
27
29
  "!dist/**/*.d.ts",
28
30
  "!dist/**/*.d.ts.map",
@@ -922,7 +924,33 @@
922
924
  "doctor:codex-native-readiness-ux": "node ./dist/scripts/doctor-codex-native-readiness-ux-check.js",
923
925
  "doctor:codex-native-repair-actions": "node ./dist/scripts/doctor-codex-native-repair-actions-check.js",
924
926
  "codex-native:feature-broker-blackbox": "node ./dist/scripts/codex-native-feature-broker-blackbox.js",
925
- "pipeline:codex-native-e2e-blackbox": "node ./dist/scripts/pipeline-codex-native-e2e-blackbox.js"
927
+ "pipeline:codex-native-e2e-blackbox": "node ./dist/scripts/pipeline-codex-native-e2e-blackbox.js",
928
+ "release:gate-script-parity": "node ./dist/scripts/release-gate-script-parity-check.js",
929
+ "release:wiring-3112-blackbox": "node ./dist/scripts/release-wiring-3112-blackbox.js",
930
+ "codex:0140-capability": "node ./dist/scripts/codex-0140-capability-check.js",
931
+ "codex:0140-feature-probes": "node ./dist/scripts/codex-0140-feature-probes-check.js",
932
+ "codex:0140-usage": "node ./dist/scripts/codex-0140-usage-check.js",
933
+ "codex:0140-goal-attachment-preservation": "node ./dist/scripts/codex-0140-goal-attachment-preservation-check.js",
934
+ "codex:0140-session-delete": "node ./dist/scripts/codex-0140-session-delete-check.js",
935
+ "codex:0140-import": "node ./dist/scripts/codex-0140-import-check.js",
936
+ "codex:0140-unified-mentions": "node ./dist/scripts/codex-0140-unified-mentions-check.js",
937
+ "codex:0140-bedrock-managed-auth": "node ./dist/scripts/codex-0140-bedrock-managed-auth-check.js",
938
+ "codex:0140-mcp-reliability": "node ./dist/scripts/codex-0140-mcp-reliability-check.js",
939
+ "codex:0140-sqlite-recovery": "node ./dist/scripts/codex-0140-sqlite-recovery-check.js",
940
+ "codex:0140-non-tty-interrupt": "node ./dist/scripts/codex-0140-non-tty-interrupt-check.js",
941
+ "codex:0140-large-repo-performance": "node ./dist/scripts/codex-0140-large-repo-performance-check.js",
942
+ "codex:0140-real-probes": "node ./dist/scripts/codex-0140-real-probes-check.js",
943
+ "codex:0140-real-probes:require-real": "node ./dist/scripts/codex-0140-real-probes-check.js --require-real --allow-network",
944
+ "pipeline:codex-0140-integration": "node ./dist/scripts/pipeline-codex-0140-integration-check.js",
945
+ "codex:0140-integration-blackbox": "node ./dist/scripts/codex-0140-integration-blackbox.js",
946
+ "doctor:fix-production-blackbox": "node ./dist/scripts/doctor-fix-production-blackbox.js",
947
+ "doctor:startup-config-repair": "node ./dist/scripts/doctor-startup-config-repair-check.js",
948
+ "doctor:startup-config-repair-blackbox": "node ./dist/scripts/doctor-startup-config-repair-blackbox.js",
949
+ "doctor:context7-mcp-repair": "node ./dist/scripts/doctor-context7-mcp-repair-check.js",
950
+ "doctor:context7-mcp-repair-blackbox": "node ./dist/scripts/doctor-context7-mcp-repair-blackbox.js",
951
+ "doctor:supabase-mcp-repair": "node ./dist/scripts/doctor-supabase-mcp-repair-check.js",
952
+ "doctor:supabase-mcp-repair-blackbox": "node ./dist/scripts/doctor-supabase-mcp-repair-blackbox.js",
953
+ "sks:3112-all-feature-regression": "node ./dist/scripts/sks-3112-all-feature-regression-blackbox.js"
926
954
  },
927
955
  "keywords": [
928
956
  "sneakoscope",
@@ -1,8 +0,0 @@
1
- {
2
- "schema": "sks.dist-build-stamp.v1",
3
- "package_name": "sneakoscope",
4
- "package_version": "3.1.11",
5
- "source_digest": "932de0e70bb2a5b787aef2782fa56cbc4956c6077d099d9e1067b9406fd8412a",
6
- "source_file_count": 2613,
7
- "built_at_source_time": 1781554790419
8
- }