sneakoscope 0.6.60 → 0.6.62
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 +5 -1
- package/package.json +2 -2
- package/src/cli/main.mjs +104 -73
- package/src/core/auto-review.mjs +29 -0
- package/src/core/cmux-ui.mjs +204 -0
- package/src/core/codex-app.mjs +1 -1
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +9 -3
- package/src/core/pipeline.mjs +1 -0
- package/src/core/questions.mjs +1 -1
- package/src/core/routes.mjs +6 -5
- package/src/core/team-live.mjs +1 -1
- package/src/core/tmux-ui.mjs +0 -167
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Codex CLI/App harness for `$` routes, Team/Ralph/QA/Research, Context7, Honest Mode, DB safety, TriWiki, Codex App skills, and release readiness.
|
|
5
|
+
Codex CLI/App harness for `$` routes, cmux-native CLI workspaces, Team/Ralph/QA/Research, Context7, Honest Mode, DB safety, TriWiki, Codex App skills, and release readiness.
|
|
6
6
|
|
|
7
7
|
Install: `npm i -g sneakoscope && sks bootstrap`
|
|
8
8
|
Fallback: `npx -y -p sneakoscope sks bootstrap`
|
|
@@ -19,6 +19,7 @@ Sneakoscope (`sks`, displayed as `ㅅㅋㅅ`) wraps Codex with a repeatable cont
|
|
|
19
19
|
| --- | --- |
|
|
20
20
|
| Codex App commands | Installs generated skills so `$Team`, `$DFix`, `$QA-LOOP`, `$Ralph`, `$DB`, `$Wiki`, `$Help`, and related routes are discoverable in prompt workflows. |
|
|
21
21
|
| CLI commands | Provides `sks commands`, `sks dollar-commands`, `sks usage <topic>`, bootstrap, setup, doctor, deps, selftest, wiki, team, QA, Ralph, DB, and GX commands. |
|
|
22
|
+
| cmux runtime | Opens Codex CLI through `sks`/`sks cmux`, exposes one-shot full-access high mode with `sks --mad --high`, and opens Team missions with cmux multi-line agent lanes when cmux is available. |
|
|
22
23
|
| Team orchestration | Routes substantial code work through ambiguity removal, scouts, TriWiki refresh, debate, consensus, concrete runtime task graph/inboxes, implementation, review, integration, reflection, and Honest Mode. |
|
|
23
24
|
| Ralph | Seals a decision contract up front, then continues without more user questions by using the agreed decision ladder. |
|
|
24
25
|
| QA loop | Dogfoods UI/API behavior with safety boundaries, evidence capture, safe remediation, and focused rechecks. |
|
|
@@ -60,6 +61,8 @@ sks usage install
|
|
|
60
61
|
sks usage team
|
|
61
62
|
sks usage qa-loop
|
|
62
63
|
sks usage codex-app
|
|
64
|
+
sks cmux check
|
|
65
|
+
sks --mad --high
|
|
63
66
|
sks setup --install-scope project
|
|
64
67
|
sks wiki refresh
|
|
65
68
|
sks wiki validate .sneakoscope/wiki/context-pack.json
|
|
@@ -111,6 +114,7 @@ This runs repo audit, changelog check, syntax packcheck, mock selftest, sizechec
|
|
|
111
114
|
- Node.js `>=20.11`
|
|
112
115
|
- npm
|
|
113
116
|
- Codex CLI/App for app-facing workflows
|
|
117
|
+
- cmux for the CLI-first runtime (`sks deps install cmux`)
|
|
114
118
|
- Context7 MCP for current-docs-gated routes
|
|
115
119
|
|
|
116
120
|
## License
|
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.62",
|
|
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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"harness",
|
|
58
58
|
"codex-cli",
|
|
59
59
|
"codex-app",
|
|
60
|
-
"
|
|
60
|
+
"cmux",
|
|
61
61
|
"auto-review",
|
|
62
62
|
"browser-use",
|
|
63
63
|
"computer-use",
|
package/src/cli/main.mjs
CHANGED
|
@@ -30,8 +30,8 @@ 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 {
|
|
34
|
-
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview } from '../core/auto-review.mjs';
|
|
33
|
+
import { buildCmuxLaunchPlan, defaultCmuxWorkspaceName, formatCmuxBanner, launchCmuxTeamView, launchCmuxUi, platformCmuxInstallHint, runCmuxStatus, sanitizeCmuxWorkspaceName, cmuxAvailable } from '../core/cmux-ui.mjs';
|
|
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);
|
|
37
37
|
const promptOf = (args) => args.filter((x) => !String(x).startsWith('--')).join(' ').trim();
|
|
@@ -48,15 +48,16 @@ function installScopeFromArgs(args = [], fallback = 'global') {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export async function main(args) {
|
|
51
|
+
if (isMadHighLaunch(args)) return madHighCommand(args);
|
|
51
52
|
if (isAutoReviewFlag(args[0])) return autoReviewCommand('start', args.slice(1));
|
|
52
53
|
const [cmd, sub, ...rest] = args;
|
|
53
54
|
const tail = sub === undefined ? [] : [sub, ...rest];
|
|
54
|
-
if (!cmd) return
|
|
55
|
+
if (!cmd) return shouldLaunchCmuxUi() ? cmuxCommand('start', []) : help();
|
|
55
56
|
if (cmd === '--help' || cmd === '-h') return help();
|
|
56
57
|
if (cmd === '--version' || cmd === '-v' || cmd === 'version') return version();
|
|
57
58
|
if (cmd === 'postinstall') return postinstall();
|
|
58
59
|
if (cmd === 'wizard' || cmd === 'ui') return wizard(tail);
|
|
59
|
-
if (cmd === '
|
|
60
|
+
if (cmd === 'cmux') return String(sub || '').startsWith('--') ? cmuxCommand('start', tail) : cmuxCommand(sub, rest);
|
|
60
61
|
if (cmd === 'auto-review' || cmd === 'autoreview') return autoReviewCommand(sub, rest);
|
|
61
62
|
if (cmd === 'update-check') return updateCheck(tail);
|
|
62
63
|
if (cmd === 'help') return help(tail);
|
|
@@ -113,12 +114,13 @@ Usage:
|
|
|
113
114
|
sks usage [${USAGE_TOPICS}]
|
|
114
115
|
sks quickstart
|
|
115
116
|
sks bootstrap [--install-scope global|project] [--local-only] [--json]
|
|
116
|
-
sks deps check|install [
|
|
117
|
+
sks deps check|install [cmux|codex|context7|all] [--yes] [--json]
|
|
117
118
|
sks codex-app
|
|
119
|
+
sks --mad --high
|
|
118
120
|
sks auto-review status|enable|start [--high]
|
|
119
121
|
sks --Auto-review [--high]
|
|
120
|
-
sks
|
|
121
|
-
sks
|
|
122
|
+
sks cmux [--workspace name]
|
|
123
|
+
sks cmux status [--once]
|
|
122
124
|
sks dollar-commands [--json]
|
|
123
125
|
sks dfix
|
|
124
126
|
sks qa-loop prepare "target"
|
|
@@ -190,14 +192,18 @@ function shouldShowWizard() {
|
|
|
190
192
|
return Boolean(input.isTTY && output.isTTY && process.env.SKS_NO_WIZARD !== '1' && process.env.CI !== 'true');
|
|
191
193
|
}
|
|
192
194
|
|
|
193
|
-
function
|
|
194
|
-
return Boolean(input.isTTY && output.isTTY && process.env.
|
|
195
|
+
function shouldLaunchCmuxUi() {
|
|
196
|
+
return Boolean(input.isTTY && output.isTTY && process.env.SKS_NO_CMUX !== '1' && process.env.CI !== 'true');
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
function isAutoReviewFlag(value) {
|
|
198
200
|
return /^--?auto[-_]?review$/i.test(String(value || ''));
|
|
199
201
|
}
|
|
200
202
|
|
|
203
|
+
function isMadHighLaunch(args = []) {
|
|
204
|
+
return /^--(?:mad|MAD|mad-sks)$/i.test(String(args[0] || ''));
|
|
205
|
+
}
|
|
206
|
+
|
|
201
207
|
async function postinstall() {
|
|
202
208
|
const installRoot = path.resolve(process.env.INIT_CWD || process.cwd());
|
|
203
209
|
const conflictScan = await scanHarnessConflicts(installRoot);
|
|
@@ -235,8 +241,8 @@ async function postinstall() {
|
|
|
235
241
|
}
|
|
236
242
|
console.log('\nNext:');
|
|
237
243
|
console.log(' sks bootstrap');
|
|
238
|
-
console.log('\nThis initializes the current project, installs SKS Codex App skills, verifies Codex App/Context7 readiness, and checks
|
|
239
|
-
console.log('Dependency repair: sks deps check; sks deps install
|
|
244
|
+
console.log('\nThis initializes the current project, installs SKS Codex App skills, verifies Codex App/Context7 readiness, and checks cmux/runtime dependencies.');
|
|
245
|
+
console.log('Dependency repair: sks deps check; sks deps install cmux');
|
|
240
246
|
console.log('Open runtime after readiness is green: sks\n');
|
|
241
247
|
}
|
|
242
248
|
|
|
@@ -366,14 +372,14 @@ async function ensureGlobalCodexSkillsDuringInstall(opts = {}) {
|
|
|
366
372
|
async function ensureRelatedCliTools(args = []) {
|
|
367
373
|
const skip = flag(args, '--skip-cli-tools') || process.env.SKS_SKIP_CLI_TOOLS === '1';
|
|
368
374
|
const codex = await ensureCodexCliTool({ skip });
|
|
369
|
-
const
|
|
375
|
+
const cmux = await cmuxAvailable().catch((err) => ({ ok: false, version: null, error: err.message }));
|
|
370
376
|
return {
|
|
371
377
|
codex,
|
|
372
|
-
|
|
373
|
-
ok: Boolean(
|
|
374
|
-
version:
|
|
375
|
-
install_hint:
|
|
376
|
-
error:
|
|
378
|
+
cmux: {
|
|
379
|
+
ok: Boolean(cmux.ok),
|
|
380
|
+
version: cmux.version || null,
|
|
381
|
+
install_hint: cmux.ok ? null : platformCmuxInstallHint(),
|
|
382
|
+
error: cmux.error || null
|
|
377
383
|
}
|
|
378
384
|
};
|
|
379
385
|
}
|
|
@@ -1005,24 +1011,24 @@ function readNumberOption(args, name, fallback) {
|
|
|
1005
1011
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
1006
1012
|
}
|
|
1007
1013
|
|
|
1008
|
-
async function
|
|
1014
|
+
async function cmuxCommand(sub = 'start', args = []) {
|
|
1009
1015
|
const action = sub || 'start';
|
|
1010
1016
|
if (action === 'status' || action === 'banner') {
|
|
1011
1017
|
if (flag(args, '--json')) {
|
|
1012
1018
|
const status = await codexAppIntegrationStatus();
|
|
1013
1019
|
return console.log(JSON.stringify(status, null, 2));
|
|
1014
1020
|
}
|
|
1015
|
-
await
|
|
1021
|
+
await runCmuxStatus(action === 'banner' ? ['--once', ...args] : args);
|
|
1016
1022
|
return;
|
|
1017
1023
|
}
|
|
1018
1024
|
if (action === 'check') {
|
|
1019
1025
|
const root = await projectRoot();
|
|
1020
|
-
const plan = await
|
|
1026
|
+
const plan = await buildCmuxLaunchPlan({ root, session: readOption(args, '--session', null) });
|
|
1021
1027
|
if (flag(args, '--json')) return console.log(JSON.stringify(plan, null, 2));
|
|
1022
|
-
console.log(
|
|
1028
|
+
console.log(formatCmuxBanner(plan.app));
|
|
1023
1029
|
console.log('');
|
|
1024
|
-
console.log(`
|
|
1025
|
-
console.log(`
|
|
1030
|
+
console.log(`cmux: ${plan.cmux.ok ? 'ok' : 'missing'} ${plan.cmux.version || ''}`.trim());
|
|
1031
|
+
console.log(`Workspace: ${plan.workspace}`);
|
|
1026
1032
|
console.log(`Project: ${plan.root}`);
|
|
1027
1033
|
console.log(`Ready: ${plan.ready ? 'yes' : 'no'}`);
|
|
1028
1034
|
if (!plan.ready) {
|
|
@@ -1032,11 +1038,21 @@ async function tmuxCommand(sub = 'start', args = []) {
|
|
|
1032
1038
|
}
|
|
1033
1039
|
return;
|
|
1034
1040
|
}
|
|
1035
|
-
if (['start', 'attach', 'connect', 'open'].includes(action)) return
|
|
1036
|
-
console.error('Usage: sks
|
|
1041
|
+
if (['start', 'attach', 'connect', 'open'].includes(action)) return launchCmuxUi(args);
|
|
1042
|
+
console.error('Usage: sks cmux [check|status|banner] [--workspace name]');
|
|
1037
1043
|
process.exitCode = 1;
|
|
1038
1044
|
}
|
|
1039
1045
|
|
|
1046
|
+
async function madHighCommand(args = []) {
|
|
1047
|
+
const cleanArgs = args.filter((arg) => !['--mad', '--MAD', '--mad-sks', '--high'].includes(arg));
|
|
1048
|
+
const profile = await enableMadHighProfile();
|
|
1049
|
+
if (flag(args, '--json')) return console.log(JSON.stringify(profile, null, 2));
|
|
1050
|
+
console.log(`SKS MAD high profile ready: ${madHighProfileName()}`);
|
|
1051
|
+
console.log('Scope: explicit cmux launch only; normal SKS/DB safety returns after this command.');
|
|
1052
|
+
const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${defaultCmuxWorkspaceName(process.cwd())}`));
|
|
1053
|
+
return launchCmuxUi([...cleanArgs, '--workspace', workspace], { codexArgs: ['--profile', profile.profile_name] });
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1040
1056
|
async function deps(sub = 'check', args = []) {
|
|
1041
1057
|
const action = sub || 'check';
|
|
1042
1058
|
if (action === 'check' || action === 'status') {
|
|
@@ -1048,7 +1064,7 @@ async function deps(sub = 'check', args = []) {
|
|
|
1048
1064
|
return;
|
|
1049
1065
|
}
|
|
1050
1066
|
if (action === 'install') return depsInstall(args);
|
|
1051
|
-
console.error('Usage: sks deps check|install [
|
|
1067
|
+
console.error('Usage: sks deps check|install [cmux|codex|context7|all] [--yes] [--json]');
|
|
1052
1068
|
process.exitCode = 1;
|
|
1053
1069
|
}
|
|
1054
1070
|
|
|
@@ -1058,7 +1074,7 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1058
1074
|
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
1059
1075
|
const app = opts.codexApp || await codexAppIntegrationStatus({ codex });
|
|
1060
1076
|
const context7 = opts.context7 || await checkContext7(root);
|
|
1061
|
-
const
|
|
1077
|
+
const cmux = opts.cmux || await cmuxAvailable().catch((err) => ({ ok: false, version: null, error: err.message }));
|
|
1062
1078
|
const brew = process.platform === 'darwin' ? await which('brew').catch(() => null) : null;
|
|
1063
1079
|
const globalBin = await discoverGlobalSksCommand();
|
|
1064
1080
|
const npmPrefix = npmBin ? await runProcess(npmBin, ['prefix', '-g'], { timeoutMs: 8000, maxOutputBytes: 4096 }).catch(() => null) : null;
|
|
@@ -1066,10 +1082,10 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1066
1082
|
const npmPrefixDir = npmPrefix?.code === 0 ? npmPrefix.stdout.trim().split(/\r?\n/).pop() : null;
|
|
1067
1083
|
const npmBinDir = npmPrefixDir ? (process.platform === 'win32' ? npmPrefixDir : path.join(npmPrefixDir, 'bin')) : null;
|
|
1068
1084
|
const nodeOk = Number(process.versions.node.split('.')[0]) >= 20;
|
|
1069
|
-
const homebrewNeeded = process.platform === 'darwin' && !
|
|
1085
|
+
const homebrewNeeded = process.platform === 'darwin' && !cmux.ok;
|
|
1070
1086
|
return {
|
|
1071
1087
|
root,
|
|
1072
|
-
ready: Boolean(nodeOk && npmBin && globalBin && codex.bin &&
|
|
1088
|
+
ready: Boolean(nodeOk && npmBin && globalBin && codex.bin && context7.ok && cmux.ok),
|
|
1073
1089
|
node: { ok: nodeOk, version: process.version },
|
|
1074
1090
|
npm: { ok: Boolean(npmBin), bin: npmBin, global_bin_dir: npmBinDir, global_bin_on_path: npmBinDir ? pathText.split(path.delimiter).includes(npmBinDir) : null },
|
|
1075
1091
|
sneakoscope: { ok: Boolean(globalBin), bin: globalBin },
|
|
@@ -1078,13 +1094,13 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1078
1094
|
context7,
|
|
1079
1095
|
browser_use: { ok: app.mcp.has_browser_use, cache: app.plugins.browser_use_cache },
|
|
1080
1096
|
computer_use: { ok: app.mcp.has_computer_use, cache: app.plugins.computer_use_cache },
|
|
1081
|
-
|
|
1082
|
-
homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew,
|
|
1083
|
-
next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7,
|
|
1097
|
+
cmux: { ok: Boolean(cmux.ok), version: cmux.version || null, install_hint: cmux.ok ? null : platformCmuxInstallHint(), error: cmux.error || null },
|
|
1098
|
+
homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew, required_for_cmux_install: homebrewNeeded } : { ok: null, bin: null, required_for_cmux_install: false },
|
|
1099
|
+
next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7, cmux, brew, nodeOk })
|
|
1084
1100
|
};
|
|
1085
1101
|
}
|
|
1086
1102
|
|
|
1087
|
-
function depsNextActions({ npmBin, globalBin, codex, app, context7,
|
|
1103
|
+
function depsNextActions({ npmBin, globalBin, codex, app, context7, cmux, brew, nodeOk }) {
|
|
1088
1104
|
const out = [];
|
|
1089
1105
|
if (!nodeOk) out.push('Install Node.js 20.11+.');
|
|
1090
1106
|
if (!npmBin) out.push('Install npm or use a Node.js distribution that includes npm.');
|
|
@@ -1092,7 +1108,7 @@ function depsNextActions({ npmBin, globalBin, codex, app, context7, tmux, brew,
|
|
|
1092
1108
|
if (!codex.bin) out.push('Run: sks deps install codex');
|
|
1093
1109
|
if (!context7.ok) out.push('Run: sks deps install context7');
|
|
1094
1110
|
if (!app.ok) out.push('Run: sks codex-app check');
|
|
1095
|
-
if (!
|
|
1111
|
+
if (!cmux.ok) out.push(process.platform === 'darwin' && !brew ? 'Install Homebrew, then run: sks deps install cmux' : 'Run: sks deps install cmux');
|
|
1096
1112
|
return out;
|
|
1097
1113
|
}
|
|
1098
1114
|
|
|
@@ -1107,7 +1123,7 @@ function printDepsStatus(status) {
|
|
|
1107
1123
|
console.log(`Context7: ${status.context7.ok ? 'ok' : 'missing'}`);
|
|
1108
1124
|
console.log(`Browser Use: ${status.browser_use.ok ? 'ok' : 'missing'}`);
|
|
1109
1125
|
console.log(`Computer Use:${status.computer_use.ok ? ' ok' : ' missing'}`);
|
|
1110
|
-
console.log(`
|
|
1126
|
+
console.log(`cmux: ${status.cmux.ok ? 'ok' : 'missing'} ${status.cmux.version || ''}`.trimEnd());
|
|
1111
1127
|
if (process.platform === 'darwin') console.log(`Homebrew: ${status.homebrew.ok ? 'ok' : 'missing'} ${status.homebrew.bin || ''}`.trimEnd());
|
|
1112
1128
|
console.log(`Ready: ${status.ready ? 'true' : 'false'}`);
|
|
1113
1129
|
if (status.next_actions.length) {
|
|
@@ -1119,11 +1135,11 @@ function printDepsStatus(status) {
|
|
|
1119
1135
|
async function depsInstall(args = []) {
|
|
1120
1136
|
const root = await projectRoot();
|
|
1121
1137
|
const target = positionalArgs(args)[0] || 'all';
|
|
1122
|
-
const wants = target === 'all' ? ['codex', 'context7', '
|
|
1138
|
+
const wants = target === 'all' ? ['codex', 'context7', 'cmux'] : [target];
|
|
1123
1139
|
const actions = [];
|
|
1124
1140
|
if (wants.includes('codex')) actions.push(await installCodexDependency(args));
|
|
1125
1141
|
if (wants.includes('context7')) actions.push(await installContext7Dependency(root));
|
|
1126
|
-
if (wants.includes('
|
|
1142
|
+
if (wants.includes('cmux')) actions.push(await installCmuxDependency(args));
|
|
1127
1143
|
const status = await depsStatus(root);
|
|
1128
1144
|
const result = { target, actions, status };
|
|
1129
1145
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
@@ -1147,18 +1163,22 @@ async function installContext7Dependency(root) {
|
|
|
1147
1163
|
return { target: 'context7', status: changed ? 'project_configured' : 'already_configured', command: 'sks context7 check' };
|
|
1148
1164
|
}
|
|
1149
1165
|
|
|
1150
|
-
async function
|
|
1151
|
-
const before = await
|
|
1152
|
-
if (before.ok) return { target: '
|
|
1166
|
+
async function installCmuxDependency(args = []) {
|
|
1167
|
+
const before = await cmuxAvailable().catch(() => ({ ok: false }));
|
|
1168
|
+
if (before.ok) return { target: 'cmux', status: 'present', version: before.version || null };
|
|
1153
1169
|
if (process.platform === 'darwin') {
|
|
1154
1170
|
const brew = await which('brew').catch(() => null);
|
|
1155
|
-
|
|
1156
|
-
if (
|
|
1157
|
-
if (
|
|
1158
|
-
|
|
1159
|
-
|
|
1171
|
+
const command = 'brew tap manaflow-ai/cmux && brew install --cask cmux';
|
|
1172
|
+
if (!brew) return { target: 'cmux', status: 'homebrew_missing', command: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && ${command}` };
|
|
1173
|
+
if (flag(args, '--dry-run')) return { target: 'cmux', status: 'dry_run', command };
|
|
1174
|
+
if (!await confirmInstall(`Install cmux with Homebrew (${command})?`, args)) return { target: 'cmux', status: 'needs_approval', command };
|
|
1175
|
+
const tap = await runProcess(brew, ['tap', 'manaflow-ai/cmux'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 });
|
|
1176
|
+
const run = tap.code === 0
|
|
1177
|
+
? await runProcess(brew, ['install', '--cask', 'cmux'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 })
|
|
1178
|
+
: tap;
|
|
1179
|
+
return { target: 'cmux', status: run.code === 0 ? 'installed' : 'failed', command, code: run.code, error: run.code === 0 ? null : `${run.stderr || run.stdout || 'brew install --cask cmux failed'}`.trim() };
|
|
1160
1180
|
}
|
|
1161
|
-
return { target: '
|
|
1181
|
+
return { target: 'cmux', status: 'manual_required', command: platformCmuxInstallHint() };
|
|
1162
1182
|
}
|
|
1163
1183
|
|
|
1164
1184
|
async function confirmInstall(question, args = []) {
|
|
@@ -1204,8 +1224,8 @@ async function autoReviewCommand(sub = 'status', args = []) {
|
|
|
1204
1224
|
if (flag(args, '--json')) return console.log(JSON.stringify(status, null, 2));
|
|
1205
1225
|
console.log(`SKS Auto-Review enabled: ${profile}`);
|
|
1206
1226
|
const sessionArg = readOption(cleanArgs, '--session', null);
|
|
1207
|
-
const session = sessionArg ||
|
|
1208
|
-
return
|
|
1227
|
+
const session = sessionArg || sanitizeCmuxWorkspaceName(`${profile}-${defaultCmuxWorkspaceName(process.cwd())}`);
|
|
1228
|
+
return launchCmuxUi([...cleanArgs, '--session', session], { codexArgs: ['--profile', profile] });
|
|
1209
1229
|
}
|
|
1210
1230
|
console.error('Usage: sks auto-review status|enable|disable|start [--high] [--json]');
|
|
1211
1231
|
console.error('Alias: sks --Auto-review [--high]');
|
|
@@ -1220,8 +1240,8 @@ First install and bootstrap this project:
|
|
|
1220
1240
|
sks bootstrap
|
|
1221
1241
|
sks
|
|
1222
1242
|
|
|
1223
|
-
If
|
|
1224
|
-
sks deps install
|
|
1243
|
+
If cmux is missing:
|
|
1244
|
+
sks deps install cmux
|
|
1225
1245
|
|
|
1226
1246
|
Initialize this project for CLI and Codex App:
|
|
1227
1247
|
sks setup --bootstrap
|
|
@@ -1234,7 +1254,7 @@ Open from terminal:
|
|
|
1234
1254
|
Verify:
|
|
1235
1255
|
sks deps check
|
|
1236
1256
|
sks codex-app check
|
|
1237
|
-
sks
|
|
1257
|
+
sks cmux check
|
|
1238
1258
|
sks auto-review status
|
|
1239
1259
|
sks doctor --fix
|
|
1240
1260
|
sks context7 check
|
|
@@ -1288,7 +1308,7 @@ async function codexAppHelp(args = []) {
|
|
|
1288
1308
|
'ㅅㅋㅅ Codex App', '',
|
|
1289
1309
|
formatCodexAppStatus(status), '',
|
|
1290
1310
|
`Skills: project=${skills.project.ok ? 'ok' : `missing ${skills.project.missing.length}`} global=${skills.global.ok ? 'ok' : `missing ${skills.global.missing.length}`}`, '',
|
|
1291
|
-
'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks
|
|
1311
|
+
'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks cmux check', '',
|
|
1292
1312
|
'Generated files:', ' .codex/config.toml', ' .codex/hooks.json', ' .agents/skills/', ' .codex/agents/', ' .codex/SNEAKOSCOPE.md', ' AGENTS.md', '',
|
|
1293
1313
|
'Prompt routes:', formatDollarCommandsCompact(' ')
|
|
1294
1314
|
].join('\n'));
|
|
@@ -1320,11 +1340,11 @@ Examples:
|
|
|
1320
1340
|
function usage(args = []) {
|
|
1321
1341
|
const topic = String(args[0] || 'overview').toLowerCase();
|
|
1322
1342
|
const blocks = {
|
|
1323
|
-
overview: ['ㅅㅋㅅ Usage', '', 'Discover:', ' sks commands', ' sks quickstart', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks
|
|
1343
|
+
overview: ['ㅅㅋㅅ Usage', '', 'Discover:', ' sks commands', ' sks quickstart', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks cmux check', ' sks dollar-commands', '', `Topics: ${USAGE_TOPICS}`],
|
|
1324
1344
|
install: ['Install', '', ' npm i -g sneakoscope', ' sks bootstrap', ' sks', '', 'Fallback:', ' npx -y -p sneakoscope sks bootstrap', '', 'Project:', ' npm i -D sneakoscope', ' npx sks setup --install-scope project'],
|
|
1325
|
-
bootstrap: ['Bootstrap', '', ' sks bootstrap', ' sks setup --bootstrap', '', 'Creates project SKS files, Codex App skills/hooks/config, state/guard files, then checks Codex App, Context7, and
|
|
1326
|
-
deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [
|
|
1327
|
-
|
|
1345
|
+
bootstrap: ['Bootstrap', '', ' sks bootstrap', ' sks setup --bootstrap', '', 'Creates project SKS files, Codex App skills/hooks/config, state/guard files, then checks Codex App, Context7, and cmux.'],
|
|
1346
|
+
deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [cmux|codex|context7|all] [--yes]', '', 'cmux on macOS uses Homebrew only after approval.'],
|
|
1347
|
+
cmux: ['cmux', '', ' sks', ' sks cmux check', ' sks cmux status --once', ' sks deps install cmux'],
|
|
1328
1348
|
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
|
|
1329
1349
|
'qa-loop': ['QA-LOOP', '', ' sks qa-loop prepare "QA this app"', ' sks qa-loop answer <MISSION_ID> answers.json', ' sks qa-loop run <MISSION_ID> --max-cycles 8', '', 'Report: YYYY-MM-DD-v<version>-qa-report.md'],
|
|
1330
1350
|
ralph: ['Ralph', '', ' sks ralph prepare "task"', ' sks ralph answer <MISSION_ID> answers.json', ' sks ralph run <MISSION_ID> --max-cycles 8'],
|
|
@@ -1353,13 +1373,13 @@ async function bootstrap(args = []) {
|
|
|
1353
1373
|
const cliTools = await ensureRelatedCliTools(args);
|
|
1354
1374
|
const context7Status = await checkContext7(root);
|
|
1355
1375
|
const appRuntime = await codexAppIntegrationStatus({ codex: await getCodexInfo().catch(() => ({})) });
|
|
1356
|
-
const deps = await depsStatus(root, { context7: context7Status, codexApp: appRuntime,
|
|
1376
|
+
const deps = await depsStatus(root, { context7: context7Status, codexApp: appRuntime, cmux: cliTools.cmux });
|
|
1357
1377
|
const install = await installStatus(root, installScope, { globalCommand });
|
|
1358
1378
|
const versioningInfo = await versioningStatus(root);
|
|
1359
1379
|
const skills = await checkRequiredSkills(root);
|
|
1360
1380
|
const guard = await harnessGuardStatus(root);
|
|
1361
1381
|
const files = await codexAppFilesStatus(root, skills, versioningInfo);
|
|
1362
|
-
const ready = Boolean(!conflicts.hard_block && install.ok && files.ok && skills.ok && guard.ok && context7Status.ok && appRuntime.ok && deps.
|
|
1382
|
+
const ready = Boolean(!conflicts.hard_block && install.ok && files.ok && skills.ok && guard.ok && context7Status.ok && appRuntime.ok && deps.cmux.ok);
|
|
1363
1383
|
const result = {
|
|
1364
1384
|
root,
|
|
1365
1385
|
ready,
|
|
@@ -1370,7 +1390,7 @@ async function bootstrap(args = []) {
|
|
|
1370
1390
|
codex_app: appRuntime,
|
|
1371
1391
|
global_skills: globalSkills,
|
|
1372
1392
|
context7: context7Status,
|
|
1373
|
-
|
|
1393
|
+
cmux: deps.cmux,
|
|
1374
1394
|
harness_guard: guard,
|
|
1375
1395
|
deps,
|
|
1376
1396
|
next: ready ? ['sks', '$Team implement ...', '$QA-LOOP run ...'] : deps.next_actions
|
|
@@ -1383,7 +1403,7 @@ async function bootstrap(args = []) {
|
|
|
1383
1403
|
console.log(`Hooks: ${files.hooks.ok ? 'ok' : 'missing'}`);
|
|
1384
1404
|
console.log(`Harness guard: ${guard.ok ? 'ok' : 'blocked'}`);
|
|
1385
1405
|
console.log(`Context7: ${context7Status.ok ? 'ok' : 'missing'}`);
|
|
1386
|
-
console.log(`
|
|
1406
|
+
console.log(`cmux: ${deps.cmux.ok ? 'ok' : 'missing'}${deps.cmux.version ? ` ${deps.cmux.version}` : ''}`);
|
|
1387
1407
|
console.log(`ready: ${ready ? 'true' : 'false'}`);
|
|
1388
1408
|
if (!ready) {
|
|
1389
1409
|
console.log('\nNext:');
|
|
@@ -1462,7 +1482,7 @@ async function setup(args) {
|
|
|
1462
1482
|
console.log('ㅅㅋㅅ Setup\n');
|
|
1463
1483
|
console.log(`Project: ${root}`);
|
|
1464
1484
|
console.log(`Install: ${install.ok ? 'ok' : 'missing'} ${install.scope} (${install.command_prefix})`);
|
|
1465
|
-
console.log(`CLI tools: Codex ${formatCodexCliToolStatus(cliTools.codex)};
|
|
1485
|
+
console.log(`CLI tools: Codex ${formatCodexCliToolStatus(cliTools.codex)}; cmux ${cliTools.cmux.ok ? `ok ${cliTools.cmux.version || ''}`.trim() : 'missing'}`);
|
|
1466
1486
|
console.log(`Hooks: ${path.relative(root, hooksPath)}`);
|
|
1467
1487
|
console.log(`Version: ${versioningInfo.enabled ? (versioningInfo.hook_installed ? 'auto-bump enabled' : 'auto-bump hook missing') : 'not enabled'}${versioningInfo.package_version ? ` (${versioningInfo.package_version})` : ''}`);
|
|
1468
1488
|
if (localOnly) console.log('Git: local-only (.git/info/exclude; user AGENTS preserved, SKS managed block refreshed)');
|
|
@@ -1474,10 +1494,10 @@ async function setup(args) {
|
|
|
1474
1494
|
console.log(`Next: sks context7 check; sks selftest --mock; sks commands; sks dollar-commands`);
|
|
1475
1495
|
if (cliTools.codex.status === 'failed') console.log(`\nCodex CLI install failed. Run manually: npm i -g @openai/codex. ${cliTools.codex.error || ''}`.trim());
|
|
1476
1496
|
if (cliTools.codex.status === 'installed_not_on_path') console.log(`\nCodex CLI installed but not on PATH. ${cliTools.codex.hint}`);
|
|
1477
|
-
if (!cliTools.
|
|
1497
|
+
if (!cliTools.cmux.ok) console.log(`\ncmux missing. Install: ${cliTools.cmux.install_hint}`);
|
|
1478
1498
|
if (!install.ok && install.scope === 'global') console.log('\nGlobal command missing. Run: npm i -g sneakoscope');
|
|
1479
1499
|
if (!install.ok && install.scope === 'project') console.log('\nProject package missing. Run: npm i -D sneakoscope');
|
|
1480
|
-
if (!appRuntime.ok) console.log('\nCodex App and first-party Browser Use/Computer Use tools are required for SKS
|
|
1500
|
+
if (!appRuntime.ok) console.log('\nCodex App and first-party Browser Use/Computer Use tools are required for SKS cmux/QA parity. Run: sks codex-app check');
|
|
1481
1501
|
}
|
|
1482
1502
|
|
|
1483
1503
|
function formatCodexCliToolStatus(status = {}) {
|
|
@@ -1544,7 +1564,7 @@ async function doctor(args) {
|
|
|
1544
1564
|
const dbScan = await scanDbSafety(root).catch((err) => ({ ok: false, findings: [{ id: 'db_safety_scan_failed', severity: 'high', reason: err.message }] }));
|
|
1545
1565
|
const context7Status = await checkContext7(root);
|
|
1546
1566
|
const appRuntime = await codexAppIntegrationStatus({ codex });
|
|
1547
|
-
const
|
|
1567
|
+
const cmuxStatus = await cmuxAvailable().catch((err) => ({ ok: false, version: null, error: err.message }));
|
|
1548
1568
|
const skillStatus = await checkRequiredSkills(root);
|
|
1549
1569
|
const globalSkillStatus = await checkRequiredSkills(null, globalCodexSkillsRoot());
|
|
1550
1570
|
const guardStatus = await harnessGuardStatus(root);
|
|
@@ -1565,7 +1585,7 @@ async function doctor(args) {
|
|
|
1565
1585
|
sneakoscope: { ok: await exists(path.join(root, '.sneakoscope')) },
|
|
1566
1586
|
context7: context7Status,
|
|
1567
1587
|
codex_app_runtime: appRuntime,
|
|
1568
|
-
runtime: {
|
|
1588
|
+
runtime: { cmux: { ok: Boolean(cmuxStatus.ok), version: cmuxStatus.version || null, install_hint: cmuxStatus.ok ? null : platformCmuxInstallHint(), error: cmuxStatus.error || null } },
|
|
1569
1589
|
harness_guard: guardStatus,
|
|
1570
1590
|
versioning: versioningInfo,
|
|
1571
1591
|
db_guard: { ok: dbPolicyExists && dbScan.ok, policy: dbPolicyExists ? await loadDbSafetyPolicy(root) : null, scan: dbScan },
|
|
@@ -1577,7 +1597,7 @@ async function doctor(args) {
|
|
|
1577
1597
|
},
|
|
1578
1598
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) }, storage
|
|
1579
1599
|
};
|
|
1580
|
-
result.ready = !result.harness_conflicts.hard_block && nodeOk && Boolean(codex.bin) && install.ok && result.sneakoscope.ok && result.context7.ok && appRuntime.ok && result.runtime.
|
|
1600
|
+
result.ready = !result.harness_conflicts.hard_block && nodeOk && Boolean(codex.bin) && install.ok && result.sneakoscope.ok && result.context7.ok && appRuntime.ok && result.runtime.cmux.ok && result.harness_guard.ok && result.versioning.ok && result.db_guard.ok && result.codex_app.ok && result.skills.ok && result.global_skills.ok;
|
|
1581
1601
|
if (result.harness_conflicts.hard_block) process.exitCode = 1;
|
|
1582
1602
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
1583
1603
|
console.log('ㅅㅋㅅ Doctor\n');
|
|
@@ -1593,7 +1613,7 @@ async function doctor(args) {
|
|
|
1593
1613
|
console.log(`State: ${result.sneakoscope.ok ? 'ok' : 'missing .sneakoscope'}`);
|
|
1594
1614
|
console.log(`Context7: ${result.context7.ok ? 'ok' : 'missing MCP config'} project=${result.context7.project.ok ? 'ok' : 'missing'} global=${result.context7.global.ok ? 'ok' : 'missing'}`);
|
|
1595
1615
|
console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'}`);
|
|
1596
|
-
console.log(`
|
|
1616
|
+
console.log(`cmux: ${result.runtime.cmux.ok ? 'ok' : 'missing'} ${result.runtime.cmux.version || ''}`.trimEnd());
|
|
1597
1617
|
console.log(`Guard: ${result.harness_guard.ok ? 'ok' : 'blocked'}${result.harness_guard.source_exception ? ' source-exception' : ''}`);
|
|
1598
1618
|
console.log(`Version: ${result.versioning.ok ? 'ok' : 'missing'}${result.versioning.enabled ? ` ${result.versioning.package_version || ''}` : ` ${result.versioning.reason || 'disabled'}`}`);
|
|
1599
1619
|
console.log(`DB Guard: ${result.db_guard.ok ? 'ok' : 'blocked'} ${dbScan.findings?.length || 0} finding(s)`);
|
|
@@ -1610,13 +1630,13 @@ async function doctor(args) {
|
|
|
1610
1630
|
if (result.harness_conflicts.hard_block) console.log(`\n${formatHarnessConflictReport(conflictScan)}`);
|
|
1611
1631
|
if (!result.context7.ok) console.log('Context7 MCP missing. Run: sks context7 setup --scope project');
|
|
1612
1632
|
if (!appRuntime.ok) console.log('Codex App or first-party MCP/plugin tools missing. Run: sks codex-app check');
|
|
1613
|
-
if (!result.runtime.
|
|
1633
|
+
if (!result.runtime.cmux.ok) console.log('cmux missing. Run: sks deps install cmux');
|
|
1614
1634
|
if (!result.harness_guard.ok) console.log('Harness guard failed. Run: sks setup from a real terminal, then sks guard check.');
|
|
1615
1635
|
if (!result.versioning.ok) console.log('Versioning hook missing. Run: sks versioning hook, or sks doctor --fix.');
|
|
1616
1636
|
if (!result.skills.ok) console.log(`Missing skills: ${result.skills.missing.join(', ')}. Run: sks setup`);
|
|
1617
1637
|
if (!result.global_skills.ok) console.log(`Missing global $ skills: ${result.global_skills.missing.join(', ')}. Run: npm i -g sneakoscope, or sks setup from a non-local-only run.`);
|
|
1618
1638
|
const blocked = [];
|
|
1619
|
-
if (!result.runtime.
|
|
1639
|
+
if (!result.runtime.cmux.ok) blocked.push(['cmux is missing', 'sks deps install cmux']);
|
|
1620
1640
|
if (!appRuntime.ok) blocked.push(['Codex App or first-party MCP/plugin tools need setup', 'sks codex-app check']);
|
|
1621
1641
|
if (blocked.length) {
|
|
1622
1642
|
console.log('\nBlocked:');
|
|
@@ -2227,7 +2247,12 @@ async function selftest() {
|
|
|
2227
2247
|
if (!bootstrapResult.project_setup?.ok || typeof bootstrapResult.ready !== 'boolean') throw new Error('selftest failed: bootstrap json did not report project setup and ready boolean');
|
|
2228
2248
|
const depsCheck = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'deps', 'check', '--json'], { cwd: bootstrapJsonTmp, env: { HOME: path.join(bootstrapJsonTmp, 'home') }, timeoutMs: 20000, maxOutputBytes: 256 * 1024 });
|
|
2229
2249
|
const depsResult = JSON.parse(depsCheck.stdout);
|
|
2230
|
-
if (!depsResult.node?.ok || !('
|
|
2250
|
+
if (!depsResult.node?.ok || !('cmux' in depsResult) || !('homebrew' in depsResult)) throw new Error('selftest failed: deps check json missing expected fields');
|
|
2251
|
+
const madProfilePath = path.join(tmp, 'mad-codex-config.toml');
|
|
2252
|
+
const madProfile = await enableMadHighProfile({ configPath: madProfilePath });
|
|
2253
|
+
const madProfileText = await safeReadText(madProfilePath);
|
|
2254
|
+
if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "never"') || !madProfileText.includes('model_reasoning_effort = "high"')) throw new Error('selftest failed: MAD high profile is not full-access high/fast');
|
|
2255
|
+
if (!isMadHighLaunch(['--mad', '--high']) || isMadHighLaunch(['db', '--mad'])) throw new Error('selftest failed: MAD high launch flag parsing is not top-level only');
|
|
2231
2256
|
const guardBlocked = await checkHarnessModification(tmp, { tool_name: 'apply_patch', command: '*** Update File: .agents/skills/team/SKILL.md\n+tamper\n' });
|
|
2232
2257
|
if (guardBlocked.action !== 'block') throw new Error('selftest failed: harness guard allowed skill tampering');
|
|
2233
2258
|
const setupBlocked = await checkHarnessModification(tmp, { command: 'sks setup --force' });
|
|
@@ -2613,7 +2638,7 @@ async function selftest() {
|
|
|
2613
2638
|
const codexConfigText = await safeReadText(path.join(tmp, '.codex', 'config.toml'));
|
|
2614
2639
|
if (!codexConfigText.includes('multi_agent = true')) throw new Error('selftest failed: multi_agent not enabled');
|
|
2615
2640
|
if (!hasContext7ConfigText(codexConfigText)) throw new Error('selftest failed: Context7 MCP not configured');
|
|
2616
|
-
if (!codexConfigText.includes('[profiles.sks-task-medium]') || !codexConfigText.includes('[profiles.sks-logic-high]') || !codexConfigText.includes('[profiles.sks-research-xhigh]')) throw new Error('selftest failed: reasoning profiles not configured');
|
|
2641
|
+
if (!codexConfigText.includes('[profiles.sks-task-medium]') || !codexConfigText.includes('[profiles.sks-logic-high]') || !codexConfigText.includes('[profiles.sks-research-xhigh]') || !codexConfigText.includes('[profiles.sks-mad-high]')) throw new Error('selftest failed: reasoning profiles not configured');
|
|
2617
2642
|
if (!codexConfigText.includes('[agents.analysis_scout]')) throw new Error('selftest failed: analysis_scout agent not configured');
|
|
2618
2643
|
if (!codexConfigText.includes('[agents.team_consensus]')) throw new Error('selftest failed: team_consensus agent not configured');
|
|
2619
2644
|
const autoReviewHome = path.join(tmp, 'auto-review-home');
|
|
@@ -2868,6 +2893,8 @@ async function selftest() {
|
|
|
2868
2893
|
if (roleTeamPlan.roster.analysis_team.length !== 5) throw new Error('selftest failed: executor role count not reflected in analysis scout team');
|
|
2869
2894
|
if (roleTeamPlan.roster.development_team.filter((agent) => agent.role === 'executor').length !== 5) throw new Error('selftest failed: executor role count not reflected in development team');
|
|
2870
2895
|
if (!roleTeamPlan.roster.debate_team.some((agent) => /inconvenience/.test(agent.persona))) throw new Error('selftest failed: user friction persona missing from debate team');
|
|
2896
|
+
const cmuxTeam = await launchCmuxTeamView({ root: tmp, missionId: teamId, plan: roleTeamPlan, json: true });
|
|
2897
|
+
if (!cmuxTeam.agents?.length || !cmuxTeam.agents.some((entry) => entry.agent === 'analysis_scout_1') || !cmuxTeam.agents.every((entry) => String(entry.command || '').includes('team watch'))) throw new Error('selftest failed: Team cmux view did not expose agent live lanes');
|
|
2871
2898
|
if (routeReasoning(routePrompt('$Research frontier idea'), '$Research frontier idea').effort !== 'xhigh') throw new Error('selftest failed: research reasoning not xhigh');
|
|
2872
2899
|
if (routeReasoning(routePrompt('$DB migration'), '$DB migration').effort !== 'high') throw new Error('selftest failed: logical reasoning not high');
|
|
2873
2900
|
if (routeReasoning(routePrompt('$DFix button label'), '$DFix button label').effort !== 'medium') throw new Error('selftest failed: simple reasoning not medium');
|
|
@@ -3514,7 +3541,7 @@ function userRequestSignal(prompt = '') {
|
|
|
3514
3541
|
const topicRules = [
|
|
3515
3542
|
['ambiguity-questions', /모호|ambiguity|clarification|질문|답변|answers?\.json|decision-contract|추론|예측/],
|
|
3516
3543
|
['triwiki-priority-memory', /triwiki|wiki|메모리|memory|기억|우선|반복|자주|카운팅|count|frequency|weight/],
|
|
3517
|
-
['install-bootstrap', /bootstrap|postinstall|doctor|deps|
|
|
3544
|
+
['install-bootstrap', /bootstrap|postinstall|doctor|deps|cmux|homebrew|최초\s*설치|셋업|setup/],
|
|
3518
3545
|
['version-release', /버전|version|publish:dry|release|npm\s+pack/],
|
|
3519
3546
|
['qa-loop', /qa|e2e|검증|리포트|report/],
|
|
3520
3547
|
['team-pipeline', /team|subagent|세션|cleanup|reflection|회고|반성/],
|
|
@@ -3796,6 +3823,7 @@ async function team(args) {
|
|
|
3796
3823
|
questions: path.join(dir, 'questions.md'),
|
|
3797
3824
|
codex_agents: ['analysis_scout', 'team_consensus', 'implementation_worker', 'db_safety_reviewer', 'qa_reviewer']
|
|
3798
3825
|
};
|
|
3826
|
+
result.cmux = await launchCmuxTeamView({ root, missionId: id, plan, promptFile: result.workflow, json: flag(args, '--json') });
|
|
3799
3827
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
3800
3828
|
console.log(`Team mission created: ${id}`);
|
|
3801
3829
|
console.log(`Plan: ${path.relative(root, result.plan)}`);
|
|
@@ -3805,8 +3833,10 @@ async function team(args) {
|
|
|
3805
3833
|
console.log(`Runtime graph: ${path.relative(root, result.team_graph)}`);
|
|
3806
3834
|
console.log(`Worker inbox: ${path.relative(root, result.worker_inbox_dir)}`);
|
|
3807
3835
|
console.log(`Live: ${path.relative(root, result.live)}`);
|
|
3836
|
+
if (result.cmux.ready) console.log(`cmux: ${result.cmux.created ? 'opened' : 'ready'} ${result.cmux.agents.length} agent lane(s)`);
|
|
3837
|
+
else console.log(`cmux: blocked (${Array.from(new Set(result.cmux.blockers || [])).join('; ')})`);
|
|
3808
3838
|
console.log(`Watch: sks team watch ${id}`);
|
|
3809
|
-
console.log('Use $Team in Codex App
|
|
3839
|
+
console.log('Use $Team in Codex App or the cmux lanes from this CLI flow to run scouts, debate/consensus, runtime graph/inbox handoff, then a fresh implementation team with disjoint ownership.');
|
|
3810
3840
|
}
|
|
3811
3841
|
|
|
3812
3842
|
function parseTeamCreateArgs(args) {
|
|
@@ -3952,6 +3982,7 @@ function buildTeamPlan(id, prompt, opts = {}) {
|
|
|
3952
3982
|
markdown: 'team-live.md',
|
|
3953
3983
|
transcript: 'team-transcript.jsonl',
|
|
3954
3984
|
dashboard: 'team-dashboard.json',
|
|
3985
|
+
cmux: 'sks team opens a cmux workspace with one live multi-line lane per visible Team agent budget when cmux is available.',
|
|
3955
3986
|
commands: [
|
|
3956
3987
|
'sks team status <mission-id>',
|
|
3957
3988
|
'sks team log <mission-id>',
|
package/src/core/auto-review.mjs
CHANGED
|
@@ -6,6 +6,7 @@ export const AUTO_REVIEW_REVIEWER = 'guardian_subagent';
|
|
|
6
6
|
export const LEGACY_AUTO_REVIEW_REVIEWER = 'auto_review';
|
|
7
7
|
export const AUTO_REVIEW_PROFILE = 'sks-auto-review';
|
|
8
8
|
export const AUTO_REVIEW_HIGH_PROFILE = 'sks-auto-review-high';
|
|
9
|
+
export const MAD_HIGH_PROFILE = 'sks-mad-high';
|
|
9
10
|
|
|
10
11
|
export function codexHome(env = process.env) {
|
|
11
12
|
return path.resolve(env.CODEX_HOME || path.join(env.HOME || os.homedir(), '.codex'));
|
|
@@ -56,6 +57,34 @@ export async function enableAutoReview(opts = {}) {
|
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
export async function enableMadHighProfile(opts = {}) {
|
|
61
|
+
const configPath = opts.configPath || codexConfigPath(opts.env || process.env);
|
|
62
|
+
await ensureDir(path.dirname(configPath));
|
|
63
|
+
const current = await readText(configPath, '');
|
|
64
|
+
let next = upsertTable(current, `profiles.${MAD_HIGH_PROFILE}`, [
|
|
65
|
+
`[profiles.${MAD_HIGH_PROFILE}]`,
|
|
66
|
+
'model = "gpt-5.5"',
|
|
67
|
+
'approval_policy = "never"',
|
|
68
|
+
'sandbox_mode = "danger-full-access"',
|
|
69
|
+
'model_reasoning_effort = "high"'
|
|
70
|
+
].join('\n'));
|
|
71
|
+
if (!next.endsWith('\n')) next += '\n';
|
|
72
|
+
await writeTextAtomic(configPath, next);
|
|
73
|
+
return {
|
|
74
|
+
config_path: configPath,
|
|
75
|
+
profile_name: MAD_HIGH_PROFILE,
|
|
76
|
+
launch_args: ['--profile', MAD_HIGH_PROFILE],
|
|
77
|
+
sandbox_mode: 'danger-full-access',
|
|
78
|
+
approval_policy: 'never',
|
|
79
|
+
model_reasoning_effort: 'high',
|
|
80
|
+
scope: 'explicit_launch_only'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function madHighProfileName() {
|
|
85
|
+
return MAD_HIGH_PROFILE;
|
|
86
|
+
}
|
|
87
|
+
|
|
59
88
|
export async function disableAutoReview(opts = {}) {
|
|
60
89
|
const configPath = opts.configPath || codexConfigPath(opts.env || process.env);
|
|
61
90
|
const current = await readText(configPath, '');
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { exists, packageRoot, projectRoot, runProcess, sha256, which } from './fsx.mjs';
|
|
4
|
+
import { getCodexInfo } from './codex-adapter.mjs';
|
|
5
|
+
import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
|
|
6
|
+
|
|
7
|
+
export const SKS_CMUX_LOGO = [
|
|
8
|
+
'+----------------------+',
|
|
9
|
+
'| ㅅㅋㅅ |',
|
|
10
|
+
'| SKS cmux |',
|
|
11
|
+
'+----------------------+'
|
|
12
|
+
].join('\n');
|
|
13
|
+
|
|
14
|
+
export function sanitizeCmuxWorkspaceName(input) {
|
|
15
|
+
const base = String(input || 'sks').trim().replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
16
|
+
return (base || 'sks').slice(0, 80);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function defaultCmuxWorkspaceName(root) {
|
|
20
|
+
const base = sanitizeCmuxWorkspaceName(path.basename(root || process.cwd()) || 'project');
|
|
21
|
+
const hash = sha256(path.resolve(root || process.cwd())).slice(0, 8);
|
|
22
|
+
return sanitizeCmuxWorkspaceName(`sks-${base}-${hash}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function shellEscape(value) {
|
|
26
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function platformCmuxInstallHint() {
|
|
30
|
+
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.';
|
|
31
|
+
return [
|
|
32
|
+
'brew tap manaflow-ai/cmux && brew install --cask cmux',
|
|
33
|
+
'then expose the CLI if needed:',
|
|
34
|
+
'sudo ln -sf "/Applications/cmux.app/Contents/Resources/bin/cmux" /usr/local/bin/cmux'
|
|
35
|
+
].join(' ');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function findCmuxBinary() {
|
|
39
|
+
const env = process.env.SKS_CMUX_BIN || process.env.CMUX_BIN;
|
|
40
|
+
if (env && await exists(env)) return env;
|
|
41
|
+
const onPath = await which('cmux').catch(() => null);
|
|
42
|
+
if (onPath) return onPath;
|
|
43
|
+
const appBin = '/Applications/cmux.app/Contents/Resources/bin/cmux';
|
|
44
|
+
if (process.platform === 'darwin' && await exists(appBin)) return appBin;
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function cmuxAvailable() {
|
|
49
|
+
const bin = await findCmuxBinary();
|
|
50
|
+
if (!bin) return { ok: false, bin: null, version: null, error: 'cmux CLI not found' };
|
|
51
|
+
const probe = await runProcess(bin, ['list-workspaces', '--json'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
52
|
+
const text = `${probe.stdout || ''}${probe.stderr || ''}`.trim();
|
|
53
|
+
return { ok: probe.code === 0, bin, version: text || 'cmux CLI', error: probe.code === 0 ? null : text || 'cmux workspace probe failed' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
57
|
+
const extraArgs = Array.isArray(codexArgs) ? codexArgs : [];
|
|
58
|
+
return [
|
|
59
|
+
'clear',
|
|
60
|
+
`printf '%s\\n' ${shellEscape(SKS_CMUX_LOGO)}`,
|
|
61
|
+
`printf '\\nProject: %s\\n' ${shellEscape(root)}`,
|
|
62
|
+
'printf \'Runtime: cmux workspace for Codex CLI\\n\'',
|
|
63
|
+
'printf \'Prompt: use canonical $ commands, for example $Team or $QA-LOOP\\n\\n\'',
|
|
64
|
+
'sleep 1',
|
|
65
|
+
`exec ${[shellEscape(codexBin), ...extraArgs.map(shellEscape), '--cd', shellEscape(root)].join(' ')}`
|
|
66
|
+
].join('; ');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function teamAgentCommand(root, missionId, agentId, phase) {
|
|
70
|
+
return [
|
|
71
|
+
`printf '%s\\n' ${shellEscape(`${SKS_CMUX_LOGO}\n\nTeam mission: ${missionId}\nAgent: ${agentId}\nPhase: ${phase}\n`)}`,
|
|
72
|
+
`cd ${shellEscape(root)}`,
|
|
73
|
+
`node ${shellEscape(path.join(packageRoot(), 'bin', 'sks.mjs'))} team watch ${shellEscape(missionId)} --follow --lines 12`
|
|
74
|
+
].join('; ');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function buildCmuxLaunchPlan(opts = {}) {
|
|
78
|
+
const root = path.resolve(opts.root || await projectRoot());
|
|
79
|
+
const workspace = sanitizeCmuxWorkspaceName(opts.workspace || opts.session || defaultCmuxWorkspaceName(root));
|
|
80
|
+
const sksBin = opts.sksBin || path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
81
|
+
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
82
|
+
const cmux = opts.cmux || await cmuxAvailable();
|
|
83
|
+
const app = opts.app || await codexAppIntegrationStatus({ codex });
|
|
84
|
+
const codexArgs = Array.isArray(opts.codexArgs) ? opts.codexArgs : [];
|
|
85
|
+
return {
|
|
86
|
+
root,
|
|
87
|
+
workspace,
|
|
88
|
+
sksBin,
|
|
89
|
+
codex,
|
|
90
|
+
cmux,
|
|
91
|
+
app,
|
|
92
|
+
codexArgs,
|
|
93
|
+
ready: Boolean(cmux.ok && codex.bin),
|
|
94
|
+
warnings: app.ok ? [] : app.guidance || [],
|
|
95
|
+
blockers: [
|
|
96
|
+
...(!cmux.ok ? [`cmux missing. Install: ${platformCmuxInstallHint()}`] : []),
|
|
97
|
+
...(!codex.bin ? ['Codex CLI missing. Install: npm i -g @openai/codex, or set SKS_CODEX_BIN.'] : [])
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function formatCmuxBanner(status = null) {
|
|
103
|
+
const lines = [
|
|
104
|
+
SKS_CMUX_LOGO,
|
|
105
|
+
'',
|
|
106
|
+
'ㅅㅋㅅ cmux runtime',
|
|
107
|
+
'',
|
|
108
|
+
'Canonical prompt commands:',
|
|
109
|
+
' $DFix $Answer $SKS $Team $QA-LOOP $Ralph $Research $AutoResearch $DB $GX $Wiki $Help',
|
|
110
|
+
'',
|
|
111
|
+
'CLI-first runtime:',
|
|
112
|
+
' sks open a cmux Codex CLI workspace',
|
|
113
|
+
' sks --mad --high open one-shot MAD-SKS high reasoning workspace',
|
|
114
|
+
' sks team "task" prepare Team mission and cmux multi-line agent view',
|
|
115
|
+
'',
|
|
116
|
+
'Useful terminal commands:',
|
|
117
|
+
' sks commands',
|
|
118
|
+
' sks dollar-commands',
|
|
119
|
+
' sks codex-app check',
|
|
120
|
+
' sks doctor --fix'
|
|
121
|
+
];
|
|
122
|
+
if (status) lines.push('', formatCodexAppStatus(status));
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function launchCmuxUi(args = [], opts = {}) {
|
|
127
|
+
const rootArg = readOption(args, '--root', opts.root);
|
|
128
|
+
const workspaceArg = readOption(args, '--workspace', readOption(args, '--session', opts.workspace || opts.session));
|
|
129
|
+
const plan = await buildCmuxLaunchPlan({ ...opts, root: rootArg, workspace: workspaceArg });
|
|
130
|
+
if (args.includes('--json')) return { plan };
|
|
131
|
+
if (!plan.ready && !args.includes('--status-only')) {
|
|
132
|
+
console.log(formatCmuxBanner(plan.app));
|
|
133
|
+
console.log('\nLaunch blocked:\n');
|
|
134
|
+
for (const blocker of Array.from(new Set(plan.blockers))) console.log(`- ${blocker}`);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
return { plan };
|
|
137
|
+
}
|
|
138
|
+
if (!args.includes('--no-open')) await openCmuxApp().catch(() => null);
|
|
139
|
+
const command = codexLaunchCommand(plan.root, plan.codex.bin, plan.codexArgs);
|
|
140
|
+
const created = spawnSync(plan.cmux.bin, ['new-workspace', '--cwd', plan.root, '--command', command], { encoding: 'utf8', stdio: args.includes('--quiet') ? 'pipe' : 'inherit' });
|
|
141
|
+
if (created.status !== 0) {
|
|
142
|
+
process.exitCode = created.status || 1;
|
|
143
|
+
if (created.stderr) process.stderr.write(created.stderr);
|
|
144
|
+
return { plan };
|
|
145
|
+
}
|
|
146
|
+
if (args.includes('--no-open')) {
|
|
147
|
+
console.log(`SKS cmux workspace requested: ${plan.workspace}`);
|
|
148
|
+
}
|
|
149
|
+
return { plan, created: true };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function openCmuxApp() {
|
|
153
|
+
if (process.platform !== 'darwin') return { ok: false, reason: 'not_macos' };
|
|
154
|
+
const run = await runProcess('open', ['-a', 'cmux'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
155
|
+
return { ok: run.code === 0, stdout: run.stdout || '', stderr: run.stderr || '' };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function launchCmuxTeamView({ root, missionId, plan = {}, promptFile = null, json = false } = {}) {
|
|
159
|
+
const launch = await buildCmuxLaunchPlan({ root, workspace: `sks-team-${missionId}` });
|
|
160
|
+
const agents = [
|
|
161
|
+
...(plan.roster?.analysis_team || []),
|
|
162
|
+
...(plan.roster?.debate_team || []),
|
|
163
|
+
...(plan.roster?.development_team || []),
|
|
164
|
+
...(plan.roster?.validation_team || [])
|
|
165
|
+
];
|
|
166
|
+
const uniqueAgents = [];
|
|
167
|
+
const seen = new Set();
|
|
168
|
+
for (const agent of agents) {
|
|
169
|
+
const id = agent.id || String(agent);
|
|
170
|
+
if (seen.has(id)) continue;
|
|
171
|
+
seen.add(id);
|
|
172
|
+
uniqueAgents.push(id);
|
|
173
|
+
}
|
|
174
|
+
const commands = uniqueAgents.slice(0, Math.max(1, plan.agent_session_count || 3)).map((agentId, index) => ({
|
|
175
|
+
agent: agentId,
|
|
176
|
+
command: teamAgentCommand(launch.root, missionId, agentId, index === 0 ? 'analysis' : 'team', promptFile)
|
|
177
|
+
}));
|
|
178
|
+
const result = { ready: launch.ready, cmux: launch.cmux, workspace: launch.workspace, agents: commands, blockers: launch.blockers };
|
|
179
|
+
if (json || !launch.ready) return result;
|
|
180
|
+
const first = commands[0]?.command || teamAgentCommand(launch.root, missionId, 'parent_orchestrator', 'team', promptFile);
|
|
181
|
+
const created = spawnSync(launch.cmux.bin, ['new-workspace', '--cwd', launch.root, '--command', first], { encoding: 'utf8', stdio: 'ignore' });
|
|
182
|
+
result.created = created.status === 0;
|
|
183
|
+
for (const entry of commands.slice(1)) {
|
|
184
|
+
spawnSync(launch.cmux.bin, ['new-split', 'right'], { encoding: 'utf8', stdio: 'ignore' });
|
|
185
|
+
spawnSync(launch.cmux.bin, ['send', `${entry.command}\n`], { encoding: 'utf8', stdio: 'ignore' });
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function runCmuxStatus(args = [], opts = {}) {
|
|
191
|
+
const once = args.includes('--once') || !args.includes('--watch');
|
|
192
|
+
do {
|
|
193
|
+
const app = await codexAppIntegrationStatus();
|
|
194
|
+
console.clear();
|
|
195
|
+
console.log(formatCmuxBanner(app));
|
|
196
|
+
if (once) return app;
|
|
197
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
198
|
+
} while (true);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function readOption(args, name, fallback = null) {
|
|
202
|
+
const i = args.indexOf(name);
|
|
203
|
+
return i >= 0 && args[i + 1] ? args[i + 1] : fallback;
|
|
204
|
+
}
|
package/src/core/codex-app.mjs
CHANGED
|
@@ -118,7 +118,7 @@ export async function codexAppIntegrationStatus(opts = {}) {
|
|
|
118
118
|
export function codexAppGuidance({ appInstalled, codex, mcpList, computerUseReady, browserUseReady }) {
|
|
119
119
|
const lines = [];
|
|
120
120
|
if (!appInstalled) {
|
|
121
|
-
lines.push('Install and open Codex App first. SKS
|
|
121
|
+
lines.push('Install and open Codex App for first-party MCP/plugin tools. SKS cmux launch can still run with Codex CLI alone, but Browser Use and Computer Use evidence will be unavailable until Codex App is ready.');
|
|
122
122
|
lines.push(`Docs: ${CODEX_APP_DOCS_URL}`);
|
|
123
123
|
}
|
|
124
124
|
if (!codex?.bin) lines.push('Install Codex CLI too: npm i -g @openai/codex, or set SKS_CODEX_BIN.');
|
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.62';
|
|
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
|
@@ -402,7 +402,13 @@ export async function initProject(root, opts = {}) {
|
|
|
402
402
|
created.push('AGENTS.md managed block');
|
|
403
403
|
}
|
|
404
404
|
|
|
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-
|
|
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
|
+
model = "gpt-5.5"
|
|
407
|
+
approval_policy = "never"
|
|
408
|
+
sandbox_mode = "danger-full-access"
|
|
409
|
+
model_reasoning_effort = "high"
|
|
410
|
+
|
|
411
|
+
[profiles.sks-default]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "medium"\n`);
|
|
406
412
|
created.push('.codex/config.toml');
|
|
407
413
|
|
|
408
414
|
await writeTextAtomic(path.join(root, '.codex', 'SNEAKOSCOPE.md'), codexAppQuickReference(installScope, hookCommandPrefix));
|
|
@@ -458,7 +464,7 @@ function codexAppQuickReference(scope, commandPrefix) {
|
|
|
458
464
|
`Install scope: \`${scope}\``,
|
|
459
465
|
`Command: \`${commandPrefix} <command>\``,
|
|
460
466
|
'Files: AGENTS.md, .codex/hooks.json, .codex/config.toml, .codex/SNEAKOSCOPE.md, .agents/skills, .codex/agents, .sneakoscope/missions.',
|
|
461
|
-
`Discover: ${commandPrefix} bootstrap; ${commandPrefix} deps check; ${commandPrefix} commands; ${commandPrefix} codex-app check; ${commandPrefix}
|
|
467
|
+
`Discover: ${commandPrefix} bootstrap; ${commandPrefix} deps check; ${commandPrefix} commands; ${commandPrefix} codex-app check; ${commandPrefix} cmux check; ${commandPrefix} dollar-commands; ${commandPrefix} pipeline status.`,
|
|
462
468
|
'dollar-commands:',
|
|
463
469
|
...DOLLAR_COMMANDS.map((c) => `- \`${c.command}\`: ${c.route}`),
|
|
464
470
|
`Picker skills: ${DOLLAR_COMMAND_ALIASES.map((x) => x.app_skill).join(', ')}.`,
|
|
@@ -466,7 +472,7 @@ function codexAppQuickReference(scope, commandPrefix) {
|
|
|
466
472
|
`Full routes write reflection.md, record lessons to ${REFLECTION_MEMORY_PATH}, refresh/pack TriWiki, validate, then final-answer with a user-visible completion summary plus Honest Mode.`,
|
|
467
473
|
`Context Tracking: TriWiki SSOT. Before each route phase read only the latest coordinate+voxel overlay pack at .sneakoscope/wiki/context-pack.json; coordinate-only legacy packs are invalid. Use attention.use_first for compact high-trust recall and hydrate attention.hydrate_first from source before risky/lower-trust decisions. During every stage hydrate low-trust claims from source/hash/RGBA anchors; after changes run ${commandPrefix} wiki refresh or pack; before handoff/final run ${commandPrefix} wiki validate .sneakoscope/wiki/context-pack.json.`,
|
|
468
474
|
stackCurrentDocsPolicyText(commandPrefix),
|
|
469
|
-
`Runtime: open Codex App once, then run ${commandPrefix} bootstrap, ${commandPrefix} deps check, or ${commandPrefix} deps install
|
|
475
|
+
`Runtime: open Codex App once, then run ${commandPrefix} bootstrap, ${commandPrefix} deps check, or ${commandPrefix} deps install cmux.`,
|
|
470
476
|
`Guard: generated harness files are immutable outside the engine source repo; check ${commandPrefix} guard check; conflicts use ${commandPrefix} conflicts prompt with human approval.`
|
|
471
477
|
].join('\n') + '\n';
|
|
472
478
|
}
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -304,6 +304,7 @@ async function prepareTeam(root, route, task, required) {
|
|
|
304
304
|
markdown: 'team-live.md',
|
|
305
305
|
transcript: 'team-transcript.jsonl',
|
|
306
306
|
dashboard: 'team-dashboard.json',
|
|
307
|
+
cmux: 'CLI Team entrypoints open cmux live lanes for the visible Team agent budget when cmux is available.',
|
|
307
308
|
commands: ['sks team status latest', 'sks team log latest', 'sks team tail latest', 'sks team watch latest', 'sks team event latest --agent <name> --phase <phase> --message "..."']
|
|
308
309
|
},
|
|
309
310
|
required_artifacts: ['team-roster.json', 'team-analysis.md', ...(fromChatImgRequired ? [FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT] : []), 'team-consensus.md', ...teamRuntimeRequiredArtifacts(), 'team-review.md', 'team-gate.json', TEAM_SESSION_CLEANUP_ARTIFACT, 'reflection.md', 'reflection-gate.json', 'team-live.md', 'team-transcript.jsonl', 'team-dashboard.json', '.sneakoscope/wiki/context-pack.json', 'context7-evidence.jsonl']
|
package/src/core/questions.mjs
CHANGED
|
@@ -78,7 +78,7 @@ export function inferAnswersForPrompt(prompt, explicitAnswers = {}) {
|
|
|
78
78
|
.trim();
|
|
79
79
|
const version = String(text || '').match(/\bv?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)\b/)?.[1] || null;
|
|
80
80
|
const versionWork = /버전|version|bump|release|publish:dry|npm\s+pack/.test(lower);
|
|
81
|
-
const installWork = /bootstrap|postinstall|doctor|deps|
|
|
81
|
+
const installWork = /bootstrap|postinstall|doctor|deps|cmux|homebrew|first install|최초\s*설치|설치\s*ux|셋업|setup/.test(lower);
|
|
82
82
|
const questionGateWork = /모호|ambiguity|clarification|질문|triwiki|추론|infer|predict|예측|answers?\.json|decision-contract/.test(lower);
|
|
83
83
|
const prioritySignalWork = /화|짜증|답답|;;|!!|강력|기억|우선|자주|반복|카운팅|count|frequency|frequent|priority|weight/.test(lower);
|
|
84
84
|
const cliSurfaceWork = /\b(cli|command|route|usage|help|sks)\b|명령|커맨드|사용법/.test(lower);
|
package/src/core/routes.mjs
CHANGED
|
@@ -4,7 +4,7 @@ export const FROM_CHAT_IMG_CHECKLIST_ARTIFACT = 'from-chat-img-checklist.md';
|
|
|
4
4
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT = 'from-chat-img-temp-triwiki.json';
|
|
5
5
|
export const FROM_CHAT_IMG_QA_LOOP_ARTIFACT = 'from-chat-img-qa-loop.json';
|
|
6
6
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS = 5;
|
|
7
|
-
export const USAGE_TOPICS = 'install|setup|bootstrap|deps|
|
|
7
|
+
export const USAGE_TOPICS = 'install|setup|bootstrap|deps|cmux|auto-review|team|qa-loop|ralph|research|db|codex-app|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|hproof|gx|wiki';
|
|
8
8
|
|
|
9
9
|
export const RECOMMENDED_MCP_SERVERS = [
|
|
10
10
|
{
|
|
@@ -330,11 +330,12 @@ export const COMMAND_CATALOG = [
|
|
|
330
330
|
{ name: 'commands', usage: 'sks commands [--json]', description: 'List every user-facing command with a short description.' },
|
|
331
331
|
{ name: 'usage', usage: `sks usage [${USAGE_TOPICS}]`, description: 'Print copy-ready workflows for common tasks.' },
|
|
332
332
|
{ name: 'quickstart', usage: 'sks quickstart', description: 'Show the shortest safe setup and verification flow.' },
|
|
333
|
-
{ name: 'bootstrap', usage: 'sks bootstrap [--install-scope global|project] [--local-only] [--json]', description: 'Initialize the current project, install SKS Codex App files/skills, check Context7/Codex App/
|
|
334
|
-
{ name: 'deps', usage: 'sks deps check|install [
|
|
333
|
+
{ name: 'bootstrap', usage: 'sks bootstrap [--install-scope global|project] [--local-only] [--json]', description: 'Initialize the current project, install SKS Codex App files/skills, check Context7/Codex App/cmux, and print ready true/false.' },
|
|
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
|
-
{ name: '
|
|
337
|
-
{ name: '
|
|
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-high', usage: 'sks --mad --high', description: 'Open a one-shot cmux Codex CLI workspace with the SKS MAD high full-access profile.' },
|
|
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.' },
|
|
338
339
|
{ name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DFix and $Team.' },
|
|
339
340
|
{ name: 'dfix', usage: 'sks dfix', description: 'Explain $DFix ultralight design/content fix mode.' },
|
|
340
341
|
{ name: 'qa-loop', usage: 'sks qa-loop prepare|answer|run|status ...', description: 'Dogfood UI/API as human proxy with safety gates, safe fixes, rechecks, Browser/Computer evidence, report.' },
|
package/src/core/team-live.mjs
CHANGED
|
@@ -93,7 +93,7 @@ ${prompt}
|
|
|
93
93
|
|
|
94
94
|
## How to Read
|
|
95
95
|
|
|
96
|
-
- This file is the Codex App-visible replacement for
|
|
96
|
+
- This file is the Codex App-visible replacement for cmux-style team panes.
|
|
97
97
|
- Use at most ${spec.agentSessions} subagent sessions at a time unless the mission is recreated with a different budget.
|
|
98
98
|
- Team mode has three bundles: parallel analysis scouts first, debate team second, then fresh parallel development team.
|
|
99
99
|
- Use relevant TriWiki context before every stage, hydrate low-trust claims from source during the stage, refresh after findings/artifact changes, and validate before handoffs or final claims.
|
package/src/core/tmux-ui.mjs
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { exists, packageRoot, projectRoot, runProcess, sha256 } from './fsx.mjs';
|
|
4
|
-
import { getCodexInfo } from './codex-adapter.mjs';
|
|
5
|
-
import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
|
|
6
|
-
|
|
7
|
-
export const SKS_TMUX_LOGO = [
|
|
8
|
-
'+----------------------+',
|
|
9
|
-
'| ㅅㅋㅅ |',
|
|
10
|
-
'| SKS CLI |',
|
|
11
|
-
'+----------------------+'
|
|
12
|
-
].join('\n');
|
|
13
|
-
|
|
14
|
-
export function sanitizeTmuxSessionName(input) {
|
|
15
|
-
const base = String(input || 'sks').trim().replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
16
|
-
return (base || 'sks').slice(0, 80);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function defaultTmuxSessionName(root) {
|
|
20
|
-
const base = sanitizeTmuxSessionName(path.basename(root || process.cwd()) || 'project');
|
|
21
|
-
const hash = sha256(path.resolve(root || process.cwd())).slice(0, 8);
|
|
22
|
-
return sanitizeTmuxSessionName(`sks-${base}-${hash}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function shellEscape(value) {
|
|
26
|
-
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function platformTmuxInstallHint() {
|
|
30
|
-
if (process.platform === 'darwin') return 'brew install tmux';
|
|
31
|
-
if (process.platform === 'win32') return 'Install WSL2 and run: sudo apt install tmux; native Windows may use psmux.';
|
|
32
|
-
return 'Ubuntu/Debian: sudo apt install tmux; Fedora: sudo dnf install tmux; Arch: sudo pacman -S tmux';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function tmuxAvailable() {
|
|
36
|
-
const out = await runProcess('tmux', ['-V'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch(() => null);
|
|
37
|
-
return { ok: Boolean(out && out.code === 0), version: out ? `${out.stdout}${out.stderr}`.trim() : null };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export async function tmuxHasSession(sessionName) {
|
|
41
|
-
const out = await runProcess('tmux', ['has-session', '-t', sessionName], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
42
|
-
return out.code === 0;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function tmuxSplashScript(root, codexBin, codexArgs = []) {
|
|
46
|
-
const logo = SKS_TMUX_LOGO;
|
|
47
|
-
const extraArgs = Array.isArray(codexArgs) ? codexArgs : [];
|
|
48
|
-
const lines = [
|
|
49
|
-
'clear',
|
|
50
|
-
`printf '%s\\n' ${shellEscape(logo)}`,
|
|
51
|
-
`printf '\\nProject: %s\\n' ${shellEscape(root)}`,
|
|
52
|
-
'printf \'Engine: Codex CLI through SKS guardrails\\n\'',
|
|
53
|
-
'printf \'Tools: Prefer Browser Use + Computer Use MCP plugins for QA/UI evidence\\n\'',
|
|
54
|
-
'printf \'Prompt: use canonical $ commands only, for example $QA-LOOP\\n\\n\'',
|
|
55
|
-
'sleep 1',
|
|
56
|
-
`exec ${[shellEscape(codexBin), ...extraArgs.map(shellEscape), '--cd', shellEscape(root)].join(' ')}`
|
|
57
|
-
];
|
|
58
|
-
return lines.join('; ');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function tmuxStatusScript(root, sksBin) {
|
|
62
|
-
return `${shellEscape(process.execPath)} ${shellEscape(sksBin)} tmux status --watch --root ${shellEscape(root)}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function buildTmuxLaunchPlan(opts = {}) {
|
|
66
|
-
const root = path.resolve(opts.root || await projectRoot());
|
|
67
|
-
const session = sanitizeTmuxSessionName(opts.session || defaultTmuxSessionName(root));
|
|
68
|
-
const sksBin = opts.sksBin || path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
69
|
-
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
70
|
-
const tmux = opts.tmux || await tmuxAvailable();
|
|
71
|
-
const app = opts.app || await codexAppIntegrationStatus({ codex });
|
|
72
|
-
const codexArgs = Array.isArray(opts.codexArgs) ? opts.codexArgs : [];
|
|
73
|
-
return {
|
|
74
|
-
root,
|
|
75
|
-
session,
|
|
76
|
-
sksBin,
|
|
77
|
-
codex,
|
|
78
|
-
tmux,
|
|
79
|
-
app,
|
|
80
|
-
codexArgs,
|
|
81
|
-
ready: Boolean(tmux.ok && codex.bin && app.ok),
|
|
82
|
-
blockers: [
|
|
83
|
-
...(!tmux.ok ? [`tmux missing. Install: ${platformTmuxInstallHint()}`] : []),
|
|
84
|
-
...(!codex.bin ? ['Codex CLI missing. Install: npm i -g @openai/codex, or set SKS_CODEX_BIN.'] : []),
|
|
85
|
-
...(!app.ok ? app.guidance : [])
|
|
86
|
-
]
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function formatTmuxBanner(status = null) {
|
|
91
|
-
const lines = [
|
|
92
|
-
SKS_TMUX_LOGO,
|
|
93
|
-
'',
|
|
94
|
-
'ㅅㅋㅅ tmux runtime',
|
|
95
|
-
'',
|
|
96
|
-
'Canonical prompt commands:',
|
|
97
|
-
' $DFix $Answer $SKS $Team $QA-LOOP $Ralph $Research $AutoResearch $DB $GX $Wiki $Help',
|
|
98
|
-
'',
|
|
99
|
-
'Preferred QA/UI tools:',
|
|
100
|
-
' Browser Use -> local browser targets, localhost, file://, current browser tab',
|
|
101
|
-
' Computer Use -> desktop apps, screenshots, browser/app interaction evidence',
|
|
102
|
-
'',
|
|
103
|
-
'Useful terminal commands:',
|
|
104
|
-
' sks commands',
|
|
105
|
-
' sks dollar-commands',
|
|
106
|
-
' sks codex-app check',
|
|
107
|
-
' sks doctor --fix'
|
|
108
|
-
];
|
|
109
|
-
if (status) lines.push('', formatCodexAppStatus(status));
|
|
110
|
-
return lines.join('\n');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export async function launchTmuxUi(args = [], opts = {}) {
|
|
114
|
-
const rootArg = readOption(args, '--root', opts.root);
|
|
115
|
-
const sessionArg = readOption(args, '--session', opts.session);
|
|
116
|
-
const plan = await buildTmuxLaunchPlan({ ...opts, root: rootArg, session: sessionArg });
|
|
117
|
-
if (args.includes('--json')) return { plan };
|
|
118
|
-
if (!plan.ready && !args.includes('--status-only')) {
|
|
119
|
-
console.log(formatTmuxBanner(plan.app));
|
|
120
|
-
console.log('\nLaunch blocked:\n');
|
|
121
|
-
for (const blocker of Array.from(new Set(plan.blockers))) console.log(`- ${blocker}`);
|
|
122
|
-
process.exitCode = 1;
|
|
123
|
-
return { plan };
|
|
124
|
-
}
|
|
125
|
-
const existing = await tmuxHasSession(plan.session);
|
|
126
|
-
if (!existing) {
|
|
127
|
-
const splash = tmuxSplashScript(plan.root, plan.codex.bin, plan.codexArgs);
|
|
128
|
-
const status = tmuxStatusScript(plan.root, plan.sksBin);
|
|
129
|
-
const create = spawnSync('tmux', ['new-session', '-d', '-P', '-F', '#{window_id} #{pane_id}', '-s', plan.session, '-n', 'codex', '-c', plan.root, splash], { encoding: 'utf8' });
|
|
130
|
-
if (create.status !== 0) {
|
|
131
|
-
process.exitCode = create.status || 1;
|
|
132
|
-
if (create.stderr) process.stderr.write(create.stderr);
|
|
133
|
-
return { plan };
|
|
134
|
-
}
|
|
135
|
-
const [windowTarget, mainPaneTarget] = String(create.stdout || '').trim().split(/\s+/);
|
|
136
|
-
const target = windowTarget || `${plan.session}:codex`;
|
|
137
|
-
spawnSync('tmux', ['split-window', '-h', '-p', '34', '-t', target, '-c', plan.root, status], { stdio: 'ignore' });
|
|
138
|
-
if (mainPaneTarget) spawnSync('tmux', ['select-pane', '-t', mainPaneTarget], { stdio: 'ignore' });
|
|
139
|
-
spawnSync('tmux', ['set-option', '-t', plan.session, 'status-left', ' ㅅㅋㅅ #[bold]#S #[default]'], { stdio: 'ignore' });
|
|
140
|
-
spawnSync('tmux', ['set-option', '-t', plan.session, 'status-right', ' #(date +%H:%M) '], { stdio: 'ignore' });
|
|
141
|
-
}
|
|
142
|
-
if (!args.includes('--no-attach')) {
|
|
143
|
-
const attach = spawnSync('tmux', ['attach-session', '-t', plan.session], { stdio: 'inherit' });
|
|
144
|
-
process.exitCode = attach.status || 0;
|
|
145
|
-
} else {
|
|
146
|
-
console.log(`SKS tmux session ready: ${plan.session}`);
|
|
147
|
-
console.log(`Attach: tmux attach-session -t ${plan.session}`);
|
|
148
|
-
}
|
|
149
|
-
return { plan, existing };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export async function runTmuxStatus(args = [], opts = {}) {
|
|
153
|
-
const root = path.resolve(readOption(args, '--root', opts.root || await projectRoot()));
|
|
154
|
-
const once = args.includes('--once') || !args.includes('--watch');
|
|
155
|
-
do {
|
|
156
|
-
const app = await codexAppIntegrationStatus();
|
|
157
|
-
console.clear();
|
|
158
|
-
console.log(formatTmuxBanner(app));
|
|
159
|
-
if (once) return app;
|
|
160
|
-
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
161
|
-
} while (true);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function readOption(args, name, fallback = null) {
|
|
165
|
-
const i = args.indexOf(name);
|
|
166
|
-
return i >= 0 && args[i + 1] ? args[i + 1] : fallback;
|
|
167
|
-
}
|