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.
- package/README.md +9 -2
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/cli/args.js +17 -0
- package/dist/cli/command-registry.js +16 -13
- package/dist/cli/router.js +8 -5
- package/dist/commands/doctor.js +97 -2
- package/dist/core/codex-app/codex-skill-sync.js +80 -154
- package/dist/core/codex-native/core-skill-integrity.js +89 -0
- package/dist/core/codex-native/core-skill-manifest.js +156 -0
- package/dist/core/codex-native/native-capability-postcheck.js +35 -0
- package/dist/core/codex-native/native-capability-repair-matrix.js +210 -0
- package/dist/core/codex-native/native-capability-repair.js +47 -0
- package/dist/core/codex-native/native-media-computer-repair.js +5 -0
- package/dist/core/codex-native/project-skill-dedupe.js +109 -0
- package/dist/core/codex-native/skill-name-canonicalizer.js +21 -0
- package/dist/core/codex-native/skill-registry-ledger.js +85 -0
- package/dist/core/commands/basic-cli.js +19 -10
- package/dist/core/commands/mad-sks-command.js +36 -13
- package/dist/core/commands/naruto-command.js +4 -1
- package/dist/core/commands/pipeline-command.js +3 -4
- package/dist/core/commands/qa-loop-command.js +36 -1
- package/dist/core/commands/research-command.js +61 -1
- package/dist/core/commands/team-command.js +63 -3
- package/dist/core/config/config-migration-journal.js +27 -0
- package/dist/core/config/managed-config-merge.js +105 -0
- package/dist/core/config/secret-preservation.js +169 -0
- package/dist/core/config/supabase-secret-preservation.js +29 -0
- package/dist/core/decision-contract.js +28 -4
- package/dist/core/doctor/command-alias-cleanup.js +64 -0
- package/dist/core/doctor/doctor-native-capability-repair.js +48 -0
- package/dist/core/feature-fixtures.js +2 -0
- package/dist/core/feature-registry.js +2 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +5 -1
- package/dist/core/naruto/naruto-work-graph.js +4 -1
- package/dist/core/pipeline-internals/runtime-core.js +50 -4
- package/dist/core/pipeline-internals/runtime-gates.js +10 -1
- package/dist/core/proof/route-proof-gate.js +1 -1
- package/dist/core/qa-loop.js +227 -11
- package/dist/core/questions.js +239 -2
- package/dist/core/routes.js +3 -4
- package/dist/core/version.js +1 -1
- package/dist/scripts/agent-native-release-gate.js +13 -4
- package/dist/scripts/sizecheck.js +8 -2
- package/dist/scripts/sks-3-1-8-check-lib.js +30 -0
- 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.
|
|
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
|
|
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
|
|
|
@@ -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
|
+
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.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
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
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'
|
package/dist/cli/router.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|