sneakoscope 3.1.6 → 3.1.8
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 +10 -3
- 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/commands/codex-app.js +20 -2
- package/dist/commands/codex-native.js +18 -2
- package/dist/commands/doctor.js +106 -12
- package/dist/core/codex-app/codex-agent-role-sync.js +9 -5
- package/dist/core/codex-app/codex-init-deep.js +6 -2
- package/dist/core/codex-app/codex-skill-sync.js +80 -152
- package/dist/core/codex-control/codex-0138-capability.js +5 -2
- package/dist/core/codex-native/codex-native-feature-broker.js +74 -6
- package/dist/core/codex-native/codex-native-pattern-analysis.js +14 -2
- package/dist/core/codex-native/codex-native-reference-cache.js +98 -0
- package/dist/core/codex-native/codex-native-reference-source.js +50 -11
- package/dist/core/codex-native/codex-native-repair-transaction.js +150 -0
- 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/codex-plugins/codex-plugin-json.js +5 -2
- package/dist/core/commands/basic-cli.js +15 -9
- package/dist/core/commands/mad-sks-command.js +16 -0
- 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/doctor/doctor-native-capability-repair.js +48 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +5 -1
- package/dist/core/loops/loop-planner.js +1 -1
- package/dist/core/loops/loop-worker-prompts.js +2 -0
- package/dist/core/loops/loop-worker-runtime.js +8 -1
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-native-runtime-e2e-fixture.js +75 -0
- package/dist/scripts/loop-worker-fixture-child.js +2 -1
- package/dist/scripts/sizecheck.js +8 -2
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +1 -1
- package/dist/scripts/sks-3-1-6-directive-check-lib.js +2 -2
- package/dist/scripts/sks-3-1-7-directive-check-lib.js +58 -0
- package/dist/scripts/sks-3-1-8-check-lib.js +30 -0
- package/package.json +39 -2
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
5
|
+
import { ensureCodexNativeReferenceSnapshot } from './codex-native-reference-cache.js';
|
|
5
6
|
const PATTERNS = [
|
|
6
7
|
{ id: 'no-global-optional-tooling', claim: 'optional tooling avoids mandatory global install', re: /\bnpx\b|no[- ]global|global install/i },
|
|
7
8
|
{ id: 'plugin-lifecycle-state-separation', claim: 'plugin install state is separated from approval/readiness', re: /\bplugin\b.+\b(install|enable|marketplace|lifecycle)\b/i },
|
|
@@ -18,7 +19,26 @@ const PATTERNS = [
|
|
|
18
19
|
];
|
|
19
20
|
export async function analyzeCodexNativeReferenceSource(input) {
|
|
20
21
|
const root = path.resolve(input.root);
|
|
21
|
-
const
|
|
22
|
+
const cacheInput = input.sourceRef ? { root, ref: input.sourceRef } : { root };
|
|
23
|
+
const cache = input.sourceDir
|
|
24
|
+
? null
|
|
25
|
+
: await ensureCodexNativeReferenceSnapshot(cacheInput).catch((err) => ({
|
|
26
|
+
schema: 'sks.codex-native-reference-cache.v1',
|
|
27
|
+
generated_at: nowIso(),
|
|
28
|
+
ok: false,
|
|
29
|
+
cache_dir: '.sneakoscope/cache/codex-native-reference',
|
|
30
|
+
source_url_hash: null,
|
|
31
|
+
source_ref: input.sourceRef || 'HEAD',
|
|
32
|
+
source_sha: null,
|
|
33
|
+
refreshed: false,
|
|
34
|
+
offline: true,
|
|
35
|
+
blockers: [messageOf(err)],
|
|
36
|
+
warnings: ['reference_cache_exception']
|
|
37
|
+
}));
|
|
38
|
+
const sourceDir = input.sourceDir
|
|
39
|
+
? path.resolve(input.sourceDir)
|
|
40
|
+
: path.join(root, cache?.cache_dir || '.sneakoscope/cache/codex-native-reference');
|
|
41
|
+
const confidence = input.sourceDir ? 'high' : cache?.ok ? cache.refreshed ? 'high' : 'medium' : 'low';
|
|
22
42
|
const files = await listTextFiles(sourceDir);
|
|
23
43
|
const evidence = [];
|
|
24
44
|
const blockers = [];
|
|
@@ -26,7 +46,7 @@ export async function analyzeCodexNativeReferenceSource(input) {
|
|
|
26
46
|
const rel = path.relative(sourceDir, file).split(path.sep).join('/');
|
|
27
47
|
const text = await fs.readFile(file, 'utf8').catch(() => '');
|
|
28
48
|
if (text)
|
|
29
|
-
evidence.push(...extractCodexNativeEvidence(rel, text));
|
|
49
|
+
evidence.push(...extractCodexNativeEvidence(rel, text).map((row) => ({ ...row, confidence })));
|
|
30
50
|
}
|
|
31
51
|
if (!files.length || !evidence.length)
|
|
32
52
|
blockers.push('source_snapshot_missing');
|
|
@@ -34,11 +54,14 @@ export async function analyzeCodexNativeReferenceSource(input) {
|
|
|
34
54
|
schema: 'sks.codex-native-reference-evidence.v1',
|
|
35
55
|
generated_at: nowIso(),
|
|
36
56
|
source_kind: 'external-reference-source',
|
|
37
|
-
source_ref: input
|
|
38
|
-
source_sha: await gitSha(sourceDir),
|
|
57
|
+
source_ref: neutralSourceRef(input, cache, sourceDir),
|
|
58
|
+
source_sha: cache?.source_sha || await gitSha(sourceDir),
|
|
59
|
+
source_url_hash: cache?.source_url_hash || null,
|
|
60
|
+
cache_report_path: cache ? '.sneakoscope/reports/codex-native-reference-cache.json' : null,
|
|
61
|
+
cache,
|
|
39
62
|
evidence,
|
|
40
|
-
blockers,
|
|
41
|
-
warnings: blockers.length ? ['reference_evidence_incomplete'] : []
|
|
63
|
+
blockers: [...new Set([...blockers, ...(cache?.blockers || [])])],
|
|
64
|
+
warnings: [...new Set([...(cache?.warnings || []), ...(blockers.length ? ['reference_evidence_incomplete'] : [])])]
|
|
42
65
|
};
|
|
43
66
|
if (input.writeReport !== false) {
|
|
44
67
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-reference-evidence.json'), report).catch(() => undefined);
|
|
@@ -74,6 +97,8 @@ export function renderCodexNativeReferenceMarkdown(report) {
|
|
|
74
97
|
'',
|
|
75
98
|
`Generated at: \`${report.generated_at}\``,
|
|
76
99
|
`Source kind: \`${report.source_kind}\``,
|
|
100
|
+
`Source URL hash: \`${report.source_url_hash || 'none'}\``,
|
|
101
|
+
`Source SHA: \`${report.source_sha || 'none'}\``,
|
|
77
102
|
'',
|
|
78
103
|
'| Pattern | File | Lines | Snippet Hash | Confidence |',
|
|
79
104
|
'|---|---|---:|---|---|',
|
|
@@ -81,21 +106,32 @@ export function renderCodexNativeReferenceMarkdown(report) {
|
|
|
81
106
|
''
|
|
82
107
|
].join('\n');
|
|
83
108
|
}
|
|
109
|
+
function neutralSourceRef(input, cache, sourceDir) {
|
|
110
|
+
if (cache)
|
|
111
|
+
return `cache:${createHash('sha256').update(`${cache.cache_dir}:${cache.source_ref}:${cache.source_sha || ''}`).digest('hex').slice(0, 16)}`;
|
|
112
|
+
if (input.sourceRef)
|
|
113
|
+
return `explicit:${createHash('sha256').update(input.sourceRef).digest('hex').slice(0, 16)}`;
|
|
114
|
+
return `explicit-source-dir:${createHash('sha256').update(sourceDir).digest('hex').slice(0, 16)}`;
|
|
115
|
+
}
|
|
84
116
|
async function listTextFiles(dir) {
|
|
85
117
|
const out = [];
|
|
86
|
-
await walk(dir, out);
|
|
87
|
-
return out
|
|
118
|
+
await walk(dir, out, 500);
|
|
119
|
+
return out;
|
|
88
120
|
}
|
|
89
|
-
async function walk(dir, out) {
|
|
121
|
+
async function walk(dir, out, maxFiles) {
|
|
122
|
+
if (out.length >= maxFiles)
|
|
123
|
+
return;
|
|
90
124
|
const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
91
125
|
for (const row of rows) {
|
|
126
|
+
if (out.length >= maxFiles)
|
|
127
|
+
return;
|
|
92
128
|
const full = path.join(dir, row.name);
|
|
93
129
|
if (row.isDirectory()) {
|
|
94
130
|
if (['.git', 'node_modules', 'dist'].includes(row.name))
|
|
95
131
|
continue;
|
|
96
|
-
await walk(full, out);
|
|
132
|
+
await walk(full, out, maxFiles);
|
|
97
133
|
}
|
|
98
|
-
else if (row.isFile()) {
|
|
134
|
+
else if (row.isFile() && /\.(md|txt|json|toml|ya?ml|js|ts|mjs|cjs)$/i.test(row.name)) {
|
|
99
135
|
out.push(full);
|
|
100
136
|
}
|
|
101
137
|
}
|
|
@@ -107,4 +143,7 @@ async function gitSha(sourceDir) {
|
|
|
107
143
|
return (await fs.readFile(path.join(sourceDir, '.git', ref), 'utf8').catch(() => '')).trim() || null;
|
|
108
144
|
return /^[0-9a-f]{40}$/i.test(head.trim()) ? head.trim() : null;
|
|
109
145
|
}
|
|
146
|
+
function messageOf(err) {
|
|
147
|
+
return err instanceof Error ? err.message : String(err);
|
|
148
|
+
}
|
|
110
149
|
//# sourceMappingURL=codex-native-reference-source.js.map
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { buildCodexHookLifecycle } from '../codex-app/codex-hook-lifecycle.js';
|
|
3
|
+
import { runCodexInitDeep } from '../codex-app/codex-init-deep.js';
|
|
4
|
+
import { syncCodexAgentRoles } from '../codex-app/codex-agent-role-sync.js';
|
|
5
|
+
import { syncCodexSksSkills } from '../codex-app/codex-skill-sync.js';
|
|
6
|
+
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
7
|
+
import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
|
|
8
|
+
import { evaluateMutation, mutationLedgerPath, recordMutation } from '../safety/mutation-ledger.js';
|
|
9
|
+
export async function repairCodexNativeManagedAssets(input) {
|
|
10
|
+
const root = path.resolve(input.root);
|
|
11
|
+
const requested = {
|
|
12
|
+
skills: input.repairSkills !== false,
|
|
13
|
+
agent_roles: input.repairAgentRoles !== false,
|
|
14
|
+
hooks: input.repairHooks !== false,
|
|
15
|
+
project_memory: input.repairProjectMemory !== false
|
|
16
|
+
};
|
|
17
|
+
const requestedAssets = Object.entries(requested)
|
|
18
|
+
.filter(([, enabled]) => enabled)
|
|
19
|
+
.map(([asset]) => asset);
|
|
20
|
+
if (input.yes !== true) {
|
|
21
|
+
const report = {
|
|
22
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
23
|
+
ok: false,
|
|
24
|
+
generated_at: nowIso(),
|
|
25
|
+
requested_by: input.requestedBy,
|
|
26
|
+
repaired: requestedAssets.map((asset) => ({
|
|
27
|
+
asset,
|
|
28
|
+
ok: false,
|
|
29
|
+
changed: false,
|
|
30
|
+
artifact_path: artifactPathFor(asset),
|
|
31
|
+
blockers: ['repair_transaction_requires_yes']
|
|
32
|
+
})),
|
|
33
|
+
confirmed: false,
|
|
34
|
+
mutation_ledger_path: null,
|
|
35
|
+
blockers: ['repair_transaction_requires_yes'],
|
|
36
|
+
warnings: []
|
|
37
|
+
};
|
|
38
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-repair-transaction.json'), report).catch(() => undefined);
|
|
39
|
+
return report;
|
|
40
|
+
}
|
|
41
|
+
const rows = [];
|
|
42
|
+
if (requested.skills) {
|
|
43
|
+
const report = await syncCodexSksSkills({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
44
|
+
rows.push({
|
|
45
|
+
asset: 'skills',
|
|
46
|
+
ok: recordOk(report) !== false,
|
|
47
|
+
changed: listLength(report, 'created') > 0,
|
|
48
|
+
artifact_path: '.sneakoscope/reports/codex-skill-sync.json',
|
|
49
|
+
blockers: blockersOf(report)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (requested.agent_roles) {
|
|
53
|
+
const report = await syncCodexAgentRoles({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
54
|
+
rows.push({
|
|
55
|
+
asset: 'agent_roles',
|
|
56
|
+
ok: recordOk(report) !== false,
|
|
57
|
+
changed: listLength(report, 'created') > 0,
|
|
58
|
+
artifact_path: '.sneakoscope/reports/codex-agent-role-sync.json',
|
|
59
|
+
blockers: blockersOf(report)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (requested.hooks) {
|
|
63
|
+
const report = await buildCodexHookLifecycle({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
64
|
+
rows.push({
|
|
65
|
+
asset: 'hooks',
|
|
66
|
+
ok: recordOk(report) !== false,
|
|
67
|
+
changed: recordOk(report) !== false,
|
|
68
|
+
artifact_path: '.sneakoscope/reports/codex-hook-lifecycle.json',
|
|
69
|
+
blockers: blockersOf(report)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (requested.project_memory) {
|
|
73
|
+
const report = await runCodexInitDeep({ root, apply: true, directoryLocal: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
74
|
+
rows.push({
|
|
75
|
+
asset: 'project_memory',
|
|
76
|
+
ok: recordOk(report) !== false,
|
|
77
|
+
changed: listLength(report, 'directory_local_agents.created') + listLength(report, 'directory_local_agents.updated') > 0,
|
|
78
|
+
artifact_path: '.sneakoscope/reports/codex-init-deep.json',
|
|
79
|
+
blockers: blockersOf(report)
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const blockers = rows.flatMap((row) => row.blockers);
|
|
83
|
+
const mutationWarnings = await recordRepairMutations(root, rows);
|
|
84
|
+
const report = {
|
|
85
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
86
|
+
ok: rows.every((row) => row.ok) && blockers.length === 0,
|
|
87
|
+
generated_at: nowIso(),
|
|
88
|
+
requested_by: input.requestedBy,
|
|
89
|
+
repaired: rows,
|
|
90
|
+
confirmed: true,
|
|
91
|
+
mutation_ledger_path: path.relative(root, mutationLedgerPath(root)).split(path.sep).join('/'),
|
|
92
|
+
blockers: [...new Set(blockers)],
|
|
93
|
+
warnings: mutationWarnings
|
|
94
|
+
};
|
|
95
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-repair-transaction.json'), report).catch(() => undefined);
|
|
96
|
+
return report;
|
|
97
|
+
}
|
|
98
|
+
async function recordRepairMutations(root, rows) {
|
|
99
|
+
const contract = createRequestedScopeContract({
|
|
100
|
+
route: 'codex-native-repair-transaction',
|
|
101
|
+
userRequest: 'Repair SKS-managed Codex Native assets after explicit --yes confirmation.',
|
|
102
|
+
projectRoot: root,
|
|
103
|
+
overrides: { global_codex_config: true }
|
|
104
|
+
});
|
|
105
|
+
const warnings = [];
|
|
106
|
+
for (const row of rows) {
|
|
107
|
+
const kind = row.asset === 'project_memory' ? 'file_write' : 'global_config_write';
|
|
108
|
+
const entry = evaluateMutation(contract, kind, {
|
|
109
|
+
target: row.asset,
|
|
110
|
+
confirmed: true,
|
|
111
|
+
applied: row.changed,
|
|
112
|
+
noOpReason: row.changed ? 'sks_managed_asset_transaction_recorded' : 'repair_noop'
|
|
113
|
+
});
|
|
114
|
+
await recordMutation(root, entry).catch((err) => warnings.push(`mutation_ledger_write_failed:${messageOf(err)}`));
|
|
115
|
+
}
|
|
116
|
+
return warnings;
|
|
117
|
+
}
|
|
118
|
+
function artifactPathFor(asset) {
|
|
119
|
+
return asset === 'skills'
|
|
120
|
+
? '.sneakoscope/reports/codex-skill-sync.json'
|
|
121
|
+
: asset === 'agent_roles'
|
|
122
|
+
? '.sneakoscope/reports/codex-agent-role-sync.json'
|
|
123
|
+
: asset === 'hooks'
|
|
124
|
+
? '.sneakoscope/reports/codex-hook-lifecycle.json'
|
|
125
|
+
: '.sneakoscope/reports/codex-init-deep.json';
|
|
126
|
+
}
|
|
127
|
+
function recordOk(value) {
|
|
128
|
+
return Boolean(value) && typeof value === 'object' && typeof value.ok === 'boolean'
|
|
129
|
+
? value.ok
|
|
130
|
+
: undefined;
|
|
131
|
+
}
|
|
132
|
+
function blockersOf(value) {
|
|
133
|
+
return Boolean(value) && typeof value === 'object' && Array.isArray(value.blockers)
|
|
134
|
+
? value.blockers.map((item) => String(item)).filter(Boolean)
|
|
135
|
+
: [];
|
|
136
|
+
}
|
|
137
|
+
function listLength(value, key) {
|
|
138
|
+
const parts = key.split('.');
|
|
139
|
+
let current = value;
|
|
140
|
+
for (const part of parts) {
|
|
141
|
+
if (!current || typeof current !== 'object')
|
|
142
|
+
return 0;
|
|
143
|
+
current = current[part];
|
|
144
|
+
}
|
|
145
|
+
return Array.isArray(current) ? current.length : 0;
|
|
146
|
+
}
|
|
147
|
+
function messageOf(err) {
|
|
148
|
+
return err instanceof Error ? err.message : String(err);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=codex-native-repair-transaction.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ensureDir, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
import { buildSksCoreSkillManifest, isSksManagedCoreSkillContent, renderCoreSkillTemplate } from './core-skill-manifest.js';
|
|
5
|
+
import { canonicalSkillName } from './skill-name-canonicalizer.js';
|
|
6
|
+
export async function syncCoreSkillsIntegrity(input) {
|
|
7
|
+
const root = path.resolve(input.root);
|
|
8
|
+
const skillsRoot = input.skillsRoot || path.join(root, '.agents', 'skills');
|
|
9
|
+
const apply = input.apply === true;
|
|
10
|
+
const manifest = buildSksCoreSkillManifest();
|
|
11
|
+
const rows = [];
|
|
12
|
+
const installed = [];
|
|
13
|
+
const restored = [];
|
|
14
|
+
const skippedUserAuthored = [];
|
|
15
|
+
const blockers = [];
|
|
16
|
+
for (const skill of manifest.skills) {
|
|
17
|
+
const skillDir = path.join(skillsRoot, skill.canonical_name);
|
|
18
|
+
const file = path.join(skillDir, 'SKILL.md');
|
|
19
|
+
const desired = renderCoreSkillTemplate(skill.canonical_name);
|
|
20
|
+
const current = await readText(file, null);
|
|
21
|
+
const beforeSha = typeof current === 'string' ? sha256(current) : null;
|
|
22
|
+
let action = 'already-current';
|
|
23
|
+
let backupPath = null;
|
|
24
|
+
let blocker = null;
|
|
25
|
+
if (current === null) {
|
|
26
|
+
action = 'install-missing-managed-copy';
|
|
27
|
+
if (apply) {
|
|
28
|
+
await ensureDir(skillDir);
|
|
29
|
+
await writeTextAtomic(file, desired);
|
|
30
|
+
installed.push(file);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (beforeSha === skill.content_sha256) {
|
|
34
|
+
action = 'already-current';
|
|
35
|
+
}
|
|
36
|
+
else if (isSksManagedCoreSkillContent(current)) {
|
|
37
|
+
action = 'restore-corrupted-managed-copy';
|
|
38
|
+
if (apply) {
|
|
39
|
+
backupPath = path.join(root, '.sneakoscope', 'backups', 'core-skills', skill.canonical_name, `${Date.now()}-${process.pid}.SKILL.md.bak`);
|
|
40
|
+
await ensureDir(path.dirname(backupPath));
|
|
41
|
+
await fs.writeFile(backupPath, current, 'utf8');
|
|
42
|
+
await writeTextAtomic(file, desired);
|
|
43
|
+
restored.push(file);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
action = 'skip-user-authored';
|
|
48
|
+
blocker = `user_authored_core_skill_collision:${skill.canonical_name}`;
|
|
49
|
+
skippedUserAuthored.push(file);
|
|
50
|
+
}
|
|
51
|
+
const after = await readText(file, null);
|
|
52
|
+
const afterSha = typeof after === 'string' ? sha256(after) : null;
|
|
53
|
+
rows.push({
|
|
54
|
+
canonical_name: skill.canonical_name,
|
|
55
|
+
path: file,
|
|
56
|
+
action,
|
|
57
|
+
before_sha256: beforeSha,
|
|
58
|
+
after_sha256: afterSha,
|
|
59
|
+
backup_path: backupPath,
|
|
60
|
+
blocker
|
|
61
|
+
});
|
|
62
|
+
if (blocker)
|
|
63
|
+
blockers.push(blocker);
|
|
64
|
+
}
|
|
65
|
+
const report = {
|
|
66
|
+
schema: 'sks.core-skill-integrity.v1',
|
|
67
|
+
generated_at: nowIso(),
|
|
68
|
+
ok: blockers.length === 0,
|
|
69
|
+
root,
|
|
70
|
+
apply,
|
|
71
|
+
skills_root: skillsRoot,
|
|
72
|
+
manifest_sha256: sha256(JSON.stringify(manifest.skills.map((skill) => [skill.canonical_name, skill.content_sha256]))),
|
|
73
|
+
rows,
|
|
74
|
+
installed,
|
|
75
|
+
restored,
|
|
76
|
+
skipped_user_authored: skippedUserAuthored,
|
|
77
|
+
blockers
|
|
78
|
+
};
|
|
79
|
+
const reportPath = input.reportPath === null
|
|
80
|
+
? null
|
|
81
|
+
: input.reportPath || path.join(root, '.sneakoscope', 'reports', 'core-skill-integrity.json');
|
|
82
|
+
if (reportPath)
|
|
83
|
+
await writeJsonAtomic(reportPath, report).catch(() => undefined);
|
|
84
|
+
return report;
|
|
85
|
+
}
|
|
86
|
+
export function coreSkillPath(skillsRoot, name) {
|
|
87
|
+
return path.join(skillsRoot, canonicalSkillName(name), 'SKILL.md');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=core-skill-integrity.js.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { PACKAGE_VERSION, nowIso, sha256 } from '../fsx.js';
|
|
2
|
+
import { canonicalSkillName } from './skill-name-canonicalizer.js';
|
|
3
|
+
export const CORE_SKILL_TEMPLATE_VERSION = '3.1.8-core-skill-template.v1';
|
|
4
|
+
export const CORE_SKILL_MANAGED_BEGIN = '<!-- BEGIN SKS IMMUTABLE CORE SKILL -->';
|
|
5
|
+
export const CORE_SKILL_MANAGED_END = '<!-- END SKS IMMUTABLE CORE SKILL -->';
|
|
6
|
+
const CORE_SKILL_DEFINITIONS = [
|
|
7
|
+
{
|
|
8
|
+
id: 'sks-core-loop',
|
|
9
|
+
canonical_name: 'loop',
|
|
10
|
+
display_name: 'loop',
|
|
11
|
+
route: '$Loop',
|
|
12
|
+
purpose: 'compile persisted route work into bounded loop plans with continuation evidence.',
|
|
13
|
+
when: 'Use for resumable route stages, memory hints, and loop mission artifacts.',
|
|
14
|
+
evidence: '.sneakoscope/loops/** plus route-local proof artifacts.',
|
|
15
|
+
fallback: 'Record the unavailable surface as blocked; do not fabricate a loop proof.'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'sks-core-naruto',
|
|
19
|
+
canonical_name: 'naruto',
|
|
20
|
+
display_name: 'naruto',
|
|
21
|
+
route: '$Naruto',
|
|
22
|
+
purpose: 'fan out bounded native worker lanes while parent integration remains owner.',
|
|
23
|
+
when: 'Use when the selected route explicitly requires high-scale parallel review or implementation.',
|
|
24
|
+
evidence: 'agent task graph, worker ledgers, leases, proof evidence, and cleanup artifacts.',
|
|
25
|
+
fallback: 'Degrade to parent-owned execution with blockers recorded if native lanes are unavailable.'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'sks-core-qa-loop',
|
|
29
|
+
canonical_name: 'qa-loop',
|
|
30
|
+
display_name: 'qa-loop',
|
|
31
|
+
route: '$QA-LOOP',
|
|
32
|
+
purpose: 'dogfood UI/API behavior with safety gates and QA reports.',
|
|
33
|
+
when: 'Use when route completion needs human-proxy verification, rechecks, and QA ledgers.',
|
|
34
|
+
evidence: 'qa-ledger.json, dated QA report, qa-gate.json, and post-fix verification.',
|
|
35
|
+
fallback: 'Mark unverified browser/native surfaces explicitly; never substitute fake visual evidence.'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'sks-core-research',
|
|
39
|
+
canonical_name: 'research',
|
|
40
|
+
display_name: 'research',
|
|
41
|
+
route: '$Research',
|
|
42
|
+
purpose: 'run evidence-bound discovery, source ledgers, and synthesis cycles.',
|
|
43
|
+
when: 'Use for discovery, evaluation, external-source claims, or frontier-style research.',
|
|
44
|
+
evidence: 'research plan, source ledger, cycle record, synthesis, and final review.',
|
|
45
|
+
fallback: 'State source/tool unavailability and avoid unsupported live-accuracy claims.'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'sks-core-dfix',
|
|
49
|
+
canonical_name: 'dfix',
|
|
50
|
+
display_name: 'dfix',
|
|
51
|
+
route: '$DFix',
|
|
52
|
+
purpose: 'perform tiny direct fixes with cheap verification.',
|
|
53
|
+
when: 'Use only for narrow copy/config/docs/labels/spacing/translation/mechanical edits.',
|
|
54
|
+
evidence: 'focused diff and DFix Honest check.',
|
|
55
|
+
fallback: 'Escalate broad implementation to a full execution route.'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'sks-core-image-ux-review',
|
|
59
|
+
canonical_name: 'image-ux-review',
|
|
60
|
+
display_name: 'image-ux-review',
|
|
61
|
+
route: '$Image-UX-Review',
|
|
62
|
+
purpose: 'produce generated annotated UI review images and extract issue ledgers.',
|
|
63
|
+
when: 'Use for screenshot/UI UX review requests that require generated raster evidence.',
|
|
64
|
+
evidence: 'source inventory, generated annotation image ledger, issue ledger, iteration report.',
|
|
65
|
+
fallback: 'Block full verification if generated annotated images cannot be produced.'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'sks-core-computer-use',
|
|
69
|
+
canonical_name: 'computer-use',
|
|
70
|
+
display_name: 'computer-use',
|
|
71
|
+
route: '$Computer-Use',
|
|
72
|
+
purpose: 'operate native macOS desktop apps through Codex Computer Use.',
|
|
73
|
+
when: 'Use only for native Mac/non-web app or OS-setting surfaces.',
|
|
74
|
+
evidence: 'native desktop interaction evidence where live Computer Use is available.',
|
|
75
|
+
fallback: 'Do not use Computer Use as browser/web evidence; mark unavailable surfaces unverified.'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'sks-core-init-deep',
|
|
79
|
+
canonical_name: 'init-deep',
|
|
80
|
+
display_name: 'init-deep',
|
|
81
|
+
route: '$Init-Deep',
|
|
82
|
+
purpose: 'refresh project-local memory, directory rules, and loop memory hints.',
|
|
83
|
+
when: 'Use when deeper local context or directory-specific recall is required.',
|
|
84
|
+
evidence: '.sneakoscope/context/AGENTS.generated.md and managed memory artifacts.',
|
|
85
|
+
fallback: 'Preserve user content and skip directories that cannot be safely updated.'
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
export function coreSkillDefinitions() {
|
|
89
|
+
return CORE_SKILL_DEFINITIONS;
|
|
90
|
+
}
|
|
91
|
+
export function isCoreSkillName(name) {
|
|
92
|
+
const canonical = canonicalSkillName(name);
|
|
93
|
+
return CORE_SKILL_DEFINITIONS.some((skill) => skill.canonical_name === canonical);
|
|
94
|
+
}
|
|
95
|
+
export function renderCoreSkillTemplate(name) {
|
|
96
|
+
const canonical = canonicalSkillName(name);
|
|
97
|
+
const skill = CORE_SKILL_DEFINITIONS.find((entry) => entry.canonical_name === canonical);
|
|
98
|
+
if (!skill)
|
|
99
|
+
throw new Error(`Unknown SKS core skill: ${name}`);
|
|
100
|
+
return [
|
|
101
|
+
'---',
|
|
102
|
+
`name: ${skill.display_name}`,
|
|
103
|
+
`description: Immutable SKS core Codex App route bridge for ${skill.route}.`,
|
|
104
|
+
'---',
|
|
105
|
+
'',
|
|
106
|
+
CORE_SKILL_MANAGED_BEGIN,
|
|
107
|
+
`id: ${skill.id}`,
|
|
108
|
+
`canonical_name: ${skill.canonical_name}`,
|
|
109
|
+
`route: ${skill.route}`,
|
|
110
|
+
`template_version: ${CORE_SKILL_TEMPLATE_VERSION}`,
|
|
111
|
+
'mutable_by_doctor: false',
|
|
112
|
+
'mutable_by_update: false',
|
|
113
|
+
'mutable_by_setup: false',
|
|
114
|
+
CORE_SKILL_MANAGED_END,
|
|
115
|
+
'',
|
|
116
|
+
`Route: ${skill.route}`,
|
|
117
|
+
`Command: ${skill.route}`,
|
|
118
|
+
`Purpose: ${skill.purpose}`,
|
|
119
|
+
`Use when: ${skill.when}`,
|
|
120
|
+
`Proof paths: ${skill.evidence}`,
|
|
121
|
+
'Safety rules: preserve user-authored skills, keep route state bounded, and stop on hard blockers instead of fabricating fallback behavior.',
|
|
122
|
+
`Failure recovery: ${skill.fallback}`,
|
|
123
|
+
''
|
|
124
|
+
].join('\n');
|
|
125
|
+
}
|
|
126
|
+
export function buildSksCoreSkillManifest(generatedAt = nowIso()) {
|
|
127
|
+
return {
|
|
128
|
+
schema: 'sks.core-skill-manifest.v1',
|
|
129
|
+
generated_at: generatedAt,
|
|
130
|
+
package_version: PACKAGE_VERSION,
|
|
131
|
+
skills: CORE_SKILL_DEFINITIONS.map((skill) => {
|
|
132
|
+
const content = renderCoreSkillTemplate(skill.canonical_name);
|
|
133
|
+
return {
|
|
134
|
+
id: skill.id,
|
|
135
|
+
canonical_name: skill.canonical_name,
|
|
136
|
+
display_name: skill.display_name,
|
|
137
|
+
route: skill.route,
|
|
138
|
+
relative_path: `.agents/skills/${skill.canonical_name}/SKILL.md`,
|
|
139
|
+
template_version: CORE_SKILL_TEMPLATE_VERSION,
|
|
140
|
+
content_sha256: sha256(content),
|
|
141
|
+
mutable_by_doctor: false,
|
|
142
|
+
mutable_by_update: false,
|
|
143
|
+
mutable_by_setup: false
|
|
144
|
+
};
|
|
145
|
+
})
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function coreSkillTemplateByCanonicalName(name) {
|
|
149
|
+
const canonical = canonicalSkillName(name);
|
|
150
|
+
return buildSksCoreSkillManifest('1970-01-01T00:00:00.000Z').skills.find((skill) => skill.canonical_name === canonical) || null;
|
|
151
|
+
}
|
|
152
|
+
export function isSksManagedCoreSkillContent(text) {
|
|
153
|
+
const value = String(text || '');
|
|
154
|
+
return value.includes(CORE_SKILL_MANAGED_BEGIN) && value.includes(CORE_SKILL_MANAGED_END);
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=core-skill-manifest.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { buildNativeCapabilityRepairMatrix } from './native-capability-repair-matrix.js';
|
|
4
|
+
export async function postcheckNativeCapabilities(input) {
|
|
5
|
+
const root = path.resolve(input.root);
|
|
6
|
+
const matrix = input.matrix || await buildNativeCapabilityRepairMatrix({ root, fixture: input.fixture || false, reportPath: null });
|
|
7
|
+
const capabilities = matrix.capabilities.map((state) => {
|
|
8
|
+
const verifiedAfterRepair = state.repairability === 'auto' || state.repairability === 'doctor-fix';
|
|
9
|
+
if (state.id === 'computer_use' && process.env.SKS_COMPUTER_USE_CAPABILITY !== 'verified') {
|
|
10
|
+
return { ...state, after: 'unknown', blockers: ['computer_use_os_permission_or_capability_unknown'] };
|
|
11
|
+
}
|
|
12
|
+
if (state.id === 'chrome_web_review' && process.env.SKS_CHROME_EXTENSION_READY !== '1' && input.fixture !== 'all-repairable') {
|
|
13
|
+
return { ...state, after: 'unknown', blockers: ['codex_chrome_extension_readiness_not_verified'] };
|
|
14
|
+
}
|
|
15
|
+
if (state.blockers.length === 0 || verifiedAfterRepair)
|
|
16
|
+
return { ...state, after: state.repairability === 'manual-required' ? 'unknown' : 'verified', blockers: state.repairability === 'manual-required' ? state.blockers : [] };
|
|
17
|
+
return { ...state, after: 'blocked' };
|
|
18
|
+
});
|
|
19
|
+
const blockers = capabilities.flatMap((state) => state.after === 'verified' ? [] : state.blockers);
|
|
20
|
+
const checked = {
|
|
21
|
+
...matrix,
|
|
22
|
+
generated_at: new Date().toISOString(),
|
|
23
|
+
ok: blockers.length === 0,
|
|
24
|
+
capabilities,
|
|
25
|
+
blockers,
|
|
26
|
+
warnings: capabilities.flatMap((state) => state.warnings)
|
|
27
|
+
};
|
|
28
|
+
const reportPath = input.reportPath === null
|
|
29
|
+
? null
|
|
30
|
+
: input.reportPath || path.join(root, '.sneakoscope', 'reports', 'native-capability-postcheck.json');
|
|
31
|
+
if (reportPath)
|
|
32
|
+
await writeJsonAtomic(reportPath, checked).catch(() => undefined);
|
|
33
|
+
return checked;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=native-capability-postcheck.js.map
|