sneakoscope 3.1.4 → 3.1.5
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 +1 -1
- 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/install-helpers.js +56 -4
- package/dist/commands/codex-lb.js +12 -9
- package/dist/core/codex-app/codex-agent-role-sync.js +63 -11
- package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
- package/dist/core/codex-app/codex-app-execution-profile.js +15 -8
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
- package/dist/core/codex-app/codex-app-harness-matrix.js +78 -26
- package/dist/core/codex-app/codex-app-types.js +21 -0
- package/dist/core/codex-app/codex-hook-approval-probe.js +188 -0
- package/dist/core/codex-app/codex-hook-lifecycle.js +31 -10
- package/dist/core/codex-app/codex-init-deep.js +97 -4
- package/dist/core/codex-app/codex-skill-sync.js +75 -3
- package/dist/core/codex-app/lazycodex-analysis.js +31 -10
- package/dist/core/codex-app/lazycodex-interop-policy.js +13 -2
- package/dist/core/codex-app/lazycodex-live-analyzer.js +98 -0
- package/dist/core/commands/mad-sks-command.js +37 -2
- package/dist/core/commands/qa-loop-command.js +3 -2
- package/dist/core/commands/research-command.js +2 -2
- package/dist/core/doctor/doctor-zellij-repair.js +5 -1
- package/dist/core/feature-fixtures.js +1 -0
- package/dist/core/feature-registry.js +4 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +4 -1
- package/dist/core/loops/loop-continuation-enforcer.js +11 -3
- package/dist/core/loops/loop-planner.js +21 -4
- package/dist/core/loops/loop-worker-runtime.js +23 -7
- package/dist/core/naruto/naruto-loop-worker-router.js +11 -2
- package/dist/core/qa-loop.js +39 -4
- package/dist/core/research/research-cycle-runner.js +1 -0
- package/dist/core/research/research-stage-runner.js +9 -2
- package/dist/core/research.js +35 -1
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/homebrew-policy.js +0 -1
- package/dist/core/zellij/zellij-self-heal-types.js +45 -0
- package/dist/core/zellij/zellij-self-heal.js +69 -8
- package/dist/core/zellij/zellij-update.js +9 -1
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +347 -0
- package/package.json +26 -2
|
@@ -1,61 +1,99 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { findCodexBinary } from '../codex-adapter.js';
|
|
4
3
|
import { codexAppIntegrationStatus } from '../codex-app.js';
|
|
5
4
|
import { detectCodex0138Capability } from '../codex-control/codex-0138-capability.js';
|
|
6
5
|
import { detectCodex0139Capability } from '../codex-control/codex-0139-capability.js';
|
|
7
6
|
import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
|
|
8
|
-
import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
|
|
9
7
|
import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
10
8
|
import { repairAgentRoleConfigs } from '../agents/agent-role-config.js';
|
|
11
9
|
import { buildLazyCodexInteropPolicy } from './lazycodex-interop-policy.js';
|
|
10
|
+
import { probeCodexAgentTypeSupport } from './codex-agent-type-probe.js';
|
|
11
|
+
import { probeCodexHookApprovalState } from './codex-hook-approval-probe.js';
|
|
12
|
+
import { isRecord } from './codex-app-types.js';
|
|
12
13
|
export async function buildCodexAppHarnessMatrix(input = { root: process.cwd() }) {
|
|
13
14
|
const root = path.resolve(input.root || process.cwd());
|
|
14
15
|
const codexBin = await findCodexBinary().catch(() => null);
|
|
15
16
|
const version = codexBin ? await codexVersion(codexBin) : null;
|
|
16
|
-
const cap0138 = await detectCodex0138Capability({ codexBin }).catch((err) => ({ blockers: [
|
|
17
|
-
const cap0139 = await detectCodex0139Capability({ codexBin }).catch((err) => ({ blockers: [
|
|
18
|
-
const app = await codexAppIntegrationStatus({ codex: { bin: codexBin, version, available: Boolean(codexBin) } }).catch((err) => ({ ok: false, blockers: [
|
|
19
|
-
const plugins = await buildCodexPluginInventory().catch((err) => ({ plugins: [], marketplace_available: false, blockers: [
|
|
20
|
-
const
|
|
21
|
-
|
|
17
|
+
const cap0138 = await detectCodex0138Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
|
|
18
|
+
const cap0139 = await detectCodex0139Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
|
|
19
|
+
const app = await codexAppIntegrationStatus({ codex: { bin: codexBin, version, available: Boolean(codexBin) } }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
20
|
+
const plugins = await buildCodexPluginInventory().catch((err) => ({ plugins: [], marketplace_available: false, blockers: [messageOf(err)] }));
|
|
21
|
+
const hookApproval = await probeCodexHookApprovalState(root, { codexBin }).catch((err) => ({
|
|
22
|
+
schema: 'sks.codex-hook-approval-probe.v1',
|
|
23
|
+
generated_at: nowIso(),
|
|
24
|
+
ok: false,
|
|
25
|
+
detectable: false,
|
|
26
|
+
approval_state: 'unknown',
|
|
27
|
+
sources_checked: [],
|
|
28
|
+
blockers: [messageOf(err)],
|
|
29
|
+
warnings: ['hook_approval_probe_failed']
|
|
30
|
+
}));
|
|
31
|
+
const agentType = await probeCodexAgentTypeSupport(root, { codexBin }).catch((err) => ({
|
|
32
|
+
schema: 'sks.codex-agent-type-probe.v1',
|
|
33
|
+
generated_at: nowIso(),
|
|
34
|
+
ok: false,
|
|
35
|
+
supported: false,
|
|
36
|
+
source: 'unknown',
|
|
37
|
+
spawn_tool_name: 'unknown',
|
|
38
|
+
schema_path: null,
|
|
39
|
+
evidence: [],
|
|
40
|
+
blockers: [messageOf(err)],
|
|
41
|
+
warnings: ['agent_type_probe_failed_message_role_fallback']
|
|
42
|
+
}));
|
|
43
|
+
const agents = await repairAgentRoleConfigs({ root, apply: input.applyRepairs === true, reportPath: path.join(root, '.sneakoscope', 'reports', 'codex-agent-role-sync.json') }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
22
44
|
const interop = await buildLazyCodexInteropPolicy({ root, inventory: plugins }).catch(() => null);
|
|
23
|
-
const
|
|
24
|
-
const
|
|
45
|
+
const appRecord = isRecord(app) ? app : {};
|
|
46
|
+
const requiredSkills = isRecord(appRecord.required_skills) ? appRecord.required_skills : {};
|
|
47
|
+
const skills = isRecord(appRecord.skills) ? appRecord.skills : {};
|
|
48
|
+
const skillShadows = isRecord(appRecord.skill_shadows) ? appRecord.skill_shadows : {};
|
|
49
|
+
const hasSkills = requiredSkills.ok === true || skills.ok === true || skillShadows.ok !== false;
|
|
50
|
+
const hookApprovalKnown = hookApproval.detectable === true;
|
|
51
|
+
const hookBlockers = hookApproval.approval_state === 'modified_requires_reapproval'
|
|
52
|
+
? ['hook_modified_requires_reapproval']
|
|
53
|
+
: [];
|
|
25
54
|
const matrix = {
|
|
26
55
|
schema: 'sks.codex-app-harness-matrix.v1',
|
|
27
56
|
generated_at: nowIso(),
|
|
28
|
-
ok: Boolean(codexBin) && cap0138
|
|
57
|
+
ok: Boolean(codexBin) && feature(cap0138, 'supports_plugin_json') !== false && recordOk(agents) !== false,
|
|
29
58
|
codex_cli: { available: Boolean(codexBin), version },
|
|
30
59
|
app_features: {
|
|
31
|
-
plugin_json: cap0138
|
|
32
|
-
marketplace_add: cap0139
|
|
33
|
-
marketplace_upgrade: cap0139
|
|
60
|
+
plugin_json: feature(cap0138, 'supports_plugin_json') === true,
|
|
61
|
+
marketplace_add: feature(cap0139, 'supports_marketplace_source_field') === true || feature(plugins, 'marketplace_available') === true,
|
|
62
|
+
marketplace_upgrade: feature(cap0139, 'supports_marketplace_source_field') === true,
|
|
34
63
|
startup_review_detectable: hookApprovalKnown,
|
|
35
64
|
hook_approval_state_detectable: hookApprovalKnown,
|
|
65
|
+
hook_approval_state: hookApproval.approval_state,
|
|
36
66
|
skill_picker_ready: Boolean(hasSkills),
|
|
37
|
-
agent_type_supported:
|
|
38
|
-
mcp_inventory_ready: Array.isArray(plugins.plugins),
|
|
39
|
-
app_handoff_ready: cap0138
|
|
40
|
-
image_path_exposure_ready: cap0138
|
|
67
|
+
agent_type_supported: agentType.supported,
|
|
68
|
+
mcp_inventory_ready: Array.isArray(isRecord(plugins) ? plugins.plugins : null),
|
|
69
|
+
app_handoff_ready: feature(cap0138, 'supports_app_handoff') === true,
|
|
70
|
+
image_path_exposure_ready: feature(cap0138, 'supports_image_path_exposure') === true
|
|
41
71
|
},
|
|
42
72
|
sks_integrations: {
|
|
43
73
|
dollar_skills_synced: Boolean(hasSkills),
|
|
44
|
-
agent_roles_synced: agents
|
|
45
|
-
hooks_synced:
|
|
74
|
+
agent_roles_synced: recordOk(agents) !== false,
|
|
75
|
+
hooks_synced: hookApproval.approval_state === 'approved',
|
|
46
76
|
init_deep_available: true,
|
|
47
77
|
loop_mesh_app_profile_available: true
|
|
48
78
|
},
|
|
79
|
+
probes: {
|
|
80
|
+
hook_approval: hookApproval,
|
|
81
|
+
agent_type: agentType
|
|
82
|
+
},
|
|
49
83
|
blockers: [
|
|
50
84
|
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
51
|
-
...(cap0138
|
|
52
|
-
...(plugins
|
|
53
|
-
...(agents
|
|
85
|
+
...blockersOf(cap0138),
|
|
86
|
+
...blockersOf(plugins),
|
|
87
|
+
...blockersOf(agents),
|
|
88
|
+
...hookBlockers
|
|
54
89
|
],
|
|
55
90
|
warnings: [
|
|
56
|
-
...(cap0139
|
|
57
|
-
...
|
|
58
|
-
...
|
|
91
|
+
...blockersOf(cap0139).map((b) => `codex_0139:${b}`),
|
|
92
|
+
...hookApproval.blockers.map((b) => `hooks:${b}`),
|
|
93
|
+
...hookApproval.warnings,
|
|
94
|
+
...agentType.warnings,
|
|
95
|
+
...(hookApproval.approval_state === 'pending_review' ? ['hook_approval_pending_review'] : []),
|
|
96
|
+
...(hookApproval.approval_state === 'not_installed' ? ['hooks_not_installed'] : []),
|
|
59
97
|
...(interop?.lazycodex_detected ? ['lazycodex_detected_coexist_mode'] : [])
|
|
60
98
|
]
|
|
61
99
|
};
|
|
@@ -72,4 +110,18 @@ async function codexVersion(bin) {
|
|
|
72
110
|
const run = await runProcess(bin, ['--version'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch(() => null);
|
|
73
111
|
return run?.code === 0 ? `${run.stdout || run.stderr || ''}`.trim() || null : null;
|
|
74
112
|
}
|
|
113
|
+
function blockersOf(value) {
|
|
114
|
+
if (!isRecord(value) || !Array.isArray(value.blockers))
|
|
115
|
+
return [];
|
|
116
|
+
return value.blockers.map((item) => String(item)).filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
function feature(value, key) {
|
|
119
|
+
return isRecord(value) && typeof value[key] === 'boolean' ? value[key] : undefined;
|
|
120
|
+
}
|
|
121
|
+
function recordOk(value) {
|
|
122
|
+
return isRecord(value) && typeof value.ok === 'boolean' ? value.ok : undefined;
|
|
123
|
+
}
|
|
124
|
+
function messageOf(err) {
|
|
125
|
+
return err instanceof Error ? err.message : String(err);
|
|
126
|
+
}
|
|
75
127
|
//# sourceMappingURL=codex-app-harness-matrix.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function isRecord(value) {
|
|
2
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function stringList(value) {
|
|
5
|
+
return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
|
|
6
|
+
}
|
|
7
|
+
export function isCodexAppHarnessMatrix(value) {
|
|
8
|
+
if (!isRecord(value))
|
|
9
|
+
return false;
|
|
10
|
+
if (value.schema !== 'sks.codex-app-harness-matrix.v1')
|
|
11
|
+
return false;
|
|
12
|
+
const app = value.app_features;
|
|
13
|
+
const sks = value.sks_integrations;
|
|
14
|
+
return isRecord(app)
|
|
15
|
+
&& isRecord(sks)
|
|
16
|
+
&& typeof value.ok === 'boolean'
|
|
17
|
+
&& isRecord(value.codex_cli)
|
|
18
|
+
&& Array.isArray(value.blockers)
|
|
19
|
+
&& Array.isArray(value.warnings);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=codex-app-types.js.map
|
|
@@ -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: [
|
|
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: [
|
|
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,36 @@ 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
|
|
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:
|
|
26
|
-
approval_state_detectable:
|
|
35
|
+
approval_state: probe.approval_state,
|
|
36
|
+
approval_state_detectable: probe.detectable,
|
|
27
37
|
lifecycle: Object.fromEntries(Object.entries(events).map(([event, actions]) => [event, {
|
|
28
38
|
actions,
|
|
29
39
|
installed: installedEvents.has(event),
|
|
30
|
-
approval_state: '
|
|
40
|
+
approval_state: installedEvents.has(event) ? probe.approval_state : 'not_installed'
|
|
31
41
|
}])),
|
|
32
42
|
actual_state: actual,
|
|
33
43
|
install,
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
probe,
|
|
45
|
+
blockers: [
|
|
46
|
+
...(Array.isArray(actual.blockers) ? actual.blockers.map(String) : []),
|
|
47
|
+
...(probe.approval_state === 'modified_requires_reapproval' ? ['hook_modified_requires_reapproval'] : [])
|
|
48
|
+
],
|
|
49
|
+
warnings: [
|
|
50
|
+
...probe.warnings,
|
|
51
|
+
...(probe.approval_state === 'pending_review' ? ['hook_approval_pending_review'] : []),
|
|
52
|
+
...(probe.approval_state === 'unknown' ? ['hook_approval_state_unknown'] : [])
|
|
53
|
+
]
|
|
36
54
|
};
|
|
37
55
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-lifecycle.json'), report).catch(() => undefined);
|
|
38
56
|
return report;
|
|
39
57
|
}
|
|
58
|
+
function messageOf(err) {
|
|
59
|
+
return err instanceof Error ? err.message : String(err);
|
|
60
|
+
}
|
|
40
61
|
//# sourceMappingURL=codex-hook-lifecycle.js.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
3
|
+
import { ensureDir, mergeManagedBlock, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
5
4
|
export async function runCodexInitDeep(input = {}) {
|
|
6
5
|
const root = path.resolve(input.root || process.cwd());
|
|
7
6
|
const dirs = await scoreDirectories(root);
|
|
@@ -9,20 +8,45 @@ export async function runCodexInitDeep(input = {}) {
|
|
|
9
8
|
const contextDir = path.join(root, '.sneakoscope', 'context');
|
|
10
9
|
const generatedPath = path.join(contextDir, 'AGENTS.generated.md');
|
|
11
10
|
const markdown = renderGeneratedAgents(selected);
|
|
11
|
+
const directoryLocalAgents = { created: [], updated: [], skipped: [], backup_paths: [], blockers: [] };
|
|
12
12
|
if (input.apply === true) {
|
|
13
13
|
await ensureDir(contextDir);
|
|
14
14
|
await writeTextAtomic(generatedPath, markdown);
|
|
15
|
+
if (input.directoryLocal === true) {
|
|
16
|
+
for (const row of selected) {
|
|
17
|
+
const agentsPath = path.join(root, row.dir, 'AGENTS.md');
|
|
18
|
+
try {
|
|
19
|
+
await ensureDir(path.dirname(agentsPath));
|
|
20
|
+
const existing = await fs.readFile(agentsPath, 'utf8').catch(() => '');
|
|
21
|
+
if (existing.trim()) {
|
|
22
|
+
const backup = `${agentsPath}.sks-backup-${Date.now()}`;
|
|
23
|
+
await fs.copyFile(agentsPath, backup);
|
|
24
|
+
directoryLocalAgents.backup_paths.push(path.relative(root, backup));
|
|
25
|
+
}
|
|
26
|
+
const status = await mergeManagedBlock(agentsPath, 'SKS INIT-DEEP MANAGED SECTION', renderDirectoryAgentsBlock(row));
|
|
27
|
+
if (status === 'created')
|
|
28
|
+
directoryLocalAgents.created.push(path.relative(root, agentsPath));
|
|
29
|
+
else
|
|
30
|
+
directoryLocalAgents.updated.push(path.relative(root, agentsPath));
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
directoryLocalAgents.skipped.push(path.relative(root, agentsPath));
|
|
34
|
+
directoryLocalAgents.blockers.push(`${path.relative(root, agentsPath)}:${messageOf(err)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
15
38
|
}
|
|
16
39
|
const report = {
|
|
17
40
|
schema: 'sks.codex-init-deep.v1',
|
|
18
41
|
generated_at: nowIso(),
|
|
19
|
-
ok:
|
|
42
|
+
ok: directoryLocalAgents.blockers.length === 0,
|
|
20
43
|
apply: input.apply === true,
|
|
21
44
|
root,
|
|
22
45
|
generated_path: path.relative(root, generatedPath),
|
|
23
46
|
root_agents_preserved: true,
|
|
24
47
|
directory_guidance: selected,
|
|
25
|
-
|
|
48
|
+
directory_local_agents: directoryLocalAgents,
|
|
49
|
+
blockers: directoryLocalAgents.blockers
|
|
26
50
|
};
|
|
27
51
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-init-deep.json'), report).catch(() => undefined);
|
|
28
52
|
return report;
|
|
@@ -32,6 +56,52 @@ export async function readInitDeepMemory(root) {
|
|
|
32
56
|
const text = await fs.readFile(file, 'utf8').catch(() => '');
|
|
33
57
|
return text.trim() ? { path: file, text } : null;
|
|
34
58
|
}
|
|
59
|
+
export async function readInitDeepMemoryHints(root, scopePaths = []) {
|
|
60
|
+
const resolvedRoot = path.resolve(root);
|
|
61
|
+
const hints = [];
|
|
62
|
+
const generated = await readInitDeepMemory(resolvedRoot).catch(() => null);
|
|
63
|
+
if (generated) {
|
|
64
|
+
hints.push({
|
|
65
|
+
path: path.relative(resolvedRoot, generated.path),
|
|
66
|
+
scope: '.',
|
|
67
|
+
summary: generated.text.split(/\r?\n/).filter((line) => /^##\s+/.test(line)).slice(0, 8).join(' | ')
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const candidateDirs = new Set();
|
|
71
|
+
for (const scopePath of scopePaths) {
|
|
72
|
+
const absolute = path.resolve(resolvedRoot, scopePath);
|
|
73
|
+
if (!absolute.startsWith(resolvedRoot))
|
|
74
|
+
continue;
|
|
75
|
+
const stat = await fs.stat(absolute).catch(() => null);
|
|
76
|
+
let dir = stat?.isFile() ? path.dirname(absolute) : absolute;
|
|
77
|
+
while (dir.startsWith(resolvedRoot)) {
|
|
78
|
+
candidateDirs.add(dir);
|
|
79
|
+
if (dir === resolvedRoot)
|
|
80
|
+
break;
|
|
81
|
+
dir = path.dirname(dir);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const dir of [...candidateDirs].sort((a, b) => b.length - a.length)) {
|
|
85
|
+
const file = path.join(dir, 'AGENTS.md');
|
|
86
|
+
const text = await fs.readFile(file, 'utf8').catch(() => '');
|
|
87
|
+
if (!text.trim())
|
|
88
|
+
continue;
|
|
89
|
+
const managed = extractManagedSection(text, 'SKS INIT-DEEP MANAGED SECTION');
|
|
90
|
+
const userSummary = text.replace(/<!-- BEGIN [\s\S]*?<!-- END [^>]+-->/g, '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, 4).join(' | ');
|
|
91
|
+
const summary = [managed ? managed.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, 4).join(' | ') : '', userSummary ? `user:${userSummary}` : ''].filter(Boolean).join(' || ');
|
|
92
|
+
if (summary) {
|
|
93
|
+
hints.push({
|
|
94
|
+
path: path.relative(resolvedRoot, file),
|
|
95
|
+
scope: path.relative(resolvedRoot, dir) || '.',
|
|
96
|
+
summary
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const unique = new Map();
|
|
101
|
+
for (const hint of hints)
|
|
102
|
+
unique.set(`${hint.path}:${hint.scope}`, hint);
|
|
103
|
+
return [...unique.values()].slice(0, 12);
|
|
104
|
+
}
|
|
35
105
|
async function scoreDirectories(root) {
|
|
36
106
|
const counts = new Map();
|
|
37
107
|
await walk(path.join(root, 'src'), root, counts);
|
|
@@ -84,4 +154,27 @@ function renderGeneratedAgents(rows) {
|
|
|
84
154
|
])
|
|
85
155
|
].join('\n');
|
|
86
156
|
}
|
|
157
|
+
function renderDirectoryAgentsBlock(row) {
|
|
158
|
+
return [
|
|
159
|
+
`# SKS Init-Deep Local Guidance: ${row.dir}`,
|
|
160
|
+
'',
|
|
161
|
+
`- Files observed: ${row.file_count}`,
|
|
162
|
+
`- Languages: ${row.languages.join(', ') || 'unknown'}`,
|
|
163
|
+
`- Guidance: ${row.guidance}`,
|
|
164
|
+
'- Preserve user-authored content outside this managed block.',
|
|
165
|
+
'- Hydrate TriWiki/current source before risky edits in this directory.'
|
|
166
|
+
].join('\n');
|
|
167
|
+
}
|
|
168
|
+
function extractManagedSection(text, markerName) {
|
|
169
|
+
const begin = `<!-- BEGIN ${markerName} -->`;
|
|
170
|
+
const end = `<!-- END ${markerName} -->`;
|
|
171
|
+
const beginIdx = text.indexOf(begin);
|
|
172
|
+
const endIdx = text.indexOf(end);
|
|
173
|
+
if (beginIdx < 0 || endIdx < beginIdx)
|
|
174
|
+
return '';
|
|
175
|
+
return text.slice(beginIdx + begin.length, endIdx).trim();
|
|
176
|
+
}
|
|
177
|
+
function messageOf(err) {
|
|
178
|
+
return err instanceof Error ? err.message : String(err);
|
|
179
|
+
}
|
|
87
180
|
//# sourceMappingURL=codex-init-deep.js.map
|