sneakoscope 9.0.1 → 9.0.3

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 CHANGED
@@ -22,7 +22,7 @@ Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, mult
22
22
  Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
23
23
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
24
24
 
25
- This README documents package **SKS 9.0.1** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
25
+ This README documents package **SKS 9.0.3** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
26
26
 
27
27
  Use the official latest stable SKS and Codex CLI releases. The Codex compatibility SSOT is always the **current latest stable** host; capability probes measure what that host can actually do. Product docs do not crown a fixed `0.x.y` string as SSOT (release pins and schema directories are measured artifacts for the current package, not a permanent product version claim). Menu Bar / Center induce updates to the latest stable build. Run `sks update-check` for what is installed and read the capability report for what is supported. Install SSOT is npm `sneakoscope@latest`; PATH `sks` and Menu Bar stamped generation must match that version or gates fail. It resolves managed SKS skills from the authoritative global install, preserves a runnable Naruto child slot when `max_threads=2`, and keeps Menu Bar repair transactional so stamped generations remain verifiable. Naruto uses stable opt-in multi-agent V2 when the host exposes it (Codex official multi-agent wrap-only; SKS does not reimplement a parallel runtime). Local code search is mode-separated (`sks search files|text|structure|symbol|context`); `context` is answered by the compiled TriWiki Context Graph (`context-graph.json` is exhaustive authority; `context-pack.json` and managed `AGENTS.md` are bounded projections) — see [docs/architecture/context-graph.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/architecture/context-graph.md) and [docs/PRODUCT-CONTRACT.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/docs/PRODUCT-CONTRACT.md). See [CHANGELOG.md](https://github.com/mandarange/Sneakoscope-Codex/blob/main/CHANGELOG.md).
28
28
 
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "9.0.1"
262
+ version = "9.0.3"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "9.0.1"
3
+ version = "9.0.3"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -95,12 +95,16 @@ async function hookUserPromptSubmitPerfInline() {
95
95
  })}\n`);
96
96
  }
97
97
  function findProjectRootSync(fs, start) {
98
+ const home = process.env.HOME || process.env.USERPROFILE || null;
99
+ const homeDir = home ? stripTrailingSlash(home) : null;
98
100
  let dir = normalizeStart(start);
99
101
  for (;;) {
100
- if (fs.existsSync(joinPath(dir, '.sneakoscope')))
101
- return dir;
102
- if (fs.existsSync(joinPath(dir, 'AGENTS.md')) && fs.existsSync(joinPath(dir, 'package.json')))
103
- return dir;
102
+ if (dir !== homeDir) {
103
+ if (fs.existsSync(joinPath(dir, '.sneakoscope')))
104
+ return dir;
105
+ if (fs.existsSync(joinPath(dir, 'AGENTS.md')) && fs.existsSync(joinPath(dir, 'package.json')))
106
+ return dir;
107
+ }
104
108
  const parent = parentDir(dir);
105
109
  if (parent === dir)
106
110
  return null;
@@ -0,0 +1,291 @@
1
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from '../core/codex-compat/codex-runtime-contract.js';
2
+ import { DOCTOR_CONSOLE_NOT_MEASURED, doctorDedupeStatus, doctorSkillStatus, formatCodexDoctorConsoleStatus, nativeCapabilityStatus, sksMenuBarRunningVersionConsoleLines, uniqueNativeManualActions } from './doctor-helpers.js';
3
+ export { DOCTOR_CONSOLE_NOT_MEASURED };
4
+ export function renderDoctorConsoleReport(result, extras) {
5
+ const lines = [];
6
+ const line = (text) => lines.push(text);
7
+ const skippedOr = (skipped, measured) => (skipped ? DOCTOR_CONSOLE_NOT_MEASURED : measured);
8
+ const ready = result.ready || {};
9
+ const codex = result.codex || {};
10
+ const codexConfig = result.codex_config || {};
11
+ const oauthDiagnostic = result.oauth_callback_port_diagnostic || { conflict: false, listeners: [], warnings: [] };
12
+ const context7Repair = result.context7_repair || {};
13
+ const codexStartupRepair = result.codex_startup_repair || {};
14
+ const codexConfigSyntaxRepair = result.codex_config_syntax_repair;
15
+ const rust = result.rust || {};
16
+ const codexApp = result.codex_app || {};
17
+ const runtimeReadiness = result.runtime_readiness || { notes: [], repair_actions: [] };
18
+ const codexNativeFeatureMatrix = result.codex_native_feature_matrix || {};
19
+ const repair = result.repair || {};
20
+ const doctorNativeCapabilityRepair = repair.doctor_native_capability;
21
+ const commandAliasCleanup = result.command_aliases || {};
22
+ const codexAppHarnessMatrix = result.codex_app_harness_matrix || {};
23
+ const codexAppUi = result.codex_app_ui || {};
24
+ const sksMenuBar = result.sks_menubar || {};
25
+ const providerContext = result.provider_context || {};
26
+ const imagegen = result.imagegen || {};
27
+ const imagegenRepair = result.imagegen_repair || {};
28
+ const computerUseRepair = repair.computer_use || {};
29
+ const browserUseRepair = repair.browser_use || {};
30
+ const mcpTransportCollisionRepair = repair.mcp_transport_collision;
31
+ const codexCurrentAppSection = result.codex_current_app || {};
32
+ const codexCurrentAppDoctor = codexCurrentAppSection.doctor || {};
33
+ const pluginPolicy = codexCurrentAppSection.plugin_app_template_policy;
34
+ const desktopBridge = result.desktop_bridge || {};
35
+ const permissionProfiles = result.codex_permission_profiles || {};
36
+ const configRepair = repair.codex_config;
37
+ const migrationJournal = repair.migration_journal;
38
+ const sksUpdate = repair.sks_update;
39
+ const globalSksInstallCleanup = repair.global_sks_installs;
40
+ line('SKS Doctor');
41
+ for (const warning of result.arg_warnings || [])
42
+ line(`Argument warning: ${warning}`);
43
+ for (const warning of result.official_subagent_config?.warnings || [])
44
+ line(`Official subagent warning: ${warning}`);
45
+ line(`Root: ${result.root}`);
46
+ if (extras.rootIsHome) {
47
+ line('Note: this is your home directory, not a project — project rows below describe the home folder. Run from your project: cd <your-project> && sks doctor');
48
+ }
49
+ line(`Node: ${result.node?.ok ? 'ok' : 'fail'} ${result.node?.version || ''}`);
50
+ line(`Codex: ${codex.bin ? 'ok' : 'missing'} ${codex.version || ''}`);
51
+ if (oauthDiagnostic.conflict) {
52
+ const listeners = (oauthDiagnostic.listeners || [])
53
+ .map((listener) => `${listener.command} pid ${listener.pid} ${listener.address}`)
54
+ .join(', ');
55
+ line(`OAuth callback port 1455: warning (${listeners})`);
56
+ for (const action of extras.oauthCallbackOperatorActions)
57
+ line(` action: ${action}`);
58
+ }
59
+ const actual = (codexConfig.checks || []).find((check) => check.name === 'actual_codex_cli_config_load');
60
+ line('Project config:');
61
+ line(` node read: ${ready.codex_config_readable_by_node ? 'ok' : 'failed'}`);
62
+ line(` codex cli read: ${ready.codex_config_readable_by_codex_cli ? 'ok' : (actual?.status || 'failed')}`);
63
+ line('Context7 MCP:');
64
+ line(` transport: ${context7Repair.preferred_transport || 'remote'}`);
65
+ line(` repair: ${context7Repair.ok ? 'ok' : 'blocked'}`);
66
+ for (const action of context7Repair.actions || [])
67
+ line(` - ${action}`);
68
+ for (const warning of context7Repair.warnings || [])
69
+ line(` warning: ${warning}`);
70
+ line('Codex startup config:');
71
+ line(` repair: ${codexStartupRepair.ok ? 'ok' : 'blocked'}`);
72
+ for (const action of codexStartupRepair.actions || [])
73
+ line(` - ${action}`);
74
+ for (const action of codexStartupRepair.manual_actions || [])
75
+ line(` manual: ${action}`);
76
+ for (const warning of codexStartupRepair.warnings || [])
77
+ line(` warning: ${warning}`);
78
+ if (codexConfigSyntaxRepair) {
79
+ line('Codex config syntax:');
80
+ line(` repair: ${codexConfigSyntaxRepair.ok ? 'ok' : 'blocked'}`);
81
+ for (const action of codexConfigSyntaxRepair.actions || [])
82
+ line(` - ${action}`);
83
+ for (const action of codexConfigSyntaxRepair.manual_actions || [])
84
+ line(` manual: ${action}`);
85
+ for (const warning of codexConfigSyntaxRepair.warnings || [])
86
+ line(` warning: ${warning}`);
87
+ }
88
+ line(` codex doctor: ${formatCodexDoctorConsoleStatus(result.codex_doctor)}`);
89
+ line(`Rust acc.: ${rust.mode || (rust.available ? 'rust_accelerated' : 'js_fallback')} ${rust.version || rust.status || ''}`);
90
+ line(`Codex App: ${codexApp.skipped === true ? DOCTOR_CONSOLE_NOT_MEASURED : ready.codex_app_ready ? 'ok' : 'optional_missing'}`);
91
+ const featureMatrixSkipped = codexNativeFeatureMatrix.skipped === true;
92
+ line('SKS Runtime Readiness:');
93
+ line(` Codex Native: ${skippedOr(featureMatrixSkipped, String(runtimeReadiness.codex_native))}`);
94
+ line(` Loop Mesh: ${skippedOr(featureMatrixSkipped, String(runtimeReadiness.loop_mesh))}`);
95
+ line(` QA Visual: ${skippedOr(featureMatrixSkipped, String(runtimeReadiness.qa_visual))}`);
96
+ line(` Research Sources: ${skippedOr(featureMatrixSkipped, String(runtimeReadiness.research_sources))}`);
97
+ line(` Image Follow-up: ${skippedOr(featureMatrixSkipped, String(runtimeReadiness.image_followup))}`);
98
+ if (!featureMatrixSkipped) {
99
+ for (const note of runtimeReadiness.notes || [])
100
+ line(` ${note}`);
101
+ if ((runtimeReadiness.repair_actions || []).length) {
102
+ line('Repair actions:');
103
+ for (const action of runtimeReadiness.repair_actions)
104
+ line(` - ${action}`);
105
+ }
106
+ }
107
+ const nativeCapabilitySource = doctorNativeCapabilityRepair?.native_capabilities;
108
+ const nativeCapabilitiesSkipped = nativeCapabilitySource?.skipped === true;
109
+ const nativeCapabilityRows = Array.isArray(nativeCapabilitySource?.capabilities)
110
+ ? nativeCapabilitySource.capabilities
111
+ : [];
112
+ const capabilityRow = (id, fallback) => skippedOr(nativeCapabilitiesSkipped, nativeCapabilityStatus(nativeCapabilityRows, id, fallback));
113
+ line('SKS Native Capabilities:');
114
+ line(` image generation: ${capabilityRow('image_generation', 'repair_required')}`);
115
+ line(` image follow-up edit: ${capabilityRow('image_followup_edit', 'degraded')}`);
116
+ line(` computer use: ${capabilityRow('computer_use', 'manual_required')}`);
117
+ line(` Chrome/web review: ${capabilityRow('chrome_web_review', 'manual_required')}`);
118
+ line(` app screenshot: ${capabilityRow('codex_app_screenshot', 'degraded')}`);
119
+ line(` app handoff: ${capabilityRow('app_handoff', 'unavailable')}`);
120
+ line(` image path exposure: ${capabilityRow('image_path_exposure', 'fallback')}`);
121
+ const nativeManualActions = uniqueNativeManualActions(nativeCapabilityRows);
122
+ if (nativeManualActions.length) {
123
+ line(' manual next actions:');
124
+ for (const action of nativeManualActions)
125
+ line(` - ${action}`);
126
+ }
127
+ line('SKS Skills:');
128
+ line(` core skills: ${doctorSkillStatus(doctorNativeCapabilityRepair?.core_skills)}`);
129
+ line(` duplicate project skills: ${doctorDedupeStatus(doctorNativeCapabilityRepair?.skill_dedupe)}`);
130
+ line('SKS Current Command Surface:');
131
+ line(` status: ${commandAliasCleanup.status || (commandAliasCleanup.ok ? 'clean' : 'blocked')}`);
132
+ line(` canonical commands: ${commandAliasCleanup.canonical_command_count ?? 0}`);
133
+ const managedRuntimeCleanup = commandAliasCleanup.cleanup?.managed_runtime;
134
+ if (managedRuntimeCleanup) {
135
+ line(` managed items reconciled: ${managedRuntimeCleanup.removed_managed_artifact_count ?? 0}`);
136
+ line(` user-authored collisions preserved: ${managedRuntimeCleanup.preserved_user_file_count ?? 0}`);
137
+ }
138
+ if (commandAliasCleanup.report_path)
139
+ line(` report: ${commandAliasCleanup.report_path}`);
140
+ line('Secret preservation:');
141
+ line(` Supabase keys: ${doctorNativeCapabilityRepair?.ok === false && String((doctorNativeCapabilityRepair?.blockers || []).join(' ')).includes('secret_preservation_failed') ? 'blocked' : 'preserved'}`);
142
+ line(' raw secret values: never recorded');
143
+ line(` migration journal: ${doctorNativeCapabilityRepair?.secret_preservation_guard || '.sneakoscope/reports/secret-preservation-guard.json'}`);
144
+ const harnessSkipped = codexAppHarnessMatrix.skipped === true;
145
+ const appFeatures = codexAppHarnessMatrix.app_features || {};
146
+ const sksIntegrations = codexAppHarnessMatrix.sks_integrations || {};
147
+ line('Codex App Harness:');
148
+ line(` plugins: ${skippedOr(harnessSkipped, appFeatures.plugin_json ? 'ok' : 'degraded')}`);
149
+ line(` hook approval: ${skippedOr(harnessSkipped, appFeatures.hook_approval_state_detectable ? 'ok' : 'unknown')}`);
150
+ line(` skills: ${skippedOr(harnessSkipped, sksIntegrations.dollar_skills_synced ? 'ok' : 'degraded')}`);
151
+ line(` agent roles: ${skippedOr(harnessSkipped, sksIntegrations.agent_roles_synced ? 'ok' : 'degraded')}`);
152
+ line(` native agent_type: ${skippedOr(harnessSkipped, appFeatures.agent_type_supported ? 'ok' : 'fallback message-role')}`);
153
+ line(` init-deep memory: ${skippedOr(harnessSkipped, sksIntegrations.init_deep_available ? 'available' : 'missing')}`);
154
+ line(` loop mesh app profile: ${skippedOr(harnessSkipped, sksIntegrations.loop_mesh_app_profile_available ? 'available' : 'missing')}`);
155
+ line('Codex App UI:');
156
+ line(` fast selector: ${codexAppUi.fast_selector || 'unknown'}`);
157
+ line(` provider selector: ${codexAppUi.provider_selector || 'unknown'}`);
158
+ if (Array.isArray(codexAppUi.provider_blockers) && codexAppUi.provider_blockers.length) {
159
+ line(` provider blockers: ${codexAppUi.provider_blockers.join(', ')}`);
160
+ }
161
+ if (Array.isArray(codexAppUi.provider_actions) && codexAppUi.provider_actions.length) {
162
+ line(' provider actions:');
163
+ for (const action of codexAppUi.provider_actions)
164
+ line(` - ${action}`);
165
+ }
166
+ line(` host-owned config: ${codexAppUi.host_owned_config || 'unknown'}`);
167
+ if (Array.isArray(codexAppUi.actions) && codexAppUi.actions.some((action) => action.changed)) {
168
+ line(' repaired files:');
169
+ for (const action of codexAppUi.actions.filter((entry) => entry.changed))
170
+ line(` - ${action.file}${action.backup_path ? ` (backup ${action.backup_path})` : ''}`);
171
+ }
172
+ if (codexAppUi.next_action)
173
+ line(` next action: ${codexAppUi.next_action}`);
174
+ line('SKS Menu Bar:');
175
+ line(` status: ${sksMenuBar.status || (sksMenuBar.ok ? 'ok' : 'blocked')}`);
176
+ for (const versionLine of sksMenuBarRunningVersionConsoleLines(sksMenuBar))
177
+ line(versionLine);
178
+ const menubarPhase = result.doctor_fix_transaction?.phases?.find((phase) => phase?.id === 'sks_menubar');
179
+ if (menubarPhase) {
180
+ const menubarSummary = menubarPhase.ok
181
+ ? (menubarPhase.repaired ? 'repaired' : 'verified')
182
+ : `blocked(${(menubarPhase.blockers || []).join(', ') || 'unknown'})`;
183
+ line(` menubar: ${menubarSummary}`);
184
+ }
185
+ if (sksMenuBar.app_path)
186
+ line(` app: ${sksMenuBar.app_path}`);
187
+ if (sksMenuBar.launch_agent_path)
188
+ line(` launch agent: ${sksMenuBar.launch_agent_path}`);
189
+ if (Array.isArray(sksMenuBar.blockers) && sksMenuBar.blockers.length)
190
+ line(` blockers: ${sksMenuBar.blockers.join(', ')}`);
191
+ if (Array.isArray(sksMenuBar.warnings) && sksMenuBar.warnings.length)
192
+ line(` warnings: ${sksMenuBar.warnings.join(', ')}`);
193
+ line(`Provider: ${providerContext.provider || 'unknown'} ${providerContext.service_tier || ''} (${providerContext.source || 'unknown'}, ${providerContext.confidence || 'low'})`);
194
+ const imagegenReady = imagegen.auth_readiness;
195
+ if (imagegenReady) {
196
+ const paths = imagegenReady.available_paths?.length ? imagegenReady.available_paths.join(', ') : 'none';
197
+ line(`Image Gen: auth=${imagegenReady.auth_mode} | headless_auto=${imagegenReady.headless_auto_available ? 'available' : 'unavailable'} | paths: ${paths}`);
198
+ if (!imagegenReady.headless_auto_available) {
199
+ for (const action of imagegenReady.next_actions || [])
200
+ line(` - ${action}`);
201
+ }
202
+ }
203
+ line(`Image Gen repair: ${skippedOr(imagegenRepair.skipped === true, String(extras.nativeCapabilityReadiness?.imagegen?.status))}`);
204
+ for (const action of imagegenRepair.manual_actions || [])
205
+ line(` - ${action}`);
206
+ line(`Computer Use repair: ${skippedOr(computerUseRepair.skipped === true, computerUseRepair.recovered ? 'ok' : computerUseRepair.attempted ? 'blocked' : 'not-needed')}`);
207
+ for (const action of computerUseRepair.next_actions || [])
208
+ line(` - ${action}`);
209
+ line(`Browser Use repair: ${skippedOr(browserUseRepair.skipped === true, browserUseRepair.recovered ? 'ok' : browserUseRepair.attempted ? 'blocked' : 'not-needed')}`);
210
+ for (const action of browserUseRepair.next_actions || [])
211
+ line(` - ${action}`);
212
+ if (mcpTransportCollisionRepair) {
213
+ const collisionCount = (mcpTransportCollisionRepair.servers || []).filter((server) => server.status === 'collision_resolved').length;
214
+ line(`MCP transport collision repair: ${mcpTransportCollisionRepair.ok ? 'ok' : 'blocked'}${collisionCount ? ` (${collisionCount} resolved)` : ''}`);
215
+ }
216
+ line(`Agent bridge: ${extras.agentBridgeManifestExists ? 'manifest present' : 'not set up'}${extras.agentBridgeManifestExists ? '' : ' — run `sks agent-bridge setup` to publish the manifest and register with an MCP host'}`);
217
+ const codexCurrentApp = extras.codexCurrentAppCapability?.report || {};
218
+ const currentAppSkipped = extras.codexCurrentAppCapability?.skipped === true;
219
+ line('Codex current compatibility:');
220
+ line(` target: ${CURRENT_CODEX_RUNTIME_CONTRACT.targetTag}`);
221
+ line(` runtime: ${codex.version || 'unknown'}`);
222
+ line(` multi-agent mode: ${skippedOr(featureMatrixSkipped, codexNativeFeatureMatrix.features?.multi_agent_mode?.ok ? 'verified' : 'unverified')}`);
223
+ line(` rollout budget: ${skippedOr(featureMatrixSkipped, codexNativeFeatureMatrix.features?.rollout_budget?.ok ? 'verified' : 'unverified')}`);
224
+ line(` indexed search: ${skippedOr(featureMatrixSkipped, codexNativeFeatureMatrix.features?.indexed_web_search?.ok ? 'verified' : 'unverified')}`);
225
+ line(` current time: ${skippedOr(featureMatrixSkipped, codexNativeFeatureMatrix.features?.current_time_read?.ok ? 'verified' : 'unverified')}`);
226
+ line('Current Codex app features:');
227
+ line(` /app handoff: ${skippedOr(currentAppSkipped, codexCurrentApp.supports_app_handoff ? 'ok' : 'unavailable')}`);
228
+ line(` plugin JSON: ${skippedOr(currentAppSkipped, codexCurrentApp.supports_plugin_json ? 'ok' : 'unavailable')}`);
229
+ line(` image path exposure: ${skippedOr(currentAppSkipped, codexCurrentApp.supports_image_path_exposure ? 'ok' : 'unavailable')}`);
230
+ line(` OAuth MCP pre-refresh: ${skippedOr(currentAppSkipped, codexCurrentApp.supports_oauth_mcp_prerefresh ? 'ok' : 'unavailable')}`);
231
+ const pluginInventorySkipped = extras.pluginInventory?.skipped === true;
232
+ const pluginReport = extras.pluginInventory?.report;
233
+ const plugins = pluginReport?.plugins || [];
234
+ const remoteMcpCount = plugins.flatMap((plugin) => plugin.remote_mcp_servers || []).length;
235
+ const unavailableTemplates = pluginPolicy?.unavailable_app_templates?.length || 0;
236
+ line(`Codex plugins: ${skippedOr(pluginInventorySkipped, pluginReport ? 'ok' : 'warning')}`);
237
+ line(` Remote MCP servers: ${pluginInventorySkipped ? DOCTOR_CONSOLE_NOT_MEASURED : `${remoteMcpCount} candidates`}`);
238
+ line(` Unavailable app templates: ${pluginInventorySkipped ? DOCTOR_CONSOLE_NOT_MEASURED : unavailableTemplates}`);
239
+ for (const warning of pluginPolicy?.doctor_warnings || [])
240
+ line(` warning: ${warning}`);
241
+ if (codexCurrentAppDoctor.fixed?.length)
242
+ line(` doctor --fix repaired: ${codexCurrentAppDoctor.fixed.join(', ')}`);
243
+ line(`Desktop Bridge: ${desktopBridge.ok ? 'ready' : 'blocked'} (${desktopBridge.status?.readiness?.state || 'unavailable'})`);
244
+ for (const providerId of ['codex-lb', 'openrouter']) {
245
+ const provider = desktopBridge.providers?.[providerId];
246
+ if (!provider)
247
+ continue;
248
+ line(` ${providerId}: ${provider.enabled ? 'enabled' : 'disabled'}; credential ${provider.credential?.state || 'unknown'} (${provider.credential?.source || 'none'}); endpoint ${provider.endpoint?.configured ? 'configured' : 'missing'}`);
249
+ }
250
+ for (const warning of desktopBridge.warnings || [])
251
+ line(` warning: ${warning}`);
252
+ for (const blocker of desktopBridge.blockers || [])
253
+ line(` blocker: ${blocker}`);
254
+ if (!desktopBridge.ok)
255
+ for (const action of desktopBridge.recovery_actions || [])
256
+ line(` action: ${action}`);
257
+ line(`Permissions: config profile and permission profile are tracked separately (${permissionProfiles.codex_config_profile_field}, ${permissionProfiles.codex_permission_profile_field})`);
258
+ line('Ready:');
259
+ line(` cli_ready: ${ready.cli_ready ? 'yes' : 'no'}`);
260
+ line(` mad_ready: ${ready.mad_ready ? 'yes' : 'no'}`);
261
+ line(` managed_state_current: ${ready.managed_state_current ? 'yes' : 'no'}`);
262
+ line(` core_ready: ${ready.core_ready ? 'yes' : 'no'}`);
263
+ line(` center_ready: ${ready.center_ready ? 'yes' : 'no'}${ready.center_attempted ? ' (repair attempted)' : ' (not attempted)'}`);
264
+ line(` ready: ${ready.ready ? 'yes' : 'no'}`);
265
+ if (!ready.ready) {
266
+ line('Primary blocker:');
267
+ line(` ${ready.primary_blocker || 'unknown'}`);
268
+ }
269
+ if (configRepair?.repair_actions?.length) {
270
+ line('What I fixed:');
271
+ for (const action of configRepair.repair_actions)
272
+ line(` - ${action.name}: ${action.ok ? 'ok' : 'failed'}`);
273
+ }
274
+ if (migrationJournal?.journal_path) {
275
+ line(`Migration journal: ${migrationJournal.journal_path} (${migrationJournal.event_count} events, ${migrationJournal.mutations_without_rollback} without rollback)`);
276
+ }
277
+ if (sksUpdate) {
278
+ line(`SKS update: ${sksUpdate.status}${sksUpdate.latest ? ` latest ${sksUpdate.latest}` : ''}${sksUpdate.error ? ` (${sksUpdate.error})` : ''}`);
279
+ }
280
+ if (globalSksInstallCleanup) {
281
+ line(`Global SKS installs: kept ${globalSksInstallCleanup.kept?.length ?? 0}, removed ${globalSksInstallCleanup.removed?.filter((entry) => entry.ok).length ?? 0}, source repo exempt ${globalSksInstallCleanup.candidates?.filter((entry) => entry.source_repo_exempt).length ?? 0}`);
282
+ if (globalSksInstallCleanup.npm_cache)
283
+ line(`NPM cache cleanup: ${globalSksInstallCleanup.npm_cache.status}`);
284
+ }
285
+ if (!ready.ready && ready.next_actions?.length) {
286
+ line('What still needs you:');
287
+ for (const action of ready.next_actions)
288
+ line(` - ${action}`);
289
+ }
290
+ return lines;
291
+ }
@@ -258,8 +258,11 @@ export function buildCodexAppUiDiagnosticFailure(apply, error) {
258
258
  blockers: [error instanceof Error ? error.message : String(error)]
259
259
  };
260
260
  }
261
+ export const DOCTOR_CONSOLE_NOT_MEASURED = 'not measured (run: sks doctor --full)';
261
262
  export function formatCodexDoctorConsoleStatus(report) {
262
- if (!report || report.available !== true)
263
+ if (!report)
264
+ return DOCTOR_CONSOLE_NOT_MEASURED;
265
+ if (report.available !== true)
263
266
  return 'unavailable';
264
267
  return report.disposition || (report.exit_code === 0 ? 'pass' : 'warn');
265
268
  }
@@ -1,5 +1,15 @@
1
+ import path from 'node:path';
1
2
  import { flag } from '../cli/args.js';
2
3
  import { sksMenuBarRestartDeferred } from '../core/codex-app/menubar/index.js';
4
+ export function doctorGlobalOnlySelection(input) {
5
+ if (!input.doctorFix)
6
+ return { global_only: false, reason: null };
7
+ if (flag(input.args, '--global-only'))
8
+ return { global_only: true, reason: 'explicit_flag' };
9
+ if (path.resolve(input.root) === path.resolve(input.home))
10
+ return { global_only: true, reason: 'home_is_root' };
11
+ return { global_only: false, reason: null };
12
+ }
3
13
  export function doctorProfileFromArgs(args = [], doctorFix = false) {
4
14
  const explicit = readOption(args, '--profile');
5
15
  if (isDoctorProfile(explicit))
@@ -29,7 +29,8 @@ import { sweepSksTempDirs } from '../core/retention.js';
29
29
  import { detectImagegenCapability } from '../core/imagegen/imagegen-capability.js';
30
30
  import { CURRENT_CODEX_RUNTIME_CONTRACT } from '../core/codex-compat/codex-runtime-contract.js';
31
31
  import { formatHarnessConflictReport, scanHarnessConflicts } from '../core/harness-conflicts.js';
32
- import { doctorArgWarnings as baseDoctorArgWarnings, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs, doctorProfileRequiresDesktopBridgeReadiness } from './doctor-profile.js';
32
+ import { doctorArgWarnings as baseDoctorArgWarnings, doctorGlobalOnlySelection, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs, doctorProfileRequiresDesktopBridgeReadiness } from './doctor-profile.js';
33
+ import { renderDoctorConsoleReport } from './doctor-console.js';
33
34
  import { buildCodexAppUiDiagnosticFailure, buildRuntimeReadiness, captureCodexConfigSnapshot, deferredNativeRepair, doctorDedupeStatus, doctorSkillStatus, fallbackCodexNativeFeatureMatrix, formatCodexDoctorConsoleStatus, installScopeFromArgs, isMigrationUserOwnedProjectConfigBlocker, mergeObservedCodexStartupWarnings, nativeCapabilityStatus, readOption, rebootstrapSksMenuBarLaunchdForDoctorFix, sksMenuBarRunningVersionConsoleLines, uniqueNativeManualActions, writeFixMigrationJournal, writeJsonReportFile } from './doctor-helpers.js';
34
35
  export { doctorMenuBarInstallPolicy, doctorProfileFromArgs, doctorProfileRequiresDesktopBridgeReadiness } from './doctor-profile.js';
35
36
  export { buildCodexAppUiDiagnosticFailure, buildRuntimeReadiness, formatCodexDoctorConsoleStatus, rebootstrapSksMenuBarLaunchdForDoctorFix, sksMenuBarRunningVersionConsoleLines };
@@ -134,7 +135,8 @@ export async function run(_command, args = [], deps = {}) {
134
135
  return report;
135
136
  }
136
137
  const doctorFix = flag(args, '--fix');
137
- const globalOnly = doctorFix && flag(args, '--global-only');
138
+ const homeDir = path.resolve(deps.home || process.env.HOME || os.homedir());
139
+ const globalOnly = doctorGlobalOnlySelection({ args, doctorFix, root, home: homeDir }).global_only;
138
140
  if (doctorFix) {
139
141
  const conflictScan = await scanHarnessConflicts(root);
140
142
  if (conflictScan.hard_block) {
@@ -167,9 +169,7 @@ export async function run(_command, args = [], deps = {}) {
167
169
  if (!doctorFix && flag(args, '--json') && doctorProfile === 'fast')
168
170
  return runDoctorJsonFastPath(args, root);
169
171
  if (doctorFix) {
170
- const guardRoot = globalOnly
171
- ? path.resolve(deps.home || process.env.HOME || os.homedir())
172
- : root;
172
+ const guardRoot = globalOnly ? homeDir : root;
173
173
  return withSecretPreservationGuard(guardRoot, 'doctor-fix', async () => (globalOnly
174
174
  ? runDoctorGlobalOnlyFix(args, root, deps)
175
175
  : runDoctor(args, root, doctorFix, deps)));
@@ -248,6 +248,7 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
248
248
  : [])
249
249
  ].map(String).filter(Boolean))];
250
250
  const ok = blockers.length === 0;
251
+ const projectRootAliasDetected = path.resolve(root) === home;
251
252
  return {
252
253
  schema: 'sks.doctor-status.v3',
253
254
  elapsed_ms: Date.now() - startedAtMs,
@@ -258,7 +259,7 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
258
259
  install_scope: 'global',
259
260
  root,
260
261
  home,
261
- project_root_alias_detected: path.resolve(root) === home,
262
+ project_root_alias_detected: projectRootAliasDetected,
262
263
  no_project_writes_performed: true,
263
264
  project_phases_skipped: [
264
265
  'project_skills_reconcile',
@@ -277,6 +278,9 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
277
278
  desktop_bridge: desktopBridge,
278
279
  blockers,
279
280
  next_actions: [
281
+ ...(projectRootAliasDetected
282
+ ? ['Project checks were skipped because this directory is your home folder. Run them from your project: cd <your-project> && sks doctor --fix']
283
+ : []),
280
284
  ...(desktopBridge.ok === false ? desktopBridge.recovery_actions || [] : []),
281
285
  'Run `sks doctor --fix --json` from a specific project directory when project-scoped repair is required.'
282
286
  ]
@@ -1597,246 +1601,17 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
1597
1601
  process.exitCode = 1;
1598
1602
  return;
1599
1603
  }
1600
- console.log('SKS Doctor');
1601
- for (const warning of argWarnings)
1602
- console.log(`Argument warning: ${warning}`);
1603
- for (const warning of officialSubagentConfig.warnings || [])
1604
- console.log(`Official subagent warning: ${warning}`);
1605
- console.log(`Root: ${root}`);
1606
- console.log(`Node: ${result.node.ok ? 'ok' : 'fail'} ${result.node.version}`);
1607
- console.log(`Codex: ${codex.bin ? 'ok' : 'missing'} ${codex.version || ''}`);
1608
- if (oauthCallbackPortDiagnostic.conflict) {
1609
- const listeners = oauthCallbackPortDiagnostic.listeners
1610
- .map((listener) => `${listener.command} pid ${listener.pid} ${listener.address}`)
1611
- .join(', ');
1612
- console.log(`OAuth callback port 1455: warning (${listeners})`);
1613
- for (const action of oauthCallbackOperatorActions)
1614
- console.log(` action: ${action}`);
1615
- }
1616
- const actual = (codexConfig.checks || []).find((check) => check.name === 'actual_codex_cli_config_load');
1617
- console.log('Project config:');
1618
- console.log(` node read: ${ready.codex_config_readable_by_node ? 'ok' : 'failed'}`);
1619
- console.log(` codex cli read: ${ready.codex_config_readable_by_codex_cli ? 'ok' : (actual?.status || 'failed')}`);
1620
- console.log('Context7 MCP:');
1621
- console.log(` transport: ${context7Repair.preferred_transport || 'remote'}`);
1622
- console.log(` repair: ${context7Repair.ok ? 'ok' : 'blocked'}`);
1623
- for (const action of context7Repair.actions || [])
1624
- console.log(` - ${action}`);
1625
- for (const warning of context7Repair.warnings || [])
1626
- console.log(` warning: ${warning}`);
1627
- console.log('Codex startup config:');
1628
- console.log(` repair: ${codexStartupRepair.ok ? 'ok' : 'blocked'}`);
1629
- for (const action of codexStartupRepair.actions || [])
1630
- console.log(` - ${action}`);
1631
- for (const action of codexStartupRepair.manual_actions || [])
1632
- console.log(` manual: ${action}`);
1633
- for (const warning of codexStartupRepair.warnings || [])
1634
- console.log(` warning: ${warning}`);
1635
- if (codexConfigSyntaxRepair) {
1636
- console.log('Codex config syntax:');
1637
- console.log(` repair: ${codexConfigSyntaxRepair.ok ? 'ok' : 'blocked'}`);
1638
- for (const action of codexConfigSyntaxRepair.actions || [])
1639
- console.log(` - ${action}`);
1640
- for (const action of codexConfigSyntaxRepair.manual_actions || [])
1641
- console.log(` manual: ${action}`);
1642
- for (const warning of codexConfigSyntaxRepair.warnings || [])
1643
- console.log(` warning: ${warning}`);
1644
- }
1645
- console.log(` codex doctor: ${formatCodexDoctorConsoleStatus(authoritativeCodexDoctor)}`);
1646
- console.log(`Rust acc.: ${rust.mode || (rust.available ? 'rust_accelerated' : 'js_fallback')} ${rust.version || rust.status || ''}`);
1647
- console.log(`Codex App: ${ready.codex_app_ready ? 'ok' : 'optional_missing'}`);
1648
- console.log('SKS Runtime Readiness:');
1649
- console.log(` Codex Native: ${runtimeReadiness.codex_native}`);
1650
- console.log(` Loop Mesh: ${runtimeReadiness.loop_mesh}`);
1651
- console.log(` QA Visual: ${runtimeReadiness.qa_visual}`);
1652
- console.log(` Research Sources: ${runtimeReadiness.research_sources}`);
1653
- console.log(` Image Follow-up: ${runtimeReadiness.image_followup}`);
1654
- for (const note of runtimeReadiness.notes)
1655
- console.log(` ${note}`);
1656
- if (runtimeReadiness.repair_actions.length) {
1657
- console.log('Repair actions:');
1658
- for (const action of runtimeReadiness.repair_actions)
1659
- console.log(` - ${action}`);
1660
- }
1661
- const nativeCapabilityRows = Array.isArray(doctorNativeCapabilityRepair?.native_capabilities?.capabilities)
1662
- ? doctorNativeCapabilityRepair.native_capabilities.capabilities
1663
- : [];
1664
- console.log('SKS Native Capabilities:');
1665
- console.log(` image generation: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_generation', 'repair_required')}`);
1666
- console.log(` image follow-up edit: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_followup_edit', 'degraded')}`);
1667
- console.log(` computer use: ${nativeCapabilityStatus(nativeCapabilityRows, 'computer_use', 'manual_required')}`);
1668
- console.log(` Chrome/web review: ${nativeCapabilityStatus(nativeCapabilityRows, 'chrome_web_review', 'manual_required')}`);
1669
- console.log(` app screenshot: ${nativeCapabilityStatus(nativeCapabilityRows, 'codex_app_screenshot', 'degraded')}`);
1670
- console.log(` app handoff: ${nativeCapabilityStatus(nativeCapabilityRows, 'app_handoff', 'unavailable')}`);
1671
- console.log(` image path exposure: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_path_exposure', 'fallback')}`);
1672
- const nativeManualActions = uniqueNativeManualActions(nativeCapabilityRows);
1673
- if (nativeManualActions.length) {
1674
- console.log(' manual next actions:');
1675
- for (const action of nativeManualActions)
1676
- console.log(` - ${action}`);
1677
- }
1678
- console.log('SKS Skills:');
1679
- console.log(` core skills: ${doctorSkillStatus(doctorNativeCapabilityRepair?.core_skills)}`);
1680
- console.log(` duplicate project skills: ${doctorDedupeStatus(doctorNativeCapabilityRepair?.skill_dedupe)}`);
1681
- console.log('SKS Current Command Surface:');
1682
- console.log(` status: ${commandAliasCleanup.status || (commandAliasCleanup.ok ? 'clean' : 'blocked')}`);
1683
- console.log(` canonical commands: ${commandAliasCleanup.canonical_command_count ?? 0}`);
1684
- const managedRuntimeCleanup = commandAliasCleanup?.cleanup?.managed_runtime;
1685
- if (managedRuntimeCleanup) {
1686
- console.log(` managed items reconciled: ${managedRuntimeCleanup.removed_managed_artifact_count ?? 0}`);
1687
- console.log(` user-authored collisions preserved: ${managedRuntimeCleanup.preserved_user_file_count ?? 0}`);
1688
- }
1689
- if (commandAliasCleanup.report_path)
1690
- console.log(` report: ${commandAliasCleanup.report_path}`);
1691
- console.log('Secret preservation:');
1692
- console.log(` Supabase keys: ${doctorNativeCapabilityRepair?.ok === false && String((doctorNativeCapabilityRepair?.blockers || []).join(' ')).includes('secret_preservation_failed') ? 'blocked' : 'preserved'}`);
1693
- console.log(' raw secret values: never recorded');
1694
- console.log(` migration journal: ${doctorNativeCapabilityRepair?.secret_preservation_guard || '.sneakoscope/reports/secret-preservation-guard.json'}`);
1695
- console.log('Codex App Harness:');
1696
- console.log(` plugins: ${codexAppHarnessMatrix.app_features?.plugin_json ? 'ok' : 'degraded'}`);
1697
- console.log(` hook approval: ${codexAppHarnessMatrix.app_features?.hook_approval_state_detectable ? 'ok' : 'unknown'}`);
1698
- console.log(` skills: ${codexAppHarnessMatrix.sks_integrations?.dollar_skills_synced ? 'ok' : 'degraded'}`);
1699
- console.log(` agent roles: ${codexAppHarnessMatrix.sks_integrations?.agent_roles_synced ? 'ok' : 'degraded'}`);
1700
- console.log(` native agent_type: ${codexAppHarnessMatrix.app_features?.agent_type_supported ? 'ok' : 'fallback message-role'}`);
1701
- console.log(` init-deep memory: ${codexAppHarnessMatrix.sks_integrations?.init_deep_available ? 'available' : 'missing'}`);
1702
- console.log(` loop mesh app profile: ${codexAppHarnessMatrix.sks_integrations?.loop_mesh_app_profile_available ? 'available' : 'missing'}`);
1703
- const codexAppUiStatus = codexAppUi;
1704
- console.log('Codex App UI:');
1705
- console.log(` fast selector: ${codexAppUi.fast_selector || 'unknown'}`);
1706
- console.log(` provider selector: ${codexAppUi.provider_selector || 'unknown'}`);
1707
- if (Array.isArray(codexAppUiStatus.provider_blockers) && codexAppUiStatus.provider_blockers.length) {
1708
- console.log(` provider blockers: ${codexAppUiStatus.provider_blockers.join(', ')}`);
1709
- }
1710
- if (Array.isArray(codexAppUiStatus.provider_actions) && codexAppUiStatus.provider_actions.length) {
1711
- console.log(' provider actions:');
1712
- for (const action of codexAppUiStatus.provider_actions)
1713
- console.log(` - ${action}`);
1714
- }
1715
- console.log(` host-owned config: ${codexAppUi.host_owned_config || 'unknown'}`);
1716
- if (Array.isArray(codexAppUi.actions) && codexAppUi.actions.some((action) => action.changed)) {
1717
- console.log(' repaired files:');
1718
- for (const action of codexAppUi.actions.filter((entry) => entry.changed))
1719
- console.log(` - ${action.file}${action.backup_path ? ` (backup ${action.backup_path})` : ''}`);
1720
- }
1721
- if (codexAppUi.next_action)
1722
- console.log(` next action: ${codexAppUi.next_action}`);
1723
- console.log('SKS Menu Bar:');
1724
- console.log(` status: ${sksMenuBar.status || (sksMenuBar.ok ? 'ok' : 'blocked')}`);
1725
- for (const line of sksMenuBarRunningVersionConsoleLines(sksMenuBar))
1726
- console.log(line);
1727
- const menubarPhase = doctorFixTransaction?.phases?.find((phase) => phase?.id === 'sks_menubar');
1728
- if (menubarPhase) {
1729
- const menubarSummary = menubarPhase.ok
1730
- ? (menubarPhase.repaired ? 'repaired' : 'verified')
1731
- : `blocked(${(menubarPhase.blockers || []).join(', ') || 'unknown'})`;
1732
- console.log(` menubar: ${menubarSummary}`);
1733
- }
1734
- if (sksMenuBar.app_path)
1735
- console.log(` app: ${sksMenuBar.app_path}`);
1736
- if (sksMenuBar.launch_agent_path)
1737
- console.log(` launch agent: ${sksMenuBar.launch_agent_path}`);
1738
- if (Array.isArray(sksMenuBar.blockers) && sksMenuBar.blockers.length)
1739
- console.log(` blockers: ${sksMenuBar.blockers.join(', ')}`);
1740
- if (Array.isArray(sksMenuBar.warnings) && sksMenuBar.warnings.length)
1741
- console.log(` warnings: ${sksMenuBar.warnings.join(', ')}`);
1742
- console.log(`Provider: ${providerContext.provider || 'unknown'} ${providerContext.service_tier || ''} (${providerContext.source || 'unknown'}, ${providerContext.confidence || 'low'})`);
1743
- const imagegenReady = imagegen.auth_readiness;
1744
- if (imagegenReady) {
1745
- const paths = imagegenReady.available_paths?.length ? imagegenReady.available_paths.join(', ') : 'none';
1746
- console.log(`Image Gen: auth=${imagegenReady.auth_mode} | headless_auto=${imagegenReady.headless_auto_available ? 'available' : 'unavailable'} | paths: ${paths}`);
1747
- if (!imagegenReady.headless_auto_available) {
1748
- for (const action of imagegenReady.next_actions || [])
1749
- console.log(` - ${action}`);
1750
- }
1751
- }
1752
- console.log(`Image Gen repair: ${nativeCapabilityReadiness.imagegen.status}`);
1753
- for (const action of imagegenRepair.manual_actions || [])
1754
- console.log(` - ${action}`);
1755
- console.log(`Computer Use repair: ${computerUseRepair.recovered ? 'ok' : computerUseRepair.attempted ? 'blocked' : 'not-needed'}`);
1756
- for (const action of computerUseRepair.next_actions || [])
1757
- console.log(` - ${action}`);
1758
- console.log(`Browser Use repair: ${browserUseRepair.recovered ? 'ok' : browserUseRepair.attempted ? 'blocked' : 'not-needed'}`);
1759
- for (const action of browserUseRepair.next_actions || [])
1760
- console.log(` - ${action}`);
1761
- if (mcpTransportCollisionRepair) {
1762
- const collisionCount = (mcpTransportCollisionRepair.servers || []).filter((s) => s.status === 'collision_resolved').length;
1763
- console.log(`MCP transport collision repair: ${mcpTransportCollisionRepair.ok ? 'ok' : 'blocked'}${collisionCount ? ` (${collisionCount} resolved)` : ''}`);
1764
- }
1765
- {
1766
- const manifestPath = path.join(root, '.sneakoscope', 'agent-bridge', 'manifest.json');
1767
- const manifestExists = await exists(manifestPath);
1768
- console.log(`Agent bridge: ${manifestExists ? 'manifest present' : 'not set up'}${manifestExists ? '' : ' — run `sks agent-bridge setup` to publish the manifest and register with an MCP host'}`);
1769
- }
1770
- const codexCurrentApp = codexCurrentAppCapability.report || {};
1771
- console.log('Codex current compatibility:');
1772
- console.log(` target: ${CURRENT_CODEX_RUNTIME_CONTRACT.targetTag}`);
1773
- console.log(` runtime: ${codex.version || 'unknown'}`);
1774
- console.log(` multi-agent mode: ${codexNativeFeatureMatrix.features?.multi_agent_mode?.ok ? 'verified' : 'unverified'}`);
1775
- console.log(` rollout budget: ${codexNativeFeatureMatrix.features?.rollout_budget?.ok ? 'verified' : 'unverified'}`);
1776
- console.log(` indexed search: ${codexNativeFeatureMatrix.features?.indexed_web_search?.ok ? 'verified' : 'unverified'}`);
1777
- console.log(` current time: ${codexNativeFeatureMatrix.features?.current_time_read?.ok ? 'verified' : 'unverified'}`);
1778
- console.log('Current Codex app features:');
1779
- console.log(` /app handoff: ${codexCurrentApp.supports_app_handoff ? 'ok' : 'unavailable'}`);
1780
- console.log(` plugin JSON: ${codexCurrentApp.supports_plugin_json ? 'ok' : 'unavailable'}`);
1781
- console.log(` image path exposure: ${codexCurrentApp.supports_image_path_exposure ? 'ok' : 'unavailable'}`);
1782
- console.log(` OAuth MCP pre-refresh: ${codexCurrentApp.supports_oauth_mcp_prerefresh ? 'ok' : 'unavailable'}`);
1783
- const plugins = pluginInventory?.report?.plugins || [];
1784
- const remoteMcpCount = plugins.flatMap((plugin) => plugin.remote_mcp_servers || []).length;
1785
- const unavailableTemplates = pluginPolicy?.unavailable_app_templates?.length || 0;
1786
- console.log(`Codex plugins: ${pluginInventory?.report ? 'ok' : 'warning'}`);
1787
- console.log(` Remote MCP servers: ${remoteMcpCount} candidates`);
1788
- console.log(` Unavailable app templates: ${unavailableTemplates}`);
1789
- for (const warning of pluginPolicy?.doctor_warnings || [])
1790
- console.log(` warning: ${warning}`);
1791
- if (codexCurrentAppDoctor?.fixed?.length)
1792
- console.log(` doctor --fix repaired: ${codexCurrentAppDoctor.fixed.join(', ')}`);
1793
- console.log(`Desktop Bridge: ${desktopBridge.ok ? 'ready' : 'blocked'} (${desktopBridge.status?.readiness?.state || 'unavailable'})`);
1794
- for (const providerId of ['codex-lb', 'openrouter']) {
1795
- const provider = desktopBridge.providers?.[providerId];
1796
- if (!provider)
1797
- continue;
1798
- console.log(` ${providerId}: ${provider.enabled ? 'enabled' : 'disabled'}; credential ${provider.credential?.state || 'unknown'} (${provider.credential?.source || 'none'}); endpoint ${provider.endpoint?.configured ? 'configured' : 'missing'}`);
1799
- }
1800
- for (const warning of desktopBridge.warnings || [])
1801
- console.log(` warning: ${warning}`);
1802
- for (const blocker of desktopBridge.blockers || [])
1803
- console.log(` blocker: ${blocker}`);
1804
- if (!desktopBridge.ok)
1805
- for (const action of desktopBridge.recovery_actions || [])
1806
- console.log(` action: ${action}`);
1807
- console.log(`Permissions: config profile and permission profile are tracked separately (${permissionProfiles.codex_config_profile_field}, ${permissionProfiles.codex_permission_profile_field})`);
1808
- console.log('Ready:');
1809
- console.log(` cli_ready: ${ready.cli_ready ? 'yes' : 'no'}`);
1810
- console.log(` mad_ready: ${ready.mad_ready ? 'yes' : 'no'}`);
1811
- console.log(` managed_state_current: ${ready.managed_state_current ? 'yes' : 'no'}`);
1812
- console.log(` core_ready: ${ready.core_ready ? 'yes' : 'no'}`);
1813
- console.log(` center_ready: ${ready.center_ready ? 'yes' : 'no'}${ready.center_attempted ? ' (repair attempted)' : ' (not attempted)'}`);
1814
- console.log(` ready: ${ready.ready ? 'yes' : 'no'}`);
1815
- if (!ready.ready) {
1816
- console.log('Primary blocker:');
1817
- console.log(` ${ready.primary_blocker || 'unknown'}`);
1818
- }
1819
- if (configRepair?.repair_actions?.length) {
1820
- console.log('What I fixed:');
1821
- for (const action of configRepair.repair_actions)
1822
- console.log(` - ${action.name}: ${action.ok ? 'ok' : 'failed'}`);
1823
- }
1824
- if (migrationJournal?.journal_path) {
1825
- console.log(`Migration journal: ${migrationJournal.journal_path} (${migrationJournal.event_count} events, ${migrationJournal.mutations_without_rollback} without rollback)`);
1826
- }
1827
- if (sksUpdate) {
1828
- console.log(`SKS update: ${sksUpdate.status}${sksUpdate.latest ? ` latest ${sksUpdate.latest}` : ''}${sksUpdate.error ? ` (${sksUpdate.error})` : ''}`);
1829
- }
1830
- if (globalSksInstallCleanup) {
1831
- console.log(`Global SKS installs: kept ${globalSksInstallCleanup.kept?.length ?? 0}, removed ${globalSksInstallCleanup.removed?.filter((entry) => entry.ok).length ?? 0}, source repo exempt ${globalSksInstallCleanup.candidates?.filter((entry) => entry.source_repo_exempt).length ?? 0}`);
1832
- if (globalSksInstallCleanup.npm_cache)
1833
- console.log(`NPM cache cleanup: ${globalSksInstallCleanup.npm_cache.status}`);
1834
- }
1835
- if (!ready.ready && ready.next_actions?.length) {
1836
- console.log('What still needs you:');
1837
- for (const action of ready.next_actions)
1838
- console.log(` - ${action}`);
1839
- }
1604
+ const agentBridgeManifestExists = await exists(path.join(root, '.sneakoscope', 'agent-bridge', 'manifest.json'));
1605
+ const consoleLines = renderDoctorConsoleReport(result, {
1606
+ oauthCallbackOperatorActions,
1607
+ nativeCapabilityReadiness,
1608
+ agentBridgeManifestExists,
1609
+ codexCurrentAppCapability,
1610
+ pluginInventory,
1611
+ rootIsHome: path.resolve(root) === path.resolve(deps.home || process.env.HOME || os.homedir())
1612
+ });
1613
+ for (const consoleLine of consoleLines)
1614
+ console.log(consoleLine);
1840
1615
  if (!result.ok)
1841
1616
  process.exitCode = 1;
1842
1617
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "9.0.1",
3
+ "package_version": "9.0.3",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -144,8 +144,13 @@ function writeHttpBridgeError(res, error, req) {
144
144
  });
145
145
  res.end(JSON.stringify({ error: { type: 'sks_bridge_error', code, message: code } }));
146
146
  }
147
+ function safeUpstreamErrorId(value) {
148
+ const text = String(value ?? '').trim();
149
+ return /^[A-Za-z0-9_.:-]{1,64}$/.test(text) ? text : null;
150
+ }
147
151
  async function readRedactedUpstreamError(response) {
148
152
  let total = 0;
153
+ const chunks = [];
149
154
  for await (const raw of response) {
150
155
  const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
151
156
  total += chunk.length;
@@ -153,14 +158,30 @@ async function readRedactedUpstreamError(response) {
153
158
  response.destroy();
154
159
  break;
155
160
  }
161
+ chunks.push(chunk);
156
162
  }
157
- return Buffer.from(JSON.stringify({
158
- error: {
159
- type: 'upstream_error',
160
- code: 'bridge_upstream_request_failed',
161
- message: 'Upstream request failed'
162
- }
163
- }));
163
+ let upstreamCode = null;
164
+ let upstreamType = null;
165
+ try {
166
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'));
167
+ upstreamCode = safeUpstreamErrorId(parsed?.error?.code) ?? safeUpstreamErrorId(parsed?.detail);
168
+ upstreamType = safeUpstreamErrorId(parsed?.error?.type);
169
+ }
170
+ catch {
171
+ }
172
+ return {
173
+ upstreamCode,
174
+ upstreamType,
175
+ body: Buffer.from(JSON.stringify({
176
+ error: {
177
+ type: 'upstream_error',
178
+ code: 'bridge_upstream_request_failed',
179
+ message: 'Upstream request failed',
180
+ ...(upstreamType ? { upstream_type: upstreamType } : {}),
181
+ ...(upstreamCode ? { upstream_code: upstreamCode } : {})
182
+ }
183
+ }))
184
+ };
164
185
  }
165
186
  const upstreamAgents = new Map();
166
187
  function upstreamAgent(secure, key, idleTimeoutMs) {
@@ -252,16 +273,16 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
252
273
  upstream.once('response', (response) => {
253
274
  const statusCode = response.statusCode || 502;
254
275
  if (statusCode >= 400) {
255
- logHttpRejection({
256
- code: `bridge_upstream_status_${statusCode}`,
257
- transport: 'http',
258
- ...(req.method === undefined ? {} : { method: req.method }),
259
- ...(req.url === undefined ? {} : { url: req.url }),
260
- status: statusCode,
261
- provider_id: provider.provider_id,
262
- public_model: request.route.public_model,
263
- });
264
- void readRedactedUpstreamError(response).then((body) => {
276
+ void readRedactedUpstreamError(response).then(({ body, upstreamCode }) => {
277
+ logHttpRejection({
278
+ code: upstreamCode ? `bridge_upstream_status_${statusCode}:${upstreamCode}` : `bridge_upstream_status_${statusCode}`,
279
+ transport: 'http',
280
+ ...(req.method === undefined ? {} : { method: req.method }),
281
+ ...(req.url === undefined ? {} : { url: req.url }),
282
+ status: statusCode,
283
+ provider_id: provider.provider_id,
284
+ public_model: request.route.public_model,
285
+ });
265
286
  responseStarted = true;
266
287
  const responseHeaders = rewriteResponseHeaders(response.headers, provider.base_url, authenticatedLocalBaseUrl);
267
288
  responseHeaders['content-length'] = String(body.length);
package/dist/core/fsx.js CHANGED
@@ -342,17 +342,23 @@ export async function findUp(start, names) {
342
342
  }
343
343
  export async function findProjectRoot(start = process.cwd()) {
344
344
  const resolved = path.resolve(start);
345
- const sine = await findUp(resolved, ['.sneakoscope', '.dcodex']);
346
- if (sine) {
347
- const rootDir = path.dirname(sine);
348
- if (rootDir !== path.parse(rootDir).root)
349
- return rootDir;
350
- }
351
- const git = await findUp(resolved, ['.git']);
352
- if (git) {
353
- const rootDir = path.dirname(git);
354
- if (rootDir !== path.parse(rootDir).root)
355
- return rootDir;
345
+ const home = path.resolve(os.homedir());
346
+ for (const markers of [['.sneakoscope', '.dcodex'], ['.git']]) {
347
+ let from = resolved;
348
+ while (true) {
349
+ const marker = await findUp(from, markers);
350
+ if (!marker)
351
+ break;
352
+ const rootDir = path.dirname(marker);
353
+ if (rootDir === path.parse(rootDir).root)
354
+ break;
355
+ if (rootDir !== home)
356
+ return rootDir;
357
+ const parent = path.dirname(rootDir);
358
+ if (parent === rootDir)
359
+ break;
360
+ from = parent;
361
+ }
356
362
  }
357
363
  return null;
358
364
  }
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = '9.0.1';
1
+ export const PACKAGE_VERSION = '9.0.3';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "9.0.1",
4
+ "version": "9.0.3",
5
5
  "description": "Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",