sneakoscope 0.6.64 → 0.6.66
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 +26 -8
- package/package.json +1 -1
- package/src/cli/main.mjs +82 -17
- package/src/core/auto-review.mjs +8 -5
- package/src/core/cmux-ui.mjs +121 -17
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +5 -1
- package/src/core/routes.mjs +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ sks selftest --mock
|
|
|
40
40
|
|
|
41
41
|
| Area | What it does |
|
|
42
42
|
| --- | --- |
|
|
43
|
-
| CLI runtime | `sks`, `sks cmux`, and `sks --mad
|
|
43
|
+
| CLI runtime | `sks`, `sks cmux`, and `sks --mad` open Codex CLI in a cmux workspace. |
|
|
44
44
|
| Codex App commands | Installs generated skills so `$Team`, `$DFix`, `$QA-LOOP`, `$Ralph`, `$DB`, `$Wiki`, `$Help`, and related routes are visible in prompt workflows. |
|
|
45
45
|
| Team orchestration | Runs substantial work through ambiguity handling, scouts, TriWiki refresh, debate, runtime task graphs, worker inboxes, implementation, review, cleanup, reflection, and Honest Mode. |
|
|
46
46
|
| QA loop | Dogfoods UI/API behavior with safety gates, Browser/Computer evidence, safe fixes, and rechecks. |
|
|
@@ -59,7 +59,7 @@ sks selftest --mock
|
|
|
59
59
|
- cmux for the CLI-first runtime
|
|
60
60
|
- Context7 MCP for current-docs-gated routes
|
|
61
61
|
|
|
62
|
-
On macOS, `sks --mad
|
|
62
|
+
On macOS, `sks --mad` can install cmux through Homebrew when cmux is missing. You can also install it manually:
|
|
63
63
|
|
|
64
64
|
```sh
|
|
65
65
|
brew tap manaflow-ai/cmux
|
|
@@ -70,8 +70,17 @@ If the CLI is not on `PATH`, SKS also checks the app bundle path:
|
|
|
70
70
|
|
|
71
71
|
```sh
|
|
72
72
|
/Applications/cmux.app/Contents/Resources/bin/cmux
|
|
73
|
+
/Applications/cmux.app/Contents/MacOS/cmux
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
`sks --mad` is stricter than the normal runtime path:
|
|
77
|
+
|
|
78
|
+
- Checks npm for a newer `sneakoscope` before launch and asks whether to update when the terminal can answer y/n.
|
|
79
|
+
- Installs the latest Codex CLI with `npm i -g @openai/codex@latest` when it is missing and you approve or pass `--yes`.
|
|
80
|
+
- Installs or upgrades the latest cmux cask through Homebrew when cmux is missing or not launchable.
|
|
81
|
+
- Re-probes the real cmux binary after install instead of trusting Homebrew's success text alone.
|
|
82
|
+
- Wakes cmux and retries the socket probe; if the socket is broken, SKS attempts a cmux app restart during that explicit launch.
|
|
83
|
+
|
|
75
84
|
## Installation
|
|
76
85
|
|
|
77
86
|
### Global Install
|
|
@@ -154,13 +163,22 @@ sks cmux status --once
|
|
|
154
163
|
|
|
155
164
|
`sks` opens a cmux workspace for Codex CLI when running in an interactive terminal. `sks cmux check` is diagnostic and prints readiness without starting a workspace.
|
|
156
165
|
|
|
157
|
-
### MAD
|
|
166
|
+
### MAD cmux Workspace
|
|
158
167
|
|
|
159
168
|
```sh
|
|
160
|
-
sks --mad
|
|
169
|
+
sks --mad
|
|
170
|
+
sks --mad --yes
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
This creates/uses the `sks-mad-high` Codex profile for a one-shot full-access, high-reasoning cmux workspace with `approval_policy = "on-request"` and `approvals_reviewer = "auto_review"`. It is scoped to that explicit command and does not change normal SKS/DB safety defaults.
|
|
174
|
+
|
|
175
|
+
Before launching, SKS checks whether a newer `sneakoscope` exists on npm. In an interactive terminal it prompts:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
SKS 0.x.y -> 0.x.z update before MAD launch? [Y/n]
|
|
161
179
|
```
|
|
162
180
|
|
|
163
|
-
|
|
181
|
+
Answer `y` to install `sneakoscope@latest`, then rerun `sks --mad`. Answer `n` to continue with the current version. Use `--yes` to approve missing dependency installs automatically.
|
|
164
182
|
|
|
165
183
|
### Team Missions
|
|
166
184
|
|
|
@@ -191,7 +209,7 @@ sks gx render homepage --format html
|
|
|
191
209
|
|
|
192
210
|
Sneakoscope has two surfaces:
|
|
193
211
|
|
|
194
|
-
- Terminal commands such as `sks deps check`, `sks team "task"`, and `sks --mad
|
|
212
|
+
- Terminal commands such as `sks deps check`, `sks team "task"`, and `sks --mad`
|
|
195
213
|
- Codex App prompt commands such as `$Team`, `$DFix`, `$QA-LOOP`, and `$Wiki`
|
|
196
214
|
|
|
197
215
|
After installing, run:
|
|
@@ -269,7 +287,7 @@ sks
|
|
|
269
287
|
For the high-reasoning full-access profile:
|
|
270
288
|
|
|
271
289
|
```sh
|
|
272
|
-
sks --mad
|
|
290
|
+
sks --mad
|
|
273
291
|
```
|
|
274
292
|
|
|
275
293
|
### Use Codex App `$Team`
|
|
@@ -331,7 +349,7 @@ sks deps install cmux
|
|
|
331
349
|
sks cmux check
|
|
332
350
|
```
|
|
333
351
|
|
|
334
|
-
`sks --mad
|
|
352
|
+
`sks --mad` also attempts Homebrew installation or upgrade automatically on macOS when cmux is missing. If Homebrew reports the cask installed but the CLI still is not reachable, SKS checks the cmux app bundle paths directly, wakes the app, and retries the socket before reporting a remaining cmux app/socket issue.
|
|
335
353
|
|
|
336
354
|
### Codex App tools are missing
|
|
337
355
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.66",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Ralph, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
package/src/cli/main.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import { context7Evidence, evaluateStop, recordContext7Evidence, recordSubagentE
|
|
|
30
30
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
|
|
31
31
|
import { appendTeamEvent, formatRoleCounts, initTeamLive, normalizeTeamSpec, parseTeamSpecArgs, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail } from '../core/team-live.mjs';
|
|
32
32
|
import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
|
|
33
|
-
import { buildCmuxLaunchPlan, defaultCmuxWorkspaceName, ensureCmuxInstalled, formatCmuxBanner, launchCmuxTeamView, launchCmuxUi, platformCmuxInstallHint, runCmuxStatus, sanitizeCmuxWorkspaceName, cmuxAvailable } from '../core/cmux-ui.mjs';
|
|
33
|
+
import { CMUX_BREW_COMMAND, CMUX_BREW_UPGRADE_COMMAND, buildCmuxLaunchPlan, defaultCmuxWorkspaceName, ensureCmuxInstalled, formatCmuxBanner, launchCmuxTeamView, launchCmuxUi, platformCmuxInstallHint, runCmuxStatus, sanitizeCmuxWorkspaceName, cmuxAvailable } from '../core/cmux-ui.mjs';
|
|
34
34
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
35
35
|
|
|
36
36
|
const flag = (args, name) => args.includes(name);
|
|
@@ -116,7 +116,7 @@ Usage:
|
|
|
116
116
|
sks bootstrap [--install-scope global|project] [--local-only] [--json]
|
|
117
117
|
sks deps check|install [cmux|codex|context7|all] [--yes] [--json]
|
|
118
118
|
sks codex-app
|
|
119
|
-
sks --mad --high
|
|
119
|
+
sks --mad [--high]
|
|
120
120
|
sks auto-review status|enable|start [--high]
|
|
121
121
|
sks --Auto-review [--high]
|
|
122
122
|
sks cmux [--workspace name]
|
|
@@ -389,13 +389,13 @@ async function ensureCodexCliTool({ skip = false } = {}) {
|
|
|
389
389
|
const before = await getCodexInfo().catch(() => ({}));
|
|
390
390
|
if (before.bin) return { status: 'present', bin: before.bin, version: before.version || null };
|
|
391
391
|
const npmBin = await which('npm');
|
|
392
|
-
if (!npmBin) return { status: 'failed', error: 'npm not found on PATH; install Codex CLI manually with npm i -g @openai/codex.' };
|
|
393
|
-
const install = await runProcess(npmBin, ['i', '-g', '@openai/codex'], {
|
|
392
|
+
if (!npmBin) return { status: 'failed', error: 'npm not found on PATH; install Codex CLI manually with npm i -g @openai/codex@latest.' };
|
|
393
|
+
const install = await runProcess(npmBin, ['i', '-g', '@openai/codex@latest'], {
|
|
394
394
|
timeoutMs: 120000,
|
|
395
395
|
maxOutputBytes: 128 * 1024
|
|
396
396
|
}).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
397
397
|
if (install.code !== 0) {
|
|
398
|
-
return { status: 'failed', error: `${install.stderr || install.stdout || 'npm i -g @openai/codex failed'}`.trim() };
|
|
398
|
+
return { status: 'failed', error: `${install.stderr || install.stdout || 'npm i -g @openai/codex@latest failed'}`.trim() };
|
|
399
399
|
}
|
|
400
400
|
const after = await getCodexInfo().catch(() => ({}));
|
|
401
401
|
return {
|
|
@@ -1045,10 +1045,30 @@ async function cmuxCommand(sub = 'start', args = []) {
|
|
|
1045
1045
|
|
|
1046
1046
|
async function madHighCommand(args = []) {
|
|
1047
1047
|
const cleanArgs = args.filter((arg) => !['--mad', '--MAD', '--mad-sks', '--high', '--no-auto-install-cmux'].includes(arg));
|
|
1048
|
+
if (flag(args, '--json')) {
|
|
1049
|
+
const profile = await enableMadHighProfile();
|
|
1050
|
+
return console.log(JSON.stringify(profile, null, 2));
|
|
1051
|
+
}
|
|
1052
|
+
const update = await maybePromptSksUpdateForMad(args);
|
|
1053
|
+
if (update.status === 'updated') {
|
|
1054
|
+
console.log(`SKS updated from ${PACKAGE_VERSION} to ${update.latest}. Rerun: sks --mad`);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (update.status === 'failed') {
|
|
1058
|
+
console.error(`SKS update failed: ${update.error}`);
|
|
1059
|
+
process.exitCode = 1;
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
const deps = await ensureMadLaunchDependencies(args);
|
|
1063
|
+
if (!deps.ready) {
|
|
1064
|
+
console.error('SKS MAD launch blocked by missing dependencies.');
|
|
1065
|
+
for (const action of deps.actions) printDepsInstallAction(action);
|
|
1066
|
+
process.exitCode = 1;
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1048
1069
|
const profile = await enableMadHighProfile();
|
|
1049
|
-
|
|
1050
|
-
console.log(
|
|
1051
|
-
console.log('Scope: explicit cmux launch only; normal SKS/DB safety returns after this command.');
|
|
1070
|
+
console.log(`SKS MAD auto-review profile ready: ${madHighProfileName()}`);
|
|
1071
|
+
console.log('Scope: explicit cmux launch only; full access uses Codex auto_review approvals when approval prompts are raised.');
|
|
1052
1072
|
const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${defaultCmuxWorkspaceName(process.cwd())}`));
|
|
1053
1073
|
return launchCmuxUi([...cleanArgs, '--workspace', workspace], {
|
|
1054
1074
|
codexArgs: ['--profile', profile.profile_name],
|
|
@@ -1057,6 +1077,44 @@ async function madHighCommand(args = []) {
|
|
|
1057
1077
|
});
|
|
1058
1078
|
}
|
|
1059
1079
|
|
|
1080
|
+
async function maybePromptSksUpdateForMad(args = []) {
|
|
1081
|
+
if (flag(args, '--json') || flag(args, '--skip-update-check') || process.env.SKS_SKIP_UPDATE_CHECK === '1') return { status: 'skipped' };
|
|
1082
|
+
const latest = await npmPackageVersion('sneakoscope');
|
|
1083
|
+
if (!latest.version || compareVersions(latest.version, PACKAGE_VERSION) <= 0) return { status: 'current', latest: latest.version || null, error: latest.error || null };
|
|
1084
|
+
const command = 'npm i -g sneakoscope@latest';
|
|
1085
|
+
if (flag(args, '--yes') || flag(args, '-y')) return installSksLatest(command, latest.version);
|
|
1086
|
+
if (!canAskYesNo()) {
|
|
1087
|
+
console.log(`SKS update available: ${PACKAGE_VERSION} -> ${latest.version}. Run: ${command}`);
|
|
1088
|
+
return { status: 'available', latest: latest.version, command };
|
|
1089
|
+
}
|
|
1090
|
+
const answer = (await askPostinstallQuestion(`SKS ${PACKAGE_VERSION} -> ${latest.version} update before MAD launch? [Y/n] `)).trim();
|
|
1091
|
+
const yes = answer === '' || /^(y|yes|예|네|응)$/i.test(answer);
|
|
1092
|
+
if (!yes) return { status: 'skipped_by_user', latest: latest.version, command };
|
|
1093
|
+
return installSksLatest(command, latest.version);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
async function installSksLatest(command, latestVersion) {
|
|
1097
|
+
const npm = await which('npm').catch(() => null);
|
|
1098
|
+
if (!npm) return { status: 'failed', latest: latestVersion, command, error: 'npm not found on PATH' };
|
|
1099
|
+
const install = await runProcess(npm, ['i', '-g', 'sneakoscope@latest'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
1100
|
+
if (install.code !== 0) return { status: 'failed', latest: latestVersion, command, error: `${install.stderr || install.stdout || command + ' failed'}`.trim() };
|
|
1101
|
+
return { status: 'updated', latest: latestVersion, command };
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
async function ensureMadLaunchDependencies(args = []) {
|
|
1105
|
+
const actions = [];
|
|
1106
|
+
if (!flag(args, '--skip-cli-tools')) {
|
|
1107
|
+
const codex = await getCodexInfo().catch(() => ({}));
|
|
1108
|
+
if (!codex.bin) actions.push(await installCodexDependency(args, { prompt: 'Codex CLI missing. Install latest Codex CLI with npm i -g @openai/codex@latest?' }));
|
|
1109
|
+
}
|
|
1110
|
+
if (!flag(args, '--no-auto-install-cmux')) {
|
|
1111
|
+
const cmux = await cmuxAvailable().catch(() => ({ ok: false }));
|
|
1112
|
+
if (!cmux.ok && !cmux.bin) actions.push(await installCmuxDependency(args));
|
|
1113
|
+
}
|
|
1114
|
+
const status = await depsStatus(await projectRoot());
|
|
1115
|
+
return { ready: Boolean(status.codex_cli.ok && (status.cmux.ok || status.cmux.bin)), actions, status };
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1060
1118
|
async function deps(sub = 'check', args = []) {
|
|
1061
1119
|
const action = sub || 'check';
|
|
1062
1120
|
if (action === 'check' || action === 'status') {
|
|
@@ -1098,7 +1156,7 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1098
1156
|
context7,
|
|
1099
1157
|
browser_use: { ok: app.mcp.has_browser_use, cache: app.plugins.browser_use_cache },
|
|
1100
1158
|
computer_use: { ok: app.mcp.has_computer_use, cache: app.plugins.computer_use_cache },
|
|
1101
|
-
cmux: { ok: Boolean(cmux.ok), version: cmux.version || null, install_hint: cmux.ok ? null : platformCmuxInstallHint(), error: cmux.error || null },
|
|
1159
|
+
cmux: { ok: Boolean(cmux.ok), bin: cmux.bin || null, version: cmux.version || null, install_hint: cmux.ok ? null : platformCmuxInstallHint(), error: cmux.error || null },
|
|
1102
1160
|
homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew, required_for_cmux_install: homebrewNeeded } : { ok: null, bin: null, required_for_cmux_install: false },
|
|
1103
1161
|
next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7, cmux, brew, nodeOk })
|
|
1104
1162
|
};
|
|
@@ -1153,10 +1211,11 @@ async function depsInstall(args = []) {
|
|
|
1153
1211
|
if (!status.ready) process.exitCode = 1;
|
|
1154
1212
|
}
|
|
1155
1213
|
|
|
1156
|
-
async function installCodexDependency(args = []) {
|
|
1214
|
+
async function installCodexDependency(args = [], opts = {}) {
|
|
1157
1215
|
const before = await getCodexInfo().catch(() => ({}));
|
|
1158
1216
|
if (before.bin) return { target: 'codex', status: 'present', bin: before.bin, version: before.version || null };
|
|
1159
|
-
|
|
1217
|
+
const command = 'npm i -g @openai/codex@latest';
|
|
1218
|
+
if (!await confirmInstall(opts.prompt || `Install Codex CLI with ${command}?`, args)) return { target: 'codex', status: 'needs_approval', command };
|
|
1160
1219
|
return { target: 'codex', ...(await ensureCodexCliTool()) };
|
|
1161
1220
|
}
|
|
1162
1221
|
|
|
@@ -1172,10 +1231,10 @@ async function installCmuxDependency(args = []) {
|
|
|
1172
1231
|
if (before.ok) return { target: 'cmux', status: 'present', version: before.version || null };
|
|
1173
1232
|
if (process.platform === 'darwin') {
|
|
1174
1233
|
const brew = await which('brew').catch(() => null);
|
|
1175
|
-
const command =
|
|
1234
|
+
const command = CMUX_BREW_COMMAND;
|
|
1176
1235
|
if (!brew) return { target: 'cmux', status: 'homebrew_missing', command: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && ${command}` };
|
|
1177
1236
|
if (flag(args, '--dry-run')) return { target: 'cmux', status: 'dry_run', command };
|
|
1178
|
-
if (!await confirmInstall(`Install cmux with Homebrew (${command})?`, args)) return { target: 'cmux', status: 'needs_approval', command };
|
|
1237
|
+
if (!await confirmInstall(`Install/update latest cmux with Homebrew (${command}; ${CMUX_BREW_UPGRADE_COMMAND} if already installed)?`, args)) return { target: 'cmux', status: 'needs_approval', command };
|
|
1179
1238
|
const installed = await ensureCmuxInstalled({ autoInstall: true });
|
|
1180
1239
|
return {
|
|
1181
1240
|
target: 'cmux',
|
|
@@ -1191,10 +1250,14 @@ async function installCmuxDependency(args = []) {
|
|
|
1191
1250
|
|
|
1192
1251
|
async function confirmInstall(question, args = []) {
|
|
1193
1252
|
if (flag(args, '--yes') || flag(args, '-y')) return true;
|
|
1194
|
-
if (!
|
|
1253
|
+
if (!canAskYesNo()) return false;
|
|
1195
1254
|
return /^(y|yes|예|네|응)$/i.test((await askPostinstallQuestion(`${question} [y/N] `)).trim());
|
|
1196
1255
|
}
|
|
1197
1256
|
|
|
1257
|
+
function canAskYesNo() {
|
|
1258
|
+
return Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1198
1261
|
function printDepsInstallAction(action) {
|
|
1199
1262
|
if (!action) return;
|
|
1200
1263
|
console.log(`${action.target}: ${action.status}${action.version ? ` ${action.version}` : ''}`);
|
|
@@ -1767,6 +1830,8 @@ async function npmGlobalSksBin() {
|
|
|
1767
1830
|
}
|
|
1768
1831
|
|
|
1769
1832
|
async function npmPackageVersion(name) {
|
|
1833
|
+
const envName = `SKS_NPM_VIEW_${String(name || '').replace(/[^A-Za-z0-9]+/g, '_').toUpperCase()}_VERSION`;
|
|
1834
|
+
if (process.env[envName]) return { version: process.env[envName] };
|
|
1770
1835
|
const npm = await which('npm').catch(() => null);
|
|
1771
1836
|
if (!npm) return { error: 'npm not found' };
|
|
1772
1837
|
const result = await runProcess(npm, ['view', name, 'version'], { timeoutMs: 5000, maxOutputBytes: 4096 });
|
|
@@ -2259,7 +2324,7 @@ async function selftest() {
|
|
|
2259
2324
|
const madProfilePath = path.join(tmp, 'mad-codex-config.toml');
|
|
2260
2325
|
const madProfile = await enableMadHighProfile({ configPath: madProfilePath });
|
|
2261
2326
|
const madProfileText = await safeReadText(madProfilePath);
|
|
2262
|
-
if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "
|
|
2327
|
+
if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "on-request"') || !madProfileText.includes('approvals_reviewer = "auto_review"') || !madProfileText.includes('model_reasoning_effort = "high"')) throw new Error('selftest failed: MAD high profile is not full-access auto-review high');
|
|
2263
2328
|
if (!isMadHighLaunch(['--mad', '--high']) || isMadHighLaunch(['db', '--mad'])) throw new Error('selftest failed: MAD high launch flag parsing is not top-level only');
|
|
2264
2329
|
const guardBlocked = await checkHarnessModification(tmp, { tool_name: 'apply_patch', command: '*** Update File: .agents/skills/team/SKILL.md\n+tamper\n' });
|
|
2265
2330
|
if (guardBlocked.action !== 'block') throw new Error('selftest failed: harness guard allowed skill tampering');
|
|
@@ -2654,11 +2719,11 @@ async function selftest() {
|
|
|
2654
2719
|
const autoReviewEnabled = await enableAutoReview({ env: autoReviewEnv, high: true });
|
|
2655
2720
|
if (!autoReviewEnabled.enabled || autoReviewEnabled.profile_name !== 'sks-auto-review-high' || !autoReviewEnabled.high_profile) throw new Error('selftest failed: auto-review high profile was not enabled');
|
|
2656
2721
|
const autoReviewConfig = await safeReadText(path.join(autoReviewHome, '.codex', 'config.toml'));
|
|
2657
|
-
if (!autoReviewConfig.includes('approvals_reviewer = "
|
|
2722
|
+
if (!autoReviewConfig.includes('approvals_reviewer = "auto_review"') || autoReviewConfig.includes('approvals_reviewer = "guardian_subagent"') || !autoReviewConfig.includes('[profiles.sks-auto-review-high]')) throw new Error('selftest failed: auto-review config not written');
|
|
2658
2723
|
const autoReviewDisabled = await disableAutoReview({ env: autoReviewEnv });
|
|
2659
2724
|
if (autoReviewDisabled.enabled || autoReviewDisabled.approvals_reviewer !== 'user') throw new Error('selftest failed: auto-review disable did not restore user reviewer');
|
|
2660
2725
|
const autoReviewDisabledConfig = await safeReadText(path.join(autoReviewHome, '.codex', 'config.toml'));
|
|
2661
|
-
if (autoReviewDisabledConfig.includes('approvals_reviewer = "
|
|
2726
|
+
if (autoReviewDisabledConfig.includes('approvals_reviewer = "guardian_subagent"')) throw new Error('selftest failed: auto-review disable left legacy reviewer values');
|
|
2662
2727
|
const analysisAgentExists = await exists(path.join(tmp, '.codex', 'agents', 'analysis-scout.toml'));
|
|
2663
2728
|
if (!analysisAgentExists) throw new Error('selftest failed: analysis scout agent not installed');
|
|
2664
2729
|
const teamAgentExists = await exists(path.join(tmp, '.codex', 'agents', 'team-consensus.toml'));
|
package/src/core/auto-review.mjs
CHANGED
|
@@ -2,8 +2,8 @@ import os from 'node:os';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { ensureDir, exists, readText, writeTextAtomic } from './fsx.mjs';
|
|
4
4
|
|
|
5
|
-
export const AUTO_REVIEW_REVIEWER = '
|
|
6
|
-
export const LEGACY_AUTO_REVIEW_REVIEWER = '
|
|
5
|
+
export const AUTO_REVIEW_REVIEWER = 'auto_review';
|
|
6
|
+
export const LEGACY_AUTO_REVIEW_REVIEWER = 'guardian_subagent';
|
|
7
7
|
export const AUTO_REVIEW_PROFILE = 'sks-auto-review';
|
|
8
8
|
export const AUTO_REVIEW_HIGH_PROFILE = 'sks-auto-review-high';
|
|
9
9
|
export const MAD_HIGH_PROFILE = 'sks-mad-high';
|
|
@@ -64,10 +64,12 @@ export async function enableMadHighProfile(opts = {}) {
|
|
|
64
64
|
let next = upsertTable(current, `profiles.${MAD_HIGH_PROFILE}`, [
|
|
65
65
|
`[profiles.${MAD_HIGH_PROFILE}]`,
|
|
66
66
|
'model = "gpt-5.5"',
|
|
67
|
-
'approval_policy = "
|
|
67
|
+
'approval_policy = "on-request"',
|
|
68
|
+
`approvals_reviewer = "${AUTO_REVIEW_REVIEWER}"`,
|
|
68
69
|
'sandbox_mode = "danger-full-access"',
|
|
69
70
|
'model_reasoning_effort = "high"'
|
|
70
71
|
].join('\n'));
|
|
72
|
+
next = upsertAutoReviewPolicy(next);
|
|
71
73
|
if (!next.endsWith('\n')) next += '\n';
|
|
72
74
|
await writeTextAtomic(configPath, next);
|
|
73
75
|
return {
|
|
@@ -75,7 +77,8 @@ export async function enableMadHighProfile(opts = {}) {
|
|
|
75
77
|
profile_name: MAD_HIGH_PROFILE,
|
|
76
78
|
launch_args: ['--profile', MAD_HIGH_PROFILE],
|
|
77
79
|
sandbox_mode: 'danger-full-access',
|
|
78
|
-
approval_policy: '
|
|
80
|
+
approval_policy: 'on-request',
|
|
81
|
+
approvals_reviewer: AUTO_REVIEW_REVIEWER,
|
|
79
82
|
model_reasoning_effort: 'high',
|
|
80
83
|
scope: 'explicit_launch_only'
|
|
81
84
|
};
|
|
@@ -111,7 +114,7 @@ export function autoReviewSummary(status = {}) {
|
|
|
111
114
|
lines.push('Launch high mode with: sks --Auto-review --high');
|
|
112
115
|
}
|
|
113
116
|
if (status.legacy_invalid) {
|
|
114
|
-
lines.push('', 'Legacy
|
|
117
|
+
lines.push('', 'Legacy reviewer value found: run sks auto-review enable or sks auto-review disable to rewrite Codex config.');
|
|
115
118
|
}
|
|
116
119
|
return lines.join('\n');
|
|
117
120
|
}
|
package/src/core/cmux-ui.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
2
3
|
import { spawnSync } from 'node:child_process';
|
|
3
4
|
import { exists, packageRoot, projectRoot, runProcess, sha256, which } from './fsx.mjs';
|
|
4
5
|
import { getCodexInfo } from './codex-adapter.mjs';
|
|
@@ -14,6 +15,7 @@ export const SKS_CMUX_LOGO = [
|
|
|
14
15
|
].join('\n');
|
|
15
16
|
|
|
16
17
|
export const CMUX_BREW_COMMAND = 'brew tap manaflow-ai/cmux && brew install --cask cmux';
|
|
18
|
+
export const CMUX_BREW_UPGRADE_COMMAND = 'brew tap manaflow-ai/cmux && brew upgrade --cask cmux';
|
|
17
19
|
|
|
18
20
|
export function sanitizeCmuxWorkspaceName(input) {
|
|
19
21
|
const base = String(input || 'sks').trim().replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
@@ -34,8 +36,8 @@ export function platformCmuxInstallHint() {
|
|
|
34
36
|
if (process.platform !== 'darwin') return 'cmux is a native macOS app; install it on macOS 14+ from https://cmux.com or https://github.com/manaflow-ai/cmux.';
|
|
35
37
|
return [
|
|
36
38
|
CMUX_BREW_COMMAND,
|
|
37
|
-
'then
|
|
38
|
-
'
|
|
39
|
+
'then run:',
|
|
40
|
+
'sks cmux check'
|
|
39
41
|
].join(' ');
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -44,17 +46,43 @@ export async function findCmuxBinary() {
|
|
|
44
46
|
if (env && await exists(env)) return env;
|
|
45
47
|
const onPath = await which('cmux').catch(() => null);
|
|
46
48
|
if (onPath) return onPath;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
+
for (const candidate of cmuxBinaryCandidates()) {
|
|
50
|
+
if (await exists(candidate)) return candidate;
|
|
51
|
+
}
|
|
49
52
|
return null;
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
export function cmuxBinaryCandidates() {
|
|
56
|
+
if (process.platform !== 'darwin') return [];
|
|
57
|
+
const envApps = String(process.env.SKS_CMUX_APP_PATHS || '')
|
|
58
|
+
.split(path.delimiter)
|
|
59
|
+
.map((entry) => entry.trim())
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
const appBundles = [
|
|
62
|
+
...envApps,
|
|
63
|
+
'/Applications/cmux.app',
|
|
64
|
+
'/Applications/Cmux.app',
|
|
65
|
+
'/Applications/CMUX.app',
|
|
66
|
+
path.join(process.env.HOME || '', 'Applications', 'cmux.app'),
|
|
67
|
+
'/opt/homebrew/Caskroom/cmux/latest/cmux.app'
|
|
68
|
+
].filter(Boolean);
|
|
69
|
+
const candidates = [];
|
|
70
|
+
for (const app of appBundles) {
|
|
71
|
+
candidates.push(
|
|
72
|
+
path.join(app, 'Contents', 'Resources', 'bin', 'cmux'),
|
|
73
|
+
path.join(app, 'Contents', 'MacOS', 'cmux')
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
candidates.push('/opt/homebrew/bin/cmux', '/usr/local/bin/cmux');
|
|
77
|
+
return Array.from(new Set(candidates));
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
export async function cmuxAvailable() {
|
|
53
81
|
const bin = await findCmuxBinary();
|
|
54
82
|
if (!bin) return { ok: false, bin: null, version: null, error: 'cmux CLI not found' };
|
|
55
|
-
const probe = await runProcess(bin, ['
|
|
83
|
+
const probe = await runProcess(bin, ['version'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
56
84
|
const text = `${probe.stdout || ''}${probe.stderr || ''}`.trim();
|
|
57
|
-
return { ok: probe.code === 0, bin, version: text || 'cmux CLI', error: probe.code === 0 ? null : text || 'cmux
|
|
85
|
+
return { ok: probe.code === 0, bin, version: text || 'cmux CLI', error: probe.code === 0 ? null : text || 'cmux CLI probe failed' };
|
|
58
86
|
}
|
|
59
87
|
|
|
60
88
|
export async function ensureCmuxInstalled(opts = {}) {
|
|
@@ -70,18 +98,28 @@ export async function ensureCmuxInstalled(opts = {}) {
|
|
|
70
98
|
if (!brew) {
|
|
71
99
|
return { target: 'cmux', status: 'homebrew_missing', cmux: before, command: CMUX_BREW_COMMAND, error: 'Homebrew is required for automatic cmux install' };
|
|
72
100
|
}
|
|
73
|
-
if (!opts.quiet) console.log('cmux CLI missing; installing cmux with Homebrew...');
|
|
101
|
+
if (!opts.quiet) console.log('cmux CLI missing; installing/updating cmux with Homebrew...');
|
|
74
102
|
const tap = await runProcess(brew, ['tap', 'manaflow-ai/cmux'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
75
103
|
const install = tap.code === 0
|
|
76
|
-
? await
|
|
104
|
+
? await installOrUpgradeCmuxCask(brew)
|
|
77
105
|
: tap;
|
|
78
106
|
let after = await cmuxAvailable().catch((err) => ({ ok: false, error: err.message || 'cmux probe failed after install' }));
|
|
79
107
|
if (!after.ok && after.bin) after = await wakeCmuxAndReprobe(after);
|
|
80
108
|
if (after.ok) return { target: 'cmux', status: 'installed', cmux: after, command: CMUX_BREW_COMMAND };
|
|
81
|
-
const
|
|
109
|
+
const installText = `${install.stderr || ''}\n${install.stdout || ''}`.trim();
|
|
110
|
+
const rawError = after.error || installText || 'brew install --cask cmux completed, but no working cmux CLI was found';
|
|
82
111
|
return { target: 'cmux', status: 'failed', cmux: after, command: CMUX_BREW_COMMAND, code: install.code, error: rawError };
|
|
83
112
|
}
|
|
84
113
|
|
|
114
|
+
async function installOrUpgradeCmuxCask(brew) {
|
|
115
|
+
const install = await runProcess(brew, ['install', '--cask', 'cmux'], { timeoutMs: 300000, maxOutputBytes: 256 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
116
|
+
if (install.code === 0) return install;
|
|
117
|
+
const text = `${install.stderr || ''}\n${install.stdout || ''}`;
|
|
118
|
+
if (!/already installed|to upgrade|brew upgrade/i.test(text)) return install;
|
|
119
|
+
const upgrade = await runProcess(brew, ['upgrade', '--cask', 'cmux'], { timeoutMs: 300000, maxOutputBytes: 256 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
120
|
+
return upgrade.code === 0 ? upgrade : install;
|
|
121
|
+
}
|
|
122
|
+
|
|
85
123
|
export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
86
124
|
const extraArgs = Array.isArray(codexArgs) ? codexArgs : [];
|
|
87
125
|
return [
|
|
@@ -139,7 +177,7 @@ export function formatCmuxBanner(status = null) {
|
|
|
139
177
|
'',
|
|
140
178
|
'CLI-first runtime:',
|
|
141
179
|
' sks open a cmux Codex CLI workspace',
|
|
142
|
-
' sks --mad
|
|
180
|
+
' sks --mad open one-shot MAD full-access auto-review workspace',
|
|
143
181
|
' sks team "task" prepare Team mission and cmux multi-line agent view',
|
|
144
182
|
'',
|
|
145
183
|
'Useful terminal commands:',
|
|
@@ -171,6 +209,13 @@ export async function launchCmuxUi(args = [], opts = {}) {
|
|
|
171
209
|
process.exitCode = 1;
|
|
172
210
|
return { plan };
|
|
173
211
|
}
|
|
212
|
+
const daemon = await ensureCmuxDaemonReady(plan.cmux);
|
|
213
|
+
if (!daemon.ok && !args.includes('--status-only')) {
|
|
214
|
+
const blocked = { ...plan, ready: false, cmux: { ...plan.cmux, ok: false, error: daemon.error || 'cmux app did not become ready' } };
|
|
215
|
+
printCmuxLaunchBlocked(blocked, { concise: opts.conciseBlockers, cmuxRepair });
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
return { plan: blocked };
|
|
218
|
+
}
|
|
174
219
|
if (!args.includes('--no-open')) await openCmuxApp().catch(() => null);
|
|
175
220
|
const command = codexLaunchCommand(plan.root, plan.codex.bin, plan.codexArgs);
|
|
176
221
|
const created = spawnSync(plan.cmux.bin, ['new-workspace', '--cwd', plan.root, '--command', command], { encoding: 'utf8', stdio: args.includes('--quiet') ? 'pipe' : 'inherit' });
|
|
@@ -190,11 +235,16 @@ function printCmuxLaunchBlocked(plan, opts = {}) {
|
|
|
190
235
|
console.error('SKS cmux launch blocked.');
|
|
191
236
|
if (!plan.cmux.ok) {
|
|
192
237
|
const repair = opts.cmuxRepair;
|
|
193
|
-
const
|
|
238
|
+
const installedButUnhealthy = Boolean(plan.cmux.bin);
|
|
239
|
+
const prefix = repair?.status
|
|
240
|
+
? `cmux ${repair.status}`
|
|
241
|
+
: installedButUnhealthy
|
|
242
|
+
? 'cmux app/socket unhealthy'
|
|
243
|
+
: 'cmux missing';
|
|
194
244
|
console.error(`- ${prefix}: ${repair?.error || plan.cmux.error || 'cmux CLI not found'}`);
|
|
195
|
-
console.error(`- Install command: ${repair?.command || CMUX_BREW_COMMAND}`);
|
|
245
|
+
console.error(`- ${installedButUnhealthy ? 'Repair command' : 'Install command'}: ${repair?.command || (installedButUnhealthy ? 'sks deps install cmux --yes' : CMUX_BREW_COMMAND)}`);
|
|
196
246
|
}
|
|
197
|
-
if (!plan.codex.bin) console.error('- Codex CLI missing. Install: npm i -g @openai/codex, or set SKS_CODEX_BIN.');
|
|
247
|
+
if (!plan.codex.bin) console.error('- Codex CLI missing. Install: npm i -g @openai/codex@latest, or set SKS_CODEX_BIN.');
|
|
198
248
|
return;
|
|
199
249
|
}
|
|
200
250
|
console.log(formatCmuxBanner(plan.app));
|
|
@@ -205,15 +255,69 @@ function printCmuxLaunchBlocked(plan, opts = {}) {
|
|
|
205
255
|
export async function openCmuxApp() {
|
|
206
256
|
if (process.platform !== 'darwin') return { ok: false, reason: 'not_macos' };
|
|
207
257
|
const run = await runProcess('open', ['-a', 'cmux'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
208
|
-
|
|
258
|
+
if (run.code === 0) return { ok: true, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
259
|
+
for (const app of ['/Applications/cmux.app', '/Applications/Cmux.app']) {
|
|
260
|
+
if (!await exists(app)) continue;
|
|
261
|
+
const byPath = await runProcess('open', [app], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
262
|
+
if (byPath.code === 0) return { ok: true, stdout: byPath.stdout || '', stderr: byPath.stderr || '' };
|
|
263
|
+
}
|
|
264
|
+
return { ok: false, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
209
265
|
}
|
|
210
266
|
|
|
211
267
|
async function wakeCmuxAndReprobe(fallback = {}) {
|
|
212
|
-
|
|
268
|
+
return ensureCmuxDaemonReady(fallback);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function ensureCmuxDaemonReady(cmux = {}) {
|
|
272
|
+
if (!cmux?.bin) return { ok: false, error: cmux?.error || 'cmux CLI not found' };
|
|
273
|
+
const first = await cmuxSocketProbe(cmux.bin);
|
|
274
|
+
if (first.ok) return { ...cmux, ok: true, error: null };
|
|
275
|
+
if (process.platform !== 'darwin') return first;
|
|
213
276
|
const opened = await openCmuxApp().catch(() => null);
|
|
214
|
-
if (!opened?.ok) return
|
|
277
|
+
if (!opened?.ok) return { ok: false, error: opened?.stderr || opened?.reason || first.error || 'cmux app launch failed' };
|
|
278
|
+
let last = first;
|
|
279
|
+
for (let i = 0; i < 8; i++) {
|
|
280
|
+
await new Promise((resolve) => setTimeout(resolve, 750));
|
|
281
|
+
last = await cmuxSocketProbe(cmux.bin);
|
|
282
|
+
if (last.ok) return { ...cmux, ok: true, error: null };
|
|
283
|
+
}
|
|
284
|
+
if (isRecoverableCmuxSocketError(last.error) && process.env.SKS_CMUX_NO_RESTART !== '1') {
|
|
285
|
+
await restartCmuxApp();
|
|
286
|
+
for (let i = 0; i < 8; i++) {
|
|
287
|
+
await new Promise((resolve) => setTimeout(resolve, 750));
|
|
288
|
+
last = await cmuxSocketProbe(cmux.bin);
|
|
289
|
+
if (last.ok) return { ...cmux, ok: true, error: null };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return { ok: false, error: last.error || 'cmux socket did not become ready' };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function cmuxSocketProbe(bin) {
|
|
296
|
+
const probe = await runProcess(bin, ['list-workspaces', '--json'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
297
|
+
const text = `${probe.stdout || ''}${probe.stderr || ''}`.trim();
|
|
298
|
+
return { ok: probe.code === 0, error: probe.code === 0 ? null : text || 'cmux socket probe failed' };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function isRecoverableCmuxSocketError(error) {
|
|
302
|
+
return /socket|broken pipe|receive timeout|connection refused/i.test(String(error || ''));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function restartCmuxApp() {
|
|
306
|
+
if (process.platform !== 'darwin') return { ok: false, reason: 'not_macos' };
|
|
307
|
+
const quit = await runProcess('osascript', ['-e', 'tell application "cmux" to quit'], { timeoutMs: 8000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
308
|
+
if (quit.code !== 0) {
|
|
309
|
+
await runProcess('pkill', ['-TERM', '-f', '/Applications/cmux.app/Contents/MacOS/cmux'], { timeoutMs: 8000, maxOutputBytes: 16 * 1024 }).catch(() => null);
|
|
310
|
+
}
|
|
215
311
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
216
|
-
|
|
312
|
+
await removeStaleCmuxSocket().catch(() => null);
|
|
313
|
+
return openCmuxApp();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function removeStaleCmuxSocket() {
|
|
317
|
+
const home = process.env.HOME;
|
|
318
|
+
if (!home) return;
|
|
319
|
+
const sock = path.join(home, 'Library', 'Application Support', 'cmux', 'cmux.sock');
|
|
320
|
+
await fsp.rm(sock, { force: true });
|
|
217
321
|
}
|
|
218
322
|
|
|
219
323
|
export async function launchCmuxTeamView({ root, missionId, plan = {}, promptFile = null, json = false } = {}) {
|
package/src/core/fsx.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.6.
|
|
8
|
+
export const PACKAGE_VERSION = '0.6.66';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
package/src/core/init.mjs
CHANGED
|
@@ -404,10 +404,14 @@ export async function initProject(root, opts = {}) {
|
|
|
404
404
|
|
|
405
405
|
await writeTextAtomic(path.join(root, '.codex', 'config.toml'), `[features]\ncodex_hooks = true\nmulti_agent = true\n\n[agents]\nmax_threads = 6\nmax_depth = 1\n\n${context7ConfigToml()}\n[agents.analysis_scout]\ndescription = "Read-only SKS scout."\nconfig_file = "./agents/analysis-scout.toml"\nnickname_candidates = ["Scout", "Mapper"]\n\n[agents.team_consensus]\ndescription = "SKS planning/debate agent."\nconfig_file = "./agents/team-consensus.toml"\nnickname_candidates = ["Consensus", "Atlas"]\n\n[agents.implementation_worker]\ndescription = "SKS bounded implementation worker."\nconfig_file = "./agents/implementation-worker.toml"\nnickname_candidates = ["Builder", "Mason"]\n\n[agents.db_safety_reviewer]\ndescription = "Read-only DB safety reviewer."\nconfig_file = "./agents/db-safety-reviewer.toml"\nnickname_candidates = ["Sentinel", "Ledger"]\n\n[agents.qa_reviewer]\ndescription = "Read-only QA reviewer."\nconfig_file = "./agents/qa-reviewer.toml"\nnickname_candidates = ["Verifier", "Scout"]\n\n[profiles.sks-task-medium]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "medium"\n\n[profiles.sks-logic-high]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-research-xhigh]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "xhigh"\n\n[profiles.sks-ralph]\nmodel = "gpt-5.5"\napproval_policy = "never"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-research]\nmodel = "gpt-5.5"\napproval_policy = "never"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "xhigh"\n\n[profiles.sks-team]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-mad-high]
|
|
406
406
|
model = "gpt-5.5"
|
|
407
|
-
approval_policy = "
|
|
407
|
+
approval_policy = "on-request"
|
|
408
|
+
approvals_reviewer = "auto_review"
|
|
408
409
|
sandbox_mode = "danger-full-access"
|
|
409
410
|
model_reasoning_effort = "high"
|
|
410
411
|
|
|
412
|
+
[auto_review]
|
|
413
|
+
policy = "Deny destructive database operations, credential exfiltration, persistent security weakening, broad file deletion, and writes outside the workspace unless explicitly authorized by the user."
|
|
414
|
+
|
|
411
415
|
[profiles.sks-default]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "medium"\n`);
|
|
412
416
|
created.push('.codex/config.toml');
|
|
413
417
|
|
package/src/core/routes.mjs
CHANGED
|
@@ -334,7 +334,7 @@ export const COMMAND_CATALOG = [
|
|
|
334
334
|
{ name: 'deps', usage: 'sks deps check|install [cmux|codex|context7|all] [--yes]', description: 'Check or guided-install Node/npm PATH, Codex CLI/App, Context7, Browser Use, Computer Use, cmux, and Homebrew on macOS.' },
|
|
335
335
|
{ name: 'codex-app', usage: 'sks codex-app [check|open]', description: 'Check Codex App install and first-party MCP/plugin readiness, then show app setup files and examples.' },
|
|
336
336
|
{ name: 'cmux', usage: 'sks cmux [check|status] [--workspace name]', description: 'Open the SKS cmux runtime with the ㅅㅋㅅ ASCII status pane and Codex CLI.' },
|
|
337
|
-
{ name: 'mad
|
|
337
|
+
{ name: 'mad', usage: 'sks --mad [--high]', description: 'Open a one-shot cmux Codex CLI workspace with the SKS MAD full-access auto-review profile.' },
|
|
338
338
|
{ name: 'auto-review', usage: 'sks auto-review status|enable|start [--high] | sks --Auto-review --high', description: 'Enable Codex automatic approval review and launch SKS cmux with the auto-review profile.' },
|
|
339
339
|
{ name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DFix and $Team.' },
|
|
340
340
|
{ name: 'dfix', usage: 'sks dfix', description: 'Explain $DFix ultralight design/content fix mode.' },
|