sneakoscope 8.0.0 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +13 -2
  2. package/config/skills-hash-ledger.v1.json +63 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/bin/fast-inline.js +14 -3
  6. package/dist/bin/sks-dispatch.js +1 -1
  7. package/dist/cli/codex-lb-setup-warning-output.js +17 -0
  8. package/dist/cli/command-manifest-lite.js +2 -13
  9. package/dist/cli/install-helpers-codex-lb-config.js +475 -8
  10. package/dist/cli/install-helpers-install-support.js +1 -1
  11. package/dist/cli/install-helpers.js +764 -68
  12. package/dist/cli/router.js +5 -2
  13. package/dist/commands/codex-lb.js +77 -47
  14. package/dist/commands/doctor-profile.js +1 -0
  15. package/dist/commands/doctor.js +165 -58
  16. package/dist/config/skills-manifest.json +193 -97
  17. package/dist/core/codex/codex-config-guard.js +70 -16
  18. package/dist/core/codex/oauth-callback-port-diagnostic.js +121 -0
  19. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -2
  20. package/dist/core/codex-app/menubar/constants.js +3 -0
  21. package/dist/core/codex-app/menubar/installer.js +32 -9
  22. package/dist/core/codex-app/menubar/launch-agent.js +58 -6
  23. package/dist/core/codex-app/menubar/migration.js +17 -6
  24. package/dist/core/codex-app/menubar/status.js +15 -3
  25. package/dist/core/codex-app.js +15 -20
  26. package/dist/core/codex-control/codex-lb-launch-recovery.js +26 -0
  27. package/dist/core/codex-lb/capability-runner.js +1 -0
  28. package/dist/core/codex-lb/codex-lb-env.js +52 -41
  29. package/dist/core/codex-lb/codex-lb-setup.js +85 -8
  30. package/dist/core/codex-lb/desktop-center-credentials.js +474 -39
  31. package/dist/core/codex-lb/desktop-controller.js +118 -4
  32. package/dist/core/codex-lb/legacy-migration.js +6 -3
  33. package/dist/core/codex-native/codex-native-feature-broker.js +5 -1
  34. package/dist/core/codex-native/core-skill-manifest.js +1 -1
  35. package/dist/core/codex-native/managed-skill-generation-lock.js +13 -0
  36. package/dist/core/codex-native/sks-skill-paths.js +97 -10
  37. package/dist/core/codex-runtime/codex-desktop-config-policy.js +1 -0
  38. package/dist/core/commands/basic-cli.js +54 -4
  39. package/dist/core/commands/mad-sks-command.js +1 -1
  40. package/dist/core/commands/menubar-command.js +24 -8
  41. package/dist/core/commands/naruto-command.js +42 -217
  42. package/dist/core/doctor/codex-config-syntax-repair.js +368 -0
  43. package/dist/core/doctor/doctor-native-capability-repair.js +12 -5
  44. package/dist/core/doctor/doctor-readiness-matrix.js +51 -4
  45. package/dist/core/doctor/skill-legacy-surface.js +0 -1
  46. package/dist/core/doctor/sks-menubar-doctor.js +21 -0
  47. package/dist/core/fsx.js +7 -14
  48. package/dist/core/hooks-runtime/hook-context.js +30 -16
  49. package/dist/core/hooks-runtime/managed-skill-admission.js +17 -0
  50. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +891 -30
  51. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +99 -6
  52. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +3 -1
  53. package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +1 -1
  54. package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +4 -4
  55. package/dist/core/hooks-runtime/subagent-skill-availability.js +9 -7
  56. package/dist/core/hooks-runtime/subagent-stop-hook.js +19 -0
  57. package/dist/core/hooks-runtime.js +41 -14
  58. package/dist/core/init/skills.js +270 -11
  59. package/dist/core/mission.js +20 -0
  60. package/dist/core/naruto/hardware-capacity-probe.js +3 -1
  61. package/dist/core/naruto/naruto-concurrency-governor.js +9 -3
  62. package/dist/core/pipeline-internals/runtime-core.js +68 -9
  63. package/dist/core/pipeline-internals/runtime-gates.js +10 -2
  64. package/dist/core/preflight/parallel-preflight-engine.js +18 -4
  65. package/dist/core/release/package-dist-snapshot.js +50 -8
  66. package/dist/core/routes.js +1 -1
  67. package/dist/core/safety/command-contract/contracts.js +10 -13
  68. package/dist/core/safety/command-contract/json-schema.js +38 -1
  69. package/dist/core/subagents/agent-catalog.js +37 -13
  70. package/dist/core/subagents/model-policy.js +14 -9
  71. package/dist/core/subagents/naruto-command-args.js +237 -0
  72. package/dist/core/subagents/naruto-command-input-contract.js +123 -0
  73. package/dist/core/subagents/naruto-help-contract.js +35 -2
  74. package/dist/core/subagents/naruto-launch-diagnostics.js +35 -0
  75. package/dist/core/subagents/official-subagent-config.js +30 -3
  76. package/dist/core/subagents/official-subagent-preparation.js +30 -5
  77. package/dist/core/subagents/official-subagent-prompt.js +4 -3
  78. package/dist/core/subagents/official-subagent-runner.js +12 -0
  79. package/dist/core/subagents/subagent-evidence.js +10 -0
  80. package/dist/core/subagents/thread-budget.js +1 -1
  81. package/dist/core/subagents/wave-lifecycle.js +41 -11
  82. package/dist/core/subagents/wave-parent-guidance.js +10 -3
  83. package/dist/core/update/update-migration-state.js +694 -63
  84. package/dist/core/update/update-operation.js +498 -32
  85. package/dist/core/update-check.js +990 -509
  86. package/dist/core/version.js +1 -1
  87. package/dist/native/sks-menubar/Sources/AppDelegate.swift +1 -0
  88. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -1
  89. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +1 -1
  90. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +127 -12
  91. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +7 -2
  92. package/dist/native/sks-menubar/Sources/ProcessClient.swift +287 -84
  93. package/dist/native/sks-menubar/Sources/ProcessExecutionState.swift +45 -0
  94. package/dist/native/sks-menubar/Sources/ProcessIdentityGuard.swift +196 -0
  95. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +169 -0
  96. package/dist/native/sks-menubar/Sources/StatusItemController.swift +23 -5
  97. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +113 -24
  98. package/dist/native/sks-menubar/Sources/main.swift +12 -0
  99. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +14 -0
  100. package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +174 -0
  101. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +4 -0
  102. package/dist/scripts/current-surface-update-e2e-check.js +24 -5
  103. package/dist/scripts/lib/ensure-dist-fresh.js +12 -8
  104. package/dist/scripts/mutation-callsite-analysis.js +381 -0
  105. package/dist/scripts/mutation-callsite-coverage-check.js +127 -62
  106. package/dist/scripts/official-subagent-workflow-check.js +1 -1
  107. package/dist/scripts/skills-manifest-continuity-check.js +19 -1
  108. package/package.json +5 -3
  109. package/runtime-required-scripts.json +5 -0
  110. package/scripts/build-clean-atomic.mjs +323 -0
@@ -146,12 +146,15 @@ async function ensureActiveRouteCommandGate(command, args) {
146
146
  return { ok: true, status: 'allowed_status_subcommand' };
147
147
  if (safeActiveRouteVisualQuery(command, args))
148
148
  return { ok: true, status: 'allowed_visual_query' };
149
- const [{ projectRoot, readJson }, { stateFile }] = await Promise.all([
149
+ const [{ projectRoot }, { loadOwnedRouteState }] = await Promise.all([
150
150
  import('../core/fsx.js'),
151
151
  import('../core/mission.js')
152
152
  ]);
153
153
  const root = await projectRoot(process.cwd()).catch(() => process.cwd());
154
- const state = await readJson(stateFile(root), {}).catch(() => ({}));
154
+ const appSessionKey = process.env.SKS_NARUTO_STANDALONE_CLI === '1'
155
+ ? ''
156
+ : String(process.env.CODEX_THREAD_ID || '').trim();
157
+ const state = await loadOwnedRouteState(root, appSessionKey);
155
158
  if (safeActiveRouteContinuation(command, args, state))
156
159
  return { ok: true, status: 'allowed_active_route_continuation' };
157
160
  if (!activeRouteStateBlocksCommand(state))
@@ -4,6 +4,7 @@ import { stdin as input, stdout as output } from 'node:process';
4
4
  import { projectRoot, readStdin, readText } from '../core/fsx.js';
5
5
  import { flag, readOption } from '../cli/args.js';
6
6
  import { printJson } from '../cli/output.js';
7
+ import { printCodexLbSetupWarnings } from '../cli/codex-lb-setup-warning-output.js';
7
8
  import { codexLbMetrics, readCodexLbCircuit, recordCodexLbHealthEvent, resetCodexLbCircuit, codexLbProofEvidence } from '../core/codex-lb-circuit.js';
8
9
  import { checkCodexLbResponseChain, codexLbStatus, configureCodexLb, repairCodexLbAuth } from '../cli/install-helpers.js';
9
10
  import { buildCodexLbSetupPlan, renderCodexLbSetupPlan } from '../core/codex-lb/codex-lb-setup.js';
@@ -175,8 +176,8 @@ export async function run(command, args = []) {
175
176
  const result = await configureCodexLb({
176
177
  host,
177
178
  apiKey: newKey,
178
- keychain: true,
179
- storeKeychain: true,
179
+ keychain: false,
180
+ storeKeychain: false,
180
181
  writeEnvFile: true,
181
182
  syncLaunchctl: true,
182
183
  desktopMode: 'cli-provider',
@@ -184,7 +185,6 @@ export async function run(command, args = []) {
184
185
  });
185
186
  let routing = null;
186
187
  const restartRequested = flag(args, '--restart-app') || flag(args, '--restart')
187
- || current.mode === 'desktop-dual-auth-compat'
188
188
  || current.mode === 'desktop-native-bridge';
189
189
  if (result.ok && current.mode === 'desktop-native-bridge') {
190
190
  routing = await activateCodexLbDesktopMode({
@@ -193,19 +193,19 @@ export async function run(command, args = []) {
193
193
  restartApp: restartRequested
194
194
  });
195
195
  }
196
- else if (result.ok && current.mode === 'desktop-dual-auth-compat') {
197
- routing = await activateCodexLbDesktopMode({
198
- ...controllerOptions(args),
199
- mode: 'desktop-dual-auth-compat',
200
- restartApp: restartRequested
201
- });
202
- }
203
196
  const ok = Boolean(result.ok && routing?.ok !== false);
204
197
  const output = {
205
198
  ...result,
206
199
  schema: 'sks.codex-lb-set-key.v2',
207
200
  ok,
208
201
  action: 'set-key',
202
+ provider_ready: result.ok === true,
203
+ routing_active: Boolean(result.ok
204
+ && current.mode === 'desktop-native-bridge'
205
+ && routing?.ok !== false),
206
+ activation_required: Boolean(result.ok
207
+ && current.mode !== 'desktop-native-bridge'
208
+ && current.mode !== 'desktop-dual-auth-compat'),
209
209
  desktop_auth_mutated: false,
210
210
  fast_mode_mutated: false,
211
211
  desktop_routing: routing,
@@ -216,6 +216,7 @@ export async function run(command, args = []) {
216
216
  if (flag(args, '--json'))
217
217
  return printJson(output);
218
218
  console.log(result.ok ? `codex-lb API key updated (${result.base_url || host}).` : `codex-lb key update failed: ${result.status}${result.error ? `: ${result.error}` : ''}`);
219
+ printCodexLbSetupWarnings(result);
219
220
  if (!ok)
220
221
  process.exitCode = 1;
221
222
  return;
@@ -239,21 +240,6 @@ export async function run(command, args = []) {
239
240
  : `Desktop Full Capability activation failed: ${String(result.status || 'failed')}`);
240
241
  return;
241
242
  }
242
- if (action === 'use-desktop-compat') {
243
- const result = await activateCodexLbDesktopMode({
244
- ...controllerOptions(args),
245
- mode: 'desktop-dual-auth-compat',
246
- gatewayAuthTransport: 'x-codex-lb-api-key'
247
- });
248
- if (result.ok !== true)
249
- process.exitCode = 1;
250
- if (flag(args, '--json'))
251
- return printJson(result);
252
- console.log(result.ok === true
253
- ? 'Codex Desktop compatibility routing enabled; ChatGPT OAuth remains active.'
254
- : `Desktop compatibility activation failed: ${String(result.status || 'failed')}`);
255
- return;
256
- }
257
243
  if (action === 'use-cli') {
258
244
  const result = await configureCodexLbCliMode(controllerOptions(args));
259
245
  if (result.ok !== true)
@@ -327,27 +313,23 @@ export async function run(command, args = []) {
327
313
  const toRaw = String(readOption(args, '--to', readOption(args, '--target', 'bridge')) || 'bridge')
328
314
  .trim()
329
315
  .toLowerCase();
330
- const mode = toRaw === 'compat'
331
- || toRaw === 'desktop-compat'
332
- || toRaw === 'desktop-dual-auth-compat'
333
- ? 'desktop-dual-auth-compat'
334
- : toRaw === 'bridge'
335
- || toRaw === 'desktop-full'
336
- || toRaw === 'desktop-native-bridge'
337
- || toRaw === ''
338
- ? 'desktop-native-bridge'
339
- : null;
316
+ const mode = toRaw === 'bridge'
317
+ || toRaw === 'desktop-full'
318
+ || toRaw === 'desktop-native-bridge'
319
+ || toRaw === ''
320
+ ? 'desktop-native-bridge'
321
+ : null;
340
322
  if (!mode) {
341
323
  const result = {
342
324
  schema: 'sks.codex-lb-legacy-migration-command.v2',
343
325
  ok: false,
344
326
  status: 'invalid_migration_target',
345
- blockers: ['migration_target_must_be_bridge_or_compat']
327
+ blockers: ['migration_target_must_be_bridge']
346
328
  };
347
329
  process.exitCode = 1;
348
330
  if (flag(args, '--json'))
349
331
  return printJson(result);
350
- console.error('Invalid --to value. Use bridge or compat.');
332
+ console.error('Invalid --to value. Use bridge.');
351
333
  return;
352
334
  }
353
335
  const result = await migrateLegacyCodexLbDesktopMode({
@@ -471,7 +453,7 @@ export async function run(command, args = []) {
471
453
  plan,
472
454
  applied_actions: [],
473
455
  persistence: plan.persistence,
474
- guidance: ['Pass --yes to acknowledge process_only_ephemeral setup, or enable --write-env-file, --keychain, --launchctl, or --shell-profile.']
456
+ guidance: ['Pass --yes to acknowledge process_only_ephemeral setup, or enable --write-env-file, --launchctl, or --shell-profile.']
475
457
  };
476
458
  if (flag(args, '--json')) {
477
459
  printJson(result);
@@ -533,6 +515,11 @@ export async function run(command, args = []) {
533
515
  env_file_chmod: options.writeEnvFile ? '0600' : null,
534
516
  desktop_auth_mutated: false,
535
517
  fast_mode_mutated: false,
518
+ provider_ready: result.ok === true,
519
+ routing_active: Boolean(result.ok && desktopActivation?.ok === true),
520
+ activation_required: Boolean(result.ok
521
+ && options.desktopMode === 'cli-provider'
522
+ && desktopActivation === null),
536
523
  desktop_activation: desktopActivation,
537
524
  capabilities
538
525
  };
@@ -551,10 +538,51 @@ export async function run(command, args = []) {
551
538
  process.exitCode = 1;
552
539
  if (flag(args, '--json'))
553
540
  return printJson(shaped);
541
+ if (!shaped.ok) {
542
+ console.error(`codex-lb setup failed: ${String(result.status || 'configuration_failed')}`);
543
+ const recoveryPaths = Array.isArray(result.rollback?.recovery_paths)
544
+ ? result.rollback.recovery_paths.map((value) => String(value || '')).filter(Boolean)
545
+ : [];
546
+ if (recoveryPaths.length > 0) {
547
+ console.error('Recovery files retained:');
548
+ for (const recoveryPath of recoveryPaths)
549
+ console.error(` ${recoveryPath}`);
550
+ }
551
+ const secretRecoveryPaths = Array.isArray(result.rollback?.secret_recovery_paths)
552
+ ? result.rollback.secret_recovery_paths.map((value) => String(value || '')).filter(Boolean)
553
+ : [];
554
+ if (secretRecoveryPaths.length > 0) {
555
+ console.error('Warning: the following owner-only recovery files may contain the submitted API key:');
556
+ for (const recoveryPath of secretRecoveryPaths)
557
+ console.error(` ${recoveryPath}`);
558
+ }
559
+ if (result.keychain?.keychain_state_verified === false) {
560
+ console.error('Keychain state is indeterminate; do not assume the previous credential was restored.');
561
+ }
562
+ printCodexLbSetupWarnings(result);
563
+ return;
564
+ }
554
565
  console.log(`codex-lb configured: ${result.base_url || result.status}`);
555
566
  console.log(`Desktop mode: ${options.desktopMode}${desktopActivation ? ` (${String(desktopActivation.status || 'applied')})` : ' (routing unchanged)'}`);
556
- if (shaped.persistence?.warning)
557
- console.log(`warning: ${shaped.persistence.warning}`);
567
+ const retainedRecoveryPaths = Array.isArray(result.recovery_paths)
568
+ ? result.recovery_paths.map((value) => String(value || '')).filter(Boolean)
569
+ : [];
570
+ const retainedSecretRecoveryPaths = Array.isArray(result.secret_recovery_paths)
571
+ ? result.secret_recovery_paths.map((value) => String(value || '')).filter(Boolean)
572
+ : [];
573
+ const secretRecoverySet = new Set(retainedSecretRecoveryPaths);
574
+ const nonSecretRecoveryPaths = retainedRecoveryPaths.filter((value) => !secretRecoverySet.has(value));
575
+ if (nonSecretRecoveryPaths.length > 0) {
576
+ console.log('Recovery backups retained:');
577
+ for (const recoveryPath of nonSecretRecoveryPaths)
578
+ console.log(` ${recoveryPath}`);
579
+ }
580
+ if (retainedSecretRecoveryPaths.length > 0) {
581
+ console.error('Warning: owner-only recovery files may contain a previous API key and require secure review:');
582
+ for (const recoveryPath of retainedSecretRecoveryPaths)
583
+ console.error(` ${recoveryPath}`);
584
+ }
585
+ printCodexLbSetupWarnings(shaped);
558
586
  if (!shaped.ok)
559
587
  process.exitCode = 1;
560
588
  return;
@@ -589,11 +617,11 @@ export async function run(command, args = []) {
589
617
  console.log(`codex-lb proof evidence: ${result.status}`);
590
618
  return;
591
619
  }
592
- console.error('Usage: sks codex-lb status|setup|set-key|use-desktop-full|use-desktop-compat|use-cli|disable|capabilities [--level shallow|transport|deep] [--evidence <file> --trust-anchors <file>]|migrate-legacy-desktop|rollback <receipt-id>|health|metrics|doctor|repair|circuit reset|circuit record-fixture|proof-evidence [--json]');
620
+ console.error('Usage: sks codex-lb status|setup|set-key|use-desktop-full|use-cli|disable|capabilities [--level shallow|transport|deep] [--evidence <file> --trust-anchors <file>]|migrate-legacy-desktop|rollback <receipt-id>|health|metrics|doctor|repair|circuit reset|circuit record-fixture|proof-evidence [--json]');
593
621
  console.error(` ${CODEX_LB_TOOL_OUTPUT_RECOVERY_OVERRIDE_FLAG} explicitly acknowledge an old/unverified proxy for this command (unsafe)`);
594
622
  console.error(' set-key swap the codex-lb API key (reuses the stored host): sks codex-lb set-key --api-key-stdin');
623
+ console.error(' --keychain fail-closed until a dedicated signed SKS Keychain helper is available; the default 0600 env file is used');
595
624
  console.error(' use-desktop-full keep ChatGPT OAuth/built-in OpenAI and route data through the local bridge');
596
- console.error(' use-desktop-compat keep ChatGPT OAuth and use the explicit dual-auth custom provider');
597
625
  console.error(' use-cli configure an unselected CLI-only provider');
598
626
  console.error(' disable remove only SKS-managed Desktop routing; auth.json is untouched');
599
627
  process.exitCode = 1;
@@ -838,11 +866,13 @@ function formatCodexLbDesktopStatusText(status) {
838
866
  'SKS codex-lb Desktop',
839
867
  '',
840
868
  `Configured: ${status.configured === true ? 'yes' : 'no'}`,
869
+ `Credentials ready: ${status.credentials_ready === true ? 'yes' : 'no'}`,
870
+ `Routing active: ${status.routing_active === true ? 'yes' : 'no'}`,
841
871
  `Mode: ${String(status.mode || 'disabled')}`,
842
872
  `ChatGPT OAuth: ${oauth.present === true ? 'present and preserved' : 'missing'}`,
843
873
  `Provider: ${String(provider.contract || 'missing')} (${provider.contract_ok === true ? 'ok' : 'blocked'})`,
844
874
  `Bridge: ${String(bridge.status || 'unknown')}${bridge.running === true ? ' (running)' : ''}`,
845
- `Gateway auth: ${String(bridge.gateway_auth_transport || 'unknown')}`,
875
+ `Gateway auth: ${String(status.gateway_auth_transport || 'inactive')}`,
846
876
  `Capabilities: ${String(status.overall || capabilities.state || 'unverified')}`,
847
877
  `Full capability: ${status.full_capability_verified === true ? 'verified' : 'unverified'}`
848
878
  ];
@@ -903,7 +933,7 @@ function normalizeDesktopSetupMode(value) {
903
933
  if (normalized === 'desktop-compat'
904
934
  || normalized === 'compat'
905
935
  || normalized === 'desktop-dual-auth-compat') {
906
- return 'desktop-dual-auth-compat';
936
+ throw new Error('desktop_dual_auth_compat_unavailable');
907
937
  }
908
938
  if (!normalized
909
939
  || normalized === 'cli'
@@ -981,7 +1011,7 @@ async function codexLbSetupOptions(args = []) {
981
1011
  host ||= (await ask('1. codex-lb domain or base URL?\n Example: lb.example.com or https://lb.example.com/backend-api/codex\n> ')).trim();
982
1012
  apiKey ||= (await askHidden('2. API key?\n Input hidden. Value will be stored securely and never printed.\n> ')).trim();
983
1013
  apiKeySource = 'hidden_prompt';
984
- desktopMode = normalizeDesktopSetupMode((await ask('3. Configure which mode? [desktop-full/desktop-compat/cli-only] (desktop-full)\n> ')).trim()
1014
+ desktopMode = normalizeDesktopSetupMode((await ask('3. Configure which mode? [desktop-full/cli-only] (desktop-full)\n> ')).trim()
985
1015
  || 'desktop-full');
986
1016
  if (desktopMode === 'desktop-native-bridge') {
987
1017
  gatewayAuthTransport = parseCodexLbGatewayAuthTransport(normalizeGatewayAuthChoice((await ask('4. Gateway key transport? [custom-header/bearer-compat] (custom-header)\n> ')).trim()));
@@ -990,8 +1020,8 @@ async function codexLbSetupOptions(args = []) {
990
1020
  gatewayAuthTransport = 'x-codex-lb-api-key';
991
1021
  }
992
1022
  writeEnvFile = parseYesNo(await ask('5. Write shell env loader to ~/.codex/sks-codex-lb.env? [Y/n] '), true);
993
- const storeKeychain = (await ask('6. Store the key in macOS Keychain when available? [Y/n] ')).trim();
994
- keychain = !/^(n|no|아니|아니요|ㄴ)$/i.test(storeKeychain || 'y');
1023
+ keychain = false;
1024
+ console.log('6. Key storage: ~/.codex/sks-codex-lb.env (owner-only mode 0600).');
995
1025
  syncLaunchctl = parseYesNo(await ask('7. Sync non-secret macOS launchctl base URL only? API keys are never stored in launchd. [y/N] '), false);
996
1026
  const profile = (await ask('8. Install shell profile snippet? [zsh/bash/fish/all/skip] ')).trim();
997
1027
  const interactiveShellProfile = normalizeShellProfile(profile || 'skip');
@@ -35,6 +35,7 @@ export function doctorPhaseIdsForProfile(profile) {
35
35
  const required = [
36
36
  'codex_startup_repair',
37
37
  'startup_config_repair',
38
+ 'codex_config_syntax_repair',
38
39
  'context7_repair',
39
40
  'context7_mcp_repair',
40
41
  'hook_trust_repair',