sneakoscope 10.5.4 → 10.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +133 -28
  2. package/config/skills-hash-ledger.v1.json +1 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/commands/doctor-console.js +6 -0
  6. package/dist/commands/doctor.js +30 -2
  7. package/dist/config/skills-manifest.json +3 -2
  8. package/dist/core/agent-guidance/harness-maintenance.js +185 -0
  9. package/dist/core/agent-guidance/official-guidance-client.js +82 -0
  10. package/dist/core/agents/native-worker-backend-router.js +12 -9
  11. package/dist/core/codex/deprecated-config.js +255 -0
  12. package/dist/core/codex-native/core-skill-manifest.js +5 -5
  13. package/dist/core/commands/align-command.js +13 -4
  14. package/dist/core/commands/naruto-command.js +3 -4
  15. package/dist/core/decisions/child-model-choice.js +16 -10
  16. package/dist/core/decisions/policy.js +4 -2
  17. package/dist/core/doctor/codex-config-syntax-repair.js +43 -19
  18. package/dist/core/doctor/doctor-codex-startup-repair.js +0 -8
  19. package/dist/core/doctor/supabase-mcp-repair.js +56 -38
  20. package/dist/core/hooks-runtime/jev-spawn-routing.js +7 -4
  21. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  22. package/dist/core/hooks-runtime/parent-orchestration-gate.js +6 -1
  23. package/dist/core/hooks-runtime/subagent-spawn-policy.js +6 -3
  24. package/dist/core/init.js +18 -51
  25. package/dist/core/routes.js +3 -3
  26. package/dist/core/subagents/child-model-allowlist.js +26 -6
  27. package/dist/core/subagents/child-model-plan.js +2 -2
  28. package/dist/core/subagents/model-tiers.js +0 -9
  29. package/dist/core/subagents/naruto-host-credentials.js +10 -3
  30. package/dist/core/subagents/official-subagent-prompt.js +8 -7
  31. package/dist/core/subagents/official-subagent-runner.js +3 -0
  32. package/dist/core/subagents/wave-lifecycle.js +2 -2
  33. package/dist/core/update/update-migration-state/simple-stages.js +9 -0
  34. package/dist/core/update/update-migration-state.js +6 -1
  35. package/dist/core/version.js +1 -1
  36. package/dist/native/sks-menubar/Sources/OpenRouterOnlyState.swift +1 -1
  37. package/dist/native/sks-menubar/Sources/SubagentModelsModels.swift +19 -5
  38. package/dist/native/sks-menubar/Sources/SubagentModelsViewController.swift +4 -4
  39. package/docs/assets/sks-center-subagent-models.png +0 -0
  40. package/package.json +2 -1
  41. package/dist/core/doctor/stale-subagent-default.js +0 -30
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  [![CI](https://github.com/mandarange/Sneakoscope-Codex/actions/workflows/ci.yml/badge.svg)](https://github.com/mandarange/Sneakoscope-Codex/actions/workflows/ci.yml)
10
10
  [![npm version](https://img.shields.io/npm/v/sneakoscope?color=cb3837&logo=npm)](https://www.npmjs.com/package/sneakoscope)
11
- [![Node.js >=20.11](https://img.shields.io/badge/node-%3E%3D20.11-339933?logo=node.js&logoColor=white)](#quick-start)
11
+ [![Recommended Node.js 24 LTS](https://img.shields.io/badge/node-24%20LTS-339933?logo=node.js&logoColor=white)](#requirements)
12
12
  [![MIT license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
13
13
 
14
14
  </div>
@@ -17,74 +17,188 @@
17
17
  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.
18
18
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
19
19
 
20
- Current package: **SKS 10.5.4**. Install the latest stable release from npm.
20
+ Current package: **SKS 10.5.6**. Install the latest stable release from npm.
21
21
 
22
22
  Sneakoscope Codex (`sks`) is a local trust layer for Codex CLI and ChatGPT Desktop. It keeps agent work bounded, preserves project context, records machine-verifiable evidence, and prevents release claims that current tests or artifacts cannot support.
23
23
 
24
24
  Use SKS when you want an AI coding session to end with something you can inspect: the change, the checks that ran, and the evidence behind the result. SKS is local-first and deterministic. It does not promise model quality, search rankings, or traffic.
25
25
 
26
+ ## SKS Center — one place for your Codex setup
27
+
28
+ On macOS, **SKS Center** puts connections, subagent model choices, updates,
29
+ and diagnostics in a native menu bar app. Open the SKS menu bar icon and choose
30
+ **Open SKS Control Center…** to manage your setup without memorizing CLI commands.
31
+
32
+ ![SKS Center showing a connection-specific subagent model list with separate reasoning efforts](docs/assets/sks-center-subagent-models.png)
33
+
34
+ *Native SKS Center UI with an example configuration. Models and available
35
+ reasoning efforts depend on your connection's current catalog.*
36
+
37
+ | Page | What you can manage |
38
+ | --- | --- |
39
+ | **Connections** | Codex-LB, OpenAI OAuth, OpenRouter, and model visibility. |
40
+ | **Subagent Models** | Separate lists per connection, a default option, reasoning effort, and criteria for Jev. The same model can have different efforts for different work. |
41
+ | **Decisions** | Jev's decision mode and cloud consent. |
42
+ | **Settings** | Naruto's execution preference and app settings. |
43
+ | **Image Generation** | Image mode, supported model choices, and provider setup. |
44
+ | **MCP Servers** | Connected tools and server configuration. |
45
+ | **Updates & Diagnostics** | Available updates, operation progress, logs, and health checks. |
46
+
47
+ After installing SKS, install or rebuild the macOS app with:
48
+
49
+ ```sh
50
+ sks menubar install
51
+ ```
52
+
53
+ Interactive macOS installs can build it automatically. Dependency, CI, and
54
+ piped installs skip the menu bar setup; scripts can opt in with
55
+ `SKS_POSTINSTALL_MENUBAR=1` or opt out with `SKS_POSTINSTALL_NO_MENUBAR=1`.
56
+ See [connection setup](docs/codex-lb.md), [subagent model lists](docs/naruto.md#connection-specific-subagent-lists),
57
+ and [image generation](docs/image-generation.md) for the underlying settings.
58
+
26
59
  ## Quick start
27
60
 
28
- Requirements: Node.js **20.11+**, npm, Git, and a current Codex CLI or supported Codex desktop host.
61
+ ### Requirements
62
+
63
+ - **Recommended: the latest patch release of Node.js 24 LTS.** Get it from the [official Node.js releases](https://nodejs.org/en/about/previous-releases).
64
+ - npm, Git, and a current Codex CLI or supported Codex desktop host.
65
+ - For source development and tests, see the locked dependency compatibility ranges in [Contributing](CONTRIBUTING.md#local-setup). CI uses the 24.x line.
66
+
67
+ The published CLI's declared `package.json` engine floor is still `>=20.11`.
68
+ That is separate from the development/test requirements above; Node 20 is now
69
+ end-of-life, so use a supported LTS release for a new installation.
70
+
71
+ ### Install SKS globally
72
+
73
+ Install SKS once for your user account so `sks` is available across projects:
74
+
75
+ ```sh
76
+ npm install --global sneakoscope@latest
77
+ sks doctor --fix --global-only --yes
78
+ sks --version
79
+ ```
80
+
81
+ Run these commands in your terminal. The global-only Doctor step reconciles
82
+ user-level SKS skills and configuration, and reports any setup blockers.
83
+ Make sure your npm global binary directory is on `PATH`.
84
+
85
+ Alternatively, the verified installer performs the global npm installation,
86
+ runs Doctor, and checks that `sks` on `PATH` resolves to the installed version:
29
87
 
30
88
  ```sh
31
89
  npm exec --yes --package=sneakoscope@latest -- sneakoscope install --yes
32
90
  ```
33
91
 
34
- From the root of a project you want SKS to understand:
92
+ ### Enable SKS for a project
93
+
94
+ From the root of each project you want SKS to understand:
35
95
 
36
96
  ```sh
37
97
  sks bootstrap
38
98
  sks doctor --json
39
99
  ```
40
100
 
41
- Then open the project in Codex. Start with the smallest useful loop:
101
+ Then open the project in Codex. Refresh its context and ask for ordinary work
102
+ directly in the conversation:
42
103
 
43
104
  ```text
44
- $sks-plan "Describe the change and the checks it needs"
45
- $sks-work
105
+ $sks-align
106
+ Fix the failing test and verify the changed behavior.
46
107
  $sks-review
47
108
  ```
48
109
 
49
- Use the terminal to inspect the same state:
110
+ `sks bootstrap` adds project-local SKS context and ignore rules. Ordinary work
111
+ stays with the main agent; choose `$sks-naruto` or `$sks-work` when you want
112
+ parallel child work.
113
+
114
+ ## Update an existing installation
115
+
116
+ Run this from your project root in a terminal:
50
117
 
51
118
  ```sh
52
- sks status --json
53
- sks review --staged
54
- sks update-check
119
+ sks update
120
+ sks --version
121
+ sks doctor --json
55
122
  ```
56
123
 
57
- The installer resolves the latest npm release, installs `sks`, runs setup and Doctor, and verifies that the command on your `PATH` is the version it installed. `sks bootstrap` adds the project-local SKS context and ignore rules; it does not replace your source of truth.
124
+ `sks update` updates the **global** SKS package, verifies the installed command,
125
+ and reconciles SKS-managed skills, hooks, configuration and SKS Center where
126
+ available. In 10.5.5 and later it also repairs the known ignored Codex settings
127
+ and refreshes managed guidance. User-authored text and explicit preferences
128
+ are preserved. If a step fails, follow the recovery action in its output.
129
+
130
+ To check available updates first, run `sks update check`. For a repair without
131
+ requesting a newer package, run `sks doctor --fix` yourself in the terminal.
58
132
 
59
133
  ## What you get
60
134
 
61
135
  | Need | SKS provides |
62
136
  | --- | --- |
63
- | Keep work focused | Answers, tiny edits, ordinary implementation, reviews, and DB work run directly in the main agent, the way Codex works by default. Naruto splits work across official Codex subagents only when you ask for it. |
137
+ | Keep work focused | Answers, tiny edits, ordinary implementation, reviews, and DB work run directly in the main agent. Naruto delegates through official Codex subagents when explicitly requested or selected by Jev. |
64
138
  | Keep context bounded | TriWiki indexes repository code into context that can be checked against source. |
65
139
  | Know what actually ran | Tests, diagnostics, and release evidence are recorded for completion claims. |
66
140
  | Recover safely | Doctor and update flows report a concrete recovery action when a check needs attention. |
67
141
  | Operate locally | The CLI works without a hosted control plane; macOS users can also use SKS Center. |
68
142
 
143
+ `$sks-align` refreshes the thin managed guidance against current official Codex
144
+ references and rebuilds TriWiki. Update and user-run `sks doctor --fix` share
145
+ that maintenance and repair known ignored configuration settings. User text
146
+ and model choices are preserved. See [harness maintenance](docs/align-modernization.md).
147
+
69
148
  ## Everyday commands
70
149
 
71
- Inside a Codex conversation:
150
+ ### Core dollar commands
151
+
152
+ Type these into the **Codex desktop app or Codex CLI conversation**:
72
153
 
73
154
  | Command | Use it for |
74
155
  | --- | --- |
156
+ | `$sks-align` | Refresh official guidance and rebuild TriWiki from the current codebase. |
157
+ | `$sks-mad-sks "scope and task"` | Explicitly authorize a bounded high-risk task; combine with another dollar command when appropriate. |
75
158
  | `$sks-plan "task"` | Write a plan without editing product code. |
76
- | `$sks-work` | Execute the latest plan with Naruto child agents. |
159
+ | `$sks-work` | Run parallel child work through the Naruto execution alias. |
77
160
  | `$sks-review` | Review the current changes. |
78
161
  | `$sks-naruto "task"` | Explicitly split independent work across official subagents. |
79
162
  | `$sks-help` | Explore available SKS workflows. |
80
163
 
81
- In a terminal:
164
+ #### `$sks-align`: keep the harness and project context current
165
+
166
+ Use it when starting in a repository, after structural code changes, or when
167
+ you want to refresh the managed instructions against current official guidance:
168
+
169
+ ```text
170
+ $sks-align
171
+ ```
172
+
173
+ Align searches current official Codex/model guidance, refreshes only
174
+ SKS-managed instruction areas, and rebuilds the source-only TriWiki index.
175
+ Your own instructions and model choices are preserved. External references
176
+ stay separate from code facts; unavailable retrieval is reported while the
177
+ previous references are retained. See [harness maintenance](docs/align-modernization.md).
178
+
179
+ #### `$sks-mad-sks`: explicitly authorize a scoped high-risk task
180
+
181
+ Name the target, allowed changes and boundaries in the request. For example:
182
+
183
+ ```text
184
+ $sks-mad-sks Fix permissions only under ./dist so this project's build can run.
185
+ Preserve source files, credentials, and other projects.
186
+ ```
187
+
188
+ It temporarily widens only the approved scopes for that invocation and can
189
+ be combined with another dollar command, such as `$sks-db` for an explicitly
190
+ authorized database task. Database work uses the bound
191
+ project's SQL-plane capability, read-back verification and final read-only
192
+ restoration. Host approval boundaries and protected control-plane operations
193
+ remain enforced. See [MAD-SKS](docs/mad-sks.md) before authorizing such work.
194
+
195
+ ### Terminal commands
82
196
 
83
197
  ```sh
84
198
  sks --help
85
199
  sks status --json
86
200
  sks doctor --json
87
- sks update-check
201
+ sks update check
88
202
  sks update
89
203
  ```
90
204
 
@@ -101,16 +215,6 @@ SKS keeps these steps observable. It does not turn a failed check into a success
101
215
 
102
216
  ## Optional integrations
103
217
 
104
- ### SKS Center on macOS
105
-
106
- The menu bar app exposes connections, updates, MCP servers, image generation, and diagnostics in one place. Build it from an interactive macOS install or run:
107
-
108
- ```sh
109
- sks menubar install
110
- ```
111
-
112
- Dependency, CI, and piped installs do not touch your home directory. Set `SKS_POSTINSTALL_MENUBAR=1` to force the build in a script, or `SKS_POSTINSTALL_NO_MENUBAR=1` to skip it.
113
-
114
218
  ### Desktop Bridge
115
219
 
116
220
  The Desktop Bridge can route eligible models through Codex-LB or OpenRouter while keeping provider choice and session pins explicit:
@@ -139,7 +243,8 @@ See [Image generation](docs/image-generation.md) for provider boundaries and evi
139
243
 
140
244
  - [Product contract](docs/PRODUCT-CONTRACT.md) — supported surfaces and ownership.
141
245
  - [Essential Trust](docs/essential-trust.md) — verification profiles and safety boundaries.
142
- - [Astra guidance](docs/astra-guidance.md) — how SKS applies official model recommendations.
246
+ - [Harness maintenance and Align](docs/align-modernization.md) — current official references, thin managed instructions, and source-only TriWiki.
247
+ - [MAD-SKS](docs/mad-sks.md) — explicit permission scope, execution boundaries, and verification.
143
248
  - [Agent Bridge](docs/AGENT-BRIDGE.md) — integrate through the CLI or MCP interface.
144
249
  - [Context Graph](docs/architecture/context-graph.md) — bounded source lookup and freshness.
145
250
  - [Release readiness](docs/release-readiness.md) — build, verify, and publish a release.
@@ -15,6 +15,7 @@
15
15
  {
16
16
  "canonical_name": "sks-align",
17
17
  "trusted_sha256": [
18
+ "c3073b4a10203e642dd1236902b01772ecff6a3a7464cdecde0aad21e3c12b80",
18
19
  "9eef36214be7e07a963eb36bb1df2c025cd0578b7419ddf4fd307081a2b62093",
19
20
  "b9db40d6e1dd2fc6e4665a2b30738d370da955cd4d6e86c88bd85059c7faa08c",
20
21
  "c485f39d4873adb9793672f7dcf4e956595ffb9dcbb032b3ffb0515b5857d31d",
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "10.5.4"
262
+ version = "10.5.6"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "10.5.4"
3
+ version = "10.5.6"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -84,6 +84,12 @@ export function renderDoctorConsoleReport(result, extras) {
84
84
  for (const warning of codexConfigSyntaxRepair.warnings || [])
85
85
  line(` warning: ${warning}`);
86
86
  }
87
+ if (result.harness_maintenance) {
88
+ const guidance = result.harness_maintenance;
89
+ line(`Official harness guidance: ${guidance.guidance_status}${guidance.fetched_at ? ` (${guidance.fetched_at})` : ''}`);
90
+ for (const warning of guidance.warnings || [])
91
+ line(` warning: ${warning}`);
92
+ }
87
93
  line(` codex doctor: ${formatCodexDoctorConsoleStatus(result.codex_doctor)}`);
88
94
  line(`Rust acc.: ${rust.mode || (rust.available ? 'rust_accelerated' : 'js_fallback')} ${rust.version || rust.status || ''}`);
89
95
  line(`Codex App: ${codexApp.skipped === true ? DOCTOR_CONSOLE_NOT_MEASURED : ready.codex_app_ready ? 'ok' : 'optional_missing'}`);
@@ -1,6 +1,6 @@
1
1
  import os from 'node:os';
2
2
  import path from 'node:path';
3
- import { projectRoot, exists, formatBytes, nowIso, writeJsonAtomic } from '../core/fsx.js';
3
+ import { projectRoot, exists, ensureDir, formatBytes, nowIso, writeJsonAtomic } from '../core/fsx.js';
4
4
  import { flag } from '../cli/args.js';
5
5
  import { printJson } from '../cli/output.js';
6
6
  import { ui as cliUi } from '../cli/cli-theme.js';
@@ -27,6 +27,7 @@ import { detectImagegenCapability } from '../core/imagegen/imagegen-capability.j
27
27
  import { formatHarnessConflictReport, scanHarnessConflicts } from '../core/harness-conflicts.js';
28
28
  import { doctorArgWarnings as baseDoctorArgWarnings, doctorGlobalOnlySelection, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs, doctorProfileRequiresDesktopBridgeReadiness } from './doctor-profile.js';
29
29
  import { renderDoctorConsoleReport } from './doctor-console.js';
30
+ import { isVerificationTestHarness } from '../core/verification-profile.js';
30
31
  import { buildCodexAppUiDiagnosticFailure, buildRuntimeReadiness, captureCodexConfigSnapshot, deferredNativeRepair, fallbackCodexNativeFeatureMatrix, formatCodexDoctorConsoleStatus, installScopeFromArgs, isMigrationUserOwnedProjectConfigBlocker, mergeObservedCodexStartupWarnings, readOption, rebootstrapSksMenuBarLaunchdForDoctorFix, sksMenuBarRunningVersionConsoleLines, writeFixMigrationJournal, writeJsonReportFile } from './doctor-helpers.js';
31
32
  export { doctorMenuBarInstallPolicy, doctorProfileFromArgs, doctorProfileRequiresDesktopBridgeReadiness } from './doctor-profile.js';
32
33
  export { buildCodexAppUiDiagnosticFailure, buildRuntimeReadiness, formatCodexDoctorConsoleStatus, rebootstrapSksMenuBarLaunchdForDoctorFix, sksMenuBarRunningVersionConsoleLines };
@@ -202,6 +203,10 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
202
203
  error: err?.message || String(err)
203
204
  }));
204
205
  const doctorProfile = doctorProfileFromArgs(args, true);
206
+ const syntaxRepair = await (await import('../core/doctor/codex-config-syntax-repair.js')).runCodexConfigSyntaxRepair({
207
+ root: home, fix: true, codexHome: doctorEnv.CODEX_HOME || path.join(home, '.codex')
208
+ });
209
+ const harnessMaintenance = await doctorHarnessMaintenance(path.resolve(deps.globalRuntimeRoot || doctorEnv.SKS_GLOBAL_ROOT || path.join(home, '.sneakoscope-global')), doctorProfile, doctorEnv, deps);
205
210
  const menuBarPolicy = doctorMenuBarInstallPolicy(args, true, doctorEnv);
206
211
  const menuBar = menuBarPolicy.phase_enabled
207
212
  ? await installMenuBarImpl({
@@ -248,6 +253,8 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
248
253
  && globalFastMode?.ok !== false;
249
254
  const menuBarReady = menuBar?.ok !== false;
250
255
  const migrationScopeBlockers = [...new Set([
256
+ ...syntaxRepair.blockers,
257
+ ...(harnessMaintenance?.blockers || []),
251
258
  ...(!globalSkillsReady ? [`global_skills_reconcile_failed:${globalSkills?.error || 'core_skill_integrity'}`] : []),
252
259
  ...(currentSurface?.ok !== true ? (currentSurface?.blockers || ['global_current_surface_reconcile_failed']) : []),
253
260
  ...(!globalFastModeReady ? [`global_fast_mode_repair_failed:${globalFastMode?.error || globalFastMode?.status || 'unknown'}`] : [])
@@ -291,6 +298,8 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
291
298
  ...migrationReceiptBlockers
292
299
  ].map(String).filter(Boolean))];
293
300
  const warnings = [...new Set([
301
+ ...syntaxRepair.warnings,
302
+ ...(harnessMaintenance?.warnings || []),
294
303
  ...(!bridgeReadinessRequired ? bridgeBlockers.map((blocker) => `migration_optional_blocker:${blocker}`) : []),
295
304
  ...(desktopBridgeRepair?.warnings || []),
296
305
  ...(desktopBridge?.warnings || [])
@@ -321,6 +330,8 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
321
330
  ],
322
331
  skills: { global: globalSkills, project: { skipped: true, reason: 'global_only_doctor' } },
323
332
  current_public_surface: currentSurface,
333
+ codex_config_syntax_repair: syntaxRepair,
334
+ harness_maintenance: harnessMaintenance,
324
335
  codex_app_fast_mode: globalFastMode,
325
336
  openrouter_provider: desktopBridge.providers?.openrouter || null,
326
337
  sks_menubar: menuBar,
@@ -569,6 +580,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
569
580
  actions: []
570
581
  })) : { ok: true, skipped: true, reason: 'doctor_without_fix', actions: [] };
571
582
  const doctorProfile = doctorProfileFromArgs(args, doctorFix);
583
+ const harnessMaintenance = doctorFix ? await doctorHarnessMaintenance(root, doctorProfile, process.env, deps) : null;
572
584
  const machineOnly = flag(args, '--machine-only');
573
585
  const reportFile = readOption(args, '--report-file', null);
574
586
  const argWarnings = doctorArgWarnings(args);
@@ -1467,6 +1479,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
1467
1479
  }
1468
1480
  const runtimeReadiness = buildRuntimeReadiness(codexNativeFeatureMatrix);
1469
1481
  const resultBlockers = [
1482
+ ...(harnessMaintenance?.blockers || []),
1470
1483
  ...(ready.ready
1471
1484
  ? []
1472
1485
  : (Array.isArray(ready.blockers) && ready.blockers.length
@@ -1511,7 +1524,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
1511
1524
  root,
1512
1525
  arg_warnings: argWarnings,
1513
1526
  blockers: [...new Set(resultBlockers.map(String))],
1514
- warnings: [...oauthCallbackPortDiagnostic.warnings, ...(desktopBridge.warnings || [])],
1527
+ warnings: [...oauthCallbackPortDiagnostic.warnings, ...(desktopBridge.warnings || []), ...(harnessMaintenance?.warnings || [])],
1515
1528
  operator_actions: [...new Set([
1516
1529
  ...oauthCallbackOperatorActions,
1517
1530
  ...(desktopBridge.ok === false ? desktopBridge.recovery_actions || [] : []),
@@ -1542,6 +1555,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
1542
1555
  codex_startup_repair: codexStartupRepair,
1543
1556
  startup_config_repair: startupConfigRepair,
1544
1557
  codex_config_syntax_repair: codexConfigSyntaxRepair,
1558
+ harness_maintenance: harnessMaintenance,
1545
1559
  context7_mcp_repair: context7McpRepair,
1546
1560
  supabase_mcp_repair: supabaseMcpRepair,
1547
1561
  doctor_fix_transaction: doctorFixTransaction,
@@ -1616,3 +1630,17 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
1616
1630
  if (!result.ok)
1617
1631
  process.exitCode = 1;
1618
1632
  }
1633
+ async function doctorHarnessMaintenance(root, profile, env, deps) {
1634
+ const duringUpdate = env.SKS_UPDATE_DEFER_MENUBAR_RESTART === '1';
1635
+ if (profile === 'migration' && !duringUpdate)
1636
+ return null;
1637
+ await ensureDir(root);
1638
+ const maintain = deps.maintainHarnessGuidanceImpl
1639
+ || (await import('../core/agent-guidance/harness-maintenance.js')).maintainHarnessGuidance;
1640
+ const codexHome = deps.codexHome || deps.env?.CODEX_HOME
1641
+ || (deps.home ? path.join(deps.home, '.codex') : !isVerificationTestHarness() ? env.CODEX_HOME : undefined);
1642
+ return maintain({
1643
+ root, trigger: duringUpdate ? 'update' : 'doctor', reuseRecent: duringUpdate,
1644
+ ...(codexHome ? { codexHome } : {})
1645
+ });
1646
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "10.5.4",
3
+ "package_version": "10.5.6",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -18,8 +18,9 @@
18
18
  {
19
19
  "canonical_name": "sks-align",
20
20
  "type": "core",
21
- "content_sha256": "9eef36214be7e07a963eb36bb1df2c025cd0578b7419ddf4fd307081a2b62093",
21
+ "content_sha256": "c3073b4a10203e642dd1236902b01772ecff6a3a7464cdecde0aad21e3c12b80",
22
22
  "hash_history": [
23
+ "9eef36214be7e07a963eb36bb1df2c025cd0578b7419ddf4fd307081a2b62093",
23
24
  "b9db40d6e1dd2fc6e4665a2b30738d370da955cd4d6e86c88bd85059c7faa08c",
24
25
  "c485f39d4873adb9793672f7dcf4e956595ffb9dcbb032b3ffb0515b5857d31d",
25
26
  "e98e1b67e228bec5c37d00c0206a2e01d2da698a83c80b527105bf04a19c136a"
@@ -0,0 +1,185 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { readText, writeTextAtomic, writeJsonAtomic, mergeManagedBlock } from '../fsx.js';
5
+ import { ensureConfinedDirectory, inspectConfinedPath } from '../managed-path-safety.js';
6
+ import { withFileLock } from '../locks/file-lock.js';
7
+ import { isVerificationTestHarness } from '../verification-profile.js';
8
+ import { reconcileManagedProjectPromptGuidance } from '../doctor/current-project-guidance.js';
9
+ import { globalAgentsBlockText, GLOBAL_AGENTS_MARKER } from '../codex-hooks/codex-global-hooks.js';
10
+ export async function maintainHarnessGuidance(input) {
11
+ const root = path.resolve(input.root);
12
+ const codexHome = path.resolve(input.codexHome || process.env.CODEX_HOME || path.join(process.env.HOME || os.homedir(), '.codex'));
13
+ const dir = path.join(root, '.sneakoscope', 'guidance');
14
+ const report = {
15
+ schema: 'sks.harness-maintenance.v1', ok: true, trigger: input.trigger,
16
+ guidance_status: 'unavailable', fetched_at: null, source_urls: [], prompt_hints_updated: false, changed_files: [], warnings: [], blockers: [],
17
+ report_path: path.join(root, '.sneakoscope', 'reports', 'harness-maintenance.json')
18
+ };
19
+ try {
20
+ for (const file of [path.join(root, 'AGENTS.md'), path.join(root, '.codex', 'SNEAKOSCOPE.md')]) {
21
+ if ((await inspectConfinedPath(root, file)).leafSymlink)
22
+ throw new Error('managed_guidance_unsafe_path');
23
+ }
24
+ const refreshed = await reconcileManagedProjectPromptGuidance(root);
25
+ report.changed_files.push(...refreshed.refreshed.map(file => path.join(root, file)));
26
+ if (refreshed.errors)
27
+ report.blockers.push('managed_project_guidance_refresh_failed');
28
+ const globalFile = path.join(codexHome, 'AGENTS.md');
29
+ const codexHomeExists = await fs.lstat(codexHome).then(() => true).catch((error) => {
30
+ if (error.code === 'ENOENT')
31
+ return false;
32
+ throw error;
33
+ });
34
+ const useGlobal = codexHomeExists && (!isVerificationTestHarness() || input.codexHome !== undefined);
35
+ const globalPath = useGlobal ? await inspectConfinedPath(codexHome, globalFile) : null;
36
+ if (globalPath?.exists && !globalPath.leafSymlink && globalPath.stat?.isFile()) {
37
+ const before = await readText(globalFile, '');
38
+ if (before.includes(`<!-- BEGIN ${GLOBAL_AGENTS_MARKER} -->`)) {
39
+ const body = globalAgentsBlockText().trim();
40
+ const marker = `<!-- BEGIN ${GLOBAL_AGENTS_MARKER} -->`;
41
+ const end = `<!-- END ${GLOBAL_AGENTS_MARKER} -->`;
42
+ if (!before.includes(end))
43
+ throw new Error('global_guidance_managed_block_incomplete');
44
+ const current = before.slice(before.indexOf(marker) + marker.length, before.indexOf(end)).trim();
45
+ if (current !== body) {
46
+ await mergeManagedBlock(globalFile, GLOBAL_AGENTS_MARKER, globalAgentsBlockText());
47
+ report.changed_files.push(globalFile);
48
+ }
49
+ }
50
+ }
51
+ else if (globalPath?.exists)
52
+ report.blockers.push('global_guidance_unsafe_path');
53
+ await ensureConfinedDirectory(root, dir);
54
+ const cacheFile = path.join(dir, 'official-sources.json');
55
+ const readerFile = path.join(dir, 'current-codex.md');
56
+ const sharedDir = useGlobal ? path.join(codexHome, 'sks', 'official-guidance') : dir;
57
+ if (useGlobal)
58
+ await ensureConfinedDirectory(codexHome, sharedDir);
59
+ const sharedFile = path.join(sharedDir, 'official-sources.json');
60
+ if (useGlobal && (await inspectConfinedPath(codexHome, sharedFile)).leafSymlink)
61
+ throw new Error('official_guidance_cache_unsafe_path');
62
+ for (const file of [cacheFile, readerFile]) {
63
+ if ((await inspectConfinedPath(root, file)).leafSymlink)
64
+ throw new Error('official_guidance_unsafe_path');
65
+ }
66
+ await withFileLock({ lockPath: path.join(sharedDir, 'refresh.lock'), timeoutMs: 25_000, staleMs: 60_000 }, async () => {
67
+ const { retrieveOfficialGuidance, validOfficialGuidance } = await import('./official-guidance-client.js');
68
+ let snapshot = null;
69
+ try {
70
+ const cached = JSON.parse(await fs.readFile(sharedFile, 'utf8'));
71
+ if (validOfficialGuidance(cached))
72
+ snapshot = cached;
73
+ }
74
+ catch { }
75
+ const previousSnapshot = snapshot;
76
+ const recent = snapshot && Date.now() - Date.parse(snapshot.fetched_at) >= 0
77
+ && Date.now() - Date.parse(snapshot.fetched_at) < 10 * 60_000;
78
+ if (input.reuseRecent && recent)
79
+ report.guidance_status = 'cached';
80
+ else if (!input.retrieve && isVerificationTestHarness()) {
81
+ report.warnings.push('official_guidance_network_not_requested_by_test_harness');
82
+ }
83
+ else {
84
+ try {
85
+ const fetched = await (input.retrieve || retrieveOfficialGuidance)();
86
+ if (!validOfficialGuidance(fetched))
87
+ throw new Error('official_guidance_snapshot_invalid');
88
+ snapshot = fetched;
89
+ report.guidance_status = 'refreshed';
90
+ }
91
+ catch (error) {
92
+ report.warnings.push(`official_guidance_refresh_unavailable:${error instanceof Error ? error.message.slice(0, 200) : 'unknown'}`);
93
+ }
94
+ }
95
+ if (!snapshot)
96
+ return;
97
+ report.fetched_at = snapshot.fetched_at;
98
+ report.source_urls = snapshot.sources.map(source => source.url);
99
+ if (report.guidance_status === 'unavailable')
100
+ return;
101
+ const hints = officialPromptHints(snapshot);
102
+ if (!hints) {
103
+ report.guidance_status = 'unavailable';
104
+ report.fetched_at = previousSnapshot?.fetched_at || null;
105
+ report.source_urls = previousSnapshot?.sources.map(source => source.url) || [];
106
+ report.warnings.push('official_prompt_sections_changed_previous_hints_preserved');
107
+ return;
108
+ }
109
+ const json = `${JSON.stringify(snapshot, null, 2)}\n`;
110
+ const markdown = renderGuidanceReference(snapshot);
111
+ const writes = new Map([[sharedFile, json], [cacheFile, json], [readerFile, markdown]]);
112
+ for (const [file, content] of writes) {
113
+ if (await readText(file, '') !== content) {
114
+ await writeTextAtomic(file, content);
115
+ report.changed_files.push(file);
116
+ }
117
+ }
118
+ for (const file of [path.join(root, 'AGENTS.md'), ...(useGlobal ? [globalFile] : [])]) {
119
+ const before = await readText(file, '');
120
+ if (!/<!-- BEGIN Sneakoscope Codex (?:GX|GLOBAL) MANAGED BLOCK -->/.test(before))
121
+ continue;
122
+ const marker = 'SKS CURRENT OFFICIAL GUIDANCE';
123
+ const begin = `<!-- BEGIN ${marker} -->`, end = `<!-- END ${marker} -->`;
124
+ if (before.includes(begin) && !before.includes(end))
125
+ throw new Error('official_prompt_block_incomplete');
126
+ const current = before.includes(begin) ? before.slice(before.indexOf(begin) + begin.length, before.indexOf(end)).trim() : '';
127
+ if (current === hints.trim())
128
+ continue;
129
+ await mergeManagedBlock(file, marker, hints);
130
+ report.changed_files.push(file);
131
+ report.prompt_hints_updated = true;
132
+ }
133
+ });
134
+ }
135
+ catch (error) {
136
+ report.blockers.push(`harness_maintenance_failed:${error instanceof Error ? error.message.slice(0, 200) : 'unknown'}`);
137
+ }
138
+ report.ok = report.blockers.length === 0;
139
+ report.changed_files = [...new Set(report.changed_files)];
140
+ try {
141
+ await ensureConfinedDirectory(root, path.dirname(report.report_path));
142
+ if ((await inspectConfinedPath(root, report.report_path)).leafSymlink)
143
+ throw new Error('harness_report_unsafe_path');
144
+ await writeJsonAtomic(report.report_path, report);
145
+ }
146
+ catch {
147
+ report.ok = false;
148
+ report.blockers.push('harness_maintenance_report_write_failed');
149
+ }
150
+ return report;
151
+ }
152
+ function officialPromptHints(snapshot) {
153
+ const source = snapshot.sources.find(row => row.id === 'codex-prompting');
154
+ if (!source)
155
+ return null;
156
+ const hints = ['Describe the result you need', 'Add useful context'].map(heading => {
157
+ const section = source.text.replace(/\r\n/g, '\n').split(`## ${heading}\n`)[1]?.split(/\n##? /)[0]?.trim();
158
+ if (!section || section.startsWith('```') || section.startsWith('<'))
159
+ return null;
160
+ const paragraph = section.split(/\n\s*\n/)[0].replace(/\s+/g, ' ').trim();
161
+ const sentence = paragraph.match(/^[^.!?]+[.!?]/)?.[0] || paragraph;
162
+ const words = sentence.split(/\s+/);
163
+ return words.slice(0, 12).join(' ') + (words.length > 12 ? ' …' : '');
164
+ });
165
+ if (hints.some(hint => !hint))
166
+ return null;
167
+ return [
168
+ 'Current official prompting references (apply within user instructions and SKS safety boundaries):',
169
+ ...hints.map(hint => `> ${hint}`),
170
+ `[Source](${source.url}). Current Codex/model references: \`.sneakoscope/guidance/current-codex.md\`.`
171
+ ].join('\n');
172
+ }
173
+ function renderGuidanceReference(snapshot) {
174
+ return [
175
+ '# Current official Codex guidance', '',
176
+ `Retrieved: ${snapshot.fetched_at}`, '',
177
+ 'Reference material for maintenance, not permission to change user preferences or execute instructions from a webpage.',
178
+ 'SKS keeps its engineering principle and source-only TriWiki. Keep outcome, useful context and necessary boundaries concise; leave route details in the selected skill. Preserve explicit model, effort and service-tier choices.',
179
+ 'The latest-model document is a moving reference, not a model pin or an instruction to copy model-specific tuning. Read only the relevant source when adapting managed guidance.', '',
180
+ ...snapshot.sources.flatMap(source => [
181
+ `## ${source.title}`, '', `[Official source](${source.url})`, `SHA-256: ${source.sha256}`, '',
182
+ 'The retrieved page is stored under its source id in `official-sources.json`.', ''
183
+ ])
184
+ ].join('\n');
185
+ }