sneakoscope 4.8.6 → 4.8.7
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.
- package/README.md +14 -14
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +63 -41
- package/dist/cli/install-helpers.js +115 -47
- package/dist/cli/router.js +58 -2
- package/dist/commands/codex-lb.js +4 -4
- package/dist/commands/doctor.js +85 -3
- package/dist/commands/zellij-monitor-pane.js +25 -0
- package/dist/commands/zellij-slot-pane.js +39 -2
- package/dist/commands/zellij-viewport-pane.js +56 -0
- package/dist/commands/zellij.js +55 -2
- package/dist/config/skills-manifest.json +465 -0
- package/dist/core/agents/agent-lifecycle.js +86 -35
- package/dist/core/agents/agent-orchestrator.js +19 -10
- package/dist/core/agents/agent-patch-queue-store.js +14 -3
- package/dist/core/agents/agent-patch-queue.js +4 -0
- package/dist/core/agents/agent-scheduler.js +2 -3
- package/dist/core/agents/native-cli-session-swarm.js +30 -13
- package/dist/core/agents/native-cli-worker.js +2 -1
- package/dist/core/auto-review.js +25 -4
- package/dist/core/codex/agent-config-file-repair.js +15 -2
- package/dist/core/codex/codex-config-eperm-repair.js +8 -2
- package/dist/core/codex/codex-config-guard.js +289 -0
- package/dist/core/codex/codex-config-toml.js +122 -0
- package/dist/core/codex/codex-project-config-policy.js +59 -7
- package/dist/core/codex-app/sks-menubar.js +767 -122
- package/dist/core/codex-control/codex-fake-sdk-adapter.js +6 -4
- package/dist/core/codex-control/codex-task-runner.js +32 -14
- package/dist/core/codex-control/python-codex-sdk-adapter.js +4 -1
- package/dist/core/codex-lb/codex-lb-setup.js +2 -5
- package/dist/core/codex-native/skill-registry-ledger.js +24 -5
- package/dist/core/commands/command-utils.js +22 -2
- package/dist/core/commands/fast-mode-command.js +36 -11
- package/dist/core/commands/mad-sks-command.js +18 -5
- package/dist/core/commands/menubar-command.js +125 -0
- package/dist/core/commands/pipeline-command.js +21 -2
- package/dist/core/commands/route-command.js +62 -0
- package/dist/core/commands/status-command.js +21 -1
- package/dist/core/commands/team-legacy-observe-command.js +221 -3
- package/dist/core/commands/uninstall-command.js +298 -0
- package/dist/core/db-safety.js +5 -1
- package/dist/core/doctor/codex-startup-config-repair.js +14 -3
- package/dist/core/doctor/confirm-repair.js +27 -0
- package/dist/core/doctor/context7-mcp-repair.js +35 -7
- package/dist/core/doctor/doctor-codex-startup-repair.js +10 -3
- package/dist/core/doctor/doctor-context7-repair.js +17 -4
- package/dist/core/doctor/doctor-dirty-planner.js +27 -9
- package/dist/core/doctor/doctor-native-capability-repair.js +12 -2
- package/dist/core/doctor/doctor-readiness-matrix.js +27 -2
- package/dist/core/doctor/doctor-repair-postcheck.js +4 -0
- package/dist/core/doctor/doctor-transaction.js +13 -5
- package/dist/core/doctor/supabase-mcp-repair.js +23 -5
- package/dist/core/feature-fixtures.js +10 -3
- package/dist/core/feature-registry.js +4 -1
- package/dist/core/fsx.js +58 -2
- package/dist/core/hooks-runtime/skill-reconcile-preflight.js +43 -0
- package/dist/core/hooks-runtime.js +86 -46
- package/dist/core/init/skills.js +261 -9
- package/dist/core/init.js +13 -10
- package/dist/core/mad-db/mad-db-coordinator.js +3 -3
- package/dist/core/mission.js +138 -5
- package/dist/core/naruto/naruto-active-pool.js +69 -26
- package/dist/core/naruto/naruto-backpressure.js +5 -1
- package/dist/core/naruto/naruto-real-worker-runtime.js +9 -7
- package/dist/core/naruto/resource-pressure-monitor.js +33 -15
- package/dist/core/pipeline-internals/runtime-core.js +62 -220
- package/dist/core/pipeline-internals/runtime-gates.js +113 -24
- package/dist/core/questions.js +1 -1
- package/dist/core/recallpulse.js +1 -1
- package/dist/core/release/gate-manifest.js +2 -2
- package/dist/core/release/release-gate-affected-selector.js +3 -10
- package/dist/core/release/release-gate-batch-runner.js +4 -2
- package/dist/core/release/release-gate-cache-v2.js +27 -14
- package/dist/core/release/release-gate-dag.js +6 -1
- package/dist/core/release/release-gate-hermetic-env.js +10 -0
- package/dist/core/retention.js +89 -4
- package/dist/core/routes/diagnostic-allowlist.js +55 -0
- package/dist/core/routes.js +18 -15
- package/dist/core/skill-forge.js +9 -4
- package/dist/core/stop-gate/stop-gate-check.js +10 -3
- package/dist/core/stop-gate/stop-gate-resolver.js +19 -3
- package/dist/core/stop-gate/stop-gate-writer.js +16 -1
- package/dist/core/team-dag.js +12 -465
- package/dist/core/team-dashboard-renderer.js +13 -94
- package/dist/core/team-live.js +68 -804
- package/dist/core/update/update-migration-state.js +20 -30
- package/dist/core/update-check.js +28 -2
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-layout-builder.js +44 -16
- package/dist/core/zellij/zellij-monitor-renderer.js +53 -0
- package/dist/core/zellij/zellij-self-heal.js +18 -26
- package/dist/core/zellij/zellij-slot-pane-renderer.js +162 -153
- package/dist/core/zellij/zellij-slot-telemetry.js +2 -0
- package/dist/core/zellij/zellij-theme.js +67 -0
- package/dist/core/zellij/zellij-ui-mode.js +16 -0
- package/dist/core/zellij/zellij-viewport-binder.js +59 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +53 -5
- package/dist/scripts/agent-patch-swarm-gate-lib.js +2 -2
- package/dist/scripts/build-dist.js +8 -0
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +106 -0
- package/dist/scripts/codex-lb-gpt55-fast-profile-check.js +105 -0
- package/dist/scripts/codex-lb-missing-env-regression.js +2 -3
- package/dist/scripts/codex-project-config-policy-merge-regression.js +3 -3
- package/dist/scripts/concurrent-session-collision-check.js +67 -0
- package/dist/scripts/core-skill-integrity-blackbox.js +3 -4
- package/dist/scripts/docs-truthfulness-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +130 -0
- package/dist/scripts/hook-latency-budget-check.js +45 -0
- package/dist/scripts/lib/codex-sdk-gate-lib.js +2 -2
- package/dist/scripts/lib/real-codex-parallel-gate.js +2 -2
- package/dist/scripts/loop-directive-check-lib.js +1 -1
- package/dist/scripts/mad-db-real-supabase-e2e.js +2 -2
- package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +3 -2
- package/dist/scripts/mad-sks-zellij-launch-check.js +1 -1
- package/dist/scripts/naruto-zellij-dynamic-right-column-check.js +7 -7
- package/dist/scripts/parallel-claim-enforcement-check.js +14 -5
- package/dist/scripts/project-skill-dedupe-check.js +23 -5
- package/dist/scripts/release-affected-selector-check.js +2 -2
- package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
- package/dist/scripts/release-gate-dag-runner-check.js +10 -2
- package/dist/scripts/release-gate-dag-runner.js +8 -0
- package/dist/scripts/release-readiness-report.js +38 -7
- package/dist/scripts/release-stability-report-check.js +12 -8
- package/dist/scripts/search-visibility-gate-lib.js +2 -2
- package/dist/scripts/skills-manifest-continuity-check.js +46 -0
- package/dist/scripts/sks-menubar-install-check.js +87 -21
- package/dist/scripts/sks-uninstall-regression-check.js +54 -0
- package/dist/scripts/uninstall-inventory-check.js +3 -0
- package/dist/scripts/zellij-initial-main-only-blackbox.js +4 -4
- package/dist/scripts/zellij-layout-valid-check.js +18 -11
- package/dist/scripts/zellij-right-column-geometry-proof.js +2 -4
- package/dist/scripts/zellij-slot-pane-renderer-check.js +9 -9
- package/dist/scripts/zellij-slot-pane-stale-detection-check.js +3 -4
- package/dist/scripts/zellij-slot-pane-telemetry-renderer-check.js +3 -2
- package/dist/scripts/zellij-slot-telemetry-renderer-check.js +4 -2
- package/dist/scripts/zellij-spawn-on-demand-layout-check.js +8 -2
- package/dist/scripts/zellij-viewport-binder-check.js +9 -0
- package/package.json +4 -1
|
@@ -8,16 +8,19 @@ import { createRequestedScopeContract } from '../core/safety/requested-scope-con
|
|
|
8
8
|
import { guardedPackageInstall, guardContextForRoute } from '../core/safety/mutation-guard.js';
|
|
9
9
|
import { EMPTY_CODEX_INFO, getCodexInfo } from '../core/codex-adapter.js';
|
|
10
10
|
import { formatHarnessConflictReport, llmHarnessCleanupPrompt, scanHarnessConflicts } from '../core/harness-conflicts.js';
|
|
11
|
-
import { initProject,
|
|
11
|
+
import { initProject, installGlobalSkills } from '../core/init.js';
|
|
12
12
|
import { context7ConfigToml, DOLLAR_SKILL_NAMES, GETDESIGN_REFERENCE, hasContext7ConfigText, RECOMMENDED_SKILLS } from '../core/routes.js';
|
|
13
13
|
import { checkZellijCapability } from '../core/zellij/zellij-capability.js';
|
|
14
14
|
import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
|
|
15
15
|
import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
|
|
16
|
+
import { cleanupMacLaunchSecretEnvironment } from '../core/codex-app/sks-menubar.js';
|
|
16
17
|
import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
|
|
17
18
|
import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
|
|
18
19
|
import { GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES } from '../core/providers/glm/glm-52-profile.js';
|
|
19
20
|
import { GLM_52_OPENROUTER_MODEL } from '../core/providers/glm/glm-52-settings.js';
|
|
20
21
|
import { buildCodexLbSetupPlan, codexLbPersistenceSummary, installCodexLbShellProfileSnippet, selectedCodexLbPersistenceModes } from '../core/codex-lb/codex-lb-setup.js';
|
|
22
|
+
import { extractTomlTable, writeCodexConfigGuarded } from '../core/codex/codex-config-guard.js';
|
|
23
|
+
import { cleanupCodexConfigBackups, validateCodexConfigRoundTrip } from '../core/codex/codex-config-toml.js';
|
|
21
24
|
import { runPostinstallGlobalDoctorAndMarkPending } from '../core/update/update-migration-state.js';
|
|
22
25
|
const CODEX_LB_PROVIDER_NAME = 'openai';
|
|
23
26
|
const CODEX_LB_PROVIDER_ENV_KEY = 'CODEX_LB_API_KEY';
|
|
@@ -397,7 +400,7 @@ export async function configureCodexLb(opts = {}) {
|
|
|
397
400
|
const useDefaultProvider = opts.useDefaultProvider !== false;
|
|
398
401
|
const writeEnvFile = opts.writeEnvFile !== false;
|
|
399
402
|
const storeKeychain = opts.storeKeychain === true || opts.keychain === true;
|
|
400
|
-
const syncLaunchctl = opts.syncLaunchctl
|
|
403
|
+
const syncLaunchctl = opts.syncLaunchctl === true || opts.syncLaunchEnv === true;
|
|
401
404
|
const shellProfile = opts.shellProfile || 'skip';
|
|
402
405
|
const setupAnswers = {
|
|
403
406
|
host_or_base_url: rawHost,
|
|
@@ -464,7 +467,7 @@ export async function configureCodexLb(opts = {}) {
|
|
|
464
467
|
appliedActions.push({ type: 'store_keychain', target: 'macOS Keychain service sks-codex-lb', ok: keychain.ok === true, status: keychain.status });
|
|
465
468
|
const codexEnvironment = await syncCodexLbProviderEnvironment({ env_path: envPath, base_url: baseUrl }, { ...opts, home, apiKey, baseUrl, syncLaunchEnv: syncLaunchctl });
|
|
466
469
|
if (syncLaunchctl)
|
|
467
|
-
appliedActions.push({ type: 'sync_launchctl', target: 'macOS launchctl user environment', ok: codexEnvironment.ok === true, status: codexEnvironment.status });
|
|
470
|
+
appliedActions.push({ type: 'sync_launchctl', target: 'macOS launchctl user environment (base URL only; API-key env removed)', ok: codexEnvironment.ok === true, status: codexEnvironment.status });
|
|
468
471
|
const shellProfileResult = await installCodexLbShellProfileSnippet({ home, envPath, shellProfile }).catch((err) => ({ ok: false, status: 'failed', files: [], error: err.message }));
|
|
469
472
|
if (shellProfile !== 'skip')
|
|
470
473
|
appliedActions.push({ type: 'install_shell_profile_snippet', target: shellProfileResult.files?.join(', ') || shellProfile, ok: shellProfileResult.ok === true, status: shellProfileResult.status });
|
|
@@ -979,6 +982,10 @@ function topLevelTomlString(text = '', key) {
|
|
|
979
982
|
const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
|
|
980
983
|
return topLevel.match(new RegExp(`(^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[2] || '';
|
|
981
984
|
}
|
|
985
|
+
function tomlTableString(text = '', table, key) {
|
|
986
|
+
const block = String(text || '').match(new RegExp(`(^|\\n)\\[${escapeRegExp(table)}\\]([\\s\\S]*?)(?=\\n\\[[^\\]]+\\]|\\s*$)`))?.[2] || '';
|
|
987
|
+
return block.match(new RegExp(`(^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[2] || '';
|
|
988
|
+
}
|
|
982
989
|
export async function repairCodexLbAuth(opts = {}) {
|
|
983
990
|
let status = await codexLbStatus(opts);
|
|
984
991
|
let configRepaired = false;
|
|
@@ -995,7 +1002,11 @@ export async function repairCodexLbAuth(opts = {}) {
|
|
|
995
1002
|
}
|
|
996
1003
|
if (status.env_key_configured && status.base_url && (!status.provider_contract_ok || !status.selected || legacyAuthMigrated || hasTopLevelCodexModeLock(currentConfig) || (opts.forceCodexLbApiKeyAuth === true && !status.ok))) {
|
|
997
1004
|
await ensureDir(path.dirname(status.config_path));
|
|
998
|
-
const
|
|
1005
|
+
const preservedUserFastMode = extractTomlTable(currentConfig, 'user.fast_mode');
|
|
1006
|
+
let next = upsertCodexLbConfig(currentConfig, status.base_url);
|
|
1007
|
+
if (preservedUserFastMode)
|
|
1008
|
+
next = upsertTomlTable(next, 'user.fast_mode', preservedUserFastMode);
|
|
1009
|
+
next = normalizeCodexFastModeUiConfig(next, {
|
|
999
1010
|
forceFastMode: opts.forceFastMode === true || opts.forceCodexLbApiKeyAuth === true
|
|
1000
1011
|
});
|
|
1001
1012
|
const safeWrite = await safeWriteCodexConfigToml(status.config_path, currentConfig, next, 'codex-lb-repair');
|
|
@@ -1337,8 +1348,8 @@ export async function unselectCodexLbProvider(opts = {}) {
|
|
|
1337
1348
|
return { status: 'not_selected', config_path: configPath };
|
|
1338
1349
|
try {
|
|
1339
1350
|
const next = ensureTrailingNewline(removeTopLevelTomlString(current, 'model_provider'));
|
|
1340
|
-
await
|
|
1341
|
-
return { status: 'unselected', config_path: configPath };
|
|
1351
|
+
const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'codex-lb-unselect');
|
|
1352
|
+
return { status: safeWrite.ok ? 'unselected' : safeWrite.status, config_path: configPath, backup_path: safeWrite.backup_path };
|
|
1342
1353
|
}
|
|
1343
1354
|
catch (err) {
|
|
1344
1355
|
return { status: 'failed', reason: 'write_failed', config_path: configPath, error: err.message };
|
|
@@ -1552,11 +1563,11 @@ async function syncCodexLbProviderEnvironment(status = {}, opts = {}) {
|
|
|
1552
1563
|
process.env.CODEX_LB_API_KEY = apiKey;
|
|
1553
1564
|
if (baseUrl)
|
|
1554
1565
|
process.env.CODEX_LB_BASE_URL = baseUrl;
|
|
1555
|
-
const launchEnv = await syncCodexLbMacLaunchEnvironment(
|
|
1566
|
+
const launchEnv = await syncCodexLbMacLaunchEnvironment(baseUrl ? { CODEX_LB_BASE_URL: baseUrl } : {}, opts);
|
|
1556
1567
|
const ok = launchEnv.ok || launchEnv.skipped || launchEnv.status === 'not_macos';
|
|
1557
1568
|
return {
|
|
1558
1569
|
ok,
|
|
1559
|
-
status: launchEnv.status === 'synced' ? '
|
|
1570
|
+
status: launchEnv.status === 'synced' ? 'launch_base_url_synced_secret_env_removed' : ok ? 'process_env' : launchEnv.status,
|
|
1560
1571
|
env_path: envPath,
|
|
1561
1572
|
base_url: baseUrl || null,
|
|
1562
1573
|
launch_environment: launchEnv,
|
|
@@ -1571,7 +1582,15 @@ async function syncCodexLbMacLaunchEnvironment(values = {}, opts = {}) {
|
|
|
1571
1582
|
const launchctl = opts.launchctlBin || await which('launchctl').catch(() => null) || await exists('/bin/launchctl').then((ok) => ok ? '/bin/launchctl' : null).catch(() => null);
|
|
1572
1583
|
if (!launchctl)
|
|
1573
1584
|
return { ok: false, status: 'launchctl_missing', error: 'launchctl not found on PATH' };
|
|
1574
|
-
const
|
|
1585
|
+
const secretCleanup = await cleanupMacLaunchSecretEnvironment({ force: opts.forceLaunchEnv === true }).catch((err) => ({
|
|
1586
|
+
ok: false,
|
|
1587
|
+
status: 'partial',
|
|
1588
|
+
variables: ['CODEX_LB_API_KEY', 'OPENROUTER_API_KEY'],
|
|
1589
|
+
cleaned: [],
|
|
1590
|
+
failed: [{ key: 'CODEX_LB_API_KEY', error: err?.message || String(err) }, { key: 'OPENROUTER_API_KEY', error: err?.message || String(err) }],
|
|
1591
|
+
next_actions: ['Run launchctl unsetenv for CODEX_LB_API_KEY and OPENROUTER_API_KEY']
|
|
1592
|
+
}));
|
|
1593
|
+
const variables = Object.entries(values).filter(([key, value]) => value && !['CODEX_LB_API_KEY', 'OPENROUTER_API_KEY'].includes(String(key)));
|
|
1575
1594
|
const results = [];
|
|
1576
1595
|
for (const [key, value] of variables) {
|
|
1577
1596
|
const result = await runProcess(launchctl, ['setenv', key, String(value)], { timeoutMs: 5000, maxOutputBytes: 8192 });
|
|
@@ -1583,8 +1602,14 @@ async function syncCodexLbMacLaunchEnvironment(values = {}, opts = {}) {
|
|
|
1583
1602
|
}
|
|
1584
1603
|
const failed = results.filter((result) => !result.ok);
|
|
1585
1604
|
if (failed.length)
|
|
1586
|
-
return { ok: false, status: 'launch_env_failed', variables: results.map((result) => result.key), failed, error: failed.map((result) => `${result.key}: ${result.error}`).join('; ') };
|
|
1587
|
-
return {
|
|
1605
|
+
return { ok: false, status: 'launch_env_failed', variables: results.map((result) => result.key), failed, secret_env_cleanup: secretCleanup, error: failed.map((result) => `${result.key}: ${result.error}`).join('; ') };
|
|
1606
|
+
return {
|
|
1607
|
+
ok: secretCleanup.ok !== false,
|
|
1608
|
+
status: variables.length ? 'synced' : 'secret_env_removed',
|
|
1609
|
+
variables: results.map((result) => result.key),
|
|
1610
|
+
skipped_secret_variables: ['CODEX_LB_API_KEY', 'OPENROUTER_API_KEY'],
|
|
1611
|
+
secret_env_cleanup: secretCleanup
|
|
1612
|
+
};
|
|
1588
1613
|
}
|
|
1589
1614
|
async function inspectCodexLbMacLaunchEnvironment(baseUrl = '', opts = {}) {
|
|
1590
1615
|
if (process.platform !== 'darwin' && !opts.forceLaunchEnv)
|
|
@@ -1598,20 +1623,33 @@ async function inspectCodexLbMacLaunchEnvironment(baseUrl = '', opts = {}) {
|
|
|
1598
1623
|
};
|
|
1599
1624
|
const currentBaseUrl = await readVar('CODEX_LB_BASE_URL');
|
|
1600
1625
|
const currentApiKey = await readVar('CODEX_LB_API_KEY');
|
|
1626
|
+
const currentOpenRouterKey = await readVar('OPENROUTER_API_KEY');
|
|
1601
1627
|
const baseMatches = !baseUrl || currentBaseUrl === String(baseUrl || '').trim();
|
|
1602
1628
|
const basePresent = Boolean(currentBaseUrl);
|
|
1603
1629
|
const keyPresent = Boolean(currentApiKey);
|
|
1630
|
+
const openRouterKeyPresent = Boolean(currentOpenRouterKey);
|
|
1604
1631
|
return {
|
|
1605
1632
|
checked: true,
|
|
1606
1633
|
available: true,
|
|
1607
|
-
status:
|
|
1634
|
+
status: keyPresent || openRouterKeyPresent
|
|
1635
|
+
? 'secret_env_present'
|
|
1636
|
+
: basePresent && baseMatches
|
|
1637
|
+
? 'base_url_only'
|
|
1638
|
+
: basePresent
|
|
1639
|
+
? 'partial'
|
|
1640
|
+
: 'missing',
|
|
1608
1641
|
variables: [
|
|
1609
1642
|
...(keyPresent ? ['CODEX_LB_API_KEY'] : []),
|
|
1643
|
+
...(openRouterKeyPresent ? ['OPENROUTER_API_KEY'] : []),
|
|
1610
1644
|
...(basePresent ? ['CODEX_LB_BASE_URL'] : [])
|
|
1611
1645
|
],
|
|
1612
1646
|
base_url_present: basePresent,
|
|
1613
1647
|
base_url_matches: baseMatches,
|
|
1614
|
-
api_key_present: keyPresent
|
|
1648
|
+
api_key_present: keyPresent,
|
|
1649
|
+
openrouter_api_key_present: openRouterKeyPresent,
|
|
1650
|
+
next_actions: keyPresent || openRouterKeyPresent
|
|
1651
|
+
? ['Run: sks doctor --fix', 'Rotate CODEX_LB_API_KEY and OPENROUTER_API_KEY if they were exposed in launchd.']
|
|
1652
|
+
: []
|
|
1615
1653
|
};
|
|
1616
1654
|
}
|
|
1617
1655
|
async function maybeSyncCodexLbSharedLogin(apiKey, opts = {}) {
|
|
@@ -1731,7 +1769,9 @@ function detectCodexLbSetupDrift(state = {}) {
|
|
|
1731
1769
|
if (!state.storeKeychain && state.keychain?.status && state.keychain.status !== 'skipped')
|
|
1732
1770
|
drift.push('keychain_touched_despite_no_keychain');
|
|
1733
1771
|
if (!state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
1734
|
-
drift.push('
|
|
1772
|
+
drift.push('launchctl_base_url_synced_despite_no_launchctl');
|
|
1773
|
+
if (state.codexEnvironment?.launch_environment?.secret_env_cleanup?.status === 'partial')
|
|
1774
|
+
drift.push('launchctl_secret_env_cleanup_incomplete');
|
|
1735
1775
|
if (state.shellProfile === 'skip' && state.shellProfileResult?.status === 'installed')
|
|
1736
1776
|
drift.push('shell_profile_written_despite_skip');
|
|
1737
1777
|
if (state.shellProfile === 'skip' && state.beforeState && state.afterState && state.beforeState.profileHash !== state.afterState.profileHash)
|
|
@@ -1773,7 +1813,7 @@ function appliedCodexLbPersistenceModes(state = {}) {
|
|
|
1773
1813
|
if (state.storeKeychain && state.keychain?.ok === true)
|
|
1774
1814
|
modes.push('durable_keychain');
|
|
1775
1815
|
if (state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
1776
|
-
modes.push('
|
|
1816
|
+
modes.push('process_only_ephemeral');
|
|
1777
1817
|
if (state.shellProfile !== 'skip' && state.shellProfileResult?.status === 'installed')
|
|
1778
1818
|
modes.push('shell_profile');
|
|
1779
1819
|
if (!modes.length && state.apiKeySource === 'process.env')
|
|
@@ -1805,14 +1845,13 @@ export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
|
|
|
1805
1845
|
});
|
|
1806
1846
|
if (next === ensureTrailingNewline(current))
|
|
1807
1847
|
return { status: 'present', config_path: configPath };
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
return { status: 'updated', config_path: configPath, backup_path: backupPath };
|
|
1848
|
+
const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'codex-fast-mode-install');
|
|
1849
|
+
return {
|
|
1850
|
+
status: safeWrite.status === 'written' ? 'updated' : safeWrite.status,
|
|
1851
|
+
config_path: configPath,
|
|
1852
|
+
backup_path: safeWrite.backup_path,
|
|
1853
|
+
parse_smoke: safeWrite.ok ? undefined : safeWrite
|
|
1854
|
+
};
|
|
1816
1855
|
}
|
|
1817
1856
|
catch (err) {
|
|
1818
1857
|
return { status: 'failed', config_path: configPath, error: err.message };
|
|
@@ -1827,7 +1866,9 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1827
1866
|
// Keep model and reasoning selection out of top-level config so Codex Desktop can
|
|
1828
1867
|
// expose its native model/speed selectors. SKS-owned defaults live in profiles below.
|
|
1829
1868
|
let next = String(text || '');
|
|
1869
|
+
const misplacedDefaultProfile = tomlTableString(next, 'user.fast_mode', 'default_profile');
|
|
1830
1870
|
next = removeLegacyTopLevelCodexModeLocks(next);
|
|
1871
|
+
next = removeTomlTableKey(next, 'user.fast_mode', 'default_profile');
|
|
1831
1872
|
next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
|
|
1832
1873
|
next = removeTomlTableKey(next, 'features', 'codex_hooks');
|
|
1833
1874
|
if (opts.forceFastMode === true) {
|
|
@@ -1846,19 +1887,25 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1846
1887
|
'fast_mode_ui = true', 'codex_git_commit = true', 'computer_use = true', 'browser_use = true',
|
|
1847
1888
|
'browser_use_external = true', 'image_generation = true', 'in_app_browser = true',
|
|
1848
1889
|
'guardian_approval = true', 'tool_suggest = true', 'apps = true', 'plugins = true'
|
|
1849
|
-
])
|
|
1850
|
-
|
|
1890
|
+
]) {
|
|
1891
|
+
const featureKey = featureLine.split('=')[0]?.trim();
|
|
1892
|
+
next = opts.forceFastMode === true && ['fast_mode', 'fast_mode_ui'].includes(String(featureKey || ''))
|
|
1893
|
+
? upsertTomlTableKey(next, 'features', featureLine)
|
|
1894
|
+
: upsertTomlTableKeyIfAbsent(next, 'features', featureLine);
|
|
1895
|
+
}
|
|
1851
1896
|
if (opts.forceFastMode === true || opts.forceFastModeOff === true) {
|
|
1852
1897
|
next = upsertTomlTableKey(next, 'user.fast_mode', 'visible = true');
|
|
1853
1898
|
next = upsertTomlTableKey(next, 'user.fast_mode', 'enabled = true');
|
|
1854
1899
|
next = opts.forceFastMode === true
|
|
1855
|
-
?
|
|
1856
|
-
:
|
|
1900
|
+
? upsertTopLevelTomlString(next, 'default_profile', 'sks-fast-high')
|
|
1901
|
+
: removeTopLevelTomlKeyIfValue(next, 'default_profile', 'sks-fast-high');
|
|
1857
1902
|
}
|
|
1858
1903
|
else {
|
|
1859
1904
|
next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'visible = true');
|
|
1860
1905
|
next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'enabled = true');
|
|
1861
|
-
|
|
1906
|
+
if (misplacedDefaultProfile === 'sks-fast-high') {
|
|
1907
|
+
next = upsertTopLevelTomlString(next, 'default_profile', 'sks-fast-high');
|
|
1908
|
+
}
|
|
1862
1909
|
}
|
|
1863
1910
|
// Keep ONLY the sks-fast-high config-profile table for explicit fast-mode opt-in
|
|
1864
1911
|
// and CLI `--profile` use. The other SKS config profiles are
|
|
@@ -1955,6 +2002,8 @@ function upsertTomlTableKey(text, table, line) {
|
|
|
1955
2002
|
return lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
1956
2003
|
}
|
|
1957
2004
|
}
|
|
2005
|
+
if (hasTomlTableKey(lines.join('\n'), table, key))
|
|
2006
|
+
return lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
1958
2007
|
lines.splice(end, 0, line);
|
|
1959
2008
|
return lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
1960
2009
|
}
|
|
@@ -2039,6 +2088,7 @@ async function backupCodexConfig(configPath, text, tag) {
|
|
|
2039
2088
|
const stamp = `${PACKAGE_VERSION}-${Date.now().toString(36)}`;
|
|
2040
2089
|
const backupPath = `${configPath}.sks-${tag}-${stamp}.bak`;
|
|
2041
2090
|
await writeTextAtomic(backupPath, text);
|
|
2091
|
+
await cleanupCodexConfigBackups(configPath, { keepPerTag: 3, maxAgeMs: 30 * 24 * 60 * 60 * 1000 }).catch(() => undefined);
|
|
2042
2092
|
return backupPath;
|
|
2043
2093
|
}
|
|
2044
2094
|
catch {
|
|
@@ -2052,20 +2102,32 @@ async function backupCodexConfig(configPath, text, tag) {
|
|
|
2052
2102
|
// string), leaving the existing config untouched,
|
|
2053
2103
|
// - otherwise back up the prior config before mutating.
|
|
2054
2104
|
export async function safeWriteCodexConfigToml(configPath, current, next, tag = 'codex-lb') {
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
const
|
|
2067
|
-
|
|
2068
|
-
|
|
2105
|
+
return writeCodexConfigGuarded({
|
|
2106
|
+
configPath,
|
|
2107
|
+
before: String(current || ''),
|
|
2108
|
+
cause: tag,
|
|
2109
|
+
removeTopLevelModeLocks: true,
|
|
2110
|
+
mutate: () => String(next || '')
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
export function codexFastModeDesktopStatus(text = '') {
|
|
2114
|
+
const validation = validateCodexConfigRoundTrip(String(text || ''));
|
|
2115
|
+
const profile = validation.parsed?.profiles?.['sks-fast-high'];
|
|
2116
|
+
const globalOn = validation.ok
|
|
2117
|
+
&& validation.default_profile === 'sks-fast-high'
|
|
2118
|
+
&& profile?.model === 'gpt-5.5'
|
|
2119
|
+
&& profile?.service_tier === 'fast';
|
|
2120
|
+
return {
|
|
2121
|
+
schema: 'sks.codex-fast-mode-desktop-status.v1',
|
|
2122
|
+
ok: validation.ok,
|
|
2123
|
+
on: Boolean(globalOn),
|
|
2124
|
+
default_profile: validation.default_profile || null,
|
|
2125
|
+
top_level_default_profile: validation.top_level_default_profile === true,
|
|
2126
|
+
user_fast_mode_default_profile: validation.user_fast_mode_default_profile,
|
|
2127
|
+
profile_model: profile?.model || null,
|
|
2128
|
+
profile_service_tier: profile?.service_tier || null,
|
|
2129
|
+
validation
|
|
2130
|
+
};
|
|
2069
2131
|
}
|
|
2070
2132
|
function upsertTopLevelTomlBoolean(text, key, value) {
|
|
2071
2133
|
const line = `${key} = ${value ? 'true' : 'false'}`;
|
|
@@ -2374,14 +2436,14 @@ export async function ensureGlobalCodexSkillsDuringInstall(opts = {}) {
|
|
|
2374
2436
|
return { status: 'skipped', reason: 'home directory unavailable' };
|
|
2375
2437
|
const root = globalCodexSkillsRoot(home);
|
|
2376
2438
|
try {
|
|
2377
|
-
const install = await
|
|
2439
|
+
const install = await installGlobalSkills(home);
|
|
2378
2440
|
const skills = await checkRequiredSkills(home, root);
|
|
2379
2441
|
return {
|
|
2380
2442
|
status: skills.ok ? 'installed' : 'partial',
|
|
2381
2443
|
root,
|
|
2382
|
-
installed_count: install.
|
|
2383
|
-
removed_aliases:
|
|
2384
|
-
removed_stale_generated_skills: install.
|
|
2444
|
+
installed_count: install.installed.length,
|
|
2445
|
+
removed_aliases: [],
|
|
2446
|
+
removed_stale_generated_skills: install.removed,
|
|
2385
2447
|
missing_skills: skills.missing
|
|
2386
2448
|
};
|
|
2387
2449
|
}
|
|
@@ -2685,7 +2747,13 @@ export async function ensureProjectContext7Config(root, transport = 'local') {
|
|
|
2685
2747
|
}
|
|
2686
2748
|
if (hasContext7ConfigText(current))
|
|
2687
2749
|
return false;
|
|
2688
|
-
await
|
|
2750
|
+
await writeCodexConfigGuarded({
|
|
2751
|
+
root,
|
|
2752
|
+
configPath,
|
|
2753
|
+
before: current,
|
|
2754
|
+
cause: 'context7-project-config',
|
|
2755
|
+
mutate: () => `${current.trimEnd()}${current.trim() ? '\n\n' : ''}${block}\n`
|
|
2756
|
+
});
|
|
2689
2757
|
return true;
|
|
2690
2758
|
}
|
|
2691
2759
|
export async function checkRequiredSkills(root, skillRoot = root ? path.join(root, '.agents', 'skills') : globalCodexSkillsRoot()) {
|
package/dist/cli/router.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { COMMAND_ALIASES, COMMANDS, } from './command-registry.js';
|
|
2
2
|
import { detectGlobalMode, glmWithoutMadResult } from './global-mode-router.js';
|
|
3
3
|
import { ensureCurrentMigrationBeforeCommand } from '../core/update/update-migration-state.js';
|
|
4
|
+
import { projectRoot, readJson } from '../core/fsx.js';
|
|
5
|
+
import { stateFile } from '../core/mission.js';
|
|
4
6
|
export function isCommandName(value) {
|
|
5
7
|
return Object.prototype.hasOwnProperty.call(COMMANDS, value);
|
|
6
8
|
}
|
|
@@ -49,15 +51,29 @@ export async function dispatch(args) {
|
|
|
49
51
|
return result;
|
|
50
52
|
}
|
|
51
53
|
const entry = COMMANDS[command];
|
|
52
|
-
const
|
|
54
|
+
const commandGate = await ensureActiveRouteCommandGate(command, rest);
|
|
55
|
+
if (!commandGate.ok) {
|
|
56
|
+
console.error(commandGate.message);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return commandGate;
|
|
59
|
+
}
|
|
60
|
+
const migrationGate = await ensureCurrentMigrationBeforeCommand({
|
|
61
|
+
command,
|
|
62
|
+
args: rest,
|
|
63
|
+
skipMigrationGate: entry.skipMigrationGate === true || entry.readonly === true
|
|
64
|
+
});
|
|
53
65
|
if (!migrationGate.ok) {
|
|
54
66
|
console.error('SKS project migration blocked.');
|
|
67
|
+
console.error(`Scope: ${migrationGate.scope || 'project'}`);
|
|
68
|
+
console.error(`Stage: ${migrationGate.failed_stage_id || migrationGate.status}`);
|
|
69
|
+
if (migrationGate.failed_stage_id)
|
|
70
|
+
console.error(`Failed stage: ${migrationGate.failed_stage_id}`);
|
|
55
71
|
for (const blocker of migrationGate.blockers)
|
|
56
72
|
console.error(`Required blocker: ${blocker}`);
|
|
57
73
|
for (const warning of migrationGate.warnings)
|
|
58
74
|
console.error(`Optional warning: ${warning}`);
|
|
59
75
|
console.error(`Receipt: ${migrationGate.receipt_path}`);
|
|
60
|
-
console.error('
|
|
76
|
+
console.error('Remedies: run `sks doctor --fix --yes`, then retry; diagnostics that must bypass this gate are marked skipMigrationGate in the command registry.');
|
|
61
77
|
process.exitCode = 1;
|
|
62
78
|
return migrationGate;
|
|
63
79
|
}
|
|
@@ -66,4 +82,44 @@ export async function dispatch(args) {
|
|
|
66
82
|
throw new Error(`Command ${command} must export run(command, args)`);
|
|
67
83
|
return mod.run(rawCommand || command, rest);
|
|
68
84
|
}
|
|
85
|
+
async function ensureActiveRouteCommandGate(command, args) {
|
|
86
|
+
const entry = COMMANDS[command];
|
|
87
|
+
if (command === 'route' || entry.readonly === true || entry.allowedDuringActiveRoute === true && entry.mutatesRouteState !== true) {
|
|
88
|
+
return { ok: true, status: 'allowed' };
|
|
89
|
+
}
|
|
90
|
+
if (entry.mutatesRouteState !== true)
|
|
91
|
+
return { ok: true, status: 'allowed' };
|
|
92
|
+
if (safeReadOnlySubcommand(args))
|
|
93
|
+
return { ok: true, status: 'allowed_status_subcommand' };
|
|
94
|
+
const root = await projectRoot(process.cwd()).catch(() => process.cwd());
|
|
95
|
+
const state = await readJson(stateFile(root), {}).catch(() => ({}));
|
|
96
|
+
if (!activeRouteStateBlocksCommand(state))
|
|
97
|
+
return { ok: true, status: 'allowed' };
|
|
98
|
+
return {
|
|
99
|
+
schema: 'sks.command-gate-active-route.v1',
|
|
100
|
+
ok: false,
|
|
101
|
+
status: 'blocked',
|
|
102
|
+
command,
|
|
103
|
+
active_mission_id: state.mission_id || null,
|
|
104
|
+
active_route: state.route || state.route_command || state.mode || null,
|
|
105
|
+
active_phase: state.phase || null,
|
|
106
|
+
message: `SKS command gate blocked '${command}' because active route mission ${state.mission_id} is not closed. Run: sks route close --mission ${state.mission_id}`
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function safeReadOnlySubcommand(args) {
|
|
110
|
+
const sub = String(args.find((arg) => !String(arg).startsWith('-')) || '').toLowerCase();
|
|
111
|
+
if (!['status', 'show', 'list', 'observe', 'watch', 'doctor', 'help'].includes(sub))
|
|
112
|
+
return false;
|
|
113
|
+
return !args.some((arg) => ['--fix', '--yes', '-y', '--write', '--apply', '--execute', '--force', '--real'].includes(String(arg)));
|
|
114
|
+
}
|
|
115
|
+
function activeRouteStateBlocksCommand(state = {}) {
|
|
116
|
+
if (!state?.mission_id || state.route_closed === true)
|
|
117
|
+
return false;
|
|
118
|
+
const mode = String(state.mode || '').toUpperCase();
|
|
119
|
+
if (!mode || ['WIKI', 'STATUS', 'HELP'].includes(mode))
|
|
120
|
+
return false;
|
|
121
|
+
if (/(?:DONE|COMPLETE|CLOSED|BLOCKED|FAILED)$/i.test(String(state.phase || '')))
|
|
122
|
+
return false;
|
|
123
|
+
return Boolean(state.route || state.route_command || ['NARUTO', 'AGENT', 'QALOOP', 'RESEARCH', 'LOOP', 'MADSKS', 'MADDB', 'GOAL'].includes(mode));
|
|
124
|
+
}
|
|
69
125
|
//# sourceMappingURL=router.js.map
|
|
@@ -443,8 +443,8 @@ function shapeCodexLbStatus(status = {}) {
|
|
|
443
443
|
modes.push('durable_env_file');
|
|
444
444
|
if (status.env_loader?.api_key?.source === 'keychain' || status.env_loader?.keychain?.status === 'present')
|
|
445
445
|
modes.push('durable_keychain');
|
|
446
|
-
if (status.launch_environment?.status === '
|
|
447
|
-
modes.push('
|
|
446
|
+
if (status.launch_environment?.status === 'secret_env_present')
|
|
447
|
+
modes.push('process_only_ephemeral');
|
|
448
448
|
if (!modes.length && status.env_loader?.api_key?.source === 'process.env')
|
|
449
449
|
modes.push('process_only_ephemeral');
|
|
450
450
|
const mode = modes[0] || 'none';
|
|
@@ -493,7 +493,7 @@ async function codexLbSetupOptions(args = []) {
|
|
|
493
493
|
writeEnvFile = true;
|
|
494
494
|
if (flag(args, '--no-keychain'))
|
|
495
495
|
keychain = false;
|
|
496
|
-
let syncLaunchctl =
|
|
496
|
+
let syncLaunchctl = false;
|
|
497
497
|
if (flag(args, '--launchctl'))
|
|
498
498
|
syncLaunchctl = true;
|
|
499
499
|
const shellProfile = normalizeShellProfile(readOption(args, '--shell-profile', 'skip'));
|
|
@@ -508,7 +508,7 @@ async function codexLbSetupOptions(args = []) {
|
|
|
508
508
|
writeEnvFile = parseYesNo(await ask('4. Write shell env loader to ~/.codex/sks-codex-lb.env? [Y/n] '), true);
|
|
509
509
|
const storeKeychain = (await ask('5. Store the key in macOS Keychain when available? [Y/n] ')).trim();
|
|
510
510
|
keychain = !/^(n|no|아니|아니요|ㄴ)$/i.test(storeKeychain || 'y');
|
|
511
|
-
syncLaunchctl = parseYesNo(await ask('6. Sync macOS launchctl
|
|
511
|
+
syncLaunchctl = parseYesNo(await ask('6. Sync non-secret macOS launchctl base URL only? API keys are never stored in launchd. [y/N] '), false);
|
|
512
512
|
const profile = (await ask('7. Install shell profile snippet? [zsh/bash/fish/all/skip] ')).trim();
|
|
513
513
|
const interactiveShellProfile = normalizeShellProfile(profile || 'skip');
|
|
514
514
|
const runHealth = (await ask('8. Run health check now? [Y/n] ')).trim();
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import { projectRoot, exists, formatBytes } from '../core/fsx.js';
|
|
2
4
|
import { flag } from '../cli/args.js';
|
|
3
5
|
import { printJson } from '../cli/output.js';
|
|
@@ -40,6 +42,8 @@ import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js
|
|
|
40
42
|
import { withSecretPreservationGuard } from '../core/config/config-migration-journal.js';
|
|
41
43
|
import { writeProjectUpdateMigrationReceipt } from '../core/update/update-migration-state.js';
|
|
42
44
|
import { installSksMenuBar } from '../core/codex-app/sks-menubar.js';
|
|
45
|
+
import { sweepSksTempDirs } from '../core/retention.js';
|
|
46
|
+
import { reconcileSkills } from '../core/init/skills.js';
|
|
43
47
|
export async function run(_command, args = []) {
|
|
44
48
|
const root = await projectRoot();
|
|
45
49
|
const doctorFix = flag(args, '--fix');
|
|
@@ -49,9 +53,15 @@ export async function run(_command, args = []) {
|
|
|
49
53
|
}
|
|
50
54
|
async function runDoctor(args = [], root, doctorFix) {
|
|
51
55
|
const startedAtMs = Date.now();
|
|
56
|
+
const sksTempSweep = doctorFix ? await sweepSksTempDirs(root, { maxAgeHours: 24 }).catch((err) => ({
|
|
57
|
+
ok: false,
|
|
58
|
+
error: err?.message || String(err),
|
|
59
|
+
actions: []
|
|
60
|
+
})) : { ok: true, skipped: true, reason: 'doctor_without_fix', actions: [] };
|
|
52
61
|
const doctorProfile = doctorProfileFromArgs(args, doctorFix);
|
|
53
62
|
const machineOnly = flag(args, '--machine-only');
|
|
54
63
|
const reportFile = readOption(args, '--report-file', null);
|
|
64
|
+
const argWarnings = doctorArgWarnings(args);
|
|
55
65
|
const deepDiagnostics = doctorProfile === 'full' || doctorProfile === 'capabilities';
|
|
56
66
|
const codexBin = readOption(args, '--codex-bin', process.env.SKS_DOCTOR_CODEX_BIN || '');
|
|
57
67
|
const actualCodexProbeRequested = flag(args, '--actual-codex') || flag(args, '--require-actual-codex') || Boolean(codexBin);
|
|
@@ -99,6 +109,20 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
99
109
|
: deepDiagnostics ? await ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) })) : { status: 'skipped', reason: 'default_doctor_no_global_fast_mode_regeneration' }
|
|
100
110
|
};
|
|
101
111
|
}
|
|
112
|
+
const skillsReconcile = doctorFix
|
|
113
|
+
? {
|
|
114
|
+
global: await reconcileSkills({
|
|
115
|
+
targetDir: path.join(os.homedir(), '.agents', 'skills'),
|
|
116
|
+
scope: 'global',
|
|
117
|
+
fix: true
|
|
118
|
+
}).catch((err) => ({ ok: false, error: err?.message || String(err) })),
|
|
119
|
+
project: await reconcileSkills({
|
|
120
|
+
targetDir: path.join(root, '.agents', 'skills'),
|
|
121
|
+
scope: 'project',
|
|
122
|
+
fix: true
|
|
123
|
+
}).catch((err) => ({ ok: false, error: err?.message || String(err) }))
|
|
124
|
+
}
|
|
125
|
+
: { skipped: true, reason: 'doctor_without_fix' };
|
|
102
126
|
const commandAliasCleanup = await runDoctorCommandAliasCleanup({
|
|
103
127
|
root,
|
|
104
128
|
fix: doctorFix
|
|
@@ -289,10 +313,18 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
289
313
|
executable_path: null,
|
|
290
314
|
launch_agent_path: null,
|
|
291
315
|
action_script_path: null,
|
|
316
|
+
build_stamp_path: null,
|
|
292
317
|
report_path: `${root}/.sneakoscope/reports/sks-menubar.json`,
|
|
293
318
|
menu_items: [],
|
|
294
319
|
actions: [],
|
|
295
320
|
launch: { requested: doctorFix, method: 'none', ok: false, error: err?.message || String(err) },
|
|
321
|
+
tcc_automation_status: 'unknown',
|
|
322
|
+
next_actions: [
|
|
323
|
+
'Run: sks menubar status',
|
|
324
|
+
'Run: sks menubar install',
|
|
325
|
+
'Run: sks menubar restart',
|
|
326
|
+
'Rotate CODEX_LB_API_KEY and OPENROUTER_API_KEY if they were previously exposed in launchd.'
|
|
327
|
+
],
|
|
296
328
|
blockers: [err?.message || String(err)],
|
|
297
329
|
warnings: []
|
|
298
330
|
}));
|
|
@@ -687,8 +719,9 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
687
719
|
const result = {
|
|
688
720
|
schema: 'sks.doctor-status.v2',
|
|
689
721
|
elapsed_ms: Date.now() - startedAtMs,
|
|
690
|
-
ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false && codexStartupRepair.ok !== false
|
|
722
|
+
ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false && codexStartupRepair.ok !== false,
|
|
691
723
|
root,
|
|
724
|
+
arg_warnings: argWarnings,
|
|
692
725
|
node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
|
|
693
726
|
codex,
|
|
694
727
|
codex_config: codexConfig,
|
|
@@ -712,11 +745,24 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
712
745
|
supabase_mcp_repair: supabaseMcpRepair,
|
|
713
746
|
doctor_fix_transaction: doctorFixTransaction,
|
|
714
747
|
doctor_fix_postcheck: doctorFixPostcheck,
|
|
748
|
+
postcheck: doctorFixPostcheck ? {
|
|
749
|
+
ok: doctorFixPostcheck.ok === true,
|
|
750
|
+
pending_manual: doctorFixPostcheck.pending_manual || [],
|
|
751
|
+
required_blockers: doctorFixPostcheck.required_blockers || [],
|
|
752
|
+
optional_warnings: doctorFixPostcheck.optional_warnings || []
|
|
753
|
+
} : null,
|
|
715
754
|
local_model: localModel,
|
|
716
755
|
agent_role_config: agentRoleConfigRepair,
|
|
717
756
|
zellij_readiness: zellijReadiness,
|
|
718
757
|
codex_permission_profiles: permissionProfiles,
|
|
719
758
|
command_aliases: commandAliasCleanup,
|
|
759
|
+
sks_temp_sweep: {
|
|
760
|
+
ok: sksTempSweep.ok !== false,
|
|
761
|
+
skipped: sksTempSweep.skipped === true,
|
|
762
|
+
action_count: Array.isArray(sksTempSweep.actions) ? sksTempSweep.actions.length : 0,
|
|
763
|
+
reason: sksTempSweep.reason || null,
|
|
764
|
+
error: sksTempSweep.error || null
|
|
765
|
+
},
|
|
720
766
|
imagegen: {
|
|
721
767
|
ok: imagegen.auth_readiness?.available_paths?.length > 0,
|
|
722
768
|
auth_readiness: imagegen.auth_readiness || null,
|
|
@@ -735,7 +781,8 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
735
781
|
ready,
|
|
736
782
|
sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
|
|
737
783
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
738
|
-
|
|
784
|
+
skills: skillsReconcile,
|
|
785
|
+
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, sks_menubar: sksMenuBar, doctor_transaction: doctorFixTransaction, doctor_dirty_plan: doctorDirtyPlan, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup, skills: skillsReconcile, sks_temp_sweep: sksTempSweep }
|
|
739
786
|
};
|
|
740
787
|
if (reportFile)
|
|
741
788
|
await writeJsonReportFile(reportFile, result);
|
|
@@ -751,6 +798,8 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
751
798
|
return;
|
|
752
799
|
}
|
|
753
800
|
console.log('SKS Doctor');
|
|
801
|
+
for (const warning of argWarnings)
|
|
802
|
+
console.log(`Argument warning: ${warning}`);
|
|
754
803
|
console.log(`Root: ${root}`);
|
|
755
804
|
console.log(`Node: ${result.node.ok ? 'ok' : 'fail'} ${result.node.version}`);
|
|
756
805
|
console.log(`Codex: ${codex.bin ? 'ok' : 'missing'} ${codex.version || ''}`);
|
|
@@ -1017,7 +1066,7 @@ function fallbackCodexNativeFeatureMatrix(codex, blockers = [], warnings = []) {
|
|
|
1017
1066
|
warnings
|
|
1018
1067
|
};
|
|
1019
1068
|
}
|
|
1020
|
-
function doctorProfileFromArgs(args = [], doctorFix = false) {
|
|
1069
|
+
export function doctorProfileFromArgs(args = [], doctorFix = false) {
|
|
1021
1070
|
const explicit = readOption(args, '--profile', null);
|
|
1022
1071
|
if (explicit === 'migration' || explicit === 'full' || explicit === 'capabilities' || explicit === 'fast' || explicit === 'fix')
|
|
1023
1072
|
return explicit;
|
|
@@ -1027,6 +1076,39 @@ function doctorProfileFromArgs(args = [], doctorFix = false) {
|
|
|
1027
1076
|
return 'capabilities';
|
|
1028
1077
|
return doctorFix ? 'fix' : 'fast';
|
|
1029
1078
|
}
|
|
1079
|
+
export function doctorArgWarnings(args = []) {
|
|
1080
|
+
const warnings = [];
|
|
1081
|
+
const explicit = readOption(args, '--profile', null);
|
|
1082
|
+
if (explicit && !['migration', 'full', 'capabilities', 'fast', 'fix'].includes(String(explicit))) {
|
|
1083
|
+
warnings.push(`unknown_profile:${explicit}; supported profiles: migration, full, capabilities, fast, fix`);
|
|
1084
|
+
}
|
|
1085
|
+
for (const flag of unknownDoctorFlags(args))
|
|
1086
|
+
warnings.push(`unknown_flag:${flag}`);
|
|
1087
|
+
return warnings;
|
|
1088
|
+
}
|
|
1089
|
+
function unknownDoctorFlags(args = []) {
|
|
1090
|
+
const knownBoolean = new Set([
|
|
1091
|
+
'--fix', '--yes', '-y', '--machine-only', '--actual-codex', '--require-actual-codex',
|
|
1092
|
+
'--full', '--capabilities', '--repair-codex-app-ui', '--repair-zellij', '--install-homebrew',
|
|
1093
|
+
'--repair-native-capabilities', '--repair-codex-native', '--local-only', '--project', '--global',
|
|
1094
|
+
'--dry-run', '--json'
|
|
1095
|
+
]);
|
|
1096
|
+
const knownValue = new Set(['--profile', '--report-file', '--codex-bin', '--install-scope']);
|
|
1097
|
+
const unknown = [];
|
|
1098
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1099
|
+
const arg = String(args[index] || '');
|
|
1100
|
+
if (!arg.startsWith('-'))
|
|
1101
|
+
continue;
|
|
1102
|
+
if (knownValue.has(arg)) {
|
|
1103
|
+
index += 1;
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
if (knownBoolean.has(arg))
|
|
1107
|
+
continue;
|
|
1108
|
+
unknown.push(arg);
|
|
1109
|
+
}
|
|
1110
|
+
return unknown;
|
|
1111
|
+
}
|
|
1030
1112
|
function doctorPhaseIdsForProfile(profile) {
|
|
1031
1113
|
const required = [
|
|
1032
1114
|
'codex_startup_repair',
|