sneakoscope 3.1.4 → 3.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +8 -36
  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/command-registry.js +1 -2
  8. package/dist/cli/install-helpers.js +56 -4
  9. package/dist/commands/codex-app.js +1 -11
  10. package/dist/commands/codex-lb.js +12 -9
  11. package/dist/commands/codex-native.js +68 -0
  12. package/dist/commands/doctor.js +64 -0
  13. package/dist/core/codex-app/codex-agent-role-sync.js +69 -11
  14. package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
  15. package/dist/core/codex-app/codex-app-execution-profile.js +43 -14
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
  17. package/dist/core/codex-app/codex-app-harness-matrix.js +4 -65
  18. package/dist/core/codex-app/codex-app-types.js +21 -0
  19. package/dist/core/codex-app/codex-hook-approval-probe.js +188 -0
  20. package/dist/core/codex-app/codex-hook-lifecycle.js +34 -10
  21. package/dist/core/codex-app/codex-init-deep.js +154 -3
  22. package/dist/core/codex-app/codex-skill-sync.js +84 -9
  23. package/dist/core/codex-native/codex-native-capability-cache.js +21 -0
  24. package/dist/core/codex-native/codex-native-feature-broker.js +182 -0
  25. package/dist/core/codex-native/codex-native-feature-matrix.js +31 -0
  26. package/dist/core/codex-native/codex-native-harness-compat.js +54 -0
  27. package/dist/core/codex-native/codex-native-interop-policy.js +58 -0
  28. package/dist/core/codex-native/codex-native-invocation-router.js +112 -0
  29. package/dist/core/codex-native/codex-native-pattern-analysis.js +56 -0
  30. package/dist/core/codex-native/codex-native-reference-evidence.js +2 -0
  31. package/dist/core/codex-native/codex-native-reference-source.js +110 -0
  32. package/dist/core/codex-native/codex-native-rename-map.js +25 -0
  33. package/dist/core/commands/mad-sks-command.js +37 -2
  34. package/dist/core/commands/qa-loop-command.js +3 -2
  35. package/dist/core/commands/research-command.js +2 -2
  36. package/dist/core/doctor/doctor-zellij-repair.js +5 -1
  37. package/dist/core/feature-fixtures.js +3 -4
  38. package/dist/core/feature-registry.js +5 -2
  39. package/dist/core/fsx.js +1 -1
  40. package/dist/core/image/image-artifact-path-contract.js +18 -1
  41. package/dist/core/init.js +4 -1
  42. package/dist/core/loops/loop-continuation-enforcer.js +11 -3
  43. package/dist/core/loops/loop-owner-inference.js +3 -0
  44. package/dist/core/loops/loop-planner.js +28 -5
  45. package/dist/core/loops/loop-worker-runtime.js +52 -8
  46. package/dist/core/naruto/naruto-loop-worker-router.js +11 -2
  47. package/dist/core/qa-loop.js +62 -4
  48. package/dist/core/research/research-cycle-runner.js +1 -0
  49. package/dist/core/research/research-stage-runner.js +9 -2
  50. package/dist/core/research.js +68 -1
  51. package/dist/core/routes.js +2 -3
  52. package/dist/core/version.js +1 -1
  53. package/dist/core/zellij/homebrew-policy.js +0 -1
  54. package/dist/core/zellij/zellij-self-heal-types.js +45 -0
  55. package/dist/core/zellij/zellij-self-heal.js +69 -8
  56. package/dist/core/zellij/zellij-update.js +9 -1
  57. package/dist/scripts/sks-3-1-4-directive-check-lib.js +1 -30
  58. package/dist/scripts/sks-3-1-5-directive-check-lib.js +318 -0
  59. package/dist/scripts/sks-3-1-6-directive-check-lib.js +522 -0
  60. package/package.json +53 -9
  61. package/dist/cli/hermes-command.js +0 -99
  62. package/dist/cli/openclaw-command.js +0 -83
  63. package/dist/commands/hermes.js +0 -5
  64. package/dist/commands/openclaw.js +0 -3
  65. package/dist/core/codex-app/lazycodex-analysis.js +0 -51
  66. package/dist/core/codex-app/lazycodex-interop-policy.js +0 -49
  67. package/dist/core/hermes.js +0 -192
  68. package/dist/core/openclaw.js +0 -171
@@ -0,0 +1,188 @@
1
+ import path from 'node:path';
2
+ import { findCodexBinary } from '../codex-adapter.js';
3
+ import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
4
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
+ import { isRecord } from './codex-app-types.js';
6
+ export async function probeCodexHookApprovalState(root, input = {}) {
7
+ const env = input.env || process.env;
8
+ const sources = [];
9
+ const fixture = normalizeApprovalState(env.SKS_CODEX_HOOK_APPROVAL_FIXTURE);
10
+ if (fixture) {
11
+ const report = buildProbe(fixture !== 'unknown', fixture, [{
12
+ source: 'config',
13
+ ok: fixture !== 'unknown',
14
+ evidence: [`fixture:${fixture}`],
15
+ blockers: fixture === 'unknown' ? ['fixture_unknown'] : []
16
+ }]);
17
+ return persist(root, report, input.writeReport !== false);
18
+ }
19
+ const actual = await readCodexHookActualState(root).catch((err) => ({
20
+ schema: 'sks.codex-hook-actual-state.v1',
21
+ ok: false,
22
+ root,
23
+ sources: [],
24
+ managed_dirs: [],
25
+ entries: [],
26
+ unsupported_handlers: [],
27
+ invalid_matchers: [],
28
+ dual_representation: [],
29
+ warnings: [],
30
+ blockers: [messageOf(err)]
31
+ }));
32
+ const actualState = approvalFromActualState(actual);
33
+ sources.push({
34
+ source: 'hook-actual-state',
35
+ ok: actual.ok !== false,
36
+ evidence: [
37
+ `entries:${actual.entries.length}`,
38
+ `trusted:${actual.entries.filter((entry) => entry.trust_status === 'Trusted' || entry.trust_status === 'Managed').length}`,
39
+ `modified:${actual.entries.filter((entry) => entry.trust_status === 'Modified').length}`,
40
+ `untrusted:${actual.entries.filter((entry) => entry.trust_status === 'Untrusted').length}`
41
+ ],
42
+ blockers: actual.blockers
43
+ });
44
+ const doctor = await probeCodexDoctorApproval(input.codexBin, env);
45
+ sources.push(doctor.source);
46
+ const doctorState = doctor.approval_state;
47
+ const state = strongestApprovalState([actualState, doctorState]);
48
+ const detectable = state !== 'unknown';
49
+ const blockers = [
50
+ ...sources.flatMap((source) => source.blockers),
51
+ ...(state === 'modified_requires_reapproval' ? ['hook_modified_requires_reapproval'] : [])
52
+ ];
53
+ const warnings = [
54
+ ...(state === 'unknown' ? ['hook_approval_state_unknown'] : []),
55
+ ...(actual.entries.length > 0 && state === 'unknown' ? ['hooks_installed_but_approval_unknown'] : [])
56
+ ];
57
+ const report = {
58
+ schema: 'sks.codex-hook-approval-probe.v1',
59
+ generated_at: nowIso(),
60
+ ok: blockers.length === 0 && state !== 'pending_review' && state !== 'modified_requires_reapproval',
61
+ detectable,
62
+ approval_state: state,
63
+ sources_checked: sources,
64
+ blockers,
65
+ warnings
66
+ };
67
+ return persist(root, report, input.writeReport !== false);
68
+ }
69
+ function approvalFromActualState(actual) {
70
+ if (actual.entries.length === 0)
71
+ return 'not_installed';
72
+ if (actual.entries.some((entry) => entry.trust_status === 'Modified'))
73
+ return 'modified_requires_reapproval';
74
+ if (actual.entries.some((entry) => entry.trust_status === 'Untrusted'))
75
+ return 'pending_review';
76
+ if (actual.entries.every((entry) => entry.trust_status === 'Trusted' || entry.trust_status === 'Managed'))
77
+ return 'approved';
78
+ return 'unknown';
79
+ }
80
+ async function probeCodexDoctorApproval(codexBin, env) {
81
+ const bin = codexBin || env.CODEX_BIN || await findCodexBinary().catch(() => null);
82
+ if (!bin) {
83
+ return { approval_state: 'unknown', source: { source: 'codex-doctor-json', ok: false, evidence: [], blockers: ['codex_cli_missing'] } };
84
+ }
85
+ const run = await runProcess(bin, ['doctor', '--json'], { env, timeoutMs: 8000, maxOutputBytes: 256 * 1024 }).catch((err) => ({
86
+ code: 1,
87
+ stdout: '',
88
+ stderr: messageOf(err)
89
+ }));
90
+ const text = `${run.stdout || ''}${run.stderr || ''}`.trim();
91
+ if (run.code !== 0 || !text) {
92
+ return {
93
+ approval_state: 'unknown',
94
+ source: { source: 'codex-doctor-json', ok: false, evidence: text ? [text.slice(0, 240)] : [], blockers: ['codex_doctor_json_unavailable'] }
95
+ };
96
+ }
97
+ try {
98
+ const parsed = JSON.parse(text);
99
+ const state = findApprovalState(parsed);
100
+ return {
101
+ approval_state: state || 'unknown',
102
+ source: {
103
+ source: 'codex-doctor-json',
104
+ ok: Boolean(state),
105
+ evidence: state ? [`approval_state:${state}`] : ['doctor_json_no_hook_approval_field'],
106
+ blockers: state ? [] : ['hook_approval_not_exposed_by_codex_doctor']
107
+ }
108
+ };
109
+ }
110
+ catch {
111
+ return {
112
+ approval_state: 'unknown',
113
+ source: { source: 'codex-doctor-json', ok: false, evidence: [text.slice(0, 240)], blockers: ['codex_doctor_json_parse_failed'] }
114
+ };
115
+ }
116
+ }
117
+ function findApprovalState(value) {
118
+ if (!isRecord(value))
119
+ return null;
120
+ for (const key of ['hook_approval_state', 'approval_state', 'hookApprovalState']) {
121
+ const state = normalizeApprovalState(value[key]);
122
+ if (state)
123
+ return state;
124
+ }
125
+ for (const nested of Object.values(value)) {
126
+ if (Array.isArray(nested)) {
127
+ for (const item of nested) {
128
+ const found = findApprovalState(item);
129
+ if (found)
130
+ return found;
131
+ }
132
+ }
133
+ else if (isRecord(nested)) {
134
+ const found = findApprovalState(nested);
135
+ if (found)
136
+ return found;
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+ function normalizeApprovalState(value) {
142
+ const raw = String(value || '').trim().toLowerCase().replace(/[-\s]+/g, '_');
143
+ if (raw === 'approved' || raw === 'trusted' || raw === 'managed')
144
+ return 'approved';
145
+ if (raw === 'pending' || raw === 'pending_review' || raw === 'untrusted')
146
+ return 'pending_review';
147
+ if (raw === 'modified' || raw === 'modified_requires_reapproval')
148
+ return 'modified_requires_reapproval';
149
+ if (raw === 'not_installed' || raw === 'missing')
150
+ return 'not_installed';
151
+ if (raw === 'unknown')
152
+ return 'unknown';
153
+ return null;
154
+ }
155
+ function strongestApprovalState(states) {
156
+ if (states.includes('modified_requires_reapproval'))
157
+ return 'modified_requires_reapproval';
158
+ if (states.includes('pending_review'))
159
+ return 'pending_review';
160
+ if (states.includes('approved'))
161
+ return 'approved';
162
+ if (states.every((state) => state === 'not_installed'))
163
+ return 'not_installed';
164
+ if (states.includes('not_installed') && states.length === 1)
165
+ return 'not_installed';
166
+ return 'unknown';
167
+ }
168
+ function buildProbe(detectable, approvalState, sources) {
169
+ return {
170
+ schema: 'sks.codex-hook-approval-probe.v1',
171
+ generated_at: nowIso(),
172
+ ok: approvalState === 'approved' || approvalState === 'not_installed',
173
+ detectable,
174
+ approval_state: approvalState,
175
+ sources_checked: sources,
176
+ blockers: sources.flatMap((source) => source.blockers),
177
+ warnings: approvalState === 'unknown' ? ['hook_approval_state_unknown'] : []
178
+ };
179
+ }
180
+ async function persist(root, report, writeReport) {
181
+ if (writeReport)
182
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-approval-probe.json'), report).catch(() => undefined);
183
+ return report;
184
+ }
185
+ function messageOf(err) {
186
+ return err instanceof Error ? err.message : String(err);
187
+ }
188
+ //# sourceMappingURL=codex-hook-approval-probe.js.map
@@ -1,14 +1,24 @@
1
- // @ts-nocheck
2
1
  import path from 'node:path';
3
2
  import { nowIso, writeJsonAtomic } from '../fsx.js';
4
3
  import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
5
4
  import { installManagedCodexHooks } from '../codex-hooks/codex-hook-managed-install.js';
5
+ import { probeCodexHookApprovalState } from './codex-hook-approval-probe.js';
6
6
  export async function buildCodexHookLifecycle(input = {}) {
7
7
  const root = path.resolve(input.root || process.cwd());
8
8
  const install = input.apply === true
9
- ? await installManagedCodexHooks(root).catch((err) => ({ ok: false, blockers: [err?.message || String(err)] }))
9
+ ? await installManagedCodexHooks(root).catch((err) => ({ ok: false, blockers: [messageOf(err)] }))
10
10
  : null;
11
- const actual = await readCodexHookActualState(root).catch((err) => ({ ok: false, entries: [], blockers: [err?.message || String(err)] }));
11
+ const actual = await readCodexHookActualState(root).catch((err) => ({ ok: false, entries: [], blockers: [messageOf(err)] }));
12
+ const probe = await probeCodexHookApprovalState(root).catch((err) => ({
13
+ schema: 'sks.codex-hook-approval-probe.v1',
14
+ generated_at: nowIso(),
15
+ ok: false,
16
+ detectable: false,
17
+ approval_state: 'unknown',
18
+ sources_checked: [],
19
+ blockers: [messageOf(err)],
20
+ warnings: ['hook_approval_probe_failed']
21
+ }));
12
22
  const events = {
13
23
  UserPromptSubmit: ['route_classifier', 'goal_to_loop_compiler', 'permission_profile_badge'],
14
24
  PreToolUse: ['requested_scope_guard', 'mad_db_priority_resolver', 'side_effect_zero_gate'],
@@ -16,25 +26,39 @@ export async function buildCodexHookLifecycle(input = {}) {
16
26
  Stop: ['continuation_enforcer', 'final_proof_check', 'loop_resume_hint'],
17
27
  Notification: ['operator_status', 'zellij_anchor', 'codex_app_status']
18
28
  };
19
- const installedEvents = new Set((actual.entries || []).map((entry) => entry.event));
29
+ const installedEvents = new Set(Array.isArray(actual.entries) ? actual.entries.map((entry) => String(entry.event || '')) : []);
20
30
  const report = {
21
31
  schema: 'sks.codex-hook-lifecycle.v1',
22
32
  generated_at: nowIso(),
23
- ok: actual.ok !== false,
33
+ ok: actual.ok !== false && probe.approval_state !== 'modified_requires_reapproval',
24
34
  apply: input.apply === true,
25
- approval_state: 'unknown',
26
- approval_state_detectable: false,
35
+ approval_state: probe.approval_state,
36
+ approval_state_detectable: probe.detectable,
27
37
  lifecycle: Object.fromEntries(Object.entries(events).map(([event, actions]) => [event, {
38
+ event,
28
39
  actions,
40
+ sks_action: actions.join(','),
29
41
  installed: installedEvents.has(event),
30
- approval_state: 'unknown'
42
+ approval_state: installedEvents.has(event) ? probe.approval_state : 'not_installed',
43
+ counted_as_evidence: installedEvents.has(event) && probe.approval_state === 'approved'
31
44
  }])),
32
45
  actual_state: actual,
33
46
  install,
34
- blockers: actual.blockers || [],
35
- warnings: ['hook_approval_state_unknown']
47
+ probe,
48
+ blockers: [
49
+ ...(Array.isArray(actual.blockers) ? actual.blockers.map(String) : []),
50
+ ...(probe.approval_state === 'modified_requires_reapproval' ? ['hook_modified_requires_reapproval'] : [])
51
+ ],
52
+ warnings: [
53
+ ...probe.warnings,
54
+ ...(probe.approval_state === 'pending_review' ? ['hook_approval_pending_review'] : []),
55
+ ...(probe.approval_state === 'unknown' ? ['hook_approval_state_unknown'] : [])
56
+ ]
36
57
  };
37
58
  await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-lifecycle.json'), report).catch(() => undefined);
38
59
  return report;
39
60
  }
61
+ function messageOf(err) {
62
+ return err instanceof Error ? err.message : String(err);
63
+ }
40
64
  //# sourceMappingURL=codex-hook-lifecycle.js.map
@@ -1,7 +1,8 @@
1
- // @ts-nocheck
2
1
  import fs from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
  import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
+ import { guardContextForRoute, guardedRm } from '../safety/mutation-guard.js';
5
+ import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
5
6
  export async function runCodexInitDeep(input = {}) {
6
7
  const root = path.resolve(input.root || process.cwd());
7
8
  const dirs = await scoreDirectories(root);
@@ -9,29 +10,154 @@ export async function runCodexInitDeep(input = {}) {
9
10
  const contextDir = path.join(root, '.sneakoscope', 'context');
10
11
  const generatedPath = path.join(contextDir, 'AGENTS.generated.md');
11
12
  const markdown = renderGeneratedAgents(selected);
13
+ const directoryLocalAgents = { created: [], updated: [], skipped: [], backup_paths: [], backups_created: 0, backups_pruned: [], unchanged_files: [], changed_only_backup: true, blockers: [] };
14
+ const backupRetention = Math.max(0, Number(process.env.SKS_INIT_DEEP_BACKUP_RETENTION || 5) || 5);
12
15
  if (input.apply === true) {
13
16
  await ensureDir(contextDir);
14
17
  await writeTextAtomic(generatedPath, markdown);
18
+ if (input.directoryLocal === true) {
19
+ for (const row of selected) {
20
+ const agentsPath = path.join(root, row.dir, 'AGENTS.md');
21
+ try {
22
+ await ensureDir(path.dirname(agentsPath));
23
+ const existing = await fs.readFile(agentsPath, 'utf8').catch(() => '');
24
+ const next = mergeManagedBlockPreview(existing, 'SKS INIT-DEEP MANAGED SECTION', renderDirectoryAgentsBlock(row));
25
+ if (existing === next) {
26
+ directoryLocalAgents.unchanged_files.push(path.relative(root, agentsPath));
27
+ continue;
28
+ }
29
+ if (existing.trim()) {
30
+ const beforeHash = hashText(existing);
31
+ const backup = `${agentsPath}.sks-backup-${beforeHash.slice(0, 12)}-${Date.now()}`;
32
+ await fs.copyFile(agentsPath, backup);
33
+ directoryLocalAgents.backup_paths.push(path.relative(root, backup));
34
+ directoryLocalAgents.backups_created += 1;
35
+ }
36
+ await writeTextAtomic(agentsPath, next);
37
+ const pruned = await pruneBackups(root, agentsPath, backupRetention);
38
+ directoryLocalAgents.backups_pruned.push(...pruned.map((file) => path.relative(root, file)));
39
+ const status = existing.trim() ? (existing.includes('BEGIN SKS INIT-DEEP MANAGED SECTION') ? 'updated' : 'appended') : 'created';
40
+ if (status === 'created')
41
+ directoryLocalAgents.created.push(path.relative(root, agentsPath));
42
+ else
43
+ directoryLocalAgents.updated.push(path.relative(root, agentsPath));
44
+ }
45
+ catch (err) {
46
+ directoryLocalAgents.skipped.push(path.relative(root, agentsPath));
47
+ directoryLocalAgents.blockers.push(`${path.relative(root, agentsPath)}:${messageOf(err)}`);
48
+ }
49
+ }
50
+ }
15
51
  }
16
52
  const report = {
17
53
  schema: 'sks.codex-init-deep.v1',
18
54
  generated_at: nowIso(),
19
- ok: true,
55
+ ok: directoryLocalAgents.blockers.length === 0,
20
56
  apply: input.apply === true,
21
57
  root,
22
58
  generated_path: path.relative(root, generatedPath),
23
59
  root_agents_preserved: true,
24
60
  directory_guidance: selected,
25
- blockers: []
61
+ directory_local_agents: directoryLocalAgents,
62
+ blockers: directoryLocalAgents.blockers
26
63
  };
27
64
  await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-init-deep.json'), report).catch(() => undefined);
28
65
  return report;
29
66
  }
67
+ function mergeManagedBlockPreview(current, markerName, content) {
68
+ const begin = `<!-- BEGIN ${markerName} -->`;
69
+ const end = `<!-- END ${markerName} -->`;
70
+ const block = `${begin}\n${content.trim()}\n${end}\n`;
71
+ if (!current.trim())
72
+ return `${block}\n`;
73
+ const beginIdx = current.indexOf(begin);
74
+ const endIdx = current.indexOf(end);
75
+ if (beginIdx >= 0 && endIdx >= beginIdx) {
76
+ const afterEnd = endIdx + end.length;
77
+ return `${current.slice(0, beginIdx)}${block}${current.slice(afterEnd).replace(/^\n/, '')}`;
78
+ }
79
+ return `${current.replace(/\s*$/, '\n\n')}${block}\n`;
80
+ }
81
+ async function pruneBackups(root, agentsPath, keep) {
82
+ if (keep < 1)
83
+ return [];
84
+ const dir = path.dirname(agentsPath);
85
+ const base = path.basename(agentsPath);
86
+ const rows = await fs.readdir(dir).catch(() => []);
87
+ const backups = rows
88
+ .filter((name) => name.startsWith(`${base}.sks-backup-`))
89
+ .map((name) => path.join(dir, name))
90
+ .sort();
91
+ const remove = backups.slice(0, Math.max(0, backups.length - keep));
92
+ const contract = createRequestedScopeContract({
93
+ route: 'codex-app:init-deep',
94
+ userRequest: 'Prune only SKS init-deep AGENTS.md backup files after creating a fresh backup.',
95
+ projectRoot: root
96
+ });
97
+ const guard = guardContextForRoute(root, contract, 'prune SKS init-deep backup retention');
98
+ for (const file of remove)
99
+ await guardedRm(guard, file, { force: true }).catch(() => undefined);
100
+ return remove;
101
+ }
102
+ function hashText(text) {
103
+ let hash = 2166136261;
104
+ for (let index = 0; index < text.length; index += 1) {
105
+ hash ^= text.charCodeAt(index);
106
+ hash = Math.imul(hash, 16777619);
107
+ }
108
+ return (hash >>> 0).toString(16).padStart(8, '0');
109
+ }
30
110
  export async function readInitDeepMemory(root) {
31
111
  const file = path.join(root, '.sneakoscope', 'context', 'AGENTS.generated.md');
32
112
  const text = await fs.readFile(file, 'utf8').catch(() => '');
33
113
  return text.trim() ? { path: file, text } : null;
34
114
  }
115
+ export async function readInitDeepMemoryHints(root, scopePaths = []) {
116
+ const resolvedRoot = path.resolve(root);
117
+ const hints = [];
118
+ const generated = await readInitDeepMemory(resolvedRoot).catch(() => null);
119
+ if (generated) {
120
+ hints.push({
121
+ path: path.relative(resolvedRoot, generated.path),
122
+ scope: '.',
123
+ summary: generated.text.split(/\r?\n/).filter((line) => /^##\s+/.test(line)).slice(0, 8).join(' | ')
124
+ });
125
+ }
126
+ const candidateDirs = new Set();
127
+ for (const scopePath of scopePaths) {
128
+ const absolute = path.resolve(resolvedRoot, scopePath);
129
+ if (!absolute.startsWith(resolvedRoot))
130
+ continue;
131
+ const stat = await fs.stat(absolute).catch(() => null);
132
+ let dir = stat?.isFile() ? path.dirname(absolute) : absolute;
133
+ while (dir.startsWith(resolvedRoot)) {
134
+ candidateDirs.add(dir);
135
+ if (dir === resolvedRoot)
136
+ break;
137
+ dir = path.dirname(dir);
138
+ }
139
+ }
140
+ for (const dir of [...candidateDirs].sort((a, b) => b.length - a.length)) {
141
+ const file = path.join(dir, 'AGENTS.md');
142
+ const text = await fs.readFile(file, 'utf8').catch(() => '');
143
+ if (!text.trim())
144
+ continue;
145
+ const managed = extractManagedSection(text, 'SKS INIT-DEEP MANAGED SECTION');
146
+ const userSummary = text.replace(/<!-- BEGIN [\s\S]*?<!-- END [^>]+-->/g, '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, 4).join(' | ');
147
+ const summary = [managed ? managed.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, 4).join(' | ') : '', userSummary ? `user:${userSummary}` : ''].filter(Boolean).join(' || ');
148
+ if (summary) {
149
+ hints.push({
150
+ path: path.relative(resolvedRoot, file),
151
+ scope: path.relative(resolvedRoot, dir) || '.',
152
+ summary
153
+ });
154
+ }
155
+ }
156
+ const unique = new Map();
157
+ for (const hint of hints)
158
+ unique.set(`${hint.path}:${hint.scope}`, hint);
159
+ return [...unique.values()].slice(0, 12);
160
+ }
35
161
  async function scoreDirectories(root) {
36
162
  const counts = new Map();
37
163
  await walk(path.join(root, 'src'), root, counts);
@@ -60,6 +186,8 @@ async function walk(dir, root, counts, depth = 0) {
60
186
  await walk(full, root, counts, depth + 1);
61
187
  }
62
188
  else if (row.isFile()) {
189
+ if (row.name.includes('.sks-backup-'))
190
+ continue;
63
191
  const relDir = path.relative(root, path.dirname(full)).split(path.sep).join('/');
64
192
  const entry = counts.get(relDir) || { file_count: 0, langs: new Set() };
65
193
  entry.file_count += 1;
@@ -84,4 +212,27 @@ function renderGeneratedAgents(rows) {
84
212
  ])
85
213
  ].join('\n');
86
214
  }
215
+ function renderDirectoryAgentsBlock(row) {
216
+ return [
217
+ `# SKS Init-Deep Local Guidance: ${row.dir}`,
218
+ '',
219
+ `- Files observed: ${row.file_count}`,
220
+ `- Languages: ${row.languages.join(', ') || 'unknown'}`,
221
+ `- Guidance: ${row.guidance}`,
222
+ '- Preserve user-authored content outside this managed block.',
223
+ '- Hydrate TriWiki/current source before risky edits in this directory.'
224
+ ].join('\n');
225
+ }
226
+ function extractManagedSection(text, markerName) {
227
+ const begin = `<!-- BEGIN ${markerName} -->`;
228
+ const end = `<!-- END ${markerName} -->`;
229
+ const beginIdx = text.indexOf(begin);
230
+ const endIdx = text.indexOf(end);
231
+ if (beginIdx < 0 || endIdx < beginIdx)
232
+ return '';
233
+ return text.slice(beginIdx + begin.length, endIdx).trim();
234
+ }
235
+ function messageOf(err) {
236
+ return err instanceof Error ? err.message : String(err);
237
+ }
87
238
  //# sourceMappingURL=codex-init-deep.js.map
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
  import os from 'node:os';
@@ -14,19 +13,19 @@ const SKS_SKILLS = [
14
13
  '$Computer-Use',
15
14
  '$Init-Deep'
16
15
  ];
17
- const LAZYCODEX_RESERVED = new Set(['ulw-loop', 'ulw-plan', 'start-work']);
16
+ const EXTERNAL_ROUTE_RESERVED = new Set(['ulw-loop', 'ulw-plan', 'start-work']);
18
17
  export async function syncCodexSksSkills(input) {
19
18
  const root = path.resolve(input.root);
20
19
  const skillsRoot = input.skillsRoot || path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'skills');
21
20
  const existing = await listSkillNames(skillsRoot);
22
- const collisions = existing.filter((name) => LAZYCODEX_RESERVED.has(name));
21
+ const collisions = existing.filter((name) => EXTERNAL_ROUTE_RESERVED.has(name));
23
22
  const desired = SKS_SKILLS.map((skill) => skillName(skill));
24
23
  const created = [];
25
24
  const skipped = [];
26
25
  if (input.apply === true) {
27
26
  await ensureDir(skillsRoot);
28
27
  for (const name of desired) {
29
- if (LAZYCODEX_RESERVED.has(name)) {
28
+ if (EXTERNAL_ROUTE_RESERVED.has(name)) {
30
29
  skipped.push(name);
31
30
  continue;
32
31
  }
@@ -53,10 +52,10 @@ export async function syncCodexSksSkills(input) {
53
52
  existing_skills: existing,
54
53
  created,
55
54
  skipped,
56
- lazycodex_reserved_present: collisions,
55
+ external_route_names_preserved: collisions,
57
56
  interop: {
58
57
  mode: 'coexist',
59
- clobbered_lazycodex: false,
58
+ clobbered_external_routes: false,
60
59
  clobbered_user_skills: false
61
60
  },
62
61
  blockers: []
@@ -72,20 +71,96 @@ function skillName(value) {
72
71
  return value.replace(/^\$/, '').toLowerCase();
73
72
  }
74
73
  function skillContent(name) {
74
+ const profile = skillProfile(name);
75
75
  const body = [
76
76
  '---',
77
77
  `name: ${name}`,
78
- `description: SKS managed Codex App route bridge for $${name}.`,
78
+ `description: SKS managed Codex App route bridge for ${profile.command}.`,
79
79
  '---',
80
80
  '',
81
81
  '<!-- BEGIN SKS MANAGED SKILL -->',
82
- `Invoke the matching SKS route for $${name}. Do not overwrite user or LazyCodex/OmO skills.`,
83
- `checksum: ${hash(name)}`,
82
+ `Command: ${profile.command}`,
83
+ `Purpose: ${profile.purpose}`,
84
+ `Use when: ${profile.when}`,
85
+ `Route: ${profile.command}`,
86
+ `Evidence: ${profile.evidence}`,
87
+ 'Safety: keep route state bounded, preserve user and external route assets, and stop on hard blockers instead of fabricating fallback behavior.',
88
+ 'Proof paths: write the route-local mission artifact named in Evidence before claiming completion.',
89
+ '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.',
90
+ `Fallback: ${profile.fallback}`,
91
+ `checksum: ${hash(`${name}:${profile.command}:${profile.evidence}:${profile.fallback}`)}`,
84
92
  '<!-- END SKS MANAGED SKILL -->',
85
93
  ''
86
94
  ].join('\n');
87
95
  return body;
88
96
  }
97
+ function skillProfile(name) {
98
+ const table = {
99
+ loop: {
100
+ command: '$Loop',
101
+ purpose: 'compile persisted route work into bounded loop plans with continuation evidence.',
102
+ when: 'a mission needs stage-by-stage execution, memory hints, or resume-safe artifacts.',
103
+ evidence: '.sneakoscope/loops/** plus codex-app-execution-profile.json',
104
+ fallback: 'use message-role routing when native agent_type is not verified.'
105
+ },
106
+ naruto: {
107
+ command: '$Naruto',
108
+ purpose: 'fan out bounded native worker lanes for high-scale review or implementation.',
109
+ when: 'parallel lanes are explicitly selected by the route and parent integration remains owner.',
110
+ evidence: 'naruto work graph, worker ledgers, and execution profile payloads.',
111
+ fallback: 'degrade to message-role workers without dropping proof artifacts.'
112
+ },
113
+ 'qa-loop': {
114
+ command: '$QA-LOOP',
115
+ purpose: 'dogfood UI/API behavior with gate artifacts and current execution profile.',
116
+ when: 'route completion needs human-proxy verification or app handoff checks.',
117
+ evidence: 'qa-loop gate/result ledgers and codex-app-execution-profile.json.',
118
+ fallback: 'record the unavailable surface as blocked rather than inventing visual proof.'
119
+ },
120
+ research: {
121
+ command: '$Research',
122
+ purpose: 'run evidence-bound research cycles with source routing and synthesis ledgers.',
123
+ when: 'the request depends on discovery, evaluation, or external-source claims.',
124
+ evidence: 'research plan, source ledger, cycle record, and execution profile routing.',
125
+ fallback: 'mark unavailable source tools explicitly and avoid unsupported live-accuracy claims.'
126
+ },
127
+ dfix: {
128
+ command: '$DFix',
129
+ purpose: 'perform tiny direct fixes without the full Team route.',
130
+ when: 'copy/config/docs/labels/spacing/translation/mechanical edits are truly narrow.',
131
+ evidence: 'focused diff plus DFix Honest check.',
132
+ fallback: 'route broad implementation through Team/Loop instead.'
133
+ },
134
+ 'image-ux-review': {
135
+ command: '$Image-UX-Review',
136
+ purpose: 'produce generated annotated UI review images and extract issue ledgers.',
137
+ when: 'visual UX critique is requested from screenshots or app captures.',
138
+ evidence: 'source inventory, generated annotation images, extracted issue ledger.',
139
+ fallback: 'block if raster annotation cannot be produced.'
140
+ },
141
+ 'computer-use': {
142
+ command: '$Computer-Use',
143
+ purpose: 'operate native macOS desktop apps through the fast Computer Use lane.',
144
+ when: 'the task depends on non-web desktop UI or OS settings.',
145
+ evidence: 'desktop interaction notes/screenshots where available.',
146
+ fallback: 'use Browser/Chrome only for web targets.'
147
+ },
148
+ 'init-deep': {
149
+ command: '$Init-Deep',
150
+ purpose: 'refresh project-local memory, directory AGENTS sections, and loop memory hints.',
151
+ when: 'a route needs deeper local context or directory-specific instruction recall.',
152
+ evidence: '.sneakoscope/context/AGENTS.generated.md and managed directory AGENTS blocks.',
153
+ fallback: 'preserve user content and skip directories that cannot be safely updated.'
154
+ }
155
+ };
156
+ return table[name] || {
157
+ command: `$${name}`,
158
+ purpose: 'bridge an SKS managed Codex App route.',
159
+ when: 'the matching SKS route is explicitly requested.',
160
+ evidence: '.sneakoscope route artifacts.',
161
+ fallback: 'record blockers with evidence.'
162
+ };
163
+ }
89
164
  function hash(value) {
90
165
  return crypto.createHash('sha256').update(`sks-skill:${value}`).digest('hex').slice(0, 12);
91
166
  }
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { writeJsonAtomic } from '../fsx.js';
4
+ const CACHE_RELATIVE_PATH = '.sneakoscope/reports/codex-native-feature-matrix.json';
5
+ export function codexNativeCapabilityCachePath(root) {
6
+ return path.join(root, CACHE_RELATIVE_PATH);
7
+ }
8
+ export async function readCodexNativeCapabilityCache(root) {
9
+ const file = codexNativeCapabilityCachePath(root);
10
+ const text = await fs.readFile(file, 'utf8').catch(() => null);
11
+ if (!text)
12
+ return null;
13
+ const parsed = JSON.parse(text);
14
+ return parsed?.schema === 'sks.codex-native-feature-matrix.v1' ? parsed : null;
15
+ }
16
+ export async function writeCodexNativeCapabilityCache(root, matrix) {
17
+ const file = codexNativeCapabilityCachePath(root);
18
+ await writeJsonAtomic(file, matrix);
19
+ return file;
20
+ }
21
+ //# sourceMappingURL=codex-native-capability-cache.js.map