sneakoscope 5.12.0 → 6.0.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 (141) hide show
  1. package/README.md +1 -1
  2. package/config/bench-baseline.json +3 -3
  3. package/config/perf-budgets.v1.json +11 -6
  4. package/crates/sks-core/Cargo.lock +1 -1
  5. package/crates/sks-core/Cargo.toml +1 -1
  6. package/crates/sks-core/src/main.rs +1 -1
  7. package/dist/bin/fast-inline.js +35 -0
  8. package/dist/bin/sks-dispatch.js +21 -0
  9. package/dist/bin/sks.js +2 -2
  10. package/dist/cli/codex-app-command.js +2 -2
  11. package/dist/cli/install-helpers.js +75 -71
  12. package/dist/cli/router.js +43 -26
  13. package/dist/commands/doctor.js +3 -4
  14. package/dist/config/skills-manifest.json +57 -57
  15. package/dist/core/agents/agent-effort-policy.js +9 -9
  16. package/dist/core/agents/native-cli-session-swarm.js +0 -1
  17. package/dist/core/agents/native-cli-worker.js +0 -6
  18. package/dist/core/auto-review.js +7 -7
  19. package/dist/core/codex/agent-config-file-repair.js +1 -4
  20. package/dist/core/codex/codex-config-guard.js +8 -8
  21. package/dist/core/codex/codex-config-toml.js +17 -35
  22. package/dist/core/codex/codex-project-config-policy.js +8 -4
  23. package/dist/core/codex-adapter.js +2 -2
  24. package/dist/core/codex-app/codex-agent-type-probe.js +29 -9
  25. package/dist/core/codex-app/codex-app-fast-ui-repair.js +3 -2
  26. package/dist/core/codex-app.js +5 -5
  27. package/dist/core/codex-control/codex-model-capabilities.js +2 -1
  28. package/dist/core/codex-control/codex-model-metadata.js +3 -2
  29. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  30. package/dist/core/codex-control/codex-task-runner.js +7 -2
  31. package/dist/core/codex-control/codex-thread-registry.js +8 -26
  32. package/dist/core/codex-model-guard.js +52 -6
  33. package/dist/core/commands/fast-mode-command.js +1 -1
  34. package/dist/core/commands/mad-db-command.js +1 -1
  35. package/dist/core/commands/mad-sks-command.js +36 -1
  36. package/dist/core/commands/naruto-command.js +127 -37
  37. package/dist/core/commands/profile-command.js +7 -6
  38. package/dist/core/commands/run-command.js +29 -8
  39. package/dist/core/commands/ui-command.js +42 -4
  40. package/dist/core/daemon/sksd-hook-daemon-entrypoint.js +12 -0
  41. package/dist/core/daemon/sksd-hook-daemon.js +178 -0
  42. package/dist/core/daemon/sksd-hook-dispatch.js +36 -0
  43. package/dist/core/doctor/doctor-codex-startup-repair.js +2 -2
  44. package/dist/core/fsx.js +11 -2
  45. package/dist/core/hooks-runtime/hook-io.js +132 -0
  46. package/dist/core/hooks-runtime.js +5 -126
  47. package/dist/core/image-ux-review/imagegen-adapter.js +2 -1
  48. package/dist/core/init/skills.js +8 -7
  49. package/dist/core/init.js +36 -22
  50. package/dist/core/locks/file-lock.js +96 -16
  51. package/dist/core/mad-db/mad-db-coordinator.js +11 -3
  52. package/dist/core/mad-db/mad-db-executor.js +17 -1
  53. package/dist/core/mad-db/mad-db-ledger.js +7 -1
  54. package/dist/core/mad-db/mad-db-lock.js +8 -30
  55. package/dist/core/mad-db/mad-db-policy-resolver.js +2 -0
  56. package/dist/core/mad-db/mad-db-postconditions.js +19 -4
  57. package/dist/core/mad-db/mad-db-recovery.js +2 -0
  58. package/dist/core/mad-db/mad-db-runtime-profile.js +8 -0
  59. package/dist/core/mad-sks/executors/db-write-executor.js +11 -7
  60. package/dist/core/mad-sks/executors/executor-base.js +15 -2
  61. package/dist/core/mad-sks/executors/file-write-executor.js +3 -1
  62. package/dist/core/mad-sks/executors/package-install-executor.js +3 -1
  63. package/dist/core/mad-sks/executors/service-control-executor.js +3 -1
  64. package/dist/core/mad-sks/executors/shell-command-executor.js +3 -1
  65. package/dist/core/mad-sks/executors/sql-plane-executor.js +9 -1
  66. package/dist/core/managed-assets/managed-assets-manifest.js +2 -2
  67. package/dist/core/naruto/naruto-active-pool.js +2 -0
  68. package/dist/core/naruto/naruto-real-worker-child.js +1 -0
  69. package/dist/core/naruto/naruto-real-worker-runtime.js +54 -3
  70. package/dist/core/naruto/naruto-real-write-proof.js +9 -1
  71. package/dist/core/naruto/naruto-verification-dag.js +15 -2
  72. package/dist/core/naruto/naruto-write-e2e.js +1 -0
  73. package/dist/core/perf/perf-budget.js +8 -32
  74. package/dist/core/pipeline-internals/runtime-core.js +1 -1
  75. package/dist/core/pipeline-internals/runtime-gates.js +1 -1
  76. package/dist/core/proof/evidence-collector.js +2 -0
  77. package/dist/core/proof/route-finalizer.js +1 -0
  78. package/dist/core/provider/model-router.js +6 -5
  79. package/dist/core/release/release-gate-affected-selector.js +7 -3
  80. package/dist/core/routes/constants.js +5 -1
  81. package/dist/core/routes/dollar-manifest-lite.js +3 -3
  82. package/dist/core/routes.js +11 -1
  83. package/dist/core/update/update-migration-state.js +80 -23
  84. package/dist/core/version.js +1 -1
  85. package/dist/scripts/agent-role-config-repair-check.js +4 -2
  86. package/dist/scripts/build-dist.js +3 -1
  87. package/dist/scripts/codex-app-ui-preservation-check.js +19 -10
  88. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +6 -7
  89. package/dist/scripts/codex-lb-gpt55-fast-profile-check.js +6 -7
  90. package/dist/scripts/codex-sdk-all-pipelines-check.js +1 -3
  91. package/dist/scripts/coding-bench-check.js +8 -2
  92. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +2 -2
  93. package/dist/scripts/install-update-preserves-config-check.js +7 -2
  94. package/dist/scripts/naruto-actual-worker-control-plane-check.js +1 -1
  95. package/dist/scripts/naruto-extreme-parallelism-real-check.js +3 -1
  96. package/dist/scripts/naruto-real-active-pool-runtime-check.js +6 -1
  97. package/dist/scripts/release-affected-selector-check.js +4 -4
  98. package/dist/scripts/release-gate-dag-runner-check.js +4 -6
  99. package/dist/scripts/release-provenance-check.js +4 -1
  100. package/dist/scripts/release-version-truth-check.js +8 -2
  101. package/dist/scripts/sizecheck.js +6 -3
  102. package/package.json +3 -3
  103. package/dist/scripts/agent-codex-child-overlap-check.js +0 -21
  104. package/dist/scripts/agent-model-authored-patch-envelope-check.js +0 -15
  105. package/dist/scripts/agent-native-cli-session-proof-check.js +0 -7
  106. package/dist/scripts/agent-no-subagent-scaling-check.js +0 -7
  107. package/dist/scripts/brand-neutrality-rename-map-check.js +0 -4
  108. package/dist/scripts/changelog-loop-productionization-check.js +0 -3
  109. package/dist/scripts/cli-check-tiers-check.js +0 -10
  110. package/dist/scripts/codex-0139-code-mode-web-search-check.js +0 -25
  111. package/dist/scripts/codex-agent-type-probe-check.js +0 -4
  112. package/dist/scripts/codex-native-interop-policy-check.js +0 -4
  113. package/dist/scripts/codex-sdk-core-skill-pipeline-check.js +0 -9
  114. package/dist/scripts/codex-sdk-dfix-pipeline-check.js +0 -9
  115. package/dist/scripts/core-skill-legacy-promotion-api-audit.js +0 -54
  116. package/dist/scripts/docs-brand-neutrality-check.js +0 -4
  117. package/dist/scripts/doctor-codex-0138-fix-check.js +0 -10
  118. package/dist/scripts/doctor-codex-0139-real-probes-check.js +0 -25
  119. package/dist/scripts/doctor-codex-doctor-parity-check.js +0 -17
  120. package/dist/scripts/doctor-codex-native-readiness-ux-check.js +0 -29
  121. package/dist/scripts/doctor-codex-native-repair-actions-check.js +0 -24
  122. package/dist/scripts/gate-pack-manifest-check.js +0 -9
  123. package/dist/scripts/git-worktree-batch-allocation-check.js +0 -10
  124. package/dist/scripts/github-release-body-helper.js +0 -65
  125. package/dist/scripts/local-llm-all-pipelines-check.js +0 -11
  126. package/dist/scripts/mad-db-ledger-check.js +0 -8
  127. package/dist/scripts/native-swarm-heartbeat-does-not-serialize-launch-check.js +0 -8
  128. package/dist/scripts/no-ts-nocheck-core-check.js +0 -4
  129. package/dist/scripts/orphan-gate-detection-check.js +0 -53
  130. package/dist/scripts/orphan-purge-final-check.js +0 -5
  131. package/dist/scripts/parallel-claim-enforcement-check.js +0 -30
  132. package/dist/scripts/release-batch-runner-check.js +0 -6
  133. package/dist/scripts/release-gate-planner.js +0 -55
  134. package/dist/scripts/release-runtime-truth-matrix-check.js +0 -47
  135. package/dist/scripts/research-quality-gate-check.js +0 -112
  136. package/dist/scripts/runtime-no-mjs-scripts-check.js +0 -45
  137. package/dist/scripts/scheduler-no-false-pending-block-check.js +0 -7
  138. package/dist/scripts/team-alias-to-naruto-check.js +0 -7
  139. package/dist/scripts/terminal-keyboard-enhancement-safety-check.js +0 -12
  140. package/dist/scripts/test-no-orphan-dist-imports-check.js +0 -73
  141. package/dist/scripts/update-gate-removed-check.js +0 -9
package/README.md CHANGED
@@ -25,7 +25,7 @@ sneakoscope is a proof-first Codex trust layer for bounded agent workflows, sear
25
25
 
26
26
  Sneakoscope Codex (`sks`) is a Codex CLI and Codex App harness for people who want parallel AI coding without losing proof. It gives Codex a simple front door, a dynamic worker swarm, a local dashboard, TriWiki project memory, and release gates that separate machine evidence from LLM opinion.
27
27
 
28
- Current release: SKS **5.12.0**. New in this release: operations maturity scorecards, explicit Naruto real-write proof, real CLI negative smokes for high-risk commands, doctor fast/full/fix semantics, upgrade and rollback recovery gates, diagnostics bundles, and stricter SEO metadata/strategy evidence. See [CHANGELOG.md](CHANGELOG.md).
28
+ Current release: SKS **6.0.0**. New in this release: GPT-5.6 Sol/Terra/Luna compatibility, removal of legacy `multi_agent_v2`/Fast-mode config stamps, renewed Codex App feature flags, safer menu bar and doctor repair paths, and release metadata prepared for `npm publish --ignore-scripts`. See [CHANGELOG.md](CHANGELOG.md).
29
29
 
30
30
  ## Install
31
31
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": "sks.coding-bench-baseline.v1",
3
3
  "baseline_version": "5.0.0",
4
- "pass_rate": 0,
5
- "min_pass_rate": 0,
6
- "notes": "Real model coding bench is skipped with no_backend unless SKS_RUN_CODING_BENCH_REAL=1 and a backend credential is present."
4
+ "pass_rate": 0.5,
5
+ "min_pass_rate": 0.5,
6
+ "notes": "Real model coding bench is skipped (status: not_run, ok: false) with no_backend unless SKS_RUN_CODING_BENCH_REAL=1 and a backend credential is present. A fully-skipped run can no longer report ok:true (20차 P0-5)."
7
7
  }
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "schema": "sks.perf-budgets.v1",
3
+ "_note": "budget_p50_ms/budget_p95_ms are judged against raw wall-clock time (no node-startup-baseline deduction, 20차 P0-11). Calibrated from raw measurement on a warm dev machine (2026-07-09: version/root/commands/super-search-doctor ~40-70ms, dollar-commands ~50-130ms, doctor --json fast-mode ~40ms, plan --help/status --json/commands/mad-sks --help added after P2-2/P2-5d verification ~45-167ms) with headroom for slower/CI hosts; retighten once real CI raw numbers are collected. Gate-runtime and $Swarm-overhead budgets from the 20차 P2-6 table are NOT covered here — the former needs the P3 gate consolidation (207->~30) done first, the latter needs a real-backend E2E run (P4 E2E-1) to measure against plain `codex exec`, neither of which this repo state has yet.",
3
4
  "commands": [
4
- { "name": "version", "argv": ["node", "./dist/bin/sks.js", "--version"], "budget_p50_ms": 120, "budget_p95_ms": 140 },
5
- { "name": "commands-json", "argv": ["node", "./dist/bin/sks.js", "commands", "--json"], "budget_p50_ms": 200, "budget_p95_ms": 240 },
6
- { "name": "root-json", "argv": ["node", "./dist/bin/sks.js", "root", "--json"], "budget_p50_ms": 150, "budget_p95_ms": 180 },
7
- { "name": "dollar-commands-json", "argv": ["node", "./dist/bin/sks.js", "dollar-commands", "--json"], "budget_p50_ms": 200, "budget_p95_ms": 240 },
8
- { "name": "super-search-doctor-json", "argv": ["node", "./dist/bin/sks.js", "super-search", "doctor", "--json"], "budget_p50_ms": 180, "budget_p95_ms": 220 },
9
- { "name": "doctor-json", "argv": ["node", "./dist/bin/sks.js", "doctor", "--json"], "budget_p50_ms": 1000, "budget_p95_ms": 1500 }
5
+ { "name": "version", "argv": ["node", "./dist/bin/sks.js", "--version"], "budget_p50_ms": 100, "budget_p95_ms": 150 },
6
+ { "name": "commands-json", "argv": ["node", "./dist/bin/sks.js", "commands", "--json"], "budget_p50_ms": 150, "budget_p95_ms": 220 },
7
+ { "name": "commands", "argv": ["node", "./dist/bin/sks.js", "commands"], "budget_p50_ms": 250, "budget_p95_ms": 300 },
8
+ { "name": "root-json", "argv": ["node", "./dist/bin/sks.js", "root", "--json"], "budget_p50_ms": 150, "budget_p95_ms": 220 },
9
+ { "name": "dollar-commands-json", "argv": ["node", "./dist/bin/sks.js", "dollar-commands", "--json"], "budget_p50_ms": 200, "budget_p95_ms": 300 },
10
+ { "name": "super-search-doctor-json", "argv": ["node", "./dist/bin/sks.js", "super-search", "doctor", "--json"], "budget_p50_ms": 150, "budget_p95_ms": 220 },
11
+ { "name": "doctor-json", "argv": ["node", "./dist/bin/sks.js", "doctor", "--json"], "budget_p50_ms": 200, "budget_p95_ms": 300 },
12
+ { "name": "status-json", "argv": ["node", "./dist/bin/sks.js", "status", "--json"], "budget_p50_ms": 220, "budget_p95_ms": 300 },
13
+ { "name": "plan-help", "argv": ["node", "./dist/bin/sks.js", "plan", "--help"], "budget_p50_ms": 100, "budget_p95_ms": 150 },
14
+ { "name": "mad-sks-help", "argv": ["node", "./dist/bin/sks.js", "mad-sks", "--help"], "budget_p50_ms": 100, "budget_p95_ms": 150 }
10
15
  ]
11
16
  }
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "5.12.0"
79
+ version = "6.0.0"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "5.12.0"
3
+ version = "6.0.0"
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 5.11.0"),
7
+ Some("--version") => println!("sks-rs {}", env!("CARGO_PKG_VERSION")),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -156,3 +156,38 @@ exports.rootJsonFastInline = rootJsonFastInline;
156
156
  exports.doctorJsonFastInline = doctorJsonFastInline;
157
157
  exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
158
158
  exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
159
+
160
+ exports.rootJsonFastInline = rootJsonFastInline;
161
+ exports.doctorJsonFastInline = doctorJsonFastInline;
162
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
163
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
164
+
165
+ exports.rootJsonFastInline = rootJsonFastInline;
166
+ exports.doctorJsonFastInline = doctorJsonFastInline;
167
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
168
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
169
+
170
+ exports.rootJsonFastInline = rootJsonFastInline;
171
+ exports.doctorJsonFastInline = doctorJsonFastInline;
172
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
173
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
174
+
175
+ exports.rootJsonFastInline = rootJsonFastInline;
176
+ exports.doctorJsonFastInline = doctorJsonFastInline;
177
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
178
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
179
+
180
+ exports.rootJsonFastInline = rootJsonFastInline;
181
+ exports.doctorJsonFastInline = doctorJsonFastInline;
182
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
183
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
184
+
185
+ exports.rootJsonFastInline = rootJsonFastInline;
186
+ exports.doctorJsonFastInline = doctorJsonFastInline;
187
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
188
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
189
+
190
+ exports.rootJsonFastInline = rootJsonFastInline;
191
+ exports.doctorJsonFastInline = doctorJsonFastInline;
192
+ exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
193
+ exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
@@ -32,6 +32,13 @@ async function runSks(args) {
32
32
  const { hookUserPromptSubmitPerfInline } = await import('./fast-inline.js');
33
33
  await hookUserPromptSubmitPerfInline();
34
34
  }
35
+ else if (args[0] === 'hook' && args[1] && process.env.SKS_HOOK_DAEMON === '1') {
36
+ // 20차 P2-1: opt-in daemon-accelerated hook path. Default hook behavior
37
+ // (.codex/hooks.json's actual command) is unchanged until this has been
38
+ // verified and deliberately turned on.
39
+ const { hookDaemonInline } = await import('../core/daemon/sksd-hook-dispatch.js');
40
+ await hookDaemonInline(args[1]);
41
+ }
35
42
  else if (args[0] === 'naruto' && (args[1] === 'help' || args.includes('--help')) && args.includes('--json')) {
36
43
  const { narutoHelpJsonFastInline } = await import('./fast-inline.js');
37
44
  narutoHelpJsonFastInline();
@@ -55,3 +62,17 @@ async function runSks(args) {
55
62
  exports.runSks = runSks;
56
63
 
57
64
  exports.runSks = runSks;
65
+
66
+ exports.runSks = runSks;
67
+
68
+ exports.runSks = runSks;
69
+
70
+ exports.runSks = runSks;
71
+
72
+ exports.runSks = runSks;
73
+
74
+ exports.runSks = runSks;
75
+
76
+ exports.runSks = runSks;
77
+
78
+ exports.runSks = runSks;
package/dist/bin/sks.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '5.12.0';
2
+ const { PACKAGE_VERSION } = require('../core/version.js');
3
3
  const firstArg = process.argv[2];
4
4
  if (firstArg === '--version' || firstArg === '-v' || firstArg === 'version') {
5
- process.stdout.write(`sneakoscope ${FAST_PACKAGE_VERSION}\n`);
5
+ process.stdout.write(`sneakoscope ${PACKAGE_VERSION}\n`);
6
6
  }
7
7
  else {
8
8
  import('./sks-dispatch.js').then(({ runSks }) => runSks(process.argv.slice(2))).catch((err) => {
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { codexRemoteControlStatus, formatCodexRemoteControlStatus } from '../core/codex-app.js';
3
- import { forceGpt55CodexConfigArgs } from '../core/codex-model-guard.js';
3
+ import { forceRequiredCodexModelConfigArgs } from '../core/codex-model-guard.js';
4
4
  export async function codexAppRemoteControlCommand(args = [], opts = {}) {
5
5
  const controlArgs = argsBeforeSeparator(args);
6
6
  if (controlArgs.includes('--help') || controlArgs.includes('-h')) {
@@ -25,7 +25,7 @@ export async function codexAppRemoteControlCommand(args = [], opts = {}) {
25
25
  process.exitCode = 1;
26
26
  return;
27
27
  }
28
- const passthrough = forceGpt55CodexConfigArgs(stripSeparator(args));
28
+ const passthrough = forceRequiredCodexModelConfigArgs(stripSeparator(args));
29
29
  const spawnFn = opts.spawn || spawn;
30
30
  const code = await spawnInherited(spawnFn, status.codex_cli.bin, ['remote-control', ...passthrough], {
31
31
  cwd: process.cwd(),
@@ -23,7 +23,7 @@ import { extractTomlTable, writeCodexConfigGuarded } from '../core/codex/codex-c
23
23
  import { cleanupCodexConfigBackups, validateCodexConfigRoundTrip } from '../core/codex/codex-config-toml.js';
24
24
  import { runPostinstallGlobalDoctorAndMarkPending } from '../core/update/update-migration-state.js';
25
25
  import { repairCodexImagegen } from '../core/doctor/imagegen-repair.js';
26
- import { REQUIRED_CODEX_MODEL } from '../core/codex-model-guard.js';
26
+ import { GPT55_CODEX_MODEL, REQUIRED_CODEX_MODEL } from '../core/codex-model-guard.js';
27
27
  const CODEX_LB_PROVIDER_NAME = 'openai';
28
28
  const CODEX_LB_PROVIDER_ENV_KEY = 'CODEX_LB_API_KEY';
29
29
  const CODEX_LB_CANONICAL_FAST_SERVICE_TIER = 'priority';
@@ -993,12 +993,12 @@ function codexLbFastModeConfigStatus(text = '') {
993
993
  const globalServiceTier = topLevelTomlString(text, 'service_tier');
994
994
  const profileBlock = String(text || '').match(/(^|\n)\[profiles\.sks-fast-high\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
995
995
  const profileServiceTier = profileBlock.match(/(^|\n)\s*service_tier\s*=\s*"([^"]+)"/)?.[2] || '';
996
- const configured = globalServiceTier === 'fast' || globalServiceTier === CODEX_LB_CANONICAL_FAST_SERVICE_TIER || profileServiceTier === 'fast';
996
+ const configured = globalServiceTier === 'fast' || globalServiceTier === CODEX_LB_CANONICAL_FAST_SERVICE_TIER;
997
997
  return {
998
998
  schema: 'sks.codex-lb-fast-mode-config.v1',
999
999
  configured,
1000
1000
  top_level_service_tier: globalServiceTier || null,
1001
- profile_service_tier: profileServiceTier || null,
1001
+ legacy_profile_service_tier: profileServiceTier || null,
1002
1002
  codex_request_service_tier: configured ? 'fast' : null,
1003
1003
  codex_lb_upstream_service_tier: configured ? CODEX_LB_CANONICAL_FAST_SERVICE_TIER : null,
1004
1004
  actual_service_tier_verified: false,
@@ -1030,10 +1030,7 @@ export async function repairCodexLbAuth(opts = {}) {
1030
1030
  }
1031
1031
  if (status.env_key_configured && status.base_url && (!status.provider_contract_ok || !status.selected || legacyAuthMigrated || hasTopLevelCodexModeLock(currentConfig) || (opts.forceCodexLbApiKeyAuth === true && !status.ok))) {
1032
1032
  await ensureDir(path.dirname(status.config_path));
1033
- const preservedUserFastMode = extractTomlTable(currentConfig, 'user.fast_mode');
1034
1033
  let next = upsertCodexLbConfig(currentConfig, status.base_url);
1035
- if (preservedUserFastMode)
1036
- next = upsertTomlTable(next, 'user.fast_mode', preservedUserFastMode);
1037
1034
  next = normalizeCodexFastModeUiConfig(next, {
1038
1035
  forceFastMode: opts.forceFastMode === true || opts.forceCodexLbApiKeyAuth === true
1039
1036
  });
@@ -1873,7 +1870,9 @@ export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
1873
1870
  });
1874
1871
  if (next === ensureTrailingNewline(current))
1875
1872
  return { status: 'present', config_path: configPath };
1876
- const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'codex-fast-mode-install');
1873
+ const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'codex-fast-mode-install', {
1874
+ preserveFastUiKeys: opts.forceFastModeOff !== true
1875
+ });
1877
1876
  return {
1878
1877
  status: safeWrite.status === 'written' ? 'updated' : safeWrite.status,
1879
1878
  config_path: configPath,
@@ -1891,65 +1890,41 @@ export function normalizeCodexFastModeUiConfig(text = '', opts = {}) {
1891
1890
  return normalizeCodexFastModeUiConfigOnce(normalizeCodexFastModeUiConfigOnce(text, opts), opts);
1892
1891
  }
1893
1892
  function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
1894
- // Keep SKS-owned model/reasoning defaults out of top-level config so Codex
1895
- // Desktop can expose its native selectors, but preserve a user's deliberate
1896
- // top-level model/service/reasoning choices across install, update, and doctor.
1893
+ // 2026-07 Codex App renewal (ChatGPT desktop merge): the config schema removed
1894
+ // `default_profile`, `[profiles.<name>]` tables, `[user.fast_mode]`, and
1895
+ // `notice.fast_default_opt_out`. The only documented fast-default mechanism is
1896
+ // the plain top-level `service_tier = "fast"`. This pass strips every legacy
1897
+ // stamp SKS ever wrote and keeps user-authored top-level choices intact.
1897
1898
  let next = String(text || '');
1898
- const misplacedDefaultProfile = tomlTableString(next, 'user.fast_mode', 'default_profile');
1899
1899
  next = removeLegacyTopLevelCodexModeLocks(next);
1900
- next = removeTomlTableKey(next, 'user.fast_mode', 'default_profile');
1900
+ next = removeTopLevelTomlKeyIfValue(next, 'default_profile', 'sks-fast-high');
1901
+ next = removeTomlTable(next, 'user.fast_mode');
1902
+ next = removeTomlTable(next, 'profiles.sks-fast-high');
1901
1903
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
1902
- next = removeTomlTableKey(next, 'features', 'codex_hooks');
1904
+ // Feature flags SKS wrote in earlier versions that are not in the current
1905
+ // [features] reference (renewal removed/renamed them). They only trigger the
1906
+ // under-development warning now — strip our own stamps.
1907
+ for (const legacyFlag of [
1908
+ 'codex_hooks', 'remote_control', 'fast_mode_ui', 'codex_git_commit', 'computer_use',
1909
+ 'browser_use', 'browser_use_external', 'image_generation', 'in_app_browser',
1910
+ 'guardian_approval', 'tool_suggest', 'plugins'
1911
+ ]) {
1912
+ next = removeTomlTableKey(next, 'features', legacyFlag);
1913
+ }
1903
1914
  if (opts.forceFastMode === true) {
1904
1915
  next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
1905
1916
  }
1906
1917
  else if (opts.forceFastModeOff === true) {
1907
- next = upsertTopLevelTomlString(next, 'service_tier', 'default');
1918
+ // "off" = remove the tier and let Codex's own default apply. The old code
1919
+ // wrote service_tier = "default", which is not a documented tier value.
1920
+ next = removeTopLevelTomlKey(next, 'service_tier');
1908
1921
  }
1909
- // Codex App feature flags / fast-mode UI / suppress-warning are SET-IF-ABSENT: a fresh
1910
- // config still gets SKS's defaults, but SKS NEVER overrides (re-enables) a feature the
1911
- // user disabled in the App, and never rejects-then-hides UI by forcing an unrecognized
1912
- // flag on an older App build. This is what stops SKS from "removing/blocking" the App UI.
1922
+ // Documented, currently-valid feature flags are SET-IF-ABSENT: a fresh config
1923
+ // gets SKS's defaults, but SKS never re-enables a feature the user disabled.
1913
1924
  next = upsertTopLevelTomlBooleanIfAbsent(next, 'suppress_unstable_features_warning', true);
1914
- for (const featureLine of [
1915
- 'hooks = true', 'remote_control = true', 'multi_agent = true', 'fast_mode = true',
1916
- 'fast_mode_ui = true', 'codex_git_commit = true', 'computer_use = true', 'browser_use = true',
1917
- 'browser_use_external = true', 'image_generation = true', 'in_app_browser = true',
1918
- 'guardian_approval = true', 'tool_suggest = true', 'apps = true', 'plugins = true'
1919
- ]) {
1920
- const featureKey = featureLine.split('=')[0]?.trim();
1921
- next = opts.forceFastMode === true && ['fast_mode', 'fast_mode_ui'].includes(String(featureKey || ''))
1922
- ? upsertTomlTableKey(next, 'features', featureLine)
1923
- : upsertTomlTableKeyIfAbsent(next, 'features', featureLine);
1924
- }
1925
- if (opts.forceFastMode === true || opts.forceFastModeOff === true) {
1926
- next = upsertTomlTableKey(next, 'user.fast_mode', 'visible = true');
1927
- next = upsertTomlTableKey(next, 'user.fast_mode', 'enabled = true');
1928
- next = opts.forceFastMode === true
1929
- ? upsertTopLevelTomlString(next, 'default_profile', 'sks-fast-high')
1930
- : removeTopLevelTomlKeyIfValue(next, 'default_profile', 'sks-fast-high');
1931
- }
1932
- else {
1933
- next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'visible = true');
1934
- next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'enabled = true');
1935
- if (misplacedDefaultProfile === 'sks-fast-high') {
1936
- next = upsertTopLevelTomlString(next, 'default_profile', 'sks-fast-high');
1937
- }
1925
+ for (const featureLine of ['hooks = true', 'multi_agent = true', 'fast_mode = true', 'apps = true']) {
1926
+ next = upsertTomlTableKeyIfAbsent(next, 'features', featureLine);
1938
1927
  }
1939
- // Keep ONLY the sks-fast-high config-profile table for explicit fast-mode opt-in
1940
- // and CLI `--profile` use. The other SKS config profiles are
1941
- // no longer written as `[profiles.sks-*]` tables here (Codex 0.134+ deprecates them);
1942
- // they are managed as per-file `<name>.config.toml` overlays by
1943
- // migrateSksProfilesToPerFile (src/core/auto-review.ts), which also writes the
1944
- // sks-fast-high overlay for CLI `--profile` use.
1945
- next = upsertTomlTableKey(next, 'profiles.sks-fast-high', `model = "${REQUIRED_CODEX_MODEL}"`);
1946
- next = upsertTomlTableKey(next, 'profiles.sks-fast-high', 'service_tier = "fast"');
1947
- next = upsertTomlTableKey(next, 'profiles.sks-fast-high', 'approval_policy = "on-request"');
1948
- // Do not force a sandbox from the Codex App fast profile. The App/IDE
1949
- // permissions selector owns full-access vs workspace-write; this profile only
1950
- // supplies SKS's model, speed, approval, and reasoning defaults.
1951
- next = removeTomlTableKey(next, 'profiles.sks-fast-high', 'sandbox_mode');
1952
- next = upsertTomlTableKey(next, 'profiles.sks-fast-high', 'model_reasoning_effort = "high"');
1953
1928
  // Plugin auto-enable is OPT-IN only. Force-writing `[plugins."name@marketplace"] enabled =
1954
1929
  // true` for marketplace plugins the App may not have installed (different build/channel)
1955
1930
  // makes the App reference plugins it cannot load -> broken/blocked plugin UI. It also
@@ -1964,12 +1939,37 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
1964
1939
  }
1965
1940
  return ensureTrailingNewline(next);
1966
1941
  }
1942
+ function removeTopLevelTomlKey(text = '', key = '') {
1943
+ const lines = String(text || '').split('\n');
1944
+ const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
1945
+ const end = firstTable === -1 ? lines.length : firstTable;
1946
+ const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
1947
+ return lines.filter((line, index) => index >= end || !keyPattern.test(line)).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
1948
+ }
1949
+ function removeTomlTable(text, table) {
1950
+ const lines = String(text || '').trimEnd().split('\n');
1951
+ const header = `[${table}]`;
1952
+ const start = lines.findIndex((x) => x.trim() === header);
1953
+ if (start === -1)
1954
+ return String(text || '');
1955
+ let end = lines.length;
1956
+ for (let i = start + 1; i < lines.length; i += 1) {
1957
+ const ln = lines[i];
1958
+ if (ln !== undefined && /^\s*\[.+\]\s*$/.test(ln)) {
1959
+ end = i;
1960
+ break;
1961
+ }
1962
+ }
1963
+ return lines.filter((_, index) => index < start || index >= end).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
1964
+ }
1967
1965
  function removeLegacyTopLevelCodexModeLocks(text = '') {
1968
1966
  const lines = String(text || '').split('\n');
1969
1967
  const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
1970
1968
  const end = firstTable === -1 ? lines.length : firstTable;
1971
1969
  const topLevelModel = topLevelTomlString(text, 'model');
1972
- const removeSksOwnedModeLock = topLevelModel === REQUIRED_CODEX_MODEL;
1970
+ // Recognize both the current default stamp and the legacy gpt-5.5 stamp older
1971
+ // SKS versions wrote, so upgrades keep cleaning locks written before the 5.6 bump.
1972
+ const removeSksOwnedModeLock = topLevelModel === REQUIRED_CODEX_MODEL || topLevelModel === GPT55_CODEX_MODEL;
1973
1973
  return lines.filter((line, index) => {
1974
1974
  if (index >= end)
1975
1975
  return true;
@@ -2134,31 +2134,29 @@ async function backupCodexConfig(configPath, text, tag) {
2134
2134
  // - refuse to WRITE a result that would not parse (e.g. a regex helper mangled a multiline
2135
2135
  // string), leaving the existing config untouched,
2136
2136
  // - otherwise back up the prior config before mutating.
2137
- export async function safeWriteCodexConfigToml(configPath, current, next, tag = 'codex-lb') {
2137
+ export async function safeWriteCodexConfigToml(configPath, current, next, tag = 'codex-lb', opts = {}) {
2138
2138
  return writeCodexConfigGuarded({
2139
2139
  configPath,
2140
2140
  before: String(current || ''),
2141
2141
  cause: tag,
2142
2142
  removeTopLevelModeLocks: true,
2143
+ ...(opts.preserveFastUiKeys === undefined ? {} : { preserveFastUiKeys: opts.preserveFastUiKeys }),
2143
2144
  mutate: () => String(next || '')
2144
2145
  });
2145
2146
  }
2146
2147
  export function codexFastModeDesktopStatus(text = '') {
2147
2148
  const validation = validateCodexConfigRoundTrip(String(text || ''));
2148
- const profile = validation.parsed?.profiles?.['sks-fast-high'];
2149
- const globalOn = validation.ok
2150
- && validation.default_profile === 'sks-fast-high'
2151
- && profile?.model === REQUIRED_CODEX_MODEL
2152
- && profile?.service_tier === 'fast';
2149
+ // Post-renewal contract: the fast default is the plain top-level
2150
+ // service_tier = "fast". default_profile/[profiles.*]/[user.fast_mode] are
2151
+ // gone from the Codex config schema and are only reported as legacy_keys.
2152
+ const globalOn = validation.ok && validation.service_tier === 'fast';
2153
2153
  return {
2154
- schema: 'sks.codex-fast-mode-desktop-status.v1',
2154
+ schema: 'sks.codex-fast-mode-desktop-status.v2',
2155
2155
  ok: validation.ok,
2156
2156
  on: Boolean(globalOn),
2157
- default_profile: validation.default_profile || null,
2158
- top_level_default_profile: validation.top_level_default_profile === true,
2159
- user_fast_mode_default_profile: validation.user_fast_mode_default_profile,
2160
- profile_model: profile?.model || null,
2161
- profile_service_tier: profile?.service_tier || null,
2157
+ service_tier: validation.service_tier ?? null,
2158
+ model: validation.model ?? null,
2159
+ legacy_keys: validation.legacy_keys,
2162
2160
  validation
2163
2161
  };
2164
2162
  }
@@ -3397,8 +3395,14 @@ export async function selftestCodexLb(tmp) {
3397
3395
  });
3398
3396
  if (brokenChain.ok || brokenChain.status !== 'previous_response_not_found' || brokenChain.chain_unhealthy !== true)
3399
3397
  throw new Error('selftest: codex-lb response chain health check did not detect previous_response_not_found');
3400
- if (!codexLbConfig.includes('hooks = true') || hasDeprecatedCodexHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('remote_control = true') || !codexLbConfig.includes('multi_agent = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('codex_git_commit = true') || !codexLbConfig.includes('computer_use = true') || !codexLbConfig.includes('browser_use = true') || !codexLbConfig.includes('browser_use_external = true') || !codexLbConfig.includes('guardian_approval = true') || !codexLbConfig.includes('tool_suggest = true') || !codexLbConfig.includes('apps = true') || !codexLbConfig.includes('plugins = true') || !codexLbConfig.includes('[plugins."latex@openai-bundled"]') || !codexLbConfig.includes('[plugins."documents@openai-primary-runtime"]') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !/\[profiles\.custom\][\s\S]*?model_reasoning_effort = "low"/.test(codexLbConfig) || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig))
3401
- throw new Error('selftest: codex-lb setup did not preserve Codex App feature flags, default plugins, profile-scoped reasoning effort, explicit Fast profile, Codex Git commit generation, or migrate the hooks feature flag');
3398
+ // 2026-07 renewal contract: only documented [features] flags remain; the flags,
3399
+ // [user.fast_mode], [profiles.sks-fast-high], and notice.fast_default_opt_out SKS
3400
+ // used to write must be STRIPPED by setup. User-authored tables ([profiles.custom])
3401
+ // are preserved untouched even though the schema dropped profile tables.
3402
+ const legacyStamps = ['remote_control = true', 'fast_mode_ui = true', 'codex_git_commit = true', 'computer_use = true', 'browser_use = true', 'browser_use_external = true', 'guardian_approval = true', 'tool_suggest = true', 'plugins = true', '[user.fast_mode]', '[profiles.sks-fast-high]', 'fast_default_opt_out = true'];
3403
+ const survivingLegacy = legacyStamps.filter((stamp) => codexLbConfig.includes(stamp));
3404
+ if (!codexLbConfig.includes('hooks = true') || hasDeprecatedCodexHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('multi_agent = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('apps = true') || survivingLegacy.length || !/\[profiles\.custom\][\s\S]*?model_reasoning_effort = "low"/.test(codexLbConfig) || hasTopLevelCodexModeLock(codexLbConfig))
3405
+ throw new Error(`selftest: codex-lb setup did not enforce the renewed feature-flag contract (documented flags present, legacy stamps stripped, user profile tables preserved)${survivingLegacy.length ? ` — surviving legacy stamps: ${survivingLegacy.join(', ')}` : ''}`);
3402
3406
  if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig))
3403
3407
  throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
3404
3408
  const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex '--model' '${REQUIRED_CODEX_MODEL}'`;
@@ -76,32 +76,39 @@ async function dispatchInner(argv) {
76
76
  return result;
77
77
  }
78
78
  const entry = COMMAND_MANIFEST_BY_NAME[command];
79
- const commandGate = await ensureActiveRouteCommandGate(command, rest);
80
- if (!commandGate.ok) {
81
- console.error(commandGate.message);
82
- process.exitCode = 1;
83
- return commandGate;
84
- }
85
- const { ensureCurrentMigrationBeforeCommand } = await import('../core/update/update-migration-state.js');
86
- const migrationGate = await ensureCurrentMigrationBeforeCommand({
87
- command,
88
- args: rest,
89
- skipMigrationGate: entry.skipMigrationGate === true || entry.readonly === true
90
- });
91
- if (!migrationGate.ok) {
92
- console.error('SKS project migration blocked.');
93
- console.error(`Scope: ${migrationGate.scope || 'project'}`);
94
- console.error(`Stage: ${migrationGate.failed_stage_id || migrationGate.status}`);
95
- if (migrationGate.failed_stage_id)
96
- console.error(`Failed stage: ${migrationGate.failed_stage_id}`);
97
- for (const blocker of migrationGate.blockers)
98
- console.error(`Required blocker: ${blocker}`);
99
- for (const warning of migrationGate.warnings)
100
- console.error(`Optional warning: ${warning}`);
101
- console.error(`Receipt: ${migrationGate.receipt_path}`);
102
- console.error('Remedies: run `sks doctor --fix --yes`, then retry; diagnostics that must bypass this gate are marked skipMigrationGate in the command registry.');
103
- process.exitCode = 1;
104
- return migrationGate;
79
+ const helpRequest = isHelpRequest(rest);
80
+ if (!helpRequest) {
81
+ const commandGate = await ensureActiveRouteCommandGate(command, rest);
82
+ if (!commandGate.ok) {
83
+ console.error(commandGate.message);
84
+ process.exitCode = 1;
85
+ return commandGate;
86
+ }
87
+ // 20차 P2-2: --help/-h/help must never wait on (or be blocked by) the
88
+ // migration gate's lock — a stuck/contended migration lock previously
89
+ // made `sks <cmd> --help` take the full MIGRATION_LOCK_WAIT_MS (20s) and
90
+ // then fail, for a request that only wants usage text.
91
+ const { ensureCurrentMigrationBeforeCommand } = await import('../core/update/update-migration-state.js');
92
+ const migrationGate = await ensureCurrentMigrationBeforeCommand({
93
+ command,
94
+ args: rest,
95
+ skipMigrationGate: entry.skipMigrationGate === true || entry.readonly === true
96
+ });
97
+ if (!migrationGate.ok) {
98
+ console.error('SKS project migration blocked.');
99
+ console.error(`Scope: ${migrationGate.scope || 'project'}`);
100
+ console.error(`Stage: ${migrationGate.failed_stage_id || migrationGate.status}`);
101
+ if (migrationGate.failed_stage_id)
102
+ console.error(`Failed stage: ${migrationGate.failed_stage_id}`);
103
+ for (const blocker of migrationGate.blockers)
104
+ console.error(`Required blocker: ${blocker}`);
105
+ for (const warning of migrationGate.warnings)
106
+ console.error(`Optional warning: ${warning}`);
107
+ console.error(`Receipt: ${migrationGate.receipt_path}`);
108
+ console.error('Remedies: run `sks doctor --fix --yes`, then retry; diagnostics that must bypass this gate are marked skipMigrationGate in the command registry.');
109
+ process.exitCode = 1;
110
+ return migrationGate;
111
+ }
105
112
  }
106
113
  const { COMMANDS } = await import('./command-registry.js');
107
114
  const commandEntry = COMMANDS[command];
@@ -110,6 +117,16 @@ async function dispatchInner(argv) {
110
117
  throw new Error(`Command ${command} must export run(command, args)`);
111
118
  return mod.run(rawCommand || command, rest);
112
119
  }
120
+ // --help/-h/help must skip the active-route and migration gates regardless
121
+ // of where it appears in args — a pure usage-text request should never wait
122
+ // on (or be blocked by) project state (20차 P2-2).
123
+ function isHelpRequest(args) {
124
+ // --help/-h are unambiguous flags wherever they appear; bare "help" is
125
+ // only treated as the request when it's the subcommand position (args[0])
126
+ // so an arbitrary value elsewhere (e.g. a commit message of "help") can't
127
+ // accidentally bypass the gates.
128
+ return args.includes('--help') || args.includes('-h') || String(args[0] || '').toLowerCase() === 'help';
129
+ }
113
130
  async function ensureActiveRouteCommandGate(command, args) {
114
131
  const entry = COMMAND_MANIFEST_BY_NAME[command];
115
132
  if (command === 'route' || entry.readonly === true || entry.allowedDuringActiveRoute === true && entry.mutatesRouteState !== true) {
@@ -254,10 +254,9 @@ async function runDoctor(args = [], root, doctorFix) {
254
254
  global_skills: installScope === 'global' && !flag(args, '--local-only')
255
255
  ? deepDiagnostics ? await (await import('../cli/install-helpers.js')).ensureGlobalCodexSkillsDuringInstall({ force: true }) : { status: 'skipped', reason: 'default_doctor_no_global_skill_regeneration' }
256
256
  : { status: 'skipped', reason: 'project or local-only repair' },
257
- // Re-seed the Codex App Fast-mode UI table ([user.fast_mode] visible/enabled/
258
- // default_profile) in the global ~/.codex/config.toml so existing installs whose
259
- // config predates the Fast-mode UI keys get the App speed selector back. Safe:
260
- // backs up + parse-validates before writing, no-op when already present.
257
+ // Normalize global Codex fast-mode config to the 2026-07 schema: preserve
258
+ // service_tier="fast" when requested, strip legacy [user.fast_mode] and
259
+ // [profiles.sks-fast-high] stamps, and parse-validate before writing.
261
260
  codex_app_fast_mode: flag(args, '--local-only')
262
261
  ? { status: 'skipped', reason: 'local-only repair' }
263
262
  : await (await import('../cli/install-helpers.js')).ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) }))