sneakoscope 4.1.0 → 4.1.1
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 +11 -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/router.js +6 -1
- package/dist/commands/doctor.js +272 -127
- package/dist/core/codex/agent-config-file-repair.js +43 -2
- package/dist/core/codex-app/codex-agent-role-sync.js +4 -4
- package/dist/core/codex-control/codex-0142-capability.js +51 -6
- package/dist/core/codex-control/codex-app-server-v2-client.js +2 -2
- package/dist/core/codex-native/codex-native-feature-broker.js +50 -0
- package/dist/core/codex-native/native-capability-postcheck.js +59 -16
- package/dist/core/codex-native/native-capability-repair-matrix.js +77 -13
- package/dist/core/commands/mad-sks-command.js +36 -30
- package/dist/core/doctor/doctor-dirty-planner.js +9 -4
- package/dist/core/doctor/doctor-native-capability-repair.js +42 -7
- package/dist/core/doctor/doctor-readiness-matrix.js +9 -5
- package/dist/core/doctor/doctor-repair-postcheck.js +10 -1
- package/dist/core/doctor/doctor-transaction.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +14 -4
- package/dist/core/providers/glm/bench/glm-benchmark-runner.js +4 -3
- package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
- package/dist/core/update/update-migration-state.js +265 -50
- package/dist/core/update-check.js +6 -6
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-launcher.js +17 -5
- package/dist/scripts/codex-0142-manifest-check.js +1 -1
- package/dist/scripts/config-managed-merge-callsite-coverage-check.js +6 -0
- package/dist/scripts/doctor-dirty-plan-check.js +1 -1
- package/dist/scripts/doctor-transaction-engine-check.js +1 -0
- package/dist/scripts/doctor-warning-only-not-blocker-check.js +18 -1
- package/dist/scripts/loop-directive-check-lib.js +2 -1
- package/dist/scripts/mad-sks-zellij-launch-check.js +7 -1
- package/dist/scripts/managed-role-manifest-parity-check.js +4 -1
- package/dist/scripts/naruto-real-parallelism-blackbox.js +17 -4
- package/dist/scripts/native-capability-postcheck-check.js +1 -0
- package/dist/scripts/native-capability-repair-matrix-check.js +2 -0
- package/dist/scripts/native-chrome-web-review-repair-check.js +1 -0
- package/dist/scripts/native-computer-use-repair-check.js +1 -0
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +1 -1
- package/dist/scripts/sks-401-all-feature-regression-blackbox.js +1 -1
- package/dist/scripts/update-concurrent-lock-check.js +1 -0
- package/dist/scripts/update-first-command-migration-check.js +4 -3
- package/package.json +2 -1
- package/schemas/update-migration.schema.json +13 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { projectRoot,
|
|
1
|
+
import { projectRoot, exists, formatBytes } from '../core/fsx.js';
|
|
2
2
|
import { flag } from '../cli/args.js';
|
|
3
3
|
import { printJson } from '../cli/output.js';
|
|
4
4
|
import { getCodexInfo } from '../core/codex-adapter.js';
|
|
@@ -38,6 +38,7 @@ import { runDoctorFixTransaction } from '../core/doctor/doctor-transaction.js';
|
|
|
38
38
|
import { planDoctorDirtyRepair } from '../core/doctor/doctor-dirty-planner.js';
|
|
39
39
|
import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js';
|
|
40
40
|
import { withSecretPreservationGuard } from '../core/config/config-migration-journal.js';
|
|
41
|
+
import { writeProjectUpdateMigrationReceipt } from '../core/update/update-migration-state.js';
|
|
41
42
|
export async function run(_command, args = []) {
|
|
42
43
|
const root = await projectRoot();
|
|
43
44
|
const doctorFix = flag(args, '--fix');
|
|
@@ -46,6 +47,20 @@ export async function run(_command, args = []) {
|
|
|
46
47
|
return runDoctor(args, root, doctorFix);
|
|
47
48
|
}
|
|
48
49
|
async function runDoctor(args = [], root, doctorFix) {
|
|
50
|
+
const startedAtMs = Date.now();
|
|
51
|
+
const doctorProfile = doctorProfileFromArgs(args, doctorFix);
|
|
52
|
+
const machineOnly = flag(args, '--machine-only');
|
|
53
|
+
const reportFile = readOption(args, '--report-file', null);
|
|
54
|
+
const deepDiagnostics = doctorProfile === 'full' || doctorProfile === 'capabilities';
|
|
55
|
+
const codexBin = readOption(args, '--codex-bin', process.env.SKS_DOCTOR_CODEX_BIN || '');
|
|
56
|
+
const actualCodexProbeRequested = flag(args, '--actual-codex') || flag(args, '--require-actual-codex') || Boolean(codexBin);
|
|
57
|
+
const actualCodexProbeEnabled = deepDiagnostics || actualCodexProbeRequested;
|
|
58
|
+
const requireActualCodexProbe = flag(args, '--require-actual-codex') || (deepDiagnostics && doctorFix);
|
|
59
|
+
const shouldEvaluateCodexAppUiRepair = deepDiagnostics || flag(args, '--repair-codex-app-ui');
|
|
60
|
+
const shouldRunZellijRepair = deepDiagnostics || flag(args, '--repair-zellij') || flag(args, '--install-homebrew') || process.env.SKS_REQUIRE_ZELLIJ === '1';
|
|
61
|
+
const nativeCapabilityDiagnosticsRequested = deepDiagnostics || flag(args, '--repair-native-capabilities');
|
|
62
|
+
const doctorPhaseIds = doctorPhaseIdsForProfile(doctorProfile);
|
|
63
|
+
const doctorDirtyPlan = doctorFix ? planDoctorDirtyRepair(root, doctorPhaseIds) : null;
|
|
49
64
|
let setupRepair = null;
|
|
50
65
|
const sksUpdate = doctorFix
|
|
51
66
|
? {
|
|
@@ -62,21 +77,17 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
62
77
|
// Snapshot config content before ANY mutation so the migration journal can
|
|
63
78
|
// record real before/after hashes for the whole --fix transaction.
|
|
64
79
|
migrationPreFix = await captureCodexConfigSnapshot();
|
|
65
|
-
const { setupCommand } = await import('../core/commands/basic-cli.js');
|
|
66
80
|
const installScope = installScopeFromArgs(args);
|
|
67
|
-
// Back up the existing managed project config before --force regeneration so a
|
|
68
|
-
// hand-edited .codex/config.toml is always recoverable (mirrors the splitter/structure
|
|
69
|
-
// repair backup contract).
|
|
70
|
-
const preFixBackup = await backupProjectConfigBeforeFix();
|
|
71
|
-
const setupArgs = ['--force', '--install-scope', installScope];
|
|
72
|
-
if (flag(args, '--local-only'))
|
|
73
|
-
setupArgs.push('--local-only');
|
|
74
|
-
await setupCommand(setupArgs);
|
|
75
81
|
setupRepair = {
|
|
82
|
+
schema: 'sks.doctor-setup-phase.v2',
|
|
83
|
+
ok: true,
|
|
84
|
+
status: 'semantic_dirty_plan_only',
|
|
85
|
+
reason: 'setup_force_removed_from_doctor_hot_path',
|
|
86
|
+
profile: doctorProfile,
|
|
76
87
|
install_scope: installScope,
|
|
77
|
-
config_backup_path:
|
|
88
|
+
config_backup_path: null,
|
|
78
89
|
global_skills: installScope === 'global' && !flag(args, '--local-only')
|
|
79
|
-
? await ensureGlobalCodexSkillsDuringInstall({ force: true })
|
|
90
|
+
? deepDiagnostics ? await ensureGlobalCodexSkillsDuringInstall({ force: true }) : { status: 'skipped', reason: 'default_doctor_no_global_skill_regeneration' }
|
|
80
91
|
: { status: 'skipped', reason: 'project or local-only repair' },
|
|
81
92
|
// Re-seed the Codex App Fast-mode UI table ([user.fast_mode] visible/enabled/
|
|
82
93
|
// default_profile) in the global ~/.codex/config.toml so existing installs whose
|
|
@@ -84,7 +95,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
84
95
|
// backs up + parse-validates before writing, no-op when already present.
|
|
85
96
|
codex_app_fast_mode: flag(args, '--local-only')
|
|
86
97
|
? { status: 'skipped', reason: 'local-only repair' }
|
|
87
|
-
: await ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) }))
|
|
98
|
+
: deepDiagnostics ? await ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) })) : { status: 'skipped', reason: 'default_doctor_no_global_fast_mode_regeneration' }
|
|
88
99
|
};
|
|
89
100
|
}
|
|
90
101
|
const commandAliasCleanup = await runDoctorCommandAliasCleanup({
|
|
@@ -106,9 +117,10 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
106
117
|
}));
|
|
107
118
|
const doctorNativeCapabilityRepair = await runDoctorNativeCapabilityRepair({
|
|
108
119
|
root,
|
|
109
|
-
fix:
|
|
120
|
+
fix: nativeCapabilityDiagnosticsRequested && doctorFix,
|
|
110
121
|
yes: flag(args, '--yes') || flag(args, '-y'),
|
|
111
|
-
flags: args.map((arg) => String(arg))
|
|
122
|
+
flags: args.map((arg) => String(arg)),
|
|
123
|
+
skipNativeCapabilities: !nativeCapabilityDiagnosticsRequested
|
|
112
124
|
}).catch((err) => ({
|
|
113
125
|
schema: 'sks.doctor-native-capability-repair.v1',
|
|
114
126
|
ok: false,
|
|
@@ -119,13 +131,16 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
119
131
|
skill_dedupe: null,
|
|
120
132
|
native_capabilities: null,
|
|
121
133
|
secret_preservation_guard: '.sneakoscope/reports/secret-preservation-guard.json',
|
|
134
|
+
core_blockers: [err?.message || String(err)],
|
|
135
|
+
route_blockers: {},
|
|
136
|
+
optional_manual_required: [],
|
|
137
|
+
optional_warnings: [],
|
|
122
138
|
blockers: [err?.message || String(err)]
|
|
123
139
|
}));
|
|
124
|
-
const codexBin = readOption(args, '--codex-bin', process.env.SKS_DOCTOR_CODEX_BIN || '');
|
|
125
140
|
const configProbeOpts = {
|
|
126
|
-
codexProbe:
|
|
127
|
-
actualCodex:
|
|
128
|
-
requireActualCodex:
|
|
141
|
+
codexProbe: actualCodexProbeEnabled,
|
|
142
|
+
actualCodex: actualCodexProbeEnabled,
|
|
143
|
+
requireActualCodex: requireActualCodexProbe,
|
|
129
144
|
codexBin: codexBin || undefined
|
|
130
145
|
};
|
|
131
146
|
let codexStartupRepair = await runDoctorCodexStartupRepair({ root, fix: doctorFix }).catch((err) => ({
|
|
@@ -141,13 +156,15 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
141
156
|
warnings: [],
|
|
142
157
|
report_path: `${root}/.sneakoscope/reports/doctor-codex-startup-repair.json`
|
|
143
158
|
}));
|
|
144
|
-
const codexDoctorBefore = flag(args, '--fix') ? await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--require-actual-codex') }).catch(() => null) : null;
|
|
159
|
+
const codexDoctorBefore = flag(args, '--fix') && deepDiagnostics ? await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--require-actual-codex') }).catch(() => null) : null;
|
|
145
160
|
const configRepair = flag(args, '--fix') ? await repairCodexConfigEperm(root, { fix: true, ...configProbeOpts }) : null;
|
|
146
161
|
const migrationJournal = flag(args, '--fix')
|
|
147
162
|
? await writeFixMigrationJournal(root, migrationPreFix, configRepair, setupRepair).catch(() => null)
|
|
148
163
|
: null;
|
|
149
164
|
let codexConfig = configRepair?.after || await inspectCodexConfigReadability(root, configProbeOpts);
|
|
150
|
-
const preRepairCodexDoctor =
|
|
165
|
+
const preRepairCodexDoctor = deepDiagnostics || flag(args, '--require-actual-codex')
|
|
166
|
+
? await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--require-actual-codex') })
|
|
167
|
+
: null;
|
|
151
168
|
const codexDoctorDiff = compareCodexDoctorBridge(codexDoctorBefore, preRepairCodexDoctor);
|
|
152
169
|
codexStartupRepair = mergeObservedCodexStartupWarnings(codexStartupRepair, preRepairCodexDoctor);
|
|
153
170
|
const codex = codexBin
|
|
@@ -160,45 +177,83 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
160
177
|
version: null,
|
|
161
178
|
error: err.message
|
|
162
179
|
}));
|
|
163
|
-
const codexApp =
|
|
180
|
+
const codexApp = deepDiagnostics
|
|
181
|
+
? await codexAppIntegrationStatus({ codex }).catch((err) => ({ ok: false, error: err.message }))
|
|
182
|
+
: { ok: false, skipped: true, warnings: ['codex_app_optional_diagnostic_skipped'] };
|
|
164
183
|
const codexLb = codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({})));
|
|
165
|
-
const providerContext =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
184
|
+
const providerContext = deepDiagnostics
|
|
185
|
+
? await resolveProviderContext({ root, route: '$Doctor', serviceTier: process.env.SKS_SERVICE_TIER || 'fast' }).catch((err) => ({
|
|
186
|
+
schema: 'sks.provider-context.v1',
|
|
187
|
+
generated_at: new Date().toISOString(),
|
|
188
|
+
provider: 'unknown',
|
|
189
|
+
auth_mode: 'unknown',
|
|
190
|
+
route: '$Doctor',
|
|
191
|
+
service_tier: 'unknown',
|
|
192
|
+
source: 'unknown',
|
|
193
|
+
confidence: 'low',
|
|
194
|
+
conflict: false,
|
|
195
|
+
warnings: [err?.message || String(err)],
|
|
196
|
+
signals: {
|
|
197
|
+
openai_api_key_present: false,
|
|
198
|
+
codex_lb_key_present: false,
|
|
199
|
+
codex_lb_explicit: false,
|
|
200
|
+
codex_app_auth_present: false,
|
|
201
|
+
model_provider: null
|
|
202
|
+
}
|
|
203
|
+
}))
|
|
204
|
+
: {
|
|
205
|
+
schema: 'sks.provider-context.v1',
|
|
206
|
+
generated_at: new Date().toISOString(),
|
|
207
|
+
provider: 'unknown',
|
|
208
|
+
auth_mode: 'unknown',
|
|
209
|
+
route: '$Doctor',
|
|
210
|
+
service_tier: process.env.SKS_SERVICE_TIER || 'fast',
|
|
211
|
+
source: 'skipped',
|
|
212
|
+
confidence: 'low',
|
|
213
|
+
conflict: false,
|
|
214
|
+
warnings: ['provider_context_optional_diagnostic_skipped'],
|
|
215
|
+
signals: {
|
|
216
|
+
openai_api_key_present: false,
|
|
217
|
+
codex_lb_key_present: false,
|
|
218
|
+
codex_lb_explicit: false,
|
|
219
|
+
codex_app_auth_present: false,
|
|
220
|
+
model_provider: null
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const explicitCodexAppUiRepair = flag(args, '--repair-codex-app-ui');
|
|
224
|
+
const codexAppUiPlan = shouldEvaluateCodexAppUiRepair
|
|
225
|
+
? await repairCodexAppFastUi(root, {
|
|
226
|
+
apply: false,
|
|
227
|
+
reportPath: `${root}/.sneakoscope/reports/codex-app-fast-ui-repair-plan.json`
|
|
228
|
+
}).catch((err) => ({
|
|
229
|
+
schema: 'sks.codex-app-fast-ui-repair.v1',
|
|
230
|
+
ok: false,
|
|
231
|
+
apply: false,
|
|
232
|
+
safe_auto_apply: false,
|
|
233
|
+
requires_confirmation: true,
|
|
234
|
+
fast_selector: 'manual_action_required',
|
|
235
|
+
provider_selector: 'ok',
|
|
236
|
+
host_owned_config: 'diagnostic_failed',
|
|
237
|
+
next_action: 'Review Codex App UI config manually.',
|
|
238
|
+
actions: [],
|
|
239
|
+
blockers: [err?.message || String(err)]
|
|
240
|
+
}))
|
|
241
|
+
: {
|
|
242
|
+
schema: 'sks.codex-app-fast-ui-repair.v1',
|
|
243
|
+
ok: true,
|
|
244
|
+
apply: false,
|
|
245
|
+
skipped: true,
|
|
246
|
+
safe_auto_apply: false,
|
|
247
|
+
requires_confirmation: false,
|
|
248
|
+
fast_selector: 'skipped_optional',
|
|
249
|
+
provider_selector: 'skipped_optional',
|
|
250
|
+
host_owned_config: 'not_inspected',
|
|
251
|
+
next_action: 'Run `sks doctor --fix --repair-codex-app-ui` for Codex App UI repair.',
|
|
252
|
+
actions: [],
|
|
253
|
+
blockers: [],
|
|
254
|
+
warnings: ['codex_app_ui_repair_deferred']
|
|
255
|
+
};
|
|
256
|
+
const shouldApplyCodexAppUiRepair = shouldEvaluateCodexAppUiRepair && doctorFix && (explicitCodexAppUiRepair ||
|
|
202
257
|
codexAppUiPlan.safe_auto_apply === true);
|
|
203
258
|
const codexAppUi = shouldApplyCodexAppUiRepair
|
|
204
259
|
? await repairCodexAppFastUi(root, {
|
|
@@ -219,23 +274,42 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
219
274
|
blockers: [err?.message || String(err)]
|
|
220
275
|
}))
|
|
221
276
|
: codexAppUiPlan;
|
|
222
|
-
const zellijRepair =
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
277
|
+
const zellijRepair = shouldRunZellijRepair
|
|
278
|
+
? await runDoctorZellijRepair({ root, args, doctorFix }).catch((err) => ({
|
|
279
|
+
schema: 'sks.zellij-self-heal.v1',
|
|
280
|
+
ok: false,
|
|
281
|
+
requested_by: 'doctor --fix',
|
|
282
|
+
fix_requested: doctorFix,
|
|
283
|
+
auto_approved: flag(args, '--yes') || flag(args, '-y'),
|
|
284
|
+
install_homebrew_allowed: false,
|
|
285
|
+
before: { status: 'unknown', version: null, bin: null },
|
|
286
|
+
latest_version: null,
|
|
287
|
+
strategy: 'failed',
|
|
288
|
+
command: 'sks doctor --fix --yes',
|
|
289
|
+
after: { status: 'unknown', version: null, bin: null },
|
|
290
|
+
mutation_guard_artifact: null,
|
|
291
|
+
homebrew: { present: false, bin: null, install_attempted: false, install_allowed: false },
|
|
292
|
+
blockers: [err?.message || String(err)],
|
|
293
|
+
warnings: []
|
|
294
|
+
}))
|
|
295
|
+
: {
|
|
296
|
+
schema: 'sks.zellij-self-heal.v1',
|
|
297
|
+
ok: true,
|
|
298
|
+
skipped: true,
|
|
299
|
+
requested_by: 'doctor --fix',
|
|
300
|
+
fix_requested: doctorFix,
|
|
301
|
+
auto_approved: false,
|
|
302
|
+
install_homebrew_allowed: false,
|
|
303
|
+
before: { status: 'skipped', version: null, bin: null },
|
|
304
|
+
latest_version: null,
|
|
305
|
+
strategy: 'deferred',
|
|
306
|
+
command: 'sks doctor --fix --full --yes',
|
|
307
|
+
after: { status: 'skipped', version: null, bin: null },
|
|
308
|
+
mutation_guard_artifact: null,
|
|
309
|
+
homebrew: { present: false, bin: null, install_attempted: false, install_allowed: false },
|
|
310
|
+
blockers: [],
|
|
311
|
+
warnings: ['zellij_repair_deferred_to_full_doctor_or_route_gate']
|
|
312
|
+
};
|
|
239
313
|
const context7Repair = await runDoctorContext7Repair({ root, fix: doctorFix }).catch((err) => ({
|
|
240
314
|
schema: 'sks.doctor-context7-repair.v1',
|
|
241
315
|
ok: false,
|
|
@@ -267,7 +341,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
267
341
|
warnings: []
|
|
268
342
|
}))
|
|
269
343
|
: null;
|
|
270
|
-
const supabaseMcpRepair = doctorFix
|
|
344
|
+
const supabaseMcpRepair = doctorFix && doctorPhaseIds.includes('supabase_mcp_repair')
|
|
271
345
|
? await repairSupabaseMcp({ root, apply: true }).catch((err) => ({
|
|
272
346
|
schema: 'sks.doctor-supabase-mcp-repair.v1',
|
|
273
347
|
ok: false,
|
|
@@ -287,18 +361,6 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
287
361
|
raw_secret_values_recorded: false
|
|
288
362
|
}))
|
|
289
363
|
: null;
|
|
290
|
-
const doctorDirtyPlan = doctorFix
|
|
291
|
-
? planDoctorDirtyRepair(root, [
|
|
292
|
-
'setup',
|
|
293
|
-
'codex_startup_repair',
|
|
294
|
-
'startup_config_repair',
|
|
295
|
-
'context7_repair',
|
|
296
|
-
'context7_mcp_repair',
|
|
297
|
-
'supabase_mcp_repair',
|
|
298
|
-
'command_alias_cleanup',
|
|
299
|
-
'native_capability_repair'
|
|
300
|
-
])
|
|
301
|
-
: null;
|
|
302
364
|
const doctorFixTransaction = doctorFix
|
|
303
365
|
? await runDoctorFixTransaction({
|
|
304
366
|
root,
|
|
@@ -384,15 +446,20 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
384
446
|
},
|
|
385
447
|
{
|
|
386
448
|
id: 'native_capability_repair',
|
|
449
|
+
required_for_ready: false,
|
|
387
450
|
run: async () => ({
|
|
388
451
|
id: 'native_capability_repair',
|
|
389
452
|
ok: doctorNativeCapabilityRepair?.ok !== false,
|
|
390
453
|
repaired: doctorFix,
|
|
454
|
+
manual_required: Array.isArray(doctorNativeCapabilityRepair?.optional_manual_required) && doctorNativeCapabilityRepair.optional_manual_required.length > 0,
|
|
455
|
+
required_for_ready: false,
|
|
391
456
|
blockers: doctorNativeCapabilityRepair?.blockers || [],
|
|
457
|
+
warnings: doctorNativeCapabilityRepair?.optional_warnings || doctorNativeCapabilityRepair?.warnings || [],
|
|
458
|
+
route_blockers: doctorNativeCapabilityRepair?.route_blockers || {},
|
|
392
459
|
rollback_evidence: doctorNativeCapabilityRepair?.secret_preservation_guard || 'native_capability_repair_report'
|
|
393
460
|
})
|
|
394
461
|
}
|
|
395
|
-
]
|
|
462
|
+
].filter((phase) => doctorPhaseIds.includes(phase.id))
|
|
396
463
|
}).catch((err) => ({
|
|
397
464
|
schema: 'sks.doctor-fix-transaction.v2',
|
|
398
465
|
generated_at: new Date().toISOString(),
|
|
@@ -434,19 +501,27 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
434
501
|
warnings_suppressed: false,
|
|
435
502
|
blockers: [err?.message || String(err)]
|
|
436
503
|
}));
|
|
437
|
-
const globalSksInstallCleanup = flag(args, '--fix') && !flag(args, '--local-only')
|
|
504
|
+
const globalSksInstallCleanup = flag(args, '--fix') && !flag(args, '--local-only') && deepDiagnostics
|
|
438
505
|
? await cleanDuplicateGlobalSksInstalls({ root, fix: true }).catch((err) => ({ schema: 'sks.global-sks-install-cleanup.v1', ok: false, fix: true, error: err?.message || String(err), blockers: ['global_sks_install_cleanup_exception'] }))
|
|
439
506
|
: null;
|
|
440
507
|
const { detectImagegenCapability } = await import('../core/imagegen/imagegen-capability.js');
|
|
441
|
-
const imagegen =
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const
|
|
508
|
+
const imagegen = deepDiagnostics
|
|
509
|
+
? await detectImagegenCapability({ codexBin: codexBin || undefined }).catch((err) => ({ ok: false, error: err.message, auth_readiness: null }))
|
|
510
|
+
: { ok: false, skipped: true, auth_readiness: null, warnings: ['imagegen_optional_diagnostic_skipped'] };
|
|
511
|
+
const codex0138Capability = deepDiagnostics
|
|
512
|
+
? await writeCodex0138CapabilityArtifacts(root, { codexBin: codexBin || null }).catch((err) => ({ error: err?.message || String(err), report: null }))
|
|
513
|
+
: { skipped: true, report: null };
|
|
514
|
+
const codex0138Doctor = deepDiagnostics
|
|
515
|
+
? await runCodex0138Doctor(root, { fix: doctorFix }).catch((err) => ({ schema: 'sks.codex-0138-doctor.v1', ok: false, error: err?.message || String(err), blockers: ['codex_0138_doctor_exception'], warnings: [] }))
|
|
516
|
+
: { schema: 'sks.codex-0138-doctor.v1', ok: true, skipped: true, blockers: [], warnings: ['historical_codex_0138_doctor_skipped'] };
|
|
517
|
+
const pluginInventory = deepDiagnostics
|
|
518
|
+
? await writeCodexPluginInventoryArtifacts(root).catch((err) => ({ error: err?.message || String(err), report: null, artifact: null }))
|
|
519
|
+
: { skipped: true, report: null, artifact: null };
|
|
445
520
|
const pluginPolicy = pluginInventory?.report ? pluginAppTemplatePolicy(pluginInventory.report) : null;
|
|
446
521
|
const mcpPluginInventory = pluginInventory?.report
|
|
447
522
|
? await writeMcpPluginInventoryArtifacts(root, { inventory: pluginInventory.report }).catch((err) => ({ error: err?.message || String(err), candidates: null }))
|
|
448
523
|
: null;
|
|
449
|
-
const repairCodexNative = doctorFix;
|
|
524
|
+
const repairCodexNative = doctorFix && doctorPhaseIds.includes('native_capability_repair');
|
|
450
525
|
const codexNativeRepair = repairCodexNative
|
|
451
526
|
? await repairCodexNativeManagedAssets({
|
|
452
527
|
root,
|
|
@@ -462,31 +537,28 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
462
537
|
warnings: []
|
|
463
538
|
}))
|
|
464
539
|
: null;
|
|
465
|
-
const codexAppHarnessMatrix =
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
blockers: [err?.message || String(err)],
|
|
488
|
-
warnings: []
|
|
489
|
-
}));
|
|
540
|
+
const codexAppHarnessMatrix = deepDiagnostics
|
|
541
|
+
? await buildCodexAppHarnessMatrix({ root, mode: 'read-only' }).catch((err) => ({
|
|
542
|
+
schema: 'sks.codex-app-harness-matrix.v1',
|
|
543
|
+
ok: false,
|
|
544
|
+
codex_cli: { available: false, version: null },
|
|
545
|
+
app_features: {},
|
|
546
|
+
sks_integrations: {},
|
|
547
|
+
blockers: [err?.message || String(err)],
|
|
548
|
+
warnings: []
|
|
549
|
+
}))
|
|
550
|
+
: {
|
|
551
|
+
schema: 'sks.codex-app-harness-matrix.v1',
|
|
552
|
+
ok: true,
|
|
553
|
+
skipped: true,
|
|
554
|
+
app_features: {},
|
|
555
|
+
sks_integrations: {},
|
|
556
|
+
blockers: [],
|
|
557
|
+
warnings: ['codex_app_harness_optional_diagnostic_skipped']
|
|
558
|
+
};
|
|
559
|
+
const codexNativeFeatureMatrix = deepDiagnostics
|
|
560
|
+
? await buildCodexNativeFeatureMatrix({ root, mode: 'read-only' }).catch((err) => fallbackCodexNativeFeatureMatrix(codex, [err?.message || String(err)]))
|
|
561
|
+
: fallbackCodexNativeFeatureMatrix(codex, [], ['native_feature_matrix_deferred_to_full_doctor_or_route_gate']);
|
|
490
562
|
// Re-probe the Codex config AFTER the MCP transport repairs (Context7 remote
|
|
491
563
|
// migration, Supabase read-only, startup config) have landed. `repairCodexConfigEperm`
|
|
492
564
|
// ran its config-load probe ~before~ those repairs, so a config that those repairs
|
|
@@ -499,7 +571,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
499
571
|
if (reinspected)
|
|
500
572
|
codexConfig = reinspected;
|
|
501
573
|
}
|
|
502
|
-
const postRepairCodexDoctor = doctorFix
|
|
574
|
+
const postRepairCodexDoctor = doctorFix && (deepDiagnostics || flag(args, '--require-actual-codex'))
|
|
503
575
|
? await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--fix') || flag(args, '--require-actual-codex') }).catch((err) => ({
|
|
504
576
|
schema: 'sks.codex-doctor-bridge.v2',
|
|
505
577
|
generated_at: new Date().toISOString(),
|
|
@@ -525,7 +597,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
525
597
|
: preRepairCodexDoctor;
|
|
526
598
|
const authoritativeCodexDoctor = postRepairCodexDoctor;
|
|
527
599
|
const codexDoctorAuthoritativeDiff = compareCodexDoctorBridge(codexDoctorBefore, authoritativeCodexDoctor);
|
|
528
|
-
const pkgBytes =
|
|
600
|
+
const pkgBytes = 0;
|
|
529
601
|
const ready = await writeDoctorReadinessMatrix(root, {
|
|
530
602
|
codex,
|
|
531
603
|
codex_config: codexConfig,
|
|
@@ -534,7 +606,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
534
606
|
codex_doctor: authoritativeCodexDoctor,
|
|
535
607
|
pre_repair_codex_doctor: preRepairCodexDoctor,
|
|
536
608
|
post_repair_codex_doctor: postRepairCodexDoctor,
|
|
537
|
-
require_codex_doctor:
|
|
609
|
+
require_codex_doctor: deepDiagnostics || flag(args, '--require-actual-codex'),
|
|
538
610
|
zellij,
|
|
539
611
|
context7_repair: context7Repair,
|
|
540
612
|
codex_startup_repair: codexStartupRepair,
|
|
@@ -553,7 +625,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
553
625
|
codex_plugin_inventory: pluginInventory?.report || null,
|
|
554
626
|
codex_plugin_app_template_policy: pluginPolicy,
|
|
555
627
|
codex_app_harness_matrix: codexAppHarnessMatrix,
|
|
556
|
-
require_codex_cli_config_load:
|
|
628
|
+
require_codex_cli_config_load: requireActualCodexProbe,
|
|
557
629
|
operator_actions: [
|
|
558
630
|
...(codexConfig.operator_actions || []),
|
|
559
631
|
...(configRepair?.operator_actions || []),
|
|
@@ -562,10 +634,22 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
562
634
|
...(pluginPolicy?.doctor_warnings || [])
|
|
563
635
|
]
|
|
564
636
|
});
|
|
637
|
+
if (doctorFix && ready.ready === true) {
|
|
638
|
+
await writeProjectUpdateMigrationReceipt({
|
|
639
|
+
root,
|
|
640
|
+
source: `doctor-${doctorProfile}`,
|
|
641
|
+
blockers: [],
|
|
642
|
+
warnings: [
|
|
643
|
+
...(doctorNativeCapabilityRepair?.optional_warnings || []),
|
|
644
|
+
...(doctorFixPostcheck?.optional_warnings || [])
|
|
645
|
+
]
|
|
646
|
+
}).catch(() => undefined);
|
|
647
|
+
}
|
|
565
648
|
const zellijReadiness = buildZellijReadiness(root, zellij, ready);
|
|
566
649
|
const runtimeReadiness = buildRuntimeReadiness(zellijReadiness, codexNativeFeatureMatrix);
|
|
567
650
|
const result = {
|
|
568
651
|
schema: 'sks.doctor-status.v2',
|
|
652
|
+
elapsed_ms: Date.now() - startedAtMs,
|
|
569
653
|
ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false && codexStartupRepair.ok !== false && (!doctorFixPostcheck || doctorFixPostcheck.ok !== false),
|
|
570
654
|
root,
|
|
571
655
|
node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
|
|
@@ -615,6 +699,13 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
615
699
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
616
700
|
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, doctor_transaction: doctorFixTransaction, doctor_dirty_plan: doctorDirtyPlan, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
|
|
617
701
|
};
|
|
702
|
+
if (reportFile)
|
|
703
|
+
await writeJsonReportFile(reportFile, result);
|
|
704
|
+
if (machineOnly && !flag(args, '--json')) {
|
|
705
|
+
if (!result.ok)
|
|
706
|
+
process.exitCode = 1;
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
618
709
|
if (flag(args, '--json')) {
|
|
619
710
|
printJson(result);
|
|
620
711
|
if (!result.ok)
|
|
@@ -739,7 +830,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
739
830
|
console.log(` rollout budget: ${codexNativeFeatureMatrix.features?.rollout_budget?.ok ? 'verified' : 'unverified'}`);
|
|
740
831
|
console.log(` indexed search: ${codexNativeFeatureMatrix.features?.indexed_web_search?.ok ? 'verified' : 'unverified'}`);
|
|
741
832
|
console.log(` current time: ${codexNativeFeatureMatrix.features?.current_time_read?.ok ? 'verified' : 'unverified'}`);
|
|
742
|
-
console.log('Historical compatibility: Codex 0.138 features');
|
|
833
|
+
console.log('Historical compatibility: Codex 0.138 features:');
|
|
743
834
|
console.log(` /app handoff: ${codex0138.supports_app_handoff ? 'ok' : 'unavailable'}`);
|
|
744
835
|
console.log(` plugin JSON: ${codex0138.supports_plugin_json ? 'ok' : 'unavailable'}`);
|
|
745
836
|
console.log(` image path exposure: ${codex0138.supports_image_path_exposure ? 'ok' : 'unavailable'}`);
|
|
@@ -816,7 +907,7 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
|
|
|
816
907
|
repairActions.push('Homebrew + Zellij: sks doctor --fix --install-homebrew --yes');
|
|
817
908
|
}
|
|
818
909
|
if (codexNative !== 'ok')
|
|
819
|
-
repairActions.push('Codex Native managed assets: sks doctor --fix --yes');
|
|
910
|
+
repairActions.push('Codex Native managed assets: sks doctor --fix --repair-codex-native --yes');
|
|
820
911
|
if (matrix?.features?.project_memory?.ok !== true)
|
|
821
912
|
repairActions.push('Project memory: sks codex-native init-deep --apply --directory-local');
|
|
822
913
|
return {
|
|
@@ -844,6 +935,60 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
|
|
|
844
935
|
repair_actions: [...new Set(repairActions)]
|
|
845
936
|
};
|
|
846
937
|
}
|
|
938
|
+
function fallbackCodexNativeFeatureMatrix(codex, blockers = [], warnings = []) {
|
|
939
|
+
return {
|
|
940
|
+
schema: 'sks.codex-native-feature-matrix.v1',
|
|
941
|
+
ok: blockers.length === 0,
|
|
942
|
+
skipped: blockers.length === 0,
|
|
943
|
+
codex_cli: { available: Boolean(codex?.bin || codex?.available), version: codex?.version || null, bin: codex?.bin || null },
|
|
944
|
+
features: {},
|
|
945
|
+
invocation_defaults: {
|
|
946
|
+
loop_worker_role_strategy: 'message-role',
|
|
947
|
+
multi_agent_mode: 'none',
|
|
948
|
+
rollout_budget_strategy: 'sks-local-only',
|
|
949
|
+
qa_visual_review_strategy: 'route-gated',
|
|
950
|
+
research_source_strategy: 'local-files',
|
|
951
|
+
image_followup_strategy: 'artifact-path',
|
|
952
|
+
hook_evidence_policy: 'unknown-do-not-count',
|
|
953
|
+
skill_bridge_strategy: 'cli-only',
|
|
954
|
+
current_time_source: 'external-clock',
|
|
955
|
+
overload_retry_policy: 'generic'
|
|
956
|
+
},
|
|
957
|
+
blockers,
|
|
958
|
+
warnings
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function doctorProfileFromArgs(args = [], doctorFix = false) {
|
|
962
|
+
const explicit = readOption(args, '--profile', null);
|
|
963
|
+
if (explicit === 'migration' || explicit === 'full' || explicit === 'capabilities' || explicit === 'fast' || explicit === 'fix')
|
|
964
|
+
return explicit;
|
|
965
|
+
if (flag(args, '--full'))
|
|
966
|
+
return 'full';
|
|
967
|
+
if (flag(args, '--capabilities'))
|
|
968
|
+
return 'capabilities';
|
|
969
|
+
return doctorFix ? 'fix' : 'fast';
|
|
970
|
+
}
|
|
971
|
+
function doctorPhaseIdsForProfile(profile) {
|
|
972
|
+
const required = [
|
|
973
|
+
'codex_startup_repair',
|
|
974
|
+
'startup_config_repair',
|
|
975
|
+
'context7_repair',
|
|
976
|
+
'context7_mcp_repair',
|
|
977
|
+
'command_alias_cleanup'
|
|
978
|
+
];
|
|
979
|
+
if (profile === 'migration')
|
|
980
|
+
return required;
|
|
981
|
+
const optional = ['supabase_mcp_repair', 'native_capability_repair'];
|
|
982
|
+
if (profile === 'full' || profile === 'capabilities')
|
|
983
|
+
return ['setup', ...required, ...optional];
|
|
984
|
+
return [...required, ...optional];
|
|
985
|
+
}
|
|
986
|
+
async function writeJsonReportFile(file, value) {
|
|
987
|
+
const fsp = await import('node:fs/promises');
|
|
988
|
+
const path = await import('node:path');
|
|
989
|
+
await fsp.mkdir(path.dirname(file), { recursive: true });
|
|
990
|
+
await fsp.writeFile(file, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
991
|
+
}
|
|
847
992
|
function nativeCapabilityStatus(rows, id, fallback) {
|
|
848
993
|
const row = rows.find((entry) => entry?.id === id);
|
|
849
994
|
if (!row)
|