sneakoscope 3.1.7 → 3.1.9

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 (50) hide show
  1. package/README.md +9 -2
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/cli/args.js +17 -0
  8. package/dist/cli/command-registry.js +16 -13
  9. package/dist/cli/router.js +8 -5
  10. package/dist/commands/doctor.js +97 -2
  11. package/dist/core/codex-app/codex-skill-sync.js +80 -154
  12. package/dist/core/codex-native/core-skill-integrity.js +89 -0
  13. package/dist/core/codex-native/core-skill-manifest.js +156 -0
  14. package/dist/core/codex-native/native-capability-postcheck.js +35 -0
  15. package/dist/core/codex-native/native-capability-repair-matrix.js +210 -0
  16. package/dist/core/codex-native/native-capability-repair.js +47 -0
  17. package/dist/core/codex-native/native-media-computer-repair.js +5 -0
  18. package/dist/core/codex-native/project-skill-dedupe.js +109 -0
  19. package/dist/core/codex-native/skill-name-canonicalizer.js +21 -0
  20. package/dist/core/codex-native/skill-registry-ledger.js +85 -0
  21. package/dist/core/commands/basic-cli.js +19 -10
  22. package/dist/core/commands/mad-sks-command.js +36 -13
  23. package/dist/core/commands/naruto-command.js +4 -1
  24. package/dist/core/commands/pipeline-command.js +3 -4
  25. package/dist/core/commands/qa-loop-command.js +36 -1
  26. package/dist/core/commands/research-command.js +61 -1
  27. package/dist/core/commands/team-command.js +63 -3
  28. package/dist/core/config/config-migration-journal.js +27 -0
  29. package/dist/core/config/managed-config-merge.js +105 -0
  30. package/dist/core/config/secret-preservation.js +169 -0
  31. package/dist/core/config/supabase-secret-preservation.js +29 -0
  32. package/dist/core/decision-contract.js +28 -4
  33. package/dist/core/doctor/command-alias-cleanup.js +64 -0
  34. package/dist/core/doctor/doctor-native-capability-repair.js +48 -0
  35. package/dist/core/feature-fixtures.js +2 -0
  36. package/dist/core/feature-registry.js +2 -2
  37. package/dist/core/fsx.js +1 -1
  38. package/dist/core/init.js +5 -1
  39. package/dist/core/naruto/naruto-work-graph.js +4 -1
  40. package/dist/core/pipeline-internals/runtime-core.js +50 -4
  41. package/dist/core/pipeline-internals/runtime-gates.js +10 -1
  42. package/dist/core/proof/route-proof-gate.js +1 -1
  43. package/dist/core/qa-loop.js +227 -11
  44. package/dist/core/questions.js +239 -2
  45. package/dist/core/routes.js +3 -4
  46. package/dist/core/version.js +1 -1
  47. package/dist/scripts/agent-native-release-gate.js +13 -4
  48. package/dist/scripts/sizecheck.js +8 -2
  49. package/dist/scripts/sks-3-1-8-check-lib.js +30 -0
  50. package/package.json +27 -1
package/README.md CHANGED
@@ -35,7 +35,14 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **3.1.7** closes Codex-native runtime-proof gaps with real routing blackboxes, neutral reference cache refresh, read-only broker checks, explicit managed-asset repair transactions, and final brand-neutral artifact scans.
38
+ SKS **3.1.9** hardens update/setup/doctor safety around four operator-facing failure modes: immutable core skills, duplicate project skills, real native capability repair status, and Supabase/secret preservation.
39
+
40
+ What changed in 3.1.8:
41
+
42
+ - **Core SKS skills are content-addressed and immutable.** The eight built-in route skills now have a manifest and no-drift gates; setup/update/doctor may install missing managed copies or restore corrupted managed copies, but they do not overwrite user-authored collisions.
43
+ - **Duplicate skills are detected and repaired safely.** Canonical skill names collapse variants such as `Loop`, `loop`, and `loop/SKILL.md`; SKS-managed duplicates can be quarantined automatically, while user-authored duplicates require explicit confirmation.
44
+ - **`sks doctor --fix` reports native capability truthfully.** Image generation, image follow-up edit paths, Computer Use, Chrome/web review, app screenshots, app handoff, and image path exposure now run through a repair matrix and postcheck instead of capability assumptions.
45
+ - **Supabase keys survive setup/update/doctor.** Protected secret surfaces are fingerprinted before and after guarded operations; reports store only redacted previews and hashes, never raw values.
39
46
 
40
47
  SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
41
48
 
@@ -622,7 +629,7 @@ TriWiki is intentionally sparse: `sks wiki sweep` records demote, soft-forget, a
622
629
 
623
630
  ```sh
624
631
  sks codex-native status --json
625
- sks codex-native invocation-plan --route '$Loop' --capability agent-role --json
632
+ sks codex-native invocation-plan --route Loop --capability agent-role --json
626
633
  sks codex-native init-deep --apply --directory-local --json
627
634
  ```
628
635
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "3.1.7"
79
+ version = "3.1.9"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "3.1.7"
3
+ version = "3.1.9"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 3.1.7"),
7
+ Some("--version") => println!("sks-rs 3.1.9"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "3.1.7",
5
- "source_digest": "8927dd50bf67d1f2ce1df5744a3b21645a870f8f9182fe7b86a92affedf416aa",
6
- "source_file_count": 2558,
7
- "built_at_source_time": 1781491235171
4
+ "package_version": "3.1.9",
5
+ "source_digest": "dbfd5ddb700abfc4a008256b36e1743363164fd504bba959542ce79c25916dad",
6
+ "source_file_count": 2605,
7
+ "built_at_source_time": 1781537952236
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '3.1.7';
2
+ const FAST_PACKAGE_VERSION = '3.1.9';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
package/dist/cli/args.js CHANGED
@@ -28,6 +28,23 @@ export function positionalArgs(args = []) {
28
28
  '--command',
29
29
  '--project-ref',
30
30
  '--agent',
31
+ '--agents',
32
+ '--target-active-slots',
33
+ '--work-items',
34
+ '--minimum-work-items',
35
+ '--max-queue-expansion',
36
+ '--concurrency',
37
+ '--backend',
38
+ '--route',
39
+ '--mission',
40
+ '--mission-id',
41
+ '--profile',
42
+ '--write-mode',
43
+ '--max-write-agents',
44
+ '--service-tier',
45
+ '--zellij-session-name',
46
+ '--worker-placement',
47
+ '--zellij-visible-pane-cap',
31
48
  '--phase',
32
49
  '--message',
33
50
  '--role',
@@ -99,21 +99,16 @@ export const COMMANDS = {
99
99
  'codex-app': entry('beta', 'Check Codex App readiness', 'dist/commands/codex-app.js', directCommand(() => import('../commands/codex-app.js'), 'dist/commands/codex-app.js')),
100
100
  'codex-native': entry('beta', 'Inspect Codex Native broker and routing readiness', 'dist/commands/codex-native.js', directCommand(() => import('../commands/codex-native.js'), 'dist/commands/codex-native.js')),
101
101
  'codex-lb': entry('beta', 'Inspect codex-lb status and circuit health', 'dist/commands/codex-lb.js', directCommand(() => import('../commands/codex-lb.js'), 'dist/commands/codex-lb.js')),
102
- auth: entry('beta', 'Alias for codex-lb auth commands', 'dist/commands/codex-lb.js', directCommand(() => import('../commands/codex-lb.js'), 'dist/commands/codex-lb.js')),
103
102
  hooks: entry('beta', 'Explain and inspect Codex hooks', 'dist/commands/hooks.js', directCommand(() => import('../commands/hooks.js'), 'dist/commands/hooks.js')),
104
103
  tmux: entry('beta', 'Show removed-runtime migration notice', 'dist/commands/tmux.js', directCommand(() => import('../commands/tmux.js'), 'dist/commands/tmux.js')),
105
104
  'zellij-lane': entry('beta', 'Render a Zellij lane frame for SKS sessions', 'dist/commands/zellij-lane.js', directCommand(() => import('../commands/zellij-lane.js'), 'dist/commands/zellij-lane.js')),
106
105
  'zellij-slot-pane': entry('beta', 'Render a compact Zellij worker slot pane', 'dist/commands/zellij-slot-pane.js', directCommand(() => import('../commands/zellij-slot-pane.js'), 'dist/commands/zellij-slot-pane.js')),
107
106
  'zellij-slot-column-anchor': entry('beta', 'Render the compact SLOTS anchor pane for first-slot-down Zellij stacks', 'dist/commands/zellij-slot-column-anchor.js', directCommand(() => import('../commands/zellij-slot-column-anchor.js'), 'dist/commands/zellij-slot-column-anchor.js')),
108
107
  zellij: entry('beta', 'Inspect Zellij runtime status and explain repair (no auto-install)', 'dist/commands/zellij.js', directCommand(() => import('../commands/zellij.js'), 'dist/commands/zellij.js')),
109
- mad: entry('beta', 'MAD-SKS Zellij permission launcher', 'dist/commands/mad-sks.js', directCommand(() => import('../commands/mad-sks.js'), 'dist/commands/mad-sks.js')),
110
108
  'mad-sks': entry('beta', 'MAD-SKS scoped permission modifier', 'dist/commands/mad-sks.js', directCommand(() => import('../commands/mad-sks.js'), 'dist/commands/mad-sks.js')),
111
109
  'mad-db': entry('beta', 'Create or inspect one-cycle Mad-DB break-glass capability tokens', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')),
112
110
  'auto-review': entry('beta', 'Manage auto-review profile', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
113
- autoreview: entry('beta', 'Alias for auto-review', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
114
111
  'dollar-commands': entry('stable', 'List Codex App dollar commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
115
- dollars: entry('stable', 'Alias for dollar-commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
116
- '$': entry('stable', 'Alias for dollar-commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
117
112
  'fast-mode': entry('stable', 'Toggle SKS Fast mode default for dollar-command routes', 'dist/core/commands/fast-mode-command.js', argsCommand(() => import('../core/commands/fast-mode-command.js'), 'fastModeCommand', 'dist/core/commands/fast-mode-command.js')),
118
113
  commit: entry('stable', 'Create a simple git commit', 'dist/commands/commit.js', directCommand(() => import('../commands/commit.js'), 'dist/commands/commit.js')),
119
114
  'commit-and-push': entry('stable', 'Create a simple git commit and push', 'dist/commands/commit-and-push.js', directCommand(() => import('../commands/commit-and-push.js'), 'dist/commands/commit-and-push.js')),
@@ -128,14 +123,9 @@ export const COMMANDS = {
128
123
  autoresearch: entry('labs', 'Alias for research/autoresearch route', 'dist/core/commands/autoresearch-command.js', subcommand(() => import('../core/commands/autoresearch-command.js'), 'autoresearchCommand', 'dist/core/commands/autoresearch-command.js', 'status')),
129
124
  ppt: entry('labs', 'Inspect/build PPT artifacts', 'dist/core/commands/ppt-command.js', commandArgsCommand(() => import('../core/commands/ppt-command.js'), 'pptCommand', 'dist/core/commands/ppt-command.js')),
130
125
  'image-ux-review': entry('labs', 'Inspect image UX artifacts', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
131
- 'ux-review': entry('labs', 'Alias for image UX review', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
132
- 'visual-review': entry('labs', 'Alias for image UX review', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
133
- 'ui-ux-review': entry('labs', 'Alias for image UX review', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
134
126
  'computer-use': entry('beta', 'Record native Mac/non-web Computer Use visual evidence', 'dist/core/commands/computer-use-command.js', commandArgsCommand(() => import('../core/commands/computer-use-command.js'), 'computerUseCommand', 'dist/core/commands/computer-use-command.js')),
135
- cu: entry('beta', 'Alias for native Computer Use', 'dist/core/commands/computer-use-command.js', commandArgsCommand(() => import('../core/commands/computer-use-command.js'), 'computerUseCommand', 'dist/core/commands/computer-use-command.js')),
136
127
  context7: entry('beta', 'Context7 checks and docs', 'dist/cli/context7-command.js', subcommand(() => import('./context7-command.js'), 'context7Command', 'dist/cli/context7-command.js', 'check')),
137
128
  xai: entry('beta', 'Set up and check xAI/Grok search MCP integration', 'dist/cli/xai-command.js', subcommand(() => import('./xai-command.js'), 'xaiCommand', 'dist/cli/xai-command.js', 'check')),
138
- grok: entry('beta', 'Alias for xAI/Grok search setup', 'dist/cli/xai-command.js', subcommand(() => import('./xai-command.js'), 'xaiCommand', 'dist/cli/xai-command.js', 'check')),
139
129
  recallpulse: entry('labs', 'RecallPulse evidence route', 'dist/commands/recallpulse.js', directCommand(() => import('../commands/recallpulse.js'), 'dist/commands/recallpulse.js')),
140
130
  pipeline: entry('beta', 'Inspect pipeline missions', 'dist/commands/pipeline.js', directCommand(() => import('../commands/pipeline.js'), 'dist/commands/pipeline.js')),
141
131
  guard: entry('beta', 'Check harness guard', 'dist/commands/guard.js', directCommand(() => import('../commands/guard.js'), 'dist/commands/guard.js')),
@@ -156,7 +146,6 @@ export const COMMANDS = {
156
146
  'skill-dream': entry('labs', 'Track skill dream counters', 'dist/core/commands/skill-dream-command.js', subcommand(() => import('../core/commands/skill-dream-command.js'), 'skillDreamCommand', 'dist/core/commands/skill-dream-command.js', 'status')),
157
147
  'code-structure': entry('labs', 'Scan source structure', 'dist/core/commands/code-structure-command.js', subcommand(() => import('../core/commands/code-structure-command.js'), 'codeStructureCommand', 'dist/core/commands/code-structure-command.js', 'scan')),
158
148
  rust: entry('beta', 'Inspect optional Rust accelerator status and smoke parity', 'dist/commands/rust.js', directCommand(() => import('../commands/rust.js'), 'dist/commands/rust.js')),
159
- memory: entry('labs', 'Run retention checks', 'dist/core/commands/gc-command.js', subcommand(() => import('../core/commands/gc-command.js'), 'memoryCommand', 'dist/core/commands/gc-command.js')),
160
149
  gx: entry('labs', 'Render/validate GX cartridges', 'dist/core/commands/gx-command.js', subcommand(() => import('../core/commands/gx-command.js'), 'gxCommand', 'dist/core/commands/gx-command.js', 'validate')),
161
150
  db: entry('beta', 'Inspect DB safety policy', 'dist/core/commands/db-command.js', subcommand(() => import('../core/commands/db-command.js'), 'dbCommand', 'dist/core/commands/db-command.js', 'policy')),
162
151
  eval: entry('labs', 'Run eval reports', 'dist/core/commands/eval-command.js', subcommand(() => import('../core/commands/eval-command.js'), 'evalCommand', 'dist/core/commands/eval-command.js', 'run')),
@@ -170,13 +159,27 @@ export const COMMANDS = {
170
159
  bench: entry('beta', 'Run core trust-kernel benchmark budgets', 'dist/core/commands/bench-command.js', argsCommand(() => import('../core/commands/bench-command.js'), 'benchCommand', 'dist/core/commands/bench-command.js'))
171
160
  };
172
161
  export const TYPED_COMMANDS = COMMANDS;
162
+ export const LEGACY_COMMAND_ALIASES = {
163
+ auth: 'codex-lb',
164
+ mad: 'mad-sks',
165
+ autoreview: 'auto-review',
166
+ dollars: 'dollar-commands',
167
+ '$': 'dollar-commands',
168
+ 'ux-review': 'image-ux-review',
169
+ 'visual-review': 'image-ux-review',
170
+ 'ui-ux-review': 'image-ux-review',
171
+ cu: 'computer-use',
172
+ grok: 'xai',
173
+ memory: 'gc'
174
+ };
173
175
  export const COMMAND_ALIASES = {
176
+ ...LEGACY_COMMAND_ALIASES,
174
177
  '--help': 'help',
175
178
  '-h': 'help',
176
179
  '--version': 'version',
177
180
  '-v': 'version',
178
- '--mad': 'mad',
179
- '--MAD': 'mad',
181
+ '--mad': 'mad-sks',
182
+ '--MAD': 'mad-sks',
180
183
  '--mad-sks': 'mad-sks',
181
184
  '--agent': 'agent',
182
185
  '--naruto': 'naruto'
@@ -5,17 +5,20 @@ export function isCommandName(value) {
5
5
  export function normalizeCommand(args = []) {
6
6
  const cmd = args[0];
7
7
  if (!cmd)
8
- return { command: null, args: [...args] };
9
- let mapped = cmd in COMMAND_ALIASES ? COMMAND_ALIASES[cmd] : cmd;
8
+ return { command: null, rawCommand: null, aliasTarget: null, args: [...args] };
9
+ const mapped = cmd in COMMAND_ALIASES ? COMMAND_ALIASES[cmd] : cmd;
10
10
  const rest = args.slice(1);
11
+ const command = isCommandName(mapped) ? mapped : null;
11
12
  return {
12
- command: isCommandName(mapped) ? mapped : null,
13
+ command,
14
+ rawCommand: cmd,
15
+ aliasTarget: command && mapped !== cmd ? command : null,
13
16
  args: rest,
14
17
  };
15
18
  }
16
19
  export async function dispatch(args) {
17
20
  const argv = args ?? process.argv.slice(2);
18
- const { command, args: rest } = normalizeCommand(argv);
21
+ const { command, rawCommand, args: rest } = normalizeCommand(argv);
19
22
  if (!command) {
20
23
  if (!argv.length) {
21
24
  const mod = await import('../commands/doctor.js');
@@ -36,6 +39,6 @@ export async function dispatch(args) {
36
39
  const mod = await entry.lazy();
37
40
  if (typeof mod.run !== 'function')
38
41
  throw new Error(`Command ${command} must export run(command, args)`);
39
- return mod.run(command, rest);
42
+ return mod.run(rawCommand || command, rest);
40
43
  }
41
44
  //# sourceMappingURL=router.js.map
@@ -27,6 +27,8 @@ import { runDoctorZellijRepair, doctorZellijRepairConsoleLine } from '../core/do
27
27
  import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-matrix.js';
28
28
  import { buildCodexNativeFeatureMatrix } from '../core/codex-native/codex-native-feature-broker.js';
29
29
  import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
30
+ import { runDoctorNativeCapabilityRepair } from '../core/doctor/doctor-native-capability-repair.js';
31
+ import { runDoctorCommandAliasCleanup } from '../core/doctor/command-alias-cleanup.js';
30
32
  export async function run(_command, args = []) {
31
33
  const doctorFix = flag(args, '--fix');
32
34
  let setupRepair = null;
@@ -69,6 +71,40 @@ export async function run(_command, args = []) {
69
71
  };
70
72
  }
71
73
  const root = await projectRoot();
74
+ const commandAliasCleanup = await runDoctorCommandAliasCleanup({
75
+ root,
76
+ fix: doctorFix
77
+ }).catch((err) => ({
78
+ schema: 'sks.command-alias-cleanup.v1',
79
+ ok: false,
80
+ status: 'blocked',
81
+ root,
82
+ fix: doctorFix,
83
+ report_path: `${root}/.sneakoscope/reports/command-alias-cleanup.json`,
84
+ canonical_command_count: 0,
85
+ legacy_alias_count: 0,
86
+ aliases: [],
87
+ detected: { registered_alias_commands: [], catalog_alias_rows: [], missing_canonical_targets: [] },
88
+ actions: [],
89
+ blockers: [err?.message || String(err)]
90
+ }));
91
+ const doctorNativeCapabilityRepair = await runDoctorNativeCapabilityRepair({
92
+ root,
93
+ fix: doctorFix || flag(args, '--repair-native-capabilities'),
94
+ yes: flag(args, '--yes') || flag(args, '-y'),
95
+ flags: args.map((arg) => String(arg))
96
+ }).catch((err) => ({
97
+ schema: 'sks.doctor-native-capability-repair.v1',
98
+ ok: false,
99
+ root,
100
+ fix: doctorFix,
101
+ yes: flag(args, '--yes') || flag(args, '-y'),
102
+ core_skills: null,
103
+ skill_dedupe: null,
104
+ native_capabilities: null,
105
+ secret_preservation_guard: '.sneakoscope/reports/secret-preservation-guard.json',
106
+ blockers: [err?.message || String(err)]
107
+ }));
72
108
  const codexBin = readOption(args, '--codex-bin', process.env.SKS_DOCTOR_CODEX_BIN || '');
73
109
  const configProbeOpts = {
74
110
  codexProbe: flag(args, '--fix') || flag(args, '--actual-codex') || Boolean(codexBin),
@@ -269,7 +305,7 @@ export async function run(_command, args = []) {
269
305
  const runtimeReadiness = buildRuntimeReadiness(zellijReadiness, codexNativeFeatureMatrix);
270
306
  const result = {
271
307
  schema: 'sks.doctor-status.v1',
272
- ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false),
308
+ ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false,
273
309
  root,
274
310
  node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
275
311
  codex,
@@ -287,6 +323,7 @@ export async function run(_command, args = []) {
287
323
  agent_role_config: agentRoleConfigRepair,
288
324
  zellij_readiness: zellijReadiness,
289
325
  codex_permission_profiles: permissionProfiles,
326
+ command_aliases: commandAliasCleanup,
290
327
  imagegen: {
291
328
  ok: imagegen.auth_readiness?.available_paths?.length > 0,
292
329
  auth_readiness: imagegen.auth_readiness || null,
@@ -305,7 +342,7 @@ export async function run(_command, args = []) {
305
342
  ready,
306
343
  sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
307
344
  package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
308
- repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, codex_native: codexNativeRepair }
345
+ repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
309
346
  };
310
347
  if (flag(args, '--json')) {
311
348
  printJson(result);
@@ -349,6 +386,30 @@ export async function run(_command, args = []) {
349
386
  for (const action of runtimeReadiness.repair_actions)
350
387
  console.log(` - ${action}`);
351
388
  }
389
+ const nativeCapabilityRows = Array.isArray(doctorNativeCapabilityRepair?.native_capabilities?.capabilities)
390
+ ? doctorNativeCapabilityRepair.native_capabilities.capabilities
391
+ : [];
392
+ console.log('SKS Native Capabilities:');
393
+ console.log(` image generation: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_generation', 'repair_required')}`);
394
+ console.log(` image follow-up edit: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_followup_edit', 'degraded')}`);
395
+ console.log(` computer use: ${nativeCapabilityStatus(nativeCapabilityRows, 'computer_use', 'manual_required')}`);
396
+ console.log(` Chrome/web review: ${nativeCapabilityStatus(nativeCapabilityRows, 'chrome_web_review', 'manual_required')}`);
397
+ console.log(` app screenshot: ${nativeCapabilityStatus(nativeCapabilityRows, 'codex_app_screenshot', 'degraded')}`);
398
+ console.log(` app handoff: ${nativeCapabilityStatus(nativeCapabilityRows, 'app_handoff', 'unavailable')}`);
399
+ console.log(` image path exposure: ${nativeCapabilityStatus(nativeCapabilityRows, 'image_path_exposure', 'fallback')}`);
400
+ console.log('SKS Skills:');
401
+ console.log(` core skills: ${doctorSkillStatus(doctorNativeCapabilityRepair?.core_skills)}`);
402
+ console.log(` duplicate project skills: ${doctorDedupeStatus(doctorNativeCapabilityRepair?.skill_dedupe)}`);
403
+ console.log('SKS Command Aliases:');
404
+ console.log(` status: ${commandAliasCleanup.status || (commandAliasCleanup.ok ? 'clean' : 'blocked')}`);
405
+ console.log(` canonical commands: ${commandAliasCleanup.canonical_command_count ?? 0}`);
406
+ console.log(` compatibility aliases: ${commandAliasCleanup.legacy_alias_count ?? 0}`);
407
+ if (commandAliasCleanup.report_path)
408
+ console.log(` report: ${commandAliasCleanup.report_path}`);
409
+ console.log('Secret preservation:');
410
+ console.log(` Supabase keys: ${doctorNativeCapabilityRepair?.ok === false && String((doctorNativeCapabilityRepair?.blockers || []).join(' ')).includes('secret_preservation_failed') ? 'blocked' : 'preserved'}`);
411
+ console.log(' secret values: redacted');
412
+ console.log(` migration journal: ${doctorNativeCapabilityRepair?.secret_preservation_guard || '.sneakoscope/reports/secret-preservation-guard.json'}`);
352
413
  console.log('Codex App Harness:');
353
414
  console.log(` plugins: ${codexAppHarnessMatrix.app_features?.plugin_json ? 'ok' : 'degraded'}`);
354
415
  console.log(` hook approval: ${codexAppHarnessMatrix.app_features?.hook_approval_state_detectable ? 'ok' : 'unknown'}`);
@@ -473,6 +534,40 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
473
534
  repair_actions: [...new Set(repairActions)]
474
535
  };
475
536
  }
537
+ function nativeCapabilityStatus(rows, id, fallback) {
538
+ const row = rows.find((entry) => entry?.id === id);
539
+ if (!row)
540
+ return fallback;
541
+ if (row.after === 'verified' || row.before === 'verified')
542
+ return 'verified';
543
+ if (row.repairability === 'manual-required')
544
+ return 'manual_required';
545
+ if (row.before === 'degraded' || row.after === 'degraded')
546
+ return 'degraded';
547
+ if (row.repairability === 'doctor-fix')
548
+ return row.after === 'blocked' ? 'blocked' : 'repair_required';
549
+ if (row.repairability === 'unavailable')
550
+ return 'unavailable';
551
+ return fallback;
552
+ }
553
+ function doctorSkillStatus(coreSkills) {
554
+ if (!coreSkills)
555
+ return 'drift_detected';
556
+ if (Array.isArray(coreSkills.restored) && coreSkills.restored.length)
557
+ return 'repaired';
558
+ if (Array.isArray(coreSkills.blockers) && coreSkills.blockers.length)
559
+ return 'drift_detected';
560
+ return 'current';
561
+ }
562
+ function doctorDedupeStatus(skillDedupe) {
563
+ if (!skillDedupe)
564
+ return 'manual_required';
565
+ if (Array.isArray(skillDedupe.actions) && skillDedupe.actions.some((action) => action.action === 'quarantined'))
566
+ return 'repaired';
567
+ if (Array.isArray(skillDedupe.blockers) && skillDedupe.blockers.length)
568
+ return 'manual_required';
569
+ return 'none';
570
+ }
476
571
  // Assemble the explicit Zellij readiness block for `doctor --json` from the
477
572
  // capability probe + readiness matrix. Proof statuses are availability-derived:
478
573
  // `verified` is reserved for a real environment run (SKS_REQUIRE_ZELLIJ=1 gates);
@@ -1,169 +1,95 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import os from 'node:os';
4
- import crypto from 'node:crypto';
5
- import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
6
- const SKS_SKILLS = [
7
- '$Loop',
8
- '$Naruto',
9
- '$QA-LOOP',
10
- '$Research',
11
- '$DFix',
12
- '$Image-UX-Review',
13
- '$Computer-Use',
14
- '$Init-Deep'
15
- ];
4
+ import { ensureDir, nowIso, writeJsonAtomic } from '../fsx.js';
5
+ import { buildSksCoreSkillManifest } from '../codex-native/core-skill-manifest.js';
6
+ import { syncCoreSkillsIntegrity } from '../codex-native/core-skill-integrity.js';
7
+ import { dedupeProjectSkills } from '../codex-native/project-skill-dedupe.js';
16
8
  const EXTERNAL_ROUTE_RESERVED = new Set(['ulw-loop', 'ulw-plan', 'start-work']);
17
9
  export async function syncCodexSksSkills(input) {
18
10
  const root = path.resolve(input.root);
19
11
  const skillsRoot = input.skillsRoot || path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'skills');
20
- const existing = await listSkillNames(skillsRoot);
21
- const collisions = existing.filter((name) => EXTERNAL_ROUTE_RESERVED.has(name));
22
- const desired = SKS_SKILLS.map((skill) => skillName(skill));
23
- const created = [];
24
- const skipped = [];
25
- if (input.apply === true) {
26
- await ensureDir(skillsRoot);
27
- for (const name of desired) {
28
- if (EXTERNAL_ROUTE_RESERVED.has(name)) {
29
- skipped.push(name);
30
- continue;
31
- }
32
- const dir = path.join(skillsRoot, name);
33
- const file = path.join(dir, 'SKILL.md');
34
- const content = skillContent(name);
35
- const current = await fs.readFile(file, 'utf8').catch(() => '');
36
- if (current && !current.includes('BEGIN SKS MANAGED SKILL')) {
37
- skipped.push(name);
38
- continue;
39
- }
40
- await ensureDir(dir);
41
- await writeTextAtomic(file, content);
42
- created.push(file);
12
+ return withSkillSyncLock(root, async () => {
13
+ const beforeExisting = await listSkillNames(skillsRoot);
14
+ const reserved = beforeExisting.filter((name) => EXTERNAL_ROUTE_RESERVED.has(name));
15
+ const manifest = buildSksCoreSkillManifest();
16
+ const desired = manifest.skills.map((skill) => skill.canonical_name);
17
+ const integrity = await syncCoreSkillsIntegrity({
18
+ root,
19
+ apply: input.apply === true,
20
+ skillsRoot,
21
+ reportPath: path.join(root, '.sneakoscope', 'reports', 'core-skill-integrity.json')
22
+ });
23
+ const dedupe = await dedupeProjectSkills({
24
+ root,
25
+ fix: input.apply === true,
26
+ yes: true,
27
+ quarantineUserDuplicates: false,
28
+ reportPath: path.join(root, '.sneakoscope', 'reports', 'project-skill-dedupe.json')
29
+ }).catch((err) => ({
30
+ ok: false,
31
+ blockers: [err instanceof Error ? err.message : String(err)],
32
+ unresolved_user_duplicates: [],
33
+ actions: []
34
+ }));
35
+ const report = {
36
+ schema: 'sks.codex-skill-sync.v1',
37
+ generated_at: nowIso(),
38
+ ok: integrity.ok && dedupe.ok !== false,
39
+ apply: input.apply === true,
40
+ skills_root: skillsRoot,
41
+ desired_skills: desired,
42
+ existing_skills: beforeExisting,
43
+ created: integrity.installed,
44
+ skipped: integrity.skipped_user_authored,
45
+ external_route_names_preserved: reserved,
46
+ integrity_report: '.sneakoscope/reports/core-skill-integrity.json',
47
+ dedupe_report: '.sneakoscope/reports/project-skill-dedupe.json',
48
+ interop: {
49
+ mode: 'coexist',
50
+ clobbered_external_routes: false,
51
+ clobbered_user_skills: false,
52
+ skipped_user_skills: integrity.skipped_user_authored,
53
+ managed_skills: desired
54
+ },
55
+ blockers: [...integrity.blockers, ...(dedupe.blockers || [])]
56
+ };
57
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-skill-sync.json'), report).catch(() => undefined);
58
+ return report;
59
+ });
60
+ }
61
+ export async function withSkillSyncLock(root, fn) {
62
+ const lockPath = path.join(path.resolve(root), '.sneakoscope', 'locks', 'skill-sync.lock');
63
+ await ensureDir(path.dirname(lockPath));
64
+ const started = Date.now();
65
+ while (true) {
66
+ try {
67
+ await fs.mkdir(lockPath);
68
+ break;
69
+ }
70
+ catch (err) {
71
+ const code = err && typeof err === 'object' && 'code' in err ? String(err.code) : '';
72
+ if (code !== 'EEXIST')
73
+ throw err;
74
+ if (Date.now() - started > 30000)
75
+ throw new Error(`Timed out waiting for skill sync lock: ${lockPath}`);
76
+ await new Promise((resolve) => setTimeout(resolve, 25));
43
77
  }
44
78
  }
45
- const report = {
46
- schema: 'sks.codex-skill-sync.v1',
47
- generated_at: nowIso(),
48
- ok: true,
49
- apply: input.apply === true,
50
- skills_root: skillsRoot,
51
- desired_skills: desired,
52
- existing_skills: existing,
53
- created,
54
- skipped,
55
- external_route_names_preserved: collisions,
56
- interop: {
57
- mode: 'coexist',
58
- clobbered_external_routes: false,
59
- clobbered_user_skills: false,
60
- skipped_user_skills: skipped,
61
- managed_skills: desired
62
- },
63
- blockers: []
64
- };
65
- await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-skill-sync.json'), report).catch(() => undefined);
66
- return report;
79
+ try {
80
+ await writeJsonAtomic(path.join(lockPath, 'owner.json'), {
81
+ schema: 'sks.skill-sync-lock.v1',
82
+ pid: process.pid,
83
+ acquired_at: nowIso()
84
+ }).catch(() => undefined);
85
+ return await fn();
86
+ }
87
+ finally {
88
+ await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);
89
+ }
67
90
  }
68
91
  async function listSkillNames(root) {
69
92
  const rows = await fs.readdir(root, { withFileTypes: true }).catch(() => []);
70
93
  return rows.filter((row) => row.isDirectory()).map((row) => row.name).sort();
71
94
  }
72
- function skillName(value) {
73
- return value.replace(/^\$/, '').toLowerCase();
74
- }
75
- function skillContent(name) {
76
- const profile = skillProfile(name);
77
- const body = [
78
- '---',
79
- `name: ${name}`,
80
- `description: SKS managed Codex App route bridge for ${profile.command}.`,
81
- '---',
82
- '',
83
- '<!-- BEGIN SKS MANAGED SKILL -->',
84
- `Command: ${profile.command}`,
85
- `Purpose: ${profile.purpose}`,
86
- `Use when: ${profile.when}`,
87
- `Route: ${profile.command}`,
88
- `Evidence: ${profile.evidence}`,
89
- 'Safety rules: keep route state bounded, preserve user and external route assets, and stop on hard blockers instead of fabricating fallback behavior.',
90
- 'Proof paths: write the route-local mission artifact named in Evidence before claiming completion.',
91
- 'Failure recovery: if a proof path cannot be produced, record the blocker and continue only when the selected SKS route has another allowed evidence path.',
92
- `Fallback: ${profile.fallback}`,
93
- `checksum: ${hash(`${name}:${profile.command}:${profile.evidence}:${profile.fallback}`)}`,
94
- '<!-- END SKS MANAGED SKILL -->',
95
- ''
96
- ].join('\n');
97
- return body;
98
- }
99
- function skillProfile(name) {
100
- const table = {
101
- loop: {
102
- command: '$Loop',
103
- purpose: 'compile persisted route work into bounded loop plans with continuation evidence.',
104
- when: 'a mission needs stage-by-stage execution, memory hints, or resume-safe artifacts.',
105
- evidence: '.sneakoscope/loops/** plus codex-app-execution-profile.json',
106
- fallback: 'use message-role routing when native agent_type is not verified.'
107
- },
108
- naruto: {
109
- command: '$Naruto',
110
- purpose: 'fan out bounded native worker lanes for high-scale review or implementation.',
111
- when: 'parallel lanes are explicitly selected by the route and parent integration remains owner.',
112
- evidence: 'naruto work graph, worker ledgers, and execution profile payloads.',
113
- fallback: 'degrade to message-role workers without dropping proof artifacts.'
114
- },
115
- 'qa-loop': {
116
- command: '$QA-LOOP',
117
- purpose: 'dogfood UI/API behavior with gate artifacts and current execution profile.',
118
- when: 'route completion needs human-proxy verification or app handoff checks.',
119
- evidence: 'qa-loop gate/result ledgers and codex-app-execution-profile.json.',
120
- fallback: 'record the unavailable surface as blocked rather than inventing visual proof.'
121
- },
122
- research: {
123
- command: '$Research',
124
- purpose: 'run evidence-bound research cycles with source routing and synthesis ledgers.',
125
- when: 'the request depends on discovery, evaluation, or external-source claims.',
126
- evidence: 'research plan, source ledger, cycle record, and execution profile routing.',
127
- fallback: 'mark unavailable source tools explicitly and avoid unsupported live-accuracy claims.'
128
- },
129
- dfix: {
130
- command: '$DFix',
131
- purpose: 'perform tiny direct fixes without the full Team route.',
132
- when: 'copy/config/docs/labels/spacing/translation/mechanical edits are truly narrow.',
133
- evidence: 'focused diff plus DFix Honest check.',
134
- fallback: 'route broad implementation through Team/Loop instead.'
135
- },
136
- 'image-ux-review': {
137
- command: '$Image-UX-Review',
138
- purpose: 'produce generated annotated UI review images and extract issue ledgers.',
139
- when: 'visual UX critique is requested from screenshots or app captures.',
140
- evidence: 'source inventory, generated annotation images, extracted issue ledger.',
141
- fallback: 'block if raster annotation cannot be produced.'
142
- },
143
- 'computer-use': {
144
- command: '$Computer-Use',
145
- purpose: 'operate native macOS desktop apps through the fast Computer Use lane.',
146
- when: 'the task depends on non-web desktop UI or OS settings.',
147
- evidence: 'desktop interaction notes/screenshots where available.',
148
- fallback: 'use Browser/Chrome only for web targets.'
149
- },
150
- 'init-deep': {
151
- command: '$Init-Deep',
152
- purpose: 'refresh project-local memory, directory AGENTS sections, and loop memory hints.',
153
- when: 'a route needs deeper local context or directory-specific instruction recall.',
154
- evidence: '.sneakoscope/context/AGENTS.generated.md and managed directory AGENTS blocks.',
155
- fallback: 'preserve user content and skip directories that cannot be safely updated.'
156
- }
157
- };
158
- return table[name] || {
159
- command: `$${name}`,
160
- purpose: 'bridge an SKS managed Codex App route.',
161
- when: 'the matching SKS route is explicitly requested.',
162
- evidence: '.sneakoscope route artifacts.',
163
- fallback: 'record blockers with evidence.'
164
- };
165
- }
166
- function hash(value) {
167
- return crypto.createHash('sha256').update(`sks-skill:${value}`).digest('hex').slice(0, 12);
168
- }
169
95
  //# sourceMappingURL=codex-skill-sync.js.map