sneakoscope 3.1.3 → 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-app.js +45 -1
- package/dist/commands/codex-lb.js +12 -9
- package/dist/commands/doctor.js +44 -1
- package/dist/core/codex-app/codex-agent-role-sync.js +119 -0
- package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
- package/dist/core/codex-app/codex-app-execution-profile.js +39 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
- package/dist/core/codex-app/codex-app-harness-matrix.js +127 -0
- 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 +61 -0
- package/dist/core/codex-app/codex-init-deep.js +180 -0
- package/dist/core/codex-app/codex-skill-sync.js +164 -0
- package/dist/core/codex-app/lazycodex-analysis.js +72 -0
- package/dist/core/codex-app/lazycodex-interop-policy.js +60 -0
- package/dist/core/codex-app/lazycodex-live-analyzer.js +98 -0
- package/dist/core/commands/loop-command.js +11 -0
- package/dist/core/commands/mad-sks-command.js +113 -17
- package/dist/core/commands/qa-loop-command.js +3 -2
- package/dist/core/commands/research-command.js +2 -2
- package/dist/core/doctor/doctor-readiness-matrix.js +7 -0
- package/dist/core/doctor/doctor-zellij-repair.js +40 -0
- 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/hooks-runtime.js +13 -0
- package/dist/core/init.js +4 -1
- package/dist/core/loops/loop-continuation-enforcer.js +40 -0
- package/dist/core/loops/loop-planner.js +29 -3
- package/dist/core/loops/loop-worker-runtime.js +27 -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 +44 -0
- package/dist/core/zellij/zellij-capability.js +32 -3
- package/dist/core/zellij/zellij-self-heal-types.js +45 -0
- package/dist/core/zellij/zellij-self-heal.js +414 -0
- package/dist/core/zellij/zellij-update.js +39 -6
- package/dist/scripts/sks-3-1-4-directive-check-lib.js +241 -0
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +347 -0
- package/package.json +52 -2
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **3.1.
|
|
38
|
+
SKS **3.1.5** productionizes the Codex App harness with typed probe surfaces, dry-run Zellij self-heal planning, hook approval and `agent_type` evidence, LazyCodex live-source analysis, init-deep directory memory hints, and execution profile routing across Loop, QA, Research, and Naruto artifacts.
|
|
39
39
|
|
|
40
40
|
SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
|
|
41
41
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 3.1.
|
|
7
|
+
Some("--version") => println!("sks-rs 3.1.5"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build-stamp.v1",
|
|
3
3
|
"package_name": "sneakoscope",
|
|
4
|
-
"package_version": "3.1.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "3.1.5",
|
|
5
|
+
"source_digest": "89b81e91ec4d82c33320922ae0dc33bd6562ccf37c58171858e7f703d7767e4b",
|
|
6
|
+
"source_file_count": 2518,
|
|
7
|
+
"built_at_source_time": 1781439913355
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
|
@@ -144,6 +144,8 @@ async function reportPostinstallCodexLbAuth() {
|
|
|
144
144
|
console.log(`codex-lb auth: restored from existing Codex login cache into ${codexLbAuth.env_path}.`);
|
|
145
145
|
else if (codexLbAuth.status === 'synced' || codexLbAuth.status === 'present' || codexLbAuth.status === 'repaired')
|
|
146
146
|
console.log(`codex-lb auth: preserved from ${codexLbAuth.env_path}.`);
|
|
147
|
+
else if (codexLbAuth.status === 'present_unselected')
|
|
148
|
+
console.log('codex-lb auth: preserved but not selected; ChatGPT OAuth remains active.');
|
|
147
149
|
else if (codexLbAuth.status === 'skipped')
|
|
148
150
|
console.log(`codex-lb auth: skipped (${codexLbAuth.reason}).`);
|
|
149
151
|
else if (codexLbAuth.status === 'missing_env_key')
|
|
@@ -284,6 +286,7 @@ async function capturePostinstallCodexLbConfigSnapshot(home = process.env.HOME |
|
|
|
284
286
|
env_path: envPath,
|
|
285
287
|
auth_path: authPath,
|
|
286
288
|
base_url: baseUrl ? normalizeCodexLbBaseUrl(baseUrl) : null,
|
|
289
|
+
selected: hasTopLevelCodexLbSelected(config),
|
|
287
290
|
auth_existed: authExisted,
|
|
288
291
|
auth_text: authText
|
|
289
292
|
};
|
|
@@ -294,7 +297,7 @@ async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
|
|
|
294
297
|
let configRestored = false;
|
|
295
298
|
if (snapshot.base_url) {
|
|
296
299
|
const current = await readText(snapshot.config_path, '');
|
|
297
|
-
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, snapshot.base_url));
|
|
300
|
+
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, snapshot.base_url, snapshot.selected === true));
|
|
298
301
|
const alreadyOk = next === ensureTrailingNewline(current) && codexLbProviderBaseUrl(current);
|
|
299
302
|
if (!alreadyOk) {
|
|
300
303
|
const safeWrite = await safeWriteCodexConfigToml(snapshot.config_path, current, next, 'codex-lb-restore');
|
|
@@ -488,6 +491,12 @@ export async function codexLbStatus(opts = {}) {
|
|
|
488
491
|
const providerEnvKey = codexLbProviderEnvKey(config);
|
|
489
492
|
const providerUsesCodexLbEnvAuth = providerConfigured && providerEnvKey === 'CODEX_LB_API_KEY' && providerOpenAiAuthDisabled;
|
|
490
493
|
const codexAppUsableWithCodexLb = providerUsesCodexLbEnvAuth && envKeyConfigured && Boolean(baseUrl);
|
|
494
|
+
const launchEnvironment = await inspectCodexLbMacLaunchEnvironment(baseUrl, opts).catch((err) => ({
|
|
495
|
+
checked: true,
|
|
496
|
+
available: false,
|
|
497
|
+
status: 'inspect_failed',
|
|
498
|
+
error: err.message
|
|
499
|
+
}));
|
|
491
500
|
return {
|
|
492
501
|
ok: providerConfigured && envKeyConfigured && Boolean(baseUrl) && providerUsesCodexLbEnvAuth,
|
|
493
502
|
config_path: configPath,
|
|
@@ -514,7 +523,8 @@ export async function codexLbStatus(opts = {}) {
|
|
|
514
523
|
auth_path: authPath,
|
|
515
524
|
auth_mode: authMode.mode,
|
|
516
525
|
auth_usable_for_codex_app: authMode.codex_app_usable || codexAppUsableWithCodexLb,
|
|
517
|
-
auth_summary: codexAppUsableWithCodexLb ? 'codex-lb provider uses CODEX_LB_API_KEY env_key; OpenAI OAuth not required' : authMode.summary
|
|
526
|
+
auth_summary: codexAppUsableWithCodexLb ? 'codex-lb provider uses CODEX_LB_API_KEY env_key; OpenAI OAuth not required' : authMode.summary,
|
|
527
|
+
launch_environment: launchEnvironment
|
|
518
528
|
};
|
|
519
529
|
}
|
|
520
530
|
export function formatCodexLbStatusText(status = {}, opts = {}) {
|
|
@@ -541,12 +551,12 @@ export function formatCodexLbStatusText(status = {}, opts = {}) {
|
|
|
541
551
|
lines.push('', 'Sign in to Codex App/CLI again, then run: sks codex-lb repair');
|
|
542
552
|
else if (status.ok && !status.selected)
|
|
543
553
|
lines.push('', 'Run: sks codex-lb repair to activate codex-lb for Codex App.');
|
|
554
|
+
else if (status.ok)
|
|
555
|
+
lines.push('', 'Status: codex-lb active; no repair needed.');
|
|
544
556
|
else if (!status.ok && status.base_url && status.env_key_configured)
|
|
545
557
|
lines.push('', 'Run: sks codex-lb repair to restore the upstream codex-lb provider block.');
|
|
546
558
|
else if (!status.ok)
|
|
547
559
|
lines.push('', 'Run: sks codex-lb setup --host <domain> --api-key <key>');
|
|
548
|
-
else
|
|
549
|
-
lines.push('', 'Repair provider auth: sks codex-lb repair');
|
|
550
560
|
if (backupPresent)
|
|
551
561
|
lines.push('Switch fully away from codex-lb: sks codex-lb release');
|
|
552
562
|
return `${lines.join('\n')}\n`;
|
|
@@ -862,6 +872,17 @@ export async function ensureCodexLbAuthDuringInstall(opts = {}) {
|
|
|
862
872
|
const status = await codexLbStatus(opts);
|
|
863
873
|
if (!status.selected && !status.provider_configured && !status.env_file)
|
|
864
874
|
return { status: 'not_configured', codex_lb: status };
|
|
875
|
+
if (status.ok && !status.selected && status.auth_mode === 'chatgpt_oauth') {
|
|
876
|
+
return {
|
|
877
|
+
ok: true,
|
|
878
|
+
status: 'present_unselected',
|
|
879
|
+
reason: 'chatgpt_oauth_active_codex_lb_unselected',
|
|
880
|
+
config_path: status.config_path,
|
|
881
|
+
env_path: status.env_path,
|
|
882
|
+
base_url: status.base_url,
|
|
883
|
+
codex_lb: status
|
|
884
|
+
};
|
|
885
|
+
}
|
|
865
886
|
await migrateCodexAuthKeyFormat({ home: opts.home });
|
|
866
887
|
if (status.ok && (!status.selected || !status.provider_uses_codex_lb_env_auth))
|
|
867
888
|
return repairCodexLbAuth(opts);
|
|
@@ -1236,6 +1257,9 @@ export async function maybePromptCodexLbSetupForLaunch(args = [], opts = {}) {
|
|
|
1236
1257
|
if (args.includes('--json') || args.includes('--skip-codex-lb') || process.env.SKS_SKIP_CODEX_LB_PROMPT === '1')
|
|
1237
1258
|
return { status: 'skipped' };
|
|
1238
1259
|
let status = await codexLbStatus(opts);
|
|
1260
|
+
if (status.env_key_configured && status.base_url && !status.selected && status.auth_mode === 'chatgpt_oauth') {
|
|
1261
|
+
return { status: 'continued_to_codex', ok: false, chain_health: null, codex_lb: status, reason: 'chatgpt_oauth_active_codex_lb_unselected' };
|
|
1262
|
+
}
|
|
1239
1263
|
if (status.env_key_configured && status.base_url && (!status.provider_configured || !status.selected || !status.provider_uses_codex_lb_env_auth)) {
|
|
1240
1264
|
let promptedRestore = false;
|
|
1241
1265
|
if (!status.provider_configured && canAskYesNo()) {
|
|
@@ -1355,6 +1379,34 @@ async function syncCodexLbMacLaunchEnvironment(values = {}, opts = {}) {
|
|
|
1355
1379
|
return { ok: false, status: 'launch_env_failed', variables: results.map((result) => result.key), failed, error: failed.map((result) => `${result.key}: ${result.error}`).join('; ') };
|
|
1356
1380
|
return { ok: true, status: 'synced', variables: results.map((result) => result.key) };
|
|
1357
1381
|
}
|
|
1382
|
+
async function inspectCodexLbMacLaunchEnvironment(baseUrl = '', opts = {}) {
|
|
1383
|
+
if (process.platform !== 'darwin' && !opts.forceLaunchEnv)
|
|
1384
|
+
return { checked: false, status: 'not_macos', skipped: true };
|
|
1385
|
+
const launchctl = opts.launchctlBin || await which('launchctl').catch(() => null) || await exists('/bin/launchctl').then((ok) => ok ? '/bin/launchctl' : null).catch(() => null);
|
|
1386
|
+
if (!launchctl)
|
|
1387
|
+
return { checked: true, available: false, status: 'launchctl_missing' };
|
|
1388
|
+
const readVar = async (key) => {
|
|
1389
|
+
const result = await runProcess(launchctl, ['getenv', key], { timeoutMs: 3000, maxOutputBytes: 8192 });
|
|
1390
|
+
return result.code === 0 ? String(result.stdout || '').trim() : '';
|
|
1391
|
+
};
|
|
1392
|
+
const currentBaseUrl = await readVar('CODEX_LB_BASE_URL');
|
|
1393
|
+
const currentApiKey = await readVar('CODEX_LB_API_KEY');
|
|
1394
|
+
const baseMatches = !baseUrl || currentBaseUrl === String(baseUrl || '').trim();
|
|
1395
|
+
const basePresent = Boolean(currentBaseUrl);
|
|
1396
|
+
const keyPresent = Boolean(currentApiKey);
|
|
1397
|
+
return {
|
|
1398
|
+
checked: true,
|
|
1399
|
+
available: true,
|
|
1400
|
+
status: basePresent && keyPresent && baseMatches ? 'synced' : basePresent || keyPresent ? 'partial' : 'missing',
|
|
1401
|
+
variables: [
|
|
1402
|
+
...(keyPresent ? ['CODEX_LB_API_KEY'] : []),
|
|
1403
|
+
...(basePresent ? ['CODEX_LB_BASE_URL'] : [])
|
|
1404
|
+
],
|
|
1405
|
+
base_url_present: basePresent,
|
|
1406
|
+
base_url_matches: baseMatches,
|
|
1407
|
+
api_key_present: keyPresent
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1358
1410
|
async function maybeSyncCodexLbSharedLogin(apiKey, opts = {}) {
|
|
1359
1411
|
if (!apiKey)
|
|
1360
1412
|
return { ok: false, status: 'missing_env_key' };
|
|
@@ -2,10 +2,35 @@ import { flag } from '../cli/args.js';
|
|
|
2
2
|
import { printJson } from '../cli/output.js';
|
|
3
3
|
import { codexAccessTokenStatus, codexAppIntegrationStatus, codexChromeExtensionStatus, codexProductDesignPluginStatus, formatCodexAppStatus, formatCodexProductDesignPluginStatus } from '../core/codex-app.js';
|
|
4
4
|
import { codexAppRemoteControlCommand } from '../cli/codex-app-command.js';
|
|
5
|
+
import { sksRoot } from '../core/fsx.js';
|
|
6
|
+
import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-matrix.js';
|
|
7
|
+
import { syncCodexSksSkills } from '../core/codex-app/codex-skill-sync.js';
|
|
8
|
+
import { syncCodexAgentRoles } from '../core/codex-app/codex-agent-role-sync.js';
|
|
9
|
+
import { runCodexInitDeep } from '../core/codex-app/codex-init-deep.js';
|
|
10
|
+
import { buildCodexHookLifecycle } from '../core/codex-app/codex-hook-lifecycle.js';
|
|
11
|
+
import { resolveCodexAppExecutionProfile } from '../core/codex-app/codex-app-execution-profile.js';
|
|
12
|
+
import { buildLazyCodexInteropPolicy } from '../core/codex-app/lazycodex-interop-policy.js';
|
|
5
13
|
export async function run(_command, args = []) {
|
|
6
14
|
const action = args[0] || 'check';
|
|
7
15
|
if (action === 'remote-control' || action === 'remote')
|
|
8
16
|
return codexAppRemoteControlCommand(args.slice(1));
|
|
17
|
+
if (action === 'harness-matrix')
|
|
18
|
+
return printCodexAppResult(args, await buildCodexAppHarnessMatrix({ root: await sksRoot(), applyRepairs: flag(args, '--fix') || flag(args, '--apply') }));
|
|
19
|
+
if (action === 'skill-sync')
|
|
20
|
+
return printCodexAppResult(args, await syncCodexSksSkills({ root: await sksRoot(), apply: flag(args, '--apply') || flag(args, '--fix') }));
|
|
21
|
+
if (action === 'agent-role-sync')
|
|
22
|
+
return printCodexAppResult(args, await syncCodexAgentRoles({ root: await sksRoot(), apply: flag(args, '--apply') || flag(args, '--fix') }));
|
|
23
|
+
if (action === 'init-deep')
|
|
24
|
+
return printCodexAppResult(args, await runCodexInitDeep({ root: await sksRoot(), apply: !flag(args, '--check-only') && !flag(args, '--dry-run') }));
|
|
25
|
+
if (action === 'hook-lifecycle')
|
|
26
|
+
return printCodexAppResult(args, await buildCodexHookLifecycle({ root: await sksRoot(), apply: flag(args, '--apply') || flag(args, '--fix') }));
|
|
27
|
+
if (action === 'execution-profile')
|
|
28
|
+
return printCodexAppResult(args, await resolveCodexAppExecutionProfile({ root: await sksRoot() }));
|
|
29
|
+
if (action === 'interop' && args[1] === 'lazycodex') {
|
|
30
|
+
const modeArg = readOption(args, '--mode', 'coexist');
|
|
31
|
+
const mode = modeArg === 'sks-primary' || modeArg === 'handoff-to-omo' ? modeArg : 'coexist';
|
|
32
|
+
return printCodexAppResult(args, await buildLazyCodexInteropPolicy({ root: await sksRoot(), mode }));
|
|
33
|
+
}
|
|
9
34
|
if (action === 'product-design' || action === 'design-product' || action === 'ensure-product-design') {
|
|
10
35
|
const checkOnly = flag(args, '--check-only') || flag(args, '--no-install');
|
|
11
36
|
const status = await codexProductDesignPluginStatus({
|
|
@@ -66,7 +91,26 @@ export async function run(_command, args = []) {
|
|
|
66
91
|
process.exitCode = 1;
|
|
67
92
|
return;
|
|
68
93
|
}
|
|
69
|
-
console.error('Usage: sks codex-app check|status|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
|
|
94
|
+
console.error('Usage: sks codex-app check|status|harness-matrix|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|interop lazycodex [--mode coexist]|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
|
|
70
95
|
process.exitCode = 1;
|
|
71
96
|
}
|
|
97
|
+
function printCodexAppResult(args = [], result) {
|
|
98
|
+
if (flag(args, '--json')) {
|
|
99
|
+
printJson(result);
|
|
100
|
+
if (result?.ok === false)
|
|
101
|
+
process.exitCode = 1;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
console.log(`${result?.schema || 'sks.codex-app-result'}: ${result?.ok === false ? 'blocked' : 'ok'}`);
|
|
105
|
+
for (const blocker of result?.blockers || [])
|
|
106
|
+
console.log(`- blocker: ${blocker}`);
|
|
107
|
+
for (const warning of result?.warnings || [])
|
|
108
|
+
console.log(`- warning: ${warning}`);
|
|
109
|
+
if (result?.ok === false)
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
function readOption(args = [], name, fallback) {
|
|
113
|
+
const index = args.indexOf(name);
|
|
114
|
+
return index >= 0 && args[index + 1] ? String(args[index + 1]) : fallback;
|
|
115
|
+
}
|
|
72
116
|
//# sourceMappingURL=codex-app.js.map
|
|
@@ -302,16 +302,19 @@ async function resolveNewApiKey(args = []) {
|
|
|
302
302
|
return '';
|
|
303
303
|
}
|
|
304
304
|
function shapeCodexLbStatus(status = {}) {
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
305
|
+
const modes = [];
|
|
306
|
+
if (status.env_file && status.env_key_configured)
|
|
307
|
+
modes.push('durable_env_file');
|
|
308
|
+
if (status.env_loader?.api_key?.source === 'keychain' || status.env_loader?.keychain?.status === 'present')
|
|
309
|
+
modes.push('durable_keychain');
|
|
310
|
+
if (status.launch_environment?.status === 'synced')
|
|
311
|
+
modes.push('durable_launchctl');
|
|
312
|
+
if (!modes.length && status.env_loader?.api_key?.source === 'process.env')
|
|
313
|
+
modes.push('process_only_ephemeral');
|
|
314
|
+
const mode = modes[0] || 'none';
|
|
312
315
|
const persistence = codexLbPersistenceSummary({
|
|
313
|
-
selectedModes:
|
|
314
|
-
appliedModes: mode === 'none' ? ['none'] : [mode],
|
|
316
|
+
selectedModes: modes.length ? modes : [],
|
|
317
|
+
appliedModes: modes.length ? modes : mode === 'none' ? ['none'] : [mode],
|
|
315
318
|
processOnly: mode === 'process_only_ephemeral'
|
|
316
319
|
});
|
|
317
320
|
return {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -23,6 +23,8 @@ import { writeCodex0138CapabilityArtifacts } from '../core/codex-control/codex-0
|
|
|
23
23
|
import { runCodex0138Doctor } from '../core/doctor/codex-0138-doctor.js';
|
|
24
24
|
import { writeCodexPluginInventoryArtifacts, pluginAppTemplatePolicy } from '../core/codex-plugins/codex-plugin-json.js';
|
|
25
25
|
import { writeMcpPluginInventoryArtifacts } from '../core/mcp/mcp-plugin-inventory.js';
|
|
26
|
+
import { runDoctorZellijRepair, doctorZellijRepairConsoleLine } from '../core/doctor/doctor-zellij-repair.js';
|
|
27
|
+
import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-matrix.js';
|
|
26
28
|
export async function run(_command, args = []) {
|
|
27
29
|
const doctorFix = flag(args, '--fix');
|
|
28
30
|
let setupRepair = null;
|
|
@@ -149,6 +151,23 @@ export async function run(_command, args = []) {
|
|
|
149
151
|
blockers: [err?.message || String(err)]
|
|
150
152
|
}))
|
|
151
153
|
: codexAppUiPlan;
|
|
154
|
+
const zellijRepair = await runDoctorZellijRepair({ root, args, doctorFix }).catch((err) => ({
|
|
155
|
+
schema: 'sks.zellij-self-heal.v1',
|
|
156
|
+
ok: false,
|
|
157
|
+
requested_by: 'doctor --fix',
|
|
158
|
+
fix_requested: doctorFix,
|
|
159
|
+
auto_approved: flag(args, '--yes') || flag(args, '-y'),
|
|
160
|
+
install_homebrew_allowed: false,
|
|
161
|
+
before: { status: 'unknown', version: null, bin: null },
|
|
162
|
+
latest_version: null,
|
|
163
|
+
strategy: 'failed',
|
|
164
|
+
command: 'sks doctor --fix --yes',
|
|
165
|
+
after: { status: 'unknown', version: null, bin: null },
|
|
166
|
+
mutation_guard_artifact: null,
|
|
167
|
+
homebrew: { present: false, bin: null, install_attempted: false, install_allowed: false },
|
|
168
|
+
blockers: [err?.message || String(err)],
|
|
169
|
+
warnings: []
|
|
170
|
+
}));
|
|
152
171
|
const zellij = await checkZellijCapability({ root, require: process.env.SKS_REQUIRE_ZELLIJ === '1' });
|
|
153
172
|
const localModel = await readLocalModelConfig().catch(() => null);
|
|
154
173
|
const permissionProfiles = await inventoryCodexPermissionProfiles(root, { writeReport: true });
|
|
@@ -178,6 +197,15 @@ export async function run(_command, args = []) {
|
|
|
178
197
|
const mcpPluginInventory = pluginInventory?.report
|
|
179
198
|
? await writeMcpPluginInventoryArtifacts(root, { inventory: pluginInventory.report }).catch((err) => ({ error: err?.message || String(err), candidates: null }))
|
|
180
199
|
: null;
|
|
200
|
+
const codexAppHarnessMatrix = await buildCodexAppHarnessMatrix({ root, applyRepairs: doctorFix }).catch((err) => ({
|
|
201
|
+
schema: 'sks.codex-app-harness-matrix.v1',
|
|
202
|
+
ok: false,
|
|
203
|
+
codex_cli: { available: false, version: null },
|
|
204
|
+
app_features: {},
|
|
205
|
+
sks_integrations: {},
|
|
206
|
+
blockers: [err?.message || String(err)],
|
|
207
|
+
warnings: []
|
|
208
|
+
}));
|
|
181
209
|
const pkgBytes = await dirSize(root).catch(() => 0);
|
|
182
210
|
const ready = await writeDoctorReadinessMatrix(root, {
|
|
183
211
|
codex,
|
|
@@ -194,10 +222,12 @@ export async function run(_command, args = []) {
|
|
|
194
222
|
codex_0138_doctor: codex0138Doctor,
|
|
195
223
|
codex_plugin_inventory: pluginInventory?.report || null,
|
|
196
224
|
codex_plugin_app_template_policy: pluginPolicy,
|
|
225
|
+
codex_app_harness_matrix: codexAppHarnessMatrix,
|
|
197
226
|
require_codex_cli_config_load: flag(args, '--fix') || flag(args, '--require-actual-codex'),
|
|
198
227
|
operator_actions: [
|
|
199
228
|
...(codexConfig.operator_actions || []),
|
|
200
229
|
...(configRepair?.operator_actions || []),
|
|
230
|
+
...(zellijRepair && !zellijRepair.ok && zellijRepair.command ? [`Run: ${zellijRepair.command}`] : []),
|
|
201
231
|
...(pluginPolicy?.doctor_warnings || [])
|
|
202
232
|
]
|
|
203
233
|
});
|
|
@@ -217,6 +247,7 @@ export async function run(_command, args = []) {
|
|
|
217
247
|
codex_doctor: codexDoctor,
|
|
218
248
|
codex_doctor_diff: codexDoctorDiff,
|
|
219
249
|
zellij,
|
|
250
|
+
zellij_repair: zellijRepair,
|
|
220
251
|
local_model: localModel,
|
|
221
252
|
agent_role_config: agentRoleConfigRepair,
|
|
222
253
|
zellij_readiness: zellijReadiness,
|
|
@@ -233,10 +264,11 @@ export async function run(_command, args = []) {
|
|
|
233
264
|
plugin_app_template_policy: pluginPolicy,
|
|
234
265
|
mcp_plugin_inventory: mcpPluginInventory?.candidates || null
|
|
235
266
|
},
|
|
267
|
+
codex_app_harness_matrix: codexAppHarnessMatrix,
|
|
236
268
|
ready,
|
|
237
269
|
sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
|
|
238
270
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
239
|
-
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair }
|
|
271
|
+
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair }
|
|
240
272
|
};
|
|
241
273
|
if (flag(args, '--json')) {
|
|
242
274
|
printJson(result);
|
|
@@ -260,9 +292,20 @@ export async function run(_command, args = []) {
|
|
|
260
292
|
console.log(` pane proof: ${zellijReadiness.pane_proof}`);
|
|
261
293
|
console.log(` screen proof:${zellijReadiness.screen_proof}`);
|
|
262
294
|
console.log(` tmux: ${zellijReadiness.tmux_removed_runtime ? 'removed_runtime' : 'present'}`);
|
|
295
|
+
const zellijRepairLine = doctorZellijRepairConsoleLine(zellijRepair);
|
|
296
|
+
if (zellijRepairLine)
|
|
297
|
+
console.log(zellijRepairLine);
|
|
263
298
|
console.log(` codex doctor: ${codexDoctor.available ? (codexDoctor.exit_code === 0 ? 'ok' : 'warning') : 'unavailable'}`);
|
|
264
299
|
console.log(`Rust acc.: ${rust.mode || (rust.available ? 'rust_accelerated' : 'js_fallback')} ${rust.version || rust.status || ''}`);
|
|
265
300
|
console.log(`Codex App: ${ready.codex_app_ready ? 'ok' : 'optional_missing'}`);
|
|
301
|
+
console.log('Codex App Harness:');
|
|
302
|
+
console.log(` plugins: ${codexAppHarnessMatrix.app_features?.plugin_json ? 'ok' : 'degraded'}`);
|
|
303
|
+
console.log(` hook approval: ${codexAppHarnessMatrix.app_features?.hook_approval_state_detectable ? 'ok' : 'unknown'}`);
|
|
304
|
+
console.log(` skills: ${codexAppHarnessMatrix.sks_integrations?.dollar_skills_synced ? 'ok' : 'degraded'}`);
|
|
305
|
+
console.log(` agent roles: ${codexAppHarnessMatrix.sks_integrations?.agent_roles_synced ? 'ok' : 'degraded'}`);
|
|
306
|
+
console.log(` native agent_type: ${codexAppHarnessMatrix.app_features?.agent_type_supported ? 'ok' : 'fallback message-role'}`);
|
|
307
|
+
console.log(` init-deep memory: ${codexAppHarnessMatrix.sks_integrations?.init_deep_available ? 'available' : 'missing'}`);
|
|
308
|
+
console.log(` loop mesh app profile: ${codexAppHarnessMatrix.sks_integrations?.loop_mesh_app_profile_available ? 'available' : 'missing'}`);
|
|
266
309
|
console.log('Codex App UI:');
|
|
267
310
|
console.log(` fast selector: ${codexAppUi.fast_selector || 'unknown'}`);
|
|
268
311
|
console.log(` provider selector: ${codexAppUi.provider_selector || 'unknown'}`);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { nowIso, writeJsonAtomic, writeTextAtomic, ensureDir } from '../fsx.js';
|
|
5
|
+
import { repairAgentRoleConfigs } from '../agents/agent-role-config.js';
|
|
6
|
+
import { agentRolePayloadFor, probeCodexAgentTypeSupport } from './codex-agent-type-probe.js';
|
|
7
|
+
const DIRECTIVE_ROLES = [
|
|
8
|
+
'sks-explorer',
|
|
9
|
+
'sks-planner',
|
|
10
|
+
'sks-implementer',
|
|
11
|
+
'sks-checker',
|
|
12
|
+
'sks-release-verifier',
|
|
13
|
+
'sks-zellij-ui-verifier',
|
|
14
|
+
'sks-codex-probe-verifier'
|
|
15
|
+
];
|
|
16
|
+
export async function syncCodexAgentRoles(input) {
|
|
17
|
+
const root = path.resolve(input.root);
|
|
18
|
+
const env = input.env || process.env;
|
|
19
|
+
const codexHome = input.codexHome || process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
20
|
+
const targetDir = path.join(codexHome, 'agents');
|
|
21
|
+
const baseRepair = await repairAgentRoleConfigs({
|
|
22
|
+
root,
|
|
23
|
+
apply: input.apply === true,
|
|
24
|
+
codexHome,
|
|
25
|
+
reportPath: path.join(root, '.sneakoscope', 'reports', 'agent-role-config-repair.json')
|
|
26
|
+
}).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
27
|
+
const agentTypeProbe = input.agentTypeSupported === undefined
|
|
28
|
+
? await probeCodexAgentTypeSupport(root, { env }).catch((err) => ({
|
|
29
|
+
schema: 'sks.codex-agent-type-probe.v1',
|
|
30
|
+
generated_at: nowIso(),
|
|
31
|
+
ok: false,
|
|
32
|
+
supported: false,
|
|
33
|
+
source: 'unknown',
|
|
34
|
+
spawn_tool_name: 'unknown',
|
|
35
|
+
schema_path: null,
|
|
36
|
+
evidence: [],
|
|
37
|
+
blockers: [messageOf(err)],
|
|
38
|
+
warnings: ['agent_type_probe_failed_message_role_fallback']
|
|
39
|
+
}))
|
|
40
|
+
: {
|
|
41
|
+
schema: 'sks.codex-agent-type-probe.v1',
|
|
42
|
+
generated_at: nowIso(),
|
|
43
|
+
ok: true,
|
|
44
|
+
supported: input.agentTypeSupported,
|
|
45
|
+
source: 'fixture',
|
|
46
|
+
spawn_tool_name: input.agentTypeSupported ? 'spawn_agent' : 'unknown',
|
|
47
|
+
schema_path: input.agentTypeSupported ? 'input.agentTypeSupported' : null,
|
|
48
|
+
evidence: [`input.agentTypeSupported=${input.agentTypeSupported}`],
|
|
49
|
+
blockers: [],
|
|
50
|
+
warnings: []
|
|
51
|
+
};
|
|
52
|
+
const rolePayloads = Object.fromEntries(DIRECTIVE_ROLES.map((role) => [role, agentRolePayloadFor(role, agentTypeProbe)]));
|
|
53
|
+
const created = [];
|
|
54
|
+
if (input.apply === true) {
|
|
55
|
+
await ensureDir(targetDir);
|
|
56
|
+
for (const role of DIRECTIVE_ROLES) {
|
|
57
|
+
const file = path.join(targetDir, `${role}.toml`);
|
|
58
|
+
const current = await fs.readFile(file, 'utf8').catch(() => '');
|
|
59
|
+
if (current && !current.includes('SKS managed 3.1.4 directive role') && !current.includes('SKS managed 3.1.5 directive role'))
|
|
60
|
+
continue;
|
|
61
|
+
await writeTextAtomic(file, roleToml(role, rolePayloads[role]));
|
|
62
|
+
created.push(file);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const report = {
|
|
66
|
+
schema: 'sks.codex-agent-role-sync.v1',
|
|
67
|
+
generated_at: nowIso(),
|
|
68
|
+
ok: recordOk(baseRepair) !== false && agentTypeProbe.ok !== false,
|
|
69
|
+
apply: input.apply === true,
|
|
70
|
+
agent_type_supported: agentTypeProbe.supported,
|
|
71
|
+
fallback: agentTypeProbe.supported ? 'agent_type' : 'message-role',
|
|
72
|
+
codex_home: codexHome,
|
|
73
|
+
directive_roles: DIRECTIVE_ROLES,
|
|
74
|
+
role_payloads: rolePayloads,
|
|
75
|
+
agent_type_probe: agentTypeProbe,
|
|
76
|
+
created,
|
|
77
|
+
base_repair: baseRepair,
|
|
78
|
+
blockers: blockersOf(baseRepair)
|
|
79
|
+
};
|
|
80
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-agent-role-sync.json'), report).catch(() => undefined);
|
|
81
|
+
return report;
|
|
82
|
+
}
|
|
83
|
+
function roleToml(role, payload) {
|
|
84
|
+
const strategyLine = payload?.strategy === 'agent_type'
|
|
85
|
+
? `agent_type = "${payload.agent_type || role}"`
|
|
86
|
+
: `message_role_prefix = "${escapeToml(payload?.message_role_prefix || `Role: ${role}.`)}"`;
|
|
87
|
+
return [
|
|
88
|
+
`name = "${role}"`,
|
|
89
|
+
`description = "SKS managed 3.1.5 directive role: ${role}"`,
|
|
90
|
+
strategyLine,
|
|
91
|
+
'model_reasoning_effort = "medium"',
|
|
92
|
+
role.includes('implementer') ? 'sandbox_mode = "workspace-write"' : 'sandbox_mode = "read-only"',
|
|
93
|
+
'approval_policy = "never"',
|
|
94
|
+
'developer_instructions = """',
|
|
95
|
+
`You are ${role}. SKS managed 3.1.5 directive role.`,
|
|
96
|
+
'Use the assigned scope only, cite concrete repo evidence, keep mutation surfaces bounded, and never clobber user files.',
|
|
97
|
+
'Report blockers as evidence-backed findings and write route artifacts before claiming completion.',
|
|
98
|
+
`Execution role strategy: ${payload?.strategy || 'message-role'}. Probe: ${payload?.probe_artifact_path || '.sneakoscope/reports/codex-agent-type-probe.json'}.`,
|
|
99
|
+
'"""',
|
|
100
|
+
''
|
|
101
|
+
].join('\n');
|
|
102
|
+
}
|
|
103
|
+
function blockersOf(value) {
|
|
104
|
+
return Boolean(value) && typeof value === 'object' && Array.isArray(value.blockers)
|
|
105
|
+
? (value.blockers).map((item) => String(item)).filter(Boolean)
|
|
106
|
+
: [];
|
|
107
|
+
}
|
|
108
|
+
function recordOk(value) {
|
|
109
|
+
return Boolean(value) && typeof value === 'object' && typeof value.ok === 'boolean'
|
|
110
|
+
? value.ok
|
|
111
|
+
: undefined;
|
|
112
|
+
}
|
|
113
|
+
function escapeToml(value) {
|
|
114
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
115
|
+
}
|
|
116
|
+
function messageOf(err) {
|
|
117
|
+
return err instanceof Error ? err.message : String(err);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=codex-agent-role-sync.js.map
|