sneakoscope 5.6.0 → 5.7.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.
- package/README.md +1 -1
- 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 +1 -1
- package/dist/cli/install-helpers.js +21 -15
- package/dist/commands/doctor.js +44 -22
- package/dist/config/skills-manifest.json +58 -58
- package/dist/core/codex/agent-config-file-repair.js +2 -1
- package/dist/core/codex/codex-config-guard.js +10 -0
- package/dist/core/codex-app/sks-menubar.js +113 -21
- package/dist/core/commands/run-command.js +5 -1
- package/dist/core/commands/wiki-command.js +19 -1
- package/dist/core/feature-fixtures.js +11 -11
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +48 -0
- package/dist/core/hooks-runtime.js +4 -0
- package/dist/core/triwiki-attention.js +16 -8
- package/dist/core/triwiki-wrongness/wrongness-schema.js +18 -1
- package/dist/core/update/update-migration-state.js +178 -33
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-lb-gpt55-fast-profile-check.js +3 -2
- package/dist/scripts/packlist-performance-check.js +4 -2
- package/dist/scripts/release-metadata-1-19-check.js +12 -1
- package/dist/scripts/sks-menubar-install-check.js +83 -0
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
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.
|
|
16
16
|
|
|
17
|
-
Current release:
|
|
17
|
+
Current release: SKS **5.7.0**. New in this release: repaired Codex App menu bar persistence and Fast Mode actions, codex-lb Fast Mode defaults pinned to the required current Codex model, legacy upgrade repair through `sks update` and `sks doctor --fix`, and a safer `npm publish --ignore-scripts` wrapper that runs the release gates before lifecycle-disabled publish. See [CHANGELOG.md](CHANGELOG.md).
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
@@ -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.
|
|
7
|
+
Some("--version") => println!("sks-rs 5.7.0"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
|
@@ -143,7 +143,7 @@ export const COMMANDS = {
|
|
|
143
143
|
'mad-db': routeStateMutator(entry('beta', 'Deprecated alias for MAD-SKS SQL-plane execution; redirects to sks mad-sks sql|apply-migration', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')), ['mad-sks-gate.json']),
|
|
144
144
|
'auto-review': entry('beta', 'Manage auto-review profile', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
|
|
145
145
|
'dollar-commands': entry('stable', 'List Codex App dollar commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
|
|
146
|
-
'fast-mode': entry('stable', 'Toggle SKS Fast mode default for dollar-command routes', 'dist/core/commands/fast-mode-command.js', argsCommand(() => import('../core/commands/fast-mode-command.js'), 'fastModeCommand', 'dist/core/commands/fast-mode-command.js')),
|
|
146
|
+
'fast-mode': skipMigrationGate(entry('stable', 'Toggle SKS Fast mode default for dollar-command routes', 'dist/core/commands/fast-mode-command.js', argsCommand(() => import('../core/commands/fast-mode-command.js'), 'fastModeCommand', 'dist/core/commands/fast-mode-command.js'))),
|
|
147
147
|
commit: entry('stable', 'Create a simple git commit', 'dist/commands/commit.js', directCommand(() => import('../commands/commit.js'), 'dist/commands/commit.js')),
|
|
148
148
|
'commit-and-push': entry('stable', 'Create a simple git commit and push', 'dist/commands/commit-and-push.js', directCommand(() => import('../commands/commit-and-push.js'), 'dist/commands/commit-and-push.js')),
|
|
149
149
|
dfix: routeStateMutator(entry('stable', 'Run DFix diagnose/plan/patch/verify loop', 'dist/core/commands/dfix-command.js', commandArgsCommand(() => import('../core/commands/dfix-command.js'), 'dfixCommand', 'dist/core/commands/dfix-command.js')), ['dfix-gate.json']),
|
|
@@ -23,6 +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
27
|
const CODEX_LB_PROVIDER_NAME = 'openai';
|
|
27
28
|
const CODEX_LB_PROVIDER_ENV_KEY = 'CODEX_LB_API_KEY';
|
|
28
29
|
const CODEX_LB_CANONICAL_FAST_SERVICE_TIER = 'priority';
|
|
@@ -275,7 +276,7 @@ async function postinstallHarnessConflictNotice(conflictScan) {
|
|
|
275
276
|
console.log(formatHarnessConflictReport(conflictScan, { includePrompt: false }));
|
|
276
277
|
console.log('\nWhat this means: npm can finish installing the package, but `sks setup` and `sks doctor --fix` will refuse to activate SKS until the conflicting harness is removed with human approval.');
|
|
277
278
|
console.log('No files were removed by postinstall.');
|
|
278
|
-
console.log(
|
|
279
|
+
console.log(`Cleanup requires a human-approved Codex App session. Recommended model: ${REQUIRED_CODEX_MODEL}, reasoning: high.`);
|
|
279
280
|
if (shouldAskPostinstallQuestion()) {
|
|
280
281
|
const answer = await askPostinstallQuestion('Show the cleanup prompt now? [y/N] ');
|
|
281
282
|
if (/^(y|yes|예|네|응)$/i.test(answer.trim())) {
|
|
@@ -901,7 +902,7 @@ export async function checkCodexLbResponseChain(status = {}, opts = {}) {
|
|
|
901
902
|
const fetchImpl = opts.fetch || globalThis.fetch;
|
|
902
903
|
if (typeof fetchImpl !== 'function')
|
|
903
904
|
return { ok: true, status: 'skipped', skipped: true, reason: 'fetch unavailable' };
|
|
904
|
-
const model = opts.model || env.SKS_CODEX_MODEL ||
|
|
905
|
+
const model = opts.model || env.SKS_CODEX_MODEL || REQUIRED_CODEX_MODEL;
|
|
905
906
|
const timeoutMs = Number(opts.timeoutMs || env.SKS_CODEX_LB_CHAIN_CHECK_TIMEOUT_MS || 8000);
|
|
906
907
|
const serviceTier = opts.fastMode === true || opts.serviceTier === 'fast' || opts.serviceTier === CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
907
908
|
? CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
@@ -1890,8 +1891,9 @@ export function normalizeCodexFastModeUiConfig(text = '', opts = {}) {
|
|
|
1890
1891
|
return normalizeCodexFastModeUiConfigOnce(normalizeCodexFastModeUiConfigOnce(text, opts), opts);
|
|
1891
1892
|
}
|
|
1892
1893
|
function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
1893
|
-
// Keep model
|
|
1894
|
-
// expose its native
|
|
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.
|
|
1895
1897
|
let next = String(text || '');
|
|
1896
1898
|
const misplacedDefaultProfile = tomlTableString(next, 'user.fast_mode', 'default_profile');
|
|
1897
1899
|
next = removeLegacyTopLevelCodexModeLocks(next);
|
|
@@ -1940,7 +1942,7 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1940
1942
|
// they are managed as per-file `<name>.config.toml` overlays by
|
|
1941
1943
|
// migrateSksProfilesToPerFile (src/core/auto-review.ts), which also writes the
|
|
1942
1944
|
// sks-fast-high overlay for CLI `--profile` use.
|
|
1943
|
-
next = upsertTomlTableKey(next, 'profiles.sks-fast-high',
|
|
1945
|
+
next = upsertTomlTableKey(next, 'profiles.sks-fast-high', `model = "${REQUIRED_CODEX_MODEL}"`);
|
|
1944
1946
|
next = upsertTomlTableKey(next, 'profiles.sks-fast-high', 'service_tier = "fast"');
|
|
1945
1947
|
next = upsertTomlTableKey(next, 'profiles.sks-fast-high', 'approval_policy = "on-request"');
|
|
1946
1948
|
// Do not force a sandbox from the Codex App fast profile. The App/IDE
|
|
@@ -1966,9 +1968,13 @@ function removeLegacyTopLevelCodexModeLocks(text = '') {
|
|
|
1966
1968
|
const lines = String(text || '').split('\n');
|
|
1967
1969
|
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
1968
1970
|
const end = firstTable === -1 ? lines.length : firstTable;
|
|
1971
|
+
const topLevelModel = topLevelTomlString(text, 'model');
|
|
1972
|
+
const removeSksOwnedModeLock = topLevelModel === REQUIRED_CODEX_MODEL;
|
|
1969
1973
|
return lines.filter((line, index) => {
|
|
1970
1974
|
if (index >= end)
|
|
1971
1975
|
return true;
|
|
1976
|
+
if (!removeSksOwnedModeLock)
|
|
1977
|
+
return true;
|
|
1972
1978
|
return !/^\s*(?:model|model_reasoning_effort)\s*=/.test(line);
|
|
1973
1979
|
}).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
1974
1980
|
}
|
|
@@ -2142,7 +2148,7 @@ export function codexFastModeDesktopStatus(text = '') {
|
|
|
2142
2148
|
const profile = validation.parsed?.profiles?.['sks-fast-high'];
|
|
2143
2149
|
const globalOn = validation.ok
|
|
2144
2150
|
&& validation.default_profile === 'sks-fast-high'
|
|
2145
|
-
&& profile?.model ===
|
|
2151
|
+
&& profile?.model === REQUIRED_CODEX_MODEL
|
|
2146
2152
|
&& profile?.service_tier === 'fast';
|
|
2147
2153
|
return {
|
|
2148
2154
|
schema: 'sks.codex-fast-mode-desktop-status.v1',
|
|
@@ -2862,7 +2868,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2862
2868
|
// escape that dash emits literally and bash collapses to `"`).
|
|
2863
2869
|
await writeTextAtomic(codexLbFakeCodex, "#!/bin/sh\nif [ \"$1\" = \"--version\" ]; then echo \"codex-cli 99.0.0\"; exit 0; fi\nif [ \"$1\" = \"login\" ] && [ \"$2\" = \"status\" ]; then echo \"logged in with browser auth\"; exit 0; fi\nif [ \"$1\" = \"login\" ] && [ \"$2\" = \"--with-api-key\" ]; then read key; mkdir -p \"$HOME/.codex\"; printf '{\"auth_mode\":\"apikey\",\"OPENAI_API_KEY\":\"%s\"}\\n' \"$key\" > \"$HOME/.codex/auth.json\"; printf '%s\\n' \"$key\" >> \"$HOME/.codex/login-calls.log\"; exit 0; fi\necho \"fake codex unsupported\" >&2\nexit 1\n");
|
|
2864
2870
|
await fsp.chmod(codexLbFakeCodex, 0o755);
|
|
2865
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
2871
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "low"\nservice_tier = "fast"\nsuppress_unstable_features_warning = true\n\n[profiles.custom]\nmodel_reasoning_effort = "low"\n\n[notice]\nfast_default_opt_out = true\n\n[features]\nhooks = true\n`);
|
|
2866
2872
|
const codexLbEnvForSelftest = { HOME: codexLbHome, SKS_GLOBAL_ROOT: path.join(tmp, 'codex-lb-global'), PATH: `${codexLbFakeBin}${path.delimiter}${process.env.PATH || ''}`, SKS_SKIP_CODEX_LB_LAUNCH_ENV: '1' };
|
|
2867
2873
|
const codexLbSetup = await runProcess(process.execPath, [packagedSksEntrypoint(), 'codex-lb', 'setup', '--host', 'lb.example.test', '--api-key', 'sk-test', '--json'], {
|
|
2868
2874
|
cwd: tmp,
|
|
@@ -2967,7 +2973,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2967
2973
|
await postinstall({
|
|
2968
2974
|
bootstrap: async () => {
|
|
2969
2975
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"browser"}\n');
|
|
2970
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
2976
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model = "${REQUIRED_CODEX_MODEL}"\nservice_tier = "fast"\nsuppress_unstable_features_warning = true\n\n[features]\nhooks = true\n`);
|
|
2971
2977
|
}
|
|
2972
2978
|
});
|
|
2973
2979
|
}
|
|
@@ -2993,7 +2999,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2993
2999
|
await writeTextAtomic(path.join(doctorProject, 'package.json'), '{"name":"codex-lb-doctor-project","version":"0.0.0"}\n');
|
|
2994
3000
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\nexport CODEX_LB_API_KEY='sk-test'\n");
|
|
2995
3001
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"browser"}\n');
|
|
2996
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3002
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model = "${REQUIRED_CODEX_MODEL}"\nservice_tier = "fast"\nsuppress_unstable_features_warning = true\n\n[features]\nhooks = true\n`);
|
|
2997
3003
|
const codexLbDoctorRepair = await runProcess(process.execPath, [packagedSksEntrypoint(), 'doctor', '--fix', '--json'], {
|
|
2998
3004
|
cwd: doctorProject,
|
|
2999
3005
|
env: { ...codexLbEnvForSelftest, SKS_GLOBAL_ROOT: path.join(tmp, 'codex-lb-doctor-global') },
|
|
@@ -3186,7 +3192,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3186
3192
|
if (!codexLbLegacyDoctorJson.repair?.codex_lb?.ok || !codexLbLegacyDoctorJson.repair.codex_lb.legacy_auth_migrated || !codexLbLegacyDoctorEnv.includes("CODEX_LB_API_KEY='sk-legacy-doctor'") || !codexLbLegacyDoctorAuth.includes('"auth_mode":"apikey"') || !codexLbLegacyDoctorAuth.includes('sk-legacy-doctor') || !hasTopLevelCodexLbSelected(codexLbLegacyDoctorConfig) || !codexLbLegacyDoctorConfig.includes('env_key = "CODEX_LB_API_KEY"'))
|
|
3187
3193
|
throw new Error('selftest: legacy doctor codex-lb restore');
|
|
3188
3194
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\n");
|
|
3189
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3195
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model = "${REQUIRED_CODEX_MODEL}"\nservice_tier = "fast"\n`);
|
|
3190
3196
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"apikey","key":"sk-env-only"}\n');
|
|
3191
3197
|
const codexLbLoginCallsBeforeEnvOnlyPostinstall = await codexLbLoginCallCount(codexLbHome);
|
|
3192
3198
|
const codexLbEnvOnlyPostinstall = await runProcess(process.execPath, [packagedSksEntrypoint(), 'postinstall'], {
|
|
@@ -3204,7 +3210,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3204
3210
|
if (!String(codexLbEnvOnlyPostinstall.stdout || '').includes('codex-lb auth: restored from existing Codex login cache') || !codexLbEnvOnlyPostinstallEnv.includes("CODEX_LB_API_KEY='sk-env-only'") || !codexLbEnvOnlyPostinstallConfig.includes('env_key = "CODEX_LB_API_KEY"') || !hasTopLevelCodexLbSelected(codexLbEnvOnlyPostinstallConfig) || !codexLbEnvOnlyPostinstallAuth.includes('sk-env-only') || codexLbLoginCallsAfterEnvOnlyPostinstall !== codexLbLoginCallsBeforeEnvOnlyPostinstall)
|
|
3205
3211
|
throw new Error('selftest: env-only codex-lb postinstall restore');
|
|
3206
3212
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\n");
|
|
3207
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3213
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model = "${REQUIRED_CODEX_MODEL}"\nservice_tier = "fast"\n`);
|
|
3208
3214
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"apikey","key":"sk-env-only-doctor"}\n');
|
|
3209
3215
|
const codexLbEnvOnlyDoctorProject = tmpdir();
|
|
3210
3216
|
await ensureDir(path.join(codexLbEnvOnlyDoctorProject, '.git'));
|
|
@@ -3334,7 +3340,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3334
3340
|
}
|
|
3335
3341
|
if (autobypassLaunch.status !== 'chain_unhealthy' || autobypassLaunch.bypass_codex_lb !== true || autobypassLaunch.chain_health?.status !== 'second_request_failed')
|
|
3336
3342
|
throw new Error('selftest: SKS_CODEX_LB_AUTOBYPASS=1 should bypass codex-lb on hard chain failure');
|
|
3337
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3343
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model = "${REQUIRED_CODEX_MODEL}"\nservice_tier = "fast"\n`);
|
|
3338
3344
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\nexport CODEX_LB_API_KEY='sk-test'\n");
|
|
3339
3345
|
const missingProviderLaunchCalls = [];
|
|
3340
3346
|
const missingProviderLaunch = await maybePromptCodexLbSetupForLaunch([], {
|
|
@@ -3395,11 +3401,11 @@ export async function selftestCodexLb(tmp) {
|
|
|
3395
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');
|
|
3396
3402
|
if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig))
|
|
3397
3403
|
throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
|
|
3398
|
-
const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex '--model' '
|
|
3404
|
+
const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex '--model' '${REQUIRED_CODEX_MODEL}'`;
|
|
3399
3405
|
if (!codexLbLaunch.includes('sks-codex-lb.env'))
|
|
3400
3406
|
throw new Error('selftest: Zellij launch command does not source codex-lb env file');
|
|
3401
|
-
if (!codexLbLaunch.includes(
|
|
3402
|
-
throw new Error(
|
|
3407
|
+
if (!codexLbLaunch.includes(`'--model' '${REQUIRED_CODEX_MODEL}'`))
|
|
3408
|
+
throw new Error(`selftest: Zellij launch command without args did not force ${REQUIRED_CODEX_MODEL}`);
|
|
3403
3409
|
const madLaunchSource = await safeReadText(path.join(packageRoot(), 'src', 'core', 'commands', 'mad-sks-command.js'));
|
|
3404
3410
|
if (!madLaunchSource.includes('const lb = await deps.maybePromptCodexLbSetupForLaunch(args)') || !madLaunchSource.includes("const launchLb = lb.status === 'present'") || !madLaunchSource.includes('codexLbImmediateLaunchOpts(cleanArgs, launchLb') || !madLaunchSource.includes('bypass_codex_lb') || !madLaunchSource.includes('model_provider="openai"') || !madLaunchSource.includes('codexLbFreshSession: true'))
|
|
3405
3411
|
throw new Error('selftest: MAD launch does not sync codex-lb auth and fresh-session launch options');
|
package/dist/commands/doctor.js
CHANGED
|
@@ -41,7 +41,7 @@ import { runDoctorFixTransaction } from '../core/doctor/doctor-transaction.js';
|
|
|
41
41
|
import { planDoctorDirtyRepair } from '../core/doctor/doctor-dirty-planner.js';
|
|
42
42
|
import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js';
|
|
43
43
|
import { withSecretPreservationGuard } from '../core/config/config-migration-journal.js';
|
|
44
|
-
import { writeProjectUpdateMigrationReceipt } from '../core/update/update-migration-state.js';
|
|
44
|
+
import { isUpdateMigrationReceiptCurrent, projectUpdateMigrationReceiptPath, writeProjectUpdateMigrationReceipt } from '../core/update/update-migration-state.js';
|
|
45
45
|
import { inspectSksMenuBarStatus, installSksMenuBar } from '../core/codex-app/sks-menubar.js';
|
|
46
46
|
import { sweepSksTempDirs } from '../core/retention.js';
|
|
47
47
|
import { reconcileSkills } from '../core/init/skills.js';
|
|
@@ -84,16 +84,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
84
84
|
const doctorPhaseIds = doctorPhaseIdsForProfile(doctorProfile);
|
|
85
85
|
const doctorDirtyPlan = doctorFix ? planDoctorDirtyRepair(root, doctorPhaseIds) : null;
|
|
86
86
|
let setupRepair = null;
|
|
87
|
-
|
|
88
|
-
? {
|
|
89
|
-
schema: 'sks.update-now.v2',
|
|
90
|
-
ok: true,
|
|
91
|
-
status: 'skipped',
|
|
92
|
-
reason: 'manual_update_commands_only',
|
|
93
|
-
stages: [],
|
|
94
|
-
migration_current: true
|
|
95
|
-
}
|
|
96
|
-
: null;
|
|
87
|
+
let sksUpdate = null;
|
|
97
88
|
let migrationPreFix = null;
|
|
98
89
|
if (doctorFix) {
|
|
99
90
|
// Snapshot config content before ANY mutation so the migration journal can
|
|
@@ -117,7 +108,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
117
108
|
// backs up + parse-validates before writing, no-op when already present.
|
|
118
109
|
codex_app_fast_mode: flag(args, '--local-only')
|
|
119
110
|
? { status: 'skipped', reason: 'local-only repair' }
|
|
120
|
-
:
|
|
111
|
+
: await ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) }))
|
|
121
112
|
};
|
|
122
113
|
}
|
|
123
114
|
const skillsReconcile = doctorFix
|
|
@@ -823,16 +814,47 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
823
814
|
...(pluginPolicy?.doctor_warnings || [])
|
|
824
815
|
]
|
|
825
816
|
});
|
|
826
|
-
if (doctorFix
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
817
|
+
if (doctorFix) {
|
|
818
|
+
const readinessBlockers = Array.isArray(ready.blockers) ? ready.blockers.map(String).filter(Boolean) : [];
|
|
819
|
+
const migrationWarnings = [
|
|
820
|
+
...(doctorNativeCapabilityRepair?.optional_warnings || []),
|
|
821
|
+
...(doctorFixPostcheck?.optional_warnings || [])
|
|
822
|
+
];
|
|
823
|
+
try {
|
|
824
|
+
const receiptInput = {
|
|
825
|
+
root,
|
|
826
|
+
source: `doctor-${doctorProfile}`,
|
|
827
|
+
blockers: readinessBlockers,
|
|
828
|
+
warnings: migrationWarnings
|
|
829
|
+
};
|
|
830
|
+
if (readinessBlockers.length)
|
|
831
|
+
receiptInput.status = 'blocked';
|
|
832
|
+
const receipt = await writeProjectUpdateMigrationReceipt(receiptInput);
|
|
833
|
+
sksUpdate = {
|
|
834
|
+
schema: 'sks.update-now.v2',
|
|
835
|
+
ok: receipt.status === 'current' && isUpdateMigrationReceiptCurrent(receipt),
|
|
836
|
+
status: receipt.status === 'current' ? 'repaired' : receipt.status,
|
|
837
|
+
reason: receipt.status === 'current' ? 'doctor_fix_wrote_current_project_migration_receipt' : 'doctor_fix_migration_receipt_blocked',
|
|
838
|
+
stages: receipt.legacy_migration_stages || [],
|
|
839
|
+
migration_current: isUpdateMigrationReceiptCurrent(receipt),
|
|
840
|
+
receipt_path: projectUpdateMigrationReceiptPath(root),
|
|
841
|
+
blockers: receipt.blockers || [],
|
|
842
|
+
warnings: receipt.warnings || []
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
catch (err) {
|
|
846
|
+
sksUpdate = {
|
|
847
|
+
schema: 'sks.update-now.v2',
|
|
848
|
+
ok: false,
|
|
849
|
+
status: 'blocked',
|
|
850
|
+
reason: 'doctor_fix_migration_receipt_failed',
|
|
851
|
+
stages: [],
|
|
852
|
+
migration_current: false,
|
|
853
|
+
receipt_path: projectUpdateMigrationReceiptPath(root),
|
|
854
|
+
blockers: [`migration_receipt_failed:${err?.message || String(err)}`],
|
|
855
|
+
warnings: migrationWarnings
|
|
856
|
+
};
|
|
857
|
+
}
|
|
836
858
|
}
|
|
837
859
|
const zellijReadiness = buildZellijReadiness(root, zellij, ready);
|
|
838
860
|
const runtimeReadiness = buildRuntimeReadiness(zellijReadiness, codexNativeFeatureMatrix);
|