sneakoscope 0.7.11 → 0.7.13
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 +27 -27
- package/package.json +2 -2
- package/src/cli/install-helpers.mjs +12 -13
- package/src/cli/main.mjs +87 -100
- package/src/cli/maintenance-commands.mjs +31 -31
- package/src/core/artifact-schemas.mjs +6 -6
- package/src/core/codex-app.mjs +1 -1
- package/src/core/evaluation.mjs +3 -3
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +6 -6
- package/src/core/pipeline.mjs +2 -2
- package/src/core/ppt.mjs +100 -19
- package/src/core/questions.mjs +1 -1
- package/src/core/routes.mjs +9 -9
- package/src/core/team-live.mjs +8 -8
- package/src/core/tmux-ui.mjs +447 -0
- package/src/core/warp-ui.mjs +0 -557
package/src/cli/main.mjs
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
PPT_CLEANUP_REPORT_ARTIFACT,
|
|
29
29
|
PPT_GATE_ARTIFACT,
|
|
30
30
|
PPT_HTML_ARTIFACT,
|
|
31
|
+
PPT_PARALLEL_REPORT_ARTIFACT,
|
|
31
32
|
PPT_PDF_ARTIFACT,
|
|
32
33
|
PPT_RENDER_REPORT_ARTIFACT,
|
|
33
34
|
PPT_SOURCE_HTML_DIR,
|
|
@@ -55,7 +56,7 @@ import { buildPromptContext } from '../core/prompt-context-builder.mjs';
|
|
|
55
56
|
import { renderTeamDashboardState, writeTeamDashboardState } from '../core/team-dashboard-renderer.mjs';
|
|
56
57
|
import { GOAL_WORKFLOW_ARTIFACT } from '../core/goal-workflow.mjs';
|
|
57
58
|
import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
|
|
58
|
-
import {
|
|
59
|
+
import { buildTmuxLaunchPlan, buildTmuxOpenArgs, createTmuxSession, isTmuxShellSession, runTmuxLaunchPlanSyntaxCheck, tmuxReadiness, tmuxStatusKind, defaultTmuxSessionName, formatTmuxBanner, launchTmuxTeamView, launchTmuxUi, platformTmuxInstallHint, runTmuxStatus, sanitizeTmuxSessionName, teamLaneStyle } from '../core/tmux-ui.mjs';
|
|
59
60
|
import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
|
|
60
61
|
import { context7Command } from './context7-command.mjs';
|
|
61
62
|
import { askPostinstallQuestion, checkContext7, checkRequiredSkills, ensureCodexCliTool, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, globalCodexSkillsRoot, postinstall, postinstallBootstrapDecision } from './install-helpers.mjs';
|
|
@@ -84,7 +85,7 @@ export async function main(args) {
|
|
|
84
85
|
if (cmd === '--version' || cmd === '-v' || cmd === 'version') return version();
|
|
85
86
|
if (cmd === 'postinstall') return postinstall({ bootstrap });
|
|
86
87
|
if (cmd === 'wizard' || cmd === 'ui') return wizard(tail);
|
|
87
|
-
if (cmd === '
|
|
88
|
+
if (cmd === 'tmux') return !sub || String(sub).startsWith('--') ? tmuxCommand('check', tail) : tmuxCommand(sub, rest);
|
|
88
89
|
if (cmd === 'auto-review' || cmd === 'autoreview') return autoReviewCommand(sub, rest);
|
|
89
90
|
if (cmd === 'update-check') return updateCheck(tail);
|
|
90
91
|
if (cmd === 'help') return help(tail);
|
|
@@ -150,13 +151,13 @@ Usage:
|
|
|
150
151
|
sks root [--json]
|
|
151
152
|
sks quickstart
|
|
152
153
|
sks bootstrap [--install-scope global|project] [--local-only] [--json]
|
|
153
|
-
sks deps check|install [
|
|
154
|
+
sks deps check|install [tmux|codex|context7|all] [--yes] [--json]
|
|
154
155
|
sks codex-app
|
|
155
156
|
sks --mad [--high]
|
|
156
157
|
sks auto-review status|enable|start [--high]
|
|
157
158
|
sks --Auto-review [--high]
|
|
158
|
-
sks
|
|
159
|
-
sks
|
|
159
|
+
sks tmux open [--workspace name]
|
|
160
|
+
sks tmux status [--once]
|
|
160
161
|
sks dollar-commands [--json]
|
|
161
162
|
sks dfix
|
|
162
163
|
sks qa-loop prepare "target"
|
|
@@ -185,7 +186,7 @@ Usage:
|
|
|
185
186
|
sks team log|tail|watch|lane|status|dashboard [mission-id|latest]
|
|
186
187
|
sks team event [mission-id|latest] --agent <name> --phase <phase> --message "..."
|
|
187
188
|
sks team message [mission-id|latest] --from <agent> --to <agent|all> --message "..."
|
|
188
|
-
sks team cleanup-
|
|
189
|
+
sks team cleanup-tmux [mission-id|latest]
|
|
189
190
|
sks research prepare "topic" [--depth frontier]
|
|
190
191
|
sks research run <mission-id|latest> [--mock] [--max-cycles N]
|
|
191
192
|
sks research status <mission-id|latest>
|
|
@@ -424,6 +425,7 @@ async function pptCommand(sub = 'status', args = []) {
|
|
|
424
425
|
console.log(`PDF: ${path.relative(root, result.files.pdf)}`);
|
|
425
426
|
console.log(`Report: ${path.relative(root, result.files.render_report)}`);
|
|
426
427
|
console.log(`Cleanup: ${path.relative(root, result.files.cleanup_report)}`);
|
|
428
|
+
console.log(`Parallel:${' '.repeat(1)}${path.relative(root, result.files.parallel_report)}`);
|
|
427
429
|
console.log(`Gate: ${result.ok ? 'passed' : 'blocked'} (${path.relative(root, result.files.gate)})`);
|
|
428
430
|
return;
|
|
429
431
|
}
|
|
@@ -441,6 +443,7 @@ async function pptCommand(sub = 'status', args = []) {
|
|
|
441
443
|
pdf: path.join(dir, PPT_PDF_ARTIFACT),
|
|
442
444
|
render_report: path.join(dir, PPT_RENDER_REPORT_ARTIFACT),
|
|
443
445
|
cleanup_report: path.join(dir, PPT_CLEANUP_REPORT_ARTIFACT),
|
|
446
|
+
parallel_report: path.join(dir, PPT_PARALLEL_REPORT_ARTIFACT),
|
|
444
447
|
gate: path.join(dir, PPT_GATE_ARTIFACT)
|
|
445
448
|
}
|
|
446
449
|
};
|
|
@@ -452,6 +455,7 @@ async function pptCommand(sub = 'status', args = []) {
|
|
|
452
455
|
console.log(`PDF: ${path.relative(root, status.files.pdf)}`);
|
|
453
456
|
console.log(`Report: ${path.relative(root, status.files.render_report)}`);
|
|
454
457
|
console.log(`Cleanup: ${path.relative(root, status.files.cleanup_report)}`);
|
|
458
|
+
console.log(`Parallel:${' '.repeat(1)}${path.relative(root, status.files.parallel_report)}`);
|
|
455
459
|
return;
|
|
456
460
|
}
|
|
457
461
|
throw new Error(`Unknown ppt command: ${action}`);
|
|
@@ -871,23 +875,23 @@ function readNumberOption(args, name, fallback) {
|
|
|
871
875
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
872
876
|
}
|
|
873
877
|
|
|
874
|
-
async function
|
|
878
|
+
async function tmuxCommand(sub = 'start', args = []) {
|
|
875
879
|
const action = sub || 'start';
|
|
876
880
|
if (action === 'status' || action === 'banner') {
|
|
877
881
|
if (flag(args, '--json')) {
|
|
878
882
|
const status = await codexAppIntegrationStatus();
|
|
879
883
|
return console.log(JSON.stringify(status, null, 2));
|
|
880
884
|
}
|
|
881
|
-
await
|
|
885
|
+
await runTmuxStatus(action === 'banner' ? ['--once', ...args] : args);
|
|
882
886
|
return;
|
|
883
887
|
}
|
|
884
888
|
if (action === 'check') {
|
|
885
889
|
const root = await sksRoot();
|
|
886
|
-
const plan = await
|
|
890
|
+
const plan = await buildTmuxLaunchPlan({ root, session: readOption(args, '--session', null) });
|
|
887
891
|
if (flag(args, '--json')) return console.log(JSON.stringify(plan, null, 2));
|
|
888
|
-
console.log(
|
|
892
|
+
console.log(formatTmuxBanner(plan.app));
|
|
889
893
|
console.log('');
|
|
890
|
-
console.log(`
|
|
894
|
+
console.log(`tmux: ${plan.tmux.ok ? 'ok' : 'missing'} ${plan.tmux.version || ''}`.trim());
|
|
891
895
|
console.log(`Workspace: ${plan.workspace}`);
|
|
892
896
|
console.log(`Project: ${plan.root}`);
|
|
893
897
|
console.log(`Ready: ${plan.ready ? 'yes' : 'no'}`);
|
|
@@ -899,16 +903,16 @@ async function warpCommand(sub = 'start', args = []) {
|
|
|
899
903
|
return;
|
|
900
904
|
}
|
|
901
905
|
if (['start', 'attach', 'connect', 'open'].includes(action)) {
|
|
902
|
-
const result = await
|
|
906
|
+
const result = await launchTmuxUi(args);
|
|
903
907
|
if (flag(args, '--json')) console.log(JSON.stringify(result, null, 2));
|
|
904
908
|
return;
|
|
905
909
|
}
|
|
906
|
-
console.error('Usage: sks
|
|
910
|
+
console.error('Usage: sks tmux open|start|check|status|banner [--workspace name]');
|
|
907
911
|
process.exitCode = 1;
|
|
908
912
|
}
|
|
909
913
|
|
|
910
914
|
async function madHighCommand(args = []) {
|
|
911
|
-
const cleanArgs = args.filter((arg) => !['--mad', '--MAD', '--mad-sks', '--high', '--no-auto-install-
|
|
915
|
+
const cleanArgs = args.filter((arg) => !['--mad', '--MAD', '--mad-sks', '--high', '--no-auto-install-tmux'].includes(arg));
|
|
912
916
|
if (flag(args, '--json')) {
|
|
913
917
|
const profile = await enableMadHighProfile();
|
|
914
918
|
return console.log(JSON.stringify(profile, null, 2));
|
|
@@ -932,11 +936,11 @@ async function madHighCommand(args = []) {
|
|
|
932
936
|
}
|
|
933
937
|
const profile = await enableMadHighProfile();
|
|
934
938
|
console.log(`SKS MAD auto-review profile ready: ${madHighProfileName()}`);
|
|
935
|
-
console.log('Scope: explicit
|
|
936
|
-
const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${
|
|
937
|
-
return
|
|
939
|
+
console.log('Scope: explicit tmux launch only; full access uses Codex auto_review approvals when approval prompts are raised.');
|
|
940
|
+
const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${defaultTmuxSessionName(process.cwd())}`));
|
|
941
|
+
return launchTmuxUi([...cleanArgs, '--workspace', workspace], {
|
|
938
942
|
codexArgs: ['--profile', profile.profile_name],
|
|
939
|
-
|
|
943
|
+
autoInstallTmux: !flag(args, '--no-auto-install-tmux'),
|
|
940
944
|
conciseBlockers: true
|
|
941
945
|
});
|
|
942
946
|
}
|
|
@@ -972,12 +976,12 @@ async function ensureMadLaunchDependencies(args = []) {
|
|
|
972
976
|
const codex = await getCodexInfo().catch(() => ({}));
|
|
973
977
|
if (!codex.bin) actions.push(await installCodexDependency(args, { prompt: 'Codex CLI missing. Install latest Codex CLI with npm i -g @openai/codex@latest?' }));
|
|
974
978
|
}
|
|
975
|
-
if (!flag(args, '--no-auto-install-
|
|
976
|
-
const
|
|
977
|
-
if (!
|
|
979
|
+
if (!flag(args, '--no-auto-install-tmux')) {
|
|
980
|
+
const tmux = await tmuxReadiness().catch(() => ({ ok: false }));
|
|
981
|
+
if (!tmux.ok) actions.push(await installTmuxDependency(args));
|
|
978
982
|
}
|
|
979
983
|
const status = await depsStatus(await sksRoot());
|
|
980
|
-
return { ready: Boolean(status.codex_cli.ok && status.
|
|
984
|
+
return { ready: Boolean(status.codex_cli.ok && status.tmux.ok), actions, status };
|
|
981
985
|
}
|
|
982
986
|
|
|
983
987
|
async function deps(sub = 'check', args = []) {
|
|
@@ -991,7 +995,7 @@ async function deps(sub = 'check', args = []) {
|
|
|
991
995
|
return;
|
|
992
996
|
}
|
|
993
997
|
if (action === 'install') return depsInstall(args);
|
|
994
|
-
console.error('Usage: sks deps check|install [
|
|
998
|
+
console.error('Usage: sks deps check|install [tmux|codex|context7|all] [--yes] [--json]');
|
|
995
999
|
process.exitCode = 1;
|
|
996
1000
|
}
|
|
997
1001
|
|
|
@@ -1001,7 +1005,7 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1001
1005
|
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
1002
1006
|
const app = opts.codexApp || await codexAppIntegrationStatus({ codex });
|
|
1003
1007
|
const context7 = opts.context7 || await checkContext7(root);
|
|
1004
|
-
const
|
|
1008
|
+
const tmux = opts.tmux || await tmuxReadiness().catch((err) => ({ ok: false, version: null, error: err.message }));
|
|
1005
1009
|
const brew = process.platform === 'darwin' ? await which('brew').catch(() => null) : null;
|
|
1006
1010
|
const globalBin = await discoverGlobalSksCommand();
|
|
1007
1011
|
const npmPrefix = npmBin ? await runProcess(npmBin, ['prefix', '-g'], { timeoutMs: 8000, maxOutputBytes: 4096 }).catch(() => null) : null;
|
|
@@ -1009,10 +1013,10 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1009
1013
|
const npmPrefixDir = npmPrefix?.code === 0 ? npmPrefix.stdout.trim().split(/\r?\n/).pop() : null;
|
|
1010
1014
|
const npmBinDir = npmPrefixDir ? (process.platform === 'win32' ? npmPrefixDir : path.join(npmPrefixDir, 'bin')) : null;
|
|
1011
1015
|
const nodeOk = Number(process.versions.node.split('.')[0]) >= 20;
|
|
1012
|
-
const homebrewNeeded = process.platform === 'darwin' && !
|
|
1016
|
+
const homebrewNeeded = process.platform === 'darwin' && !tmux.ok;
|
|
1013
1017
|
return {
|
|
1014
1018
|
root,
|
|
1015
|
-
ready: Boolean(nodeOk && npmBin && globalBin && codex.bin && context7.ok &&
|
|
1019
|
+
ready: Boolean(nodeOk && npmBin && globalBin && codex.bin && context7.ok && tmux.ok),
|
|
1016
1020
|
node: { ok: nodeOk, version: process.version },
|
|
1017
1021
|
npm: { ok: Boolean(npmBin), bin: npmBin, global_bin_dir: npmBinDir, global_bin_on_path: npmBinDir ? pathText.split(path.delimiter).includes(npmBinDir) : null },
|
|
1018
1022
|
sneakoscope: { ok: Boolean(globalBin), bin: globalBin },
|
|
@@ -1021,13 +1025,13 @@ async function depsStatus(root = null, opts = {}) {
|
|
|
1021
1025
|
context7,
|
|
1022
1026
|
browser_use: { ok: app.mcp.has_browser_use, cache: app.plugins.browser_use_cache },
|
|
1023
1027
|
computer_use: { ok: app.mcp.has_computer_use, cache: app.plugins.computer_use_cache },
|
|
1024
|
-
|
|
1025
|
-
homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew,
|
|
1026
|
-
next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7,
|
|
1028
|
+
tmux: { ok: Boolean(tmux.ok), bin: tmux.bin || null, version: tmux.version || null, min_version: tmux.min_version || '3.0', current_session: Boolean(tmux.current_session), install_hint: tmux.ok ? null : platformTmuxInstallHint(), error: tmux.error || null },
|
|
1029
|
+
homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew, required_for_tmux_install: homebrewNeeded } : { ok: null, bin: null, required_for_tmux_install: false },
|
|
1030
|
+
next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7, tmux, brew, nodeOk })
|
|
1027
1031
|
};
|
|
1028
1032
|
}
|
|
1029
1033
|
|
|
1030
|
-
function depsNextActions({ npmBin, globalBin, codex, app, context7,
|
|
1034
|
+
function depsNextActions({ npmBin, globalBin, codex, app, context7, tmux, brew, nodeOk }) {
|
|
1031
1035
|
const out = [];
|
|
1032
1036
|
if (!nodeOk) out.push('Install Node.js 20.11+.');
|
|
1033
1037
|
if (!npmBin) out.push('Install npm or use a Node.js distribution that includes npm.');
|
|
@@ -1035,7 +1039,7 @@ function depsNextActions({ npmBin, globalBin, codex, app, context7, warp, brew,
|
|
|
1035
1039
|
if (!codex.bin) out.push('Run: sks deps install codex');
|
|
1036
1040
|
if (!context7.ok) out.push('Run: sks deps install context7');
|
|
1037
1041
|
if (!app.ok) out.push('Run: sks codex-app check');
|
|
1038
|
-
if (!
|
|
1042
|
+
if (!tmux.ok) out.push(process.platform === 'darwin' && !brew ? 'Install tmux from https://www.tmux.dev/download, or install Homebrew then run: sks deps install tmux' : 'Run: sks deps install tmux');
|
|
1039
1043
|
return out;
|
|
1040
1044
|
}
|
|
1041
1045
|
|
|
@@ -1050,7 +1054,7 @@ function printDepsStatus(status) {
|
|
|
1050
1054
|
console.log(`Context7: ${status.context7.ok ? 'ok' : 'missing'}`);
|
|
1051
1055
|
console.log(`Browser Use: ${status.browser_use.ok ? 'ok' : 'missing'}`);
|
|
1052
1056
|
console.log(`Computer Use:${status.computer_use.ok ? ' ok' : ' missing'}`);
|
|
1053
|
-
console.log(`
|
|
1057
|
+
console.log(`tmux: ${tmuxStatusKind(status.tmux)} ${status.tmux.version || status.tmux.error || ''}`.trimEnd());
|
|
1054
1058
|
if (process.platform === 'darwin') console.log(`Homebrew: ${status.homebrew.ok ? 'ok' : 'missing'} ${status.homebrew.bin || ''}`.trimEnd());
|
|
1055
1059
|
console.log(`Ready: ${status.ready ? 'true' : 'false'}`);
|
|
1056
1060
|
if (status.next_actions.length) {
|
|
@@ -1062,11 +1066,11 @@ function printDepsStatus(status) {
|
|
|
1062
1066
|
async function depsInstall(args = []) {
|
|
1063
1067
|
const root = await sksRoot();
|
|
1064
1068
|
const target = positionalArgs(args)[0] || 'all';
|
|
1065
|
-
const wants = target === 'all' ? ['codex', 'context7', '
|
|
1069
|
+
const wants = target === 'all' ? ['codex', 'context7', 'tmux'] : [target];
|
|
1066
1070
|
const actions = [];
|
|
1067
1071
|
if (wants.includes('codex')) actions.push(await installCodexDependency(args));
|
|
1068
1072
|
if (wants.includes('context7')) actions.push(await installContext7Dependency(root));
|
|
1069
|
-
if (wants.includes('
|
|
1073
|
+
if (wants.includes('tmux')) actions.push(await installTmuxDependency(args));
|
|
1070
1074
|
const status = await depsStatus(root);
|
|
1071
1075
|
const result = { target, actions, status };
|
|
1072
1076
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
@@ -1091,12 +1095,12 @@ async function installContext7Dependency(root) {
|
|
|
1091
1095
|
return { target: 'context7', status: changed ? 'project_configured' : 'already_configured', command: 'sks context7 check' };
|
|
1092
1096
|
}
|
|
1093
1097
|
|
|
1094
|
-
async function
|
|
1095
|
-
const before = await
|
|
1096
|
-
if (before.ok) return { target: '
|
|
1097
|
-
const command = process.platform === 'darwin' ? 'brew install
|
|
1098
|
-
if (flag(args, '--dry-run')) return { target: '
|
|
1099
|
-
return { target: '
|
|
1098
|
+
async function installTmuxDependency(args = []) {
|
|
1099
|
+
const before = await tmuxReadiness().catch(() => ({ ok: false }));
|
|
1100
|
+
if (before.ok) return { target: 'tmux', status: 'present', version: before.version || null, app: before.app || null, cli: before.cli || null };
|
|
1101
|
+
const command = process.platform === 'darwin' ? 'brew install tmux' : platformTmuxInstallHint();
|
|
1102
|
+
if (flag(args, '--dry-run')) return { target: 'tmux', status: 'dry_run', command };
|
|
1103
|
+
return { target: 'tmux', status: 'manual_required', command, error: before.error || 'tmux not found' };
|
|
1100
1104
|
}
|
|
1101
1105
|
|
|
1102
1106
|
async function confirmInstall(question, args = []) {
|
|
@@ -1146,8 +1150,8 @@ async function autoReviewCommand(sub = 'status', args = []) {
|
|
|
1146
1150
|
if (flag(args, '--json')) return console.log(JSON.stringify(status, null, 2));
|
|
1147
1151
|
console.log(`SKS Auto-Review enabled: ${profile}`);
|
|
1148
1152
|
const sessionArg = readOption(cleanArgs, '--session', null);
|
|
1149
|
-
const session = sessionArg ||
|
|
1150
|
-
return
|
|
1153
|
+
const session = sessionArg || sanitizeTmuxSessionName(`${profile}-${defaultTmuxSessionName(process.cwd())}`);
|
|
1154
|
+
return launchTmuxUi([...cleanArgs, '--session', session], { codexArgs: ['--profile', profile] });
|
|
1151
1155
|
}
|
|
1152
1156
|
console.error('Usage: sks auto-review status|enable|disable|start [--high] [--json]');
|
|
1153
1157
|
console.error('Alias: sks --Auto-review [--high]');
|
|
@@ -1182,7 +1186,7 @@ async function codexAppHelp(args = []) {
|
|
|
1182
1186
|
'ㅅㅋㅅ Codex App', '',
|
|
1183
1187
|
formatCodexAppStatus(status), '',
|
|
1184
1188
|
`Skills: project=${skills.project.ok ? 'ok' : `missing ${skills.project.missing.length}`} global=${skills.global.ok ? 'ok' : `missing ${skills.global.missing.length}`}`, '',
|
|
1185
|
-
'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks
|
|
1189
|
+
'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks tmux check', '',
|
|
1186
1190
|
'Generated files:', ' .codex/config.toml', ' .codex/hooks.json', ' .agents/skills/', ' .codex/agents/', ' .codex/SNEAKOSCOPE.md', ' AGENTS.md', '',
|
|
1187
1191
|
'Git ignore:', ' default setup writes .gitignore entries for .sneakoscope/, .codex/, .agents/, AGENTS.md', ' --local-only writes those patterns to .git/info/exclude instead', '',
|
|
1188
1192
|
'Prompt routes:', formatDollarCommandsCompact(' ')
|
|
@@ -1215,20 +1219,20 @@ Examples:
|
|
|
1215
1219
|
function usage(args = []) {
|
|
1216
1220
|
const topic = String(args[0] || 'overview').toLowerCase();
|
|
1217
1221
|
const blocks = {
|
|
1218
|
-
overview: ['ㅅㅋㅅ Usage', '', 'Discover:', ' sks commands', ' sks quickstart', ' sks root', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks
|
|
1222
|
+
overview: ['ㅅㅋㅅ Usage', '', 'Discover:', ' sks commands', ' sks quickstart', ' sks root', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks tmux check', ' sks dollar-commands', '', `Topics: ${USAGE_TOPICS}`],
|
|
1219
1223
|
install: ['Install', '', ' npm i -g sneakoscope', ' sks root', ' sks', '', 'Project bootstrap:', ' sks bootstrap', '', 'Fallback:', ' npx -y -p sneakoscope sks root', '', 'Project:', ' npm i -D sneakoscope', ' npx sks setup --install-scope project'],
|
|
1220
|
-
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
|
|
1224
|
+
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 tmux.'],
|
|
1221
1225
|
root: ['Root', '', ' sks root [--json]', '', 'Inside a project, SKS uses that project root. Outside any project marker, runtime commands use the per-user global SKS root instead of writing .sneakoscope into the current random folder.'],
|
|
1222
|
-
deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [
|
|
1223
|
-
|
|
1224
|
-
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', ' sks team lane latest --agent analysis_scout_1 --follow', ' sks team message latest --from analysis_scout_1 --to executor_1 --message "handoff note"', ' sks team cleanup-
|
|
1226
|
+
deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [tmux|codex|context7|all] [--yes]', '', 'tmux on macOS uses Homebrew only after approval.'],
|
|
1227
|
+
tmux: ['tmux', '', ' sks tmux open', ' sks tmux check', ' sks tmux status --once', ' sks deps install tmux', '', 'tmux launch is explicit. Running bare `sks` prints help and never opens tmux by itself.'],
|
|
1228
|
+
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', ' sks team lane latest --agent analysis_scout_1 --follow', ' sks team message latest --from analysis_scout_1 --to executor_1 --message "handoff note"', ' sks team cleanup-tmux latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
|
|
1225
1229
|
'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'],
|
|
1226
|
-
ppt: ['PPT', '', ' $PPT 투자자용 피치덱을 HTML 기반 PDF로 만들어줘', ' $PPT 우리 SaaS 소개자료 만들어줘', ' sks ppt build latest --json', ' sks ppt status latest --json', '', '$PPT asks delivery context, audience profile, STP strategy, decision context, and 3+ pain-point/solution/aha mappings before source research, design-system work, HTML/PDF export, and render QA. The visual system must stay simple, restrained, and information-first; editable source HTML is kept under source-html/, PPT-only temporary build files are cleaned, and generated image assets prefer Codex App built-in image generation through imagegen.'],
|
|
1230
|
+
ppt: ['PPT', '', ' $PPT 투자자용 피치덱을 HTML 기반 PDF로 만들어줘', ' $PPT 우리 SaaS 소개자료 만들어줘', ' sks ppt build latest --json', ' sks ppt status latest --json', '', '$PPT asks delivery context, audience profile, STP strategy, decision context, and 3+ pain-point/solution/aha mappings before source research, design-system work, HTML/PDF export, and render QA. Independent strategy/render/file-write phases run in parallel where inputs allow and are recorded in ppt-parallel-report.json. The visual system must stay simple, restrained, and information-first; editable source HTML is kept under source-html/, PPT-only temporary build files are cleaned, and generated image assets prefer Codex App built-in image generation through imagegen.'],
|
|
1227
1231
|
goal: ['Goal', '', ' sks goal create "task"', ' sks goal status latest', ' sks goal pause latest', ' sks goal resume latest', ' sks goal clear latest'],
|
|
1228
1232
|
'codex-app': ['Codex App', '', ' sks bootstrap', ' sks codex-app check', ' sks dollar-commands', ' cat .codex/SNEAKOSCOPE.md'],
|
|
1229
1233
|
dollar: ['Dollar Commands', '', formatDollarCommandsCompact(' '), '', 'Terminal: sks dollar-commands [--json]'],
|
|
1230
1234
|
wiki: ['TriWiki', '', ' sks wiki pack', ' sks wiki refresh [--prune]', ' sks wiki sweep latest --json', ' sks wiki validate .sneakoscope/wiki/context-pack.json', ' sks wiki prune --dry-run --json', '', 'Packs include attention.use_first and attention.hydrate_first for compact recall plus source hydration. Sweep records intentional forgetting and promotion candidates.'],
|
|
1231
|
-
harness: ['Harness Growth', '', ' sks harness fixture --json', ' sks harness review --json', '', 'Runs deterministic fixtures for deliberate forgetting, skill cards, harness experiments, tool error taxonomy, permission profiles, MultiAgentV2, and
|
|
1235
|
+
harness: ['Harness Growth', '', ' sks harness fixture --json', ' sks harness review --json', '', 'Runs deterministic fixtures for deliberate forgetting, skill cards, harness experiments, tool error taxonomy, permission profiles, MultiAgentV2, and tmux cockpit views.'],
|
|
1232
1236
|
'skill-dream': ['Skill Dreaming', '', ' sks skill-dream status', ' sks skill-dream run --json', ' sks skill-dream record --route team --skills team,prompt-pipeline', '', 'Records cheap JSON usage counters in .sneakoscope/skills/dream-state.json and periodically writes recommendation-only keep/merge/prune/improve reports. It never deletes or merges skills automatically.'],
|
|
1233
1237
|
'code-structure': ['Code Structure', '', ' sks code-structure scan', ' sks code-structure scan --json', '', 'Flags handwritten source files above 1000/2000/3000-line thresholds and records split-review exceptions.'],
|
|
1234
1238
|
gx: ['GX', '', ' sks gx init architecture-atlas', ' sks gx render architecture-atlas --format all', ' sks gx validate architecture-atlas']
|
|
@@ -1253,13 +1257,13 @@ async function bootstrap(args = []) {
|
|
|
1253
1257
|
const cliTools = await ensureRelatedCliTools(args);
|
|
1254
1258
|
const context7Status = await checkContext7(root);
|
|
1255
1259
|
const appRuntime = await codexAppIntegrationStatus({ codex: await getCodexInfo().catch(() => ({})) });
|
|
1256
|
-
const deps = await depsStatus(root, { context7: context7Status, codexApp: appRuntime,
|
|
1260
|
+
const deps = await depsStatus(root, { context7: context7Status, codexApp: appRuntime, tmux: cliTools.tmux });
|
|
1257
1261
|
const install = await installStatus(root, installScope, { globalCommand });
|
|
1258
1262
|
const versioningInfo = await versioningStatus(root);
|
|
1259
1263
|
const skills = await checkRequiredSkills(root);
|
|
1260
1264
|
const guard = await harnessGuardStatus(root);
|
|
1261
1265
|
const files = await codexAppFilesStatus(root, skills, versioningInfo);
|
|
1262
|
-
const ready = Boolean(!conflicts.hard_block && install.ok && files.ok && skills.ok && guard.ok && context7Status.ok && appRuntime.ok && deps.
|
|
1266
|
+
const ready = Boolean(!conflicts.hard_block && install.ok && files.ok && skills.ok && guard.ok && context7Status.ok && appRuntime.ok && deps.tmux.ok);
|
|
1263
1267
|
const result = {
|
|
1264
1268
|
root,
|
|
1265
1269
|
ready,
|
|
@@ -1270,7 +1274,7 @@ async function bootstrap(args = []) {
|
|
|
1270
1274
|
codex_app: appRuntime,
|
|
1271
1275
|
global_skills: globalSkills,
|
|
1272
1276
|
context7: context7Status,
|
|
1273
|
-
|
|
1277
|
+
tmux: deps.tmux,
|
|
1274
1278
|
harness_guard: guard,
|
|
1275
1279
|
deps,
|
|
1276
1280
|
next: ready ? ['sks', '$Team implement ...', '$QA-LOOP run ...'] : deps.next_actions
|
|
@@ -1283,7 +1287,7 @@ async function bootstrap(args = []) {
|
|
|
1283
1287
|
console.log(`Hooks: ${files.hooks.ok ? 'ok' : 'missing'}`);
|
|
1284
1288
|
console.log(`Harness guard: ${guard.ok ? 'ok' : 'blocked'}`);
|
|
1285
1289
|
console.log(`Context7: ${context7Status.ok ? 'ok' : 'missing'}`);
|
|
1286
|
-
console.log(`
|
|
1290
|
+
console.log(`tmux: ${deps.tmux.ok ? 'ok' : 'missing'}${deps.tmux.version ? ` ${deps.tmux.version}` : ''}`);
|
|
1287
1291
|
console.log(`ready: ${ready ? 'true' : 'false'}`);
|
|
1288
1292
|
if (!ready) {
|
|
1289
1293
|
console.log('\nNext:');
|
|
@@ -1362,7 +1366,7 @@ async function setup(args) {
|
|
|
1362
1366
|
console.log('ㅅㅋㅅ Setup\n');
|
|
1363
1367
|
console.log(`Project: ${root}`);
|
|
1364
1368
|
console.log(`Install: ${install.ok ? 'ok' : 'missing'} ${install.scope} (${install.command_prefix})`);
|
|
1365
|
-
console.log(`CLI tools: Codex ${formatCodexCliToolStatus(cliTools.codex)};
|
|
1369
|
+
console.log(`CLI tools: Codex ${formatCodexCliToolStatus(cliTools.codex)}; tmux ${tmuxStatusKind(cliTools.tmux)} ${cliTools.tmux.version || cliTools.tmux.error || ''}`.trimEnd());
|
|
1366
1370
|
console.log(`Hooks: ${path.relative(root, hooksPath)}`);
|
|
1367
1371
|
console.log(`Version: ${versioningInfo.enabled ? (versioningInfo.hook_installed ? 'auto-bump enabled' : 'auto-bump hook missing') : 'not enabled'}${versioningInfo.package_version ? ` (${versioningInfo.package_version})` : ''}`);
|
|
1368
1372
|
if (localOnly) console.log('Git: local-only (.git/info/exclude; user AGENTS preserved, SKS managed block refreshed)');
|
|
@@ -1375,7 +1379,7 @@ async function setup(args) {
|
|
|
1375
1379
|
console.log(`Next: sks context7 check; sks selftest --mock; sks commands; sks dollar-commands`);
|
|
1376
1380
|
if (cliTools.codex.status === 'failed') console.log(`\nCodex CLI install failed. Run manually: npm i -g @openai/codex. ${cliTools.codex.error || ''}`.trim());
|
|
1377
1381
|
if (cliTools.codex.status === 'installed_not_on_path') console.log(`\nCodex CLI installed but not on PATH. ${cliTools.codex.hint}`);
|
|
1378
|
-
if (!cliTools.
|
|
1382
|
+
if (!cliTools.tmux.ok) console.log(`\ntmux ${tmuxStatusKind(cliTools.tmux)}. Install: ${cliTools.tmux.install_hint}`);
|
|
1379
1383
|
if (!install.ok && install.scope === 'global') console.log('\nGlobal command missing. Run: npm i -g sneakoscope');
|
|
1380
1384
|
if (!install.ok && install.scope === 'project') console.log('\nProject package missing. Run: npm i -D sneakoscope');
|
|
1381
1385
|
if (!appRuntime.ok) console.log('\nCodex App and first-party Codex Computer Use are required for SKS QA/visual evidence; Browser Use is not a UI verification substitute. Run: sks codex-app check');
|
|
@@ -1446,7 +1450,7 @@ async function doctor(args) {
|
|
|
1446
1450
|
const dbScan = await scanDbSafety(root).catch((err) => ({ ok: false, findings: [{ id: 'db_safety_scan_failed', severity: 'high', reason: err.message }] }));
|
|
1447
1451
|
const context7Status = await checkContext7(root);
|
|
1448
1452
|
const appRuntime = await codexAppIntegrationStatus({ codex });
|
|
1449
|
-
const
|
|
1453
|
+
const tmuxStatus = await tmuxReadiness().catch((err) => ({ ok: false, version: null, error: err.message }));
|
|
1450
1454
|
const skillStatus = await checkRequiredSkills(root);
|
|
1451
1455
|
const globalSkillStatus = await checkRequiredSkills(null, globalCodexSkillsRoot());
|
|
1452
1456
|
const guardStatus = await harnessGuardStatus(root);
|
|
@@ -1467,7 +1471,7 @@ async function doctor(args) {
|
|
|
1467
1471
|
sneakoscope: { ok: await exists(path.join(root, '.sneakoscope')) },
|
|
1468
1472
|
context7: context7Status,
|
|
1469
1473
|
codex_app_runtime: appRuntime,
|
|
1470
|
-
runtime: {
|
|
1474
|
+
runtime: { tmux: { ok: Boolean(tmuxStatus.ok), bin: tmuxStatus.bin || null, version: tmuxStatus.version || null, min_version: tmuxStatus.min_version || '3.0', current_session: Boolean(tmuxStatus.current_session), install_hint: tmuxStatus.ok ? null : platformTmuxInstallHint(), error: tmuxStatus.error || null } },
|
|
1471
1475
|
harness_guard: guardStatus,
|
|
1472
1476
|
versioning: versioningInfo,
|
|
1473
1477
|
db_guard: { ok: dbPolicyExists && dbScan.ok, policy: dbPolicyExists ? await loadDbSafetyPolicy(root) : null, scan: dbScan },
|
|
@@ -1479,7 +1483,7 @@ async function doctor(args) {
|
|
|
1479
1483
|
},
|
|
1480
1484
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) }, storage
|
|
1481
1485
|
};
|
|
1482
|
-
result.ready = !result.harness_conflicts.hard_block && nodeOk && Boolean(codex.bin) && install.ok && result.sneakoscope.ok && result.context7.ok && appRuntime.ok && result.runtime.
|
|
1486
|
+
result.ready = !result.harness_conflicts.hard_block && nodeOk && Boolean(codex.bin) && install.ok && result.sneakoscope.ok && result.context7.ok && appRuntime.ok && result.runtime.tmux.ok && result.harness_guard.ok && result.versioning.ok && result.db_guard.ok && result.codex_app.ok && result.skills.ok && result.global_skills.ok;
|
|
1483
1487
|
if (result.harness_conflicts.hard_block) process.exitCode = 1;
|
|
1484
1488
|
if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
|
|
1485
1489
|
console.log('ㅅㅋㅅ Doctor\n');
|
|
@@ -1495,7 +1499,7 @@ async function doctor(args) {
|
|
|
1495
1499
|
console.log(`State: ${result.sneakoscope.ok ? 'ok' : 'missing .sneakoscope'}`);
|
|
1496
1500
|
console.log(`Context7: ${result.context7.ok ? 'ok' : 'missing MCP config'} project=${result.context7.project.ok ? 'ok' : 'missing'} global=${result.context7.global.ok ? 'ok' : 'missing'}`);
|
|
1497
1501
|
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'}`);
|
|
1498
|
-
console.log(`
|
|
1502
|
+
console.log(`tmux: ${tmuxStatusKind(result.runtime.tmux)} ${result.runtime.tmux.version || result.runtime.tmux.error || ''}`.trimEnd());
|
|
1499
1503
|
console.log(`Guard: ${result.harness_guard.ok ? 'ok' : 'blocked'}${result.harness_guard.source_exception ? ' source-exception' : ''}`);
|
|
1500
1504
|
console.log(`Version: ${result.versioning.ok ? 'ok' : 'missing'}${result.versioning.enabled ? ` ${result.versioning.package_version || ''}` : ` ${result.versioning.reason || 'disabled'}`}`);
|
|
1501
1505
|
console.log(`DB Guard: ${result.db_guard.ok ? 'ok' : 'blocked'} ${dbScan.findings?.length || 0} finding(s)`);
|
|
@@ -1512,13 +1516,13 @@ async function doctor(args) {
|
|
|
1512
1516
|
if (result.harness_conflicts.hard_block) console.log(`\n${formatHarnessConflictReport(conflictScan)}`);
|
|
1513
1517
|
if (!result.context7.ok) console.log('Context7 MCP missing. Run: sks context7 setup --scope project');
|
|
1514
1518
|
if (!appRuntime.ok) console.log('Codex App or first-party MCP/plugin tools missing. Run: sks codex-app check');
|
|
1515
|
-
if (!result.runtime.
|
|
1519
|
+
if (!result.runtime.tmux.ok) console.log('tmux missing. Run: sks deps install tmux');
|
|
1516
1520
|
if (!result.harness_guard.ok) console.log('Harness guard failed. Run: sks setup from a real terminal, then sks guard check.');
|
|
1517
1521
|
if (!result.versioning.ok) console.log('Versioning hook missing. Run: sks versioning hook, or sks doctor --fix.');
|
|
1518
1522
|
if (!result.skills.ok) console.log(`Missing skills: ${result.skills.missing.join(', ')}. Run: sks setup`);
|
|
1519
1523
|
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.`);
|
|
1520
1524
|
const blocked = [];
|
|
1521
|
-
if (!result.runtime.
|
|
1525
|
+
if (!result.runtime.tmux.ok) blocked.push(['tmux is missing', 'sks deps install tmux']);
|
|
1522
1526
|
if (!appRuntime.ok) blocked.push(['Codex App or first-party MCP/plugin tools need setup', 'sks codex-app check']);
|
|
1523
1527
|
if (blocked.length) {
|
|
1524
1528
|
console.log('\nBlocked:');
|
|
@@ -1827,7 +1831,7 @@ async function selftest() {
|
|
|
1827
1831
|
if (!bootstrapResult.project_setup?.ok || typeof bootstrapResult.ready !== 'boolean') throw new Error('selftest failed: bootstrap json did not report project setup and ready boolean');
|
|
1828
1832
|
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 });
|
|
1829
1833
|
const depsResult = JSON.parse(depsCheck.stdout);
|
|
1830
|
-
if (!depsResult.node?.ok || !('
|
|
1834
|
+
if (!depsResult.node?.ok || !('tmux' in depsResult) || !('homebrew' in depsResult)) throw new Error('selftest failed: deps check json missing expected fields');
|
|
1831
1835
|
const globalCwd = tmpdir();
|
|
1832
1836
|
const globalRuntimeRoot = path.join(tmpdir(), 'sks-global-root');
|
|
1833
1837
|
const globalRootProbe = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'root', '--json'], { cwd: globalCwd, env: { SKS_GLOBAL_ROOT: globalRuntimeRoot }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
@@ -1845,32 +1849,13 @@ async function selftest() {
|
|
|
1845
1849
|
const madProfileText = await safeReadText(madProfilePath);
|
|
1846
1850
|
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"') || !madProfileText.includes('unrequested fallback implementation code')) throw new Error('selftest failed: MAD high profile is not full-access auto-review high with fallback-code guard');
|
|
1847
1851
|
if (!isMadHighLaunch(['--mad', '--high']) || isMadHighLaunch(['db', '--mad'])) throw new Error('selftest failed: MAD high launch flag parsing is not top-level only');
|
|
1848
|
-
const workspacePlan = {
|
|
1849
|
-
const
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
if (!
|
|
1854
|
-
if (
|
|
1855
|
-
const warpNestedDecision = warpOpenLaunchDecision({ env: { TERM_PROGRAM: 'WarpTerminal' } });
|
|
1856
|
-
if (warpNestedDecision.open || !warpNestedDecision.current_session) throw new Error('selftest failed: nested Warp launch was not redirected to current session');
|
|
1857
|
-
const oldWarpConfigDir = process.env.SKS_WARP_LAUNCH_CONFIG_DIR;
|
|
1858
|
-
process.env.SKS_WARP_LAUNCH_CONFIG_DIR = path.join(tmp, 'warp-launch-configs');
|
|
1859
|
-
const writtenWarpConfig = await writeWarpLaunchConfig({ ...workspacePlan, command: 'codex', title: 'sks-mad-selftest' }, [{ cwd: tmp, command: 'codex --profile sks-mad-high', focused: true }]);
|
|
1860
|
-
if (!(await exists(writtenWarpConfig.config_path)) || !writtenWarpConfig.record.launch_uri.includes('warp://launch/')) throw new Error('selftest failed: Warp launch configuration was not persisted for URI launch');
|
|
1861
|
-
const currentSessionLaunch = await launchWarpUi(['--workspace', 'sks-current-session-selftest'], {
|
|
1862
|
-
root: tmp,
|
|
1863
|
-
codex: { bin: 'printf', version: 'mock' },
|
|
1864
|
-
app: { ok: true, guidance: [] },
|
|
1865
|
-
warp: { ok: true, version: 'Warp.app' },
|
|
1866
|
-
env: { TERM_PROGRAM: 'WarpTerminal' },
|
|
1867
|
-
dryRunCurrentSession: true,
|
|
1868
|
-
quiet: true
|
|
1869
|
-
});
|
|
1870
|
-
if (!currentSessionLaunch.opened?.current_session || currentSessionLaunch.opened?.skipped) throw new Error('selftest failed: Warp shell launch did not stay in the current session');
|
|
1871
|
-
if (oldWarpConfigDir === undefined) delete process.env.SKS_WARP_LAUNCH_CONFIG_DIR;
|
|
1872
|
-
else process.env.SKS_WARP_LAUNCH_CONFIG_DIR = oldWarpConfigDir;
|
|
1873
|
-
if (warpStatusKind({ ok: false, bin: null }) !== 'missing') throw new Error('selftest failed: missing warp was not labeled missing');
|
|
1852
|
+
const workspacePlan = { session: 'sks-mad-selftest', root: tmp, codexArgs: ['--profile', 'sks-mad-high'] };
|
|
1853
|
+
const tmuxSyntax = runTmuxLaunchPlanSyntaxCheck(workspacePlan);
|
|
1854
|
+
if (!tmuxSyntax.ok || !tmuxSyntax.command.includes('tmux attach-session -t sks-mad-selftest')) throw new Error('selftest failed: MAD tmux attach plan is not stable by session name');
|
|
1855
|
+
const tmuxOpenArgs = buildTmuxOpenArgs(workspacePlan);
|
|
1856
|
+
if (tmuxOpenArgs.join(' ') !== 'attach-session -t sks-mad-selftest') throw new Error('selftest failed: MAD tmux attach args are not stable by session name');
|
|
1857
|
+
if (!isTmuxShellSession({ TMUX: '/tmp/tmux-501/default,1,0' })) throw new Error('selftest failed: tmux shell session env was not detected');
|
|
1858
|
+
if (tmuxStatusKind({ ok: false, bin: null }) !== 'missing') throw new Error('selftest failed: missing tmux was not labeled missing');
|
|
1874
1859
|
const guardBlocked = await checkHarnessModification(tmp, { tool_name: 'apply_patch', command: '*** Update File: .agents/skills/team/SKILL.md\n+tamper\n' });
|
|
1875
1860
|
if (guardBlocked.action !== 'block') throw new Error('selftest failed: harness guard allowed skill tampering');
|
|
1876
1861
|
const setupBlocked = await checkHarnessModification(tmp, { command: 'sks setup --force' });
|
|
@@ -2664,19 +2649,19 @@ async function selftest() {
|
|
|
2664
2649
|
if (maxTextParsed.agentSessions !== 6 || maxTextParsed.roleCounts.executor !== 6) throw new Error('selftest failed: team max-agent text parsing');
|
|
2665
2650
|
const roleParsed = parseTeamCreateArgs(['executor:5', 'reviewer:2', 'user:1', '작업']);
|
|
2666
2651
|
if (roleParsed.roleCounts.executor !== 5 || roleParsed.roleCounts.reviewer !== 2 || roleParsed.agentSessions !== 5 || roleParsed.prompt !== '작업') throw new Error('selftest failed: team role-count parsing');
|
|
2667
|
-
const
|
|
2668
|
-
if (
|
|
2652
|
+
const openTmuxFlagParsed = parseTeamCreateArgs(['--open-tmux', '작업']);
|
|
2653
|
+
if (openTmuxFlagParsed.prompt !== '작업') throw new Error('selftest failed: team --open-tmux leaked into prompt');
|
|
2669
2654
|
const roleTeamPlan = buildTeamPlan(teamId, '역할 팀 테스트', { roleCounts: roleParsed.roleCounts });
|
|
2670
2655
|
if (roleTeamPlan.roster.debate_team.length !== 5) throw new Error('selftest failed: executor role count not reflected in debate team size');
|
|
2671
2656
|
if (roleTeamPlan.roster.analysis_team.length !== 5) throw new Error('selftest failed: executor role count not reflected in analysis scout team');
|
|
2672
2657
|
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');
|
|
2673
2658
|
if (!roleTeamPlan.roster.debate_team.some((agent) => /inconvenience/.test(agent.persona))) throw new Error('selftest failed: user friction persona missing from debate team');
|
|
2674
|
-
const
|
|
2675
|
-
if (!
|
|
2676
|
-
if (!
|
|
2677
|
-
if (teamLaneStyle('analysis_scout_1').role !== 'scout' || teamLaneStyle('executor_1').role !== 'execution' || teamLaneStyle('reviewer_1').role !== 'review') throw new Error('selftest failed: Team
|
|
2678
|
-
if (!String(
|
|
2679
|
-
if (!
|
|
2659
|
+
const tmuxTeam = await launchTmuxTeamView({ root: tmp, missionId: teamId, plan: roleTeamPlan, json: true });
|
|
2660
|
+
if (!tmuxTeam.agents?.length || !tmuxTeam.agents.some((entry) => entry.agent === 'analysis_scout_1') || !tmuxTeam.agents.every((entry) => String(entry.command || '').includes('team lane') && String(entry.command || '').includes('--agent'))) throw new Error('selftest failed: Team tmux view did not expose agent live lanes');
|
|
2661
|
+
if (!tmuxTeam.overview?.command?.includes('team watch') || !tmuxTeam.lanes?.some((entry) => entry.role === 'overview') || !tmuxTeam.lanes?.some((entry) => entry.agent === 'analysis_scout_1')) throw new Error('selftest failed: Team tmux view did not expose orchestration overview plus agent lanes');
|
|
2662
|
+
if (teamLaneStyle('analysis_scout_1').role !== 'scout' || teamLaneStyle('executor_1').role !== 'execution' || teamLaneStyle('reviewer_1').role !== 'review') throw new Error('selftest failed: Team tmux role palette did not classify lane roles');
|
|
2663
|
+
if (!String(tmuxTeam.cleanup_policy || '').includes('mark-complete') || !tmuxTeam.lanes.every((entry) => entry.style?.color && entry.title)) throw new Error('selftest failed: Team tmux view did not expose color/title metadata and cleanup policy');
|
|
2664
|
+
if (tmuxTeam.session !== `sks-team-${teamId}` || !tmuxTeam.attach_command?.includes(`sks-team-${teamId}`)) throw new Error('selftest failed: Team tmux session is not named for visibility');
|
|
2680
2665
|
if (routeReasoning(routePrompt('$Research frontier idea'), '$Research frontier idea').effort !== 'xhigh') throw new Error('selftest failed: research reasoning not xhigh');
|
|
2681
2666
|
if (routeReasoning(routePrompt('$From-Chat-IMG 채팅 이미지 작업'), '$From-Chat-IMG 채팅 이미지 작업').effort !== 'xhigh') throw new Error('selftest failed: From-Chat-IMG reasoning not xhigh');
|
|
2682
2667
|
if (routeReasoning(routePrompt('$Computer-Use localhost UI smoke'), '$Computer-Use localhost UI smoke').effort !== 'low') throw new Error('selftest failed: Computer Use fast lane reasoning not low');
|
|
@@ -2775,7 +2760,7 @@ async function selftest() {
|
|
|
2775
2760
|
const pptSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'ppt', 'SKILL.md'));
|
|
2776
2761
|
if (!pptSkillText.includes('STP') || !pptSkillText.includes('target audience profile') || !pptSkillText.includes('decision context') || !pptSkillText.includes('3+ pain-point to solution mappings')) throw new Error('selftest failed: generated PPT skill missing STP/audience/pain-point guidance');
|
|
2777
2762
|
if (!pptSkillText.includes('simple, restrained, and information-first') || !pptSkillText.includes('over-designed decoration') || !pptSkillText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL)) throw new Error('selftest failed: generated PPT skill missing restrained design/imagegen guidance');
|
|
2778
|
-
if (!pptSkillText.includes('source-html/') || !pptSkillText.includes('temporary build files')) throw new Error('selftest failed: generated PPT skill missing source preservation/temp cleanup guidance');
|
|
2763
|
+
if (!pptSkillText.includes('source-html/') || !pptSkillText.includes('temporary build files') || !pptSkillText.includes('ppt-parallel-report.json')) throw new Error('selftest failed: generated PPT skill missing source preservation/temp cleanup/parallel guidance');
|
|
2779
2764
|
if (routeRequiresSubagents(pptRoute, '$PPT 투자자용 피치덱 만들어줘')) throw new Error('selftest failed: PPT route should not require subagents by default');
|
|
2780
2765
|
if (!reflectionRequiredForRoute(pptRoute)) throw new Error('selftest failed: PPT route should require reflection');
|
|
2781
2766
|
const pptMission = await createMission(tmp, { mode: 'ppt', prompt: '$PPT 투자자용 피치덱 만들어줘' });
|
|
@@ -2798,7 +2783,7 @@ async function selftest() {
|
|
|
2798
2783
|
const pptBuildResult = await runProcess(process.execPath, [hookBin, 'ppt', 'build', pptMission.id, '--json'], { cwd: tmp, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
|
|
2799
2784
|
if (pptBuildResult.code !== 0) throw new Error(`selftest failed: sks ppt build failed: ${pptBuildResult.stderr || pptBuildResult.stdout}`);
|
|
2800
2785
|
const pptBuild = JSON.parse(pptBuildResult.stdout);
|
|
2801
|
-
if (!pptBuild.ok || !pptBuild.gate?.passed || !pptBuild.gate?.html_artifact_created || !pptBuild.gate?.source_html_preserved || !pptBuild.gate?.pdf_exported_or_explicitly_deferred || !pptBuild.gate?.render_qa_recorded || !pptBuild.gate?.temp_cleanup_recorded) throw new Error('selftest failed: PPT build did not pass artifact gate');
|
|
2786
|
+
if (!pptBuild.ok || !pptBuild.gate?.passed || !pptBuild.gate?.parallel_build_recorded || !pptBuild.gate?.html_artifact_created || !pptBuild.gate?.source_html_preserved || !pptBuild.gate?.pdf_exported_or_explicitly_deferred || !pptBuild.gate?.render_qa_recorded || !pptBuild.gate?.temp_cleanup_recorded) throw new Error('selftest failed: PPT build did not pass artifact gate');
|
|
2802
2787
|
if (!PPT_HTML_ARTIFACT.startsWith(`${PPT_SOURCE_HTML_DIR}/`)) throw new Error('selftest failed: PPT HTML source must be stored in source-html folder');
|
|
2803
2788
|
const pptHtml = await safeReadText(path.join(pptMission.dir, PPT_HTML_ARTIFACT));
|
|
2804
2789
|
if (!pptHtml.includes('<html') || pptHtml.includes('gradient')) throw new Error('selftest failed: PPT HTML artifact missing or over-designed');
|
|
@@ -2809,6 +2794,8 @@ async function selftest() {
|
|
|
2809
2794
|
if (pptPdfBytes.subarray(0, 5).toString('utf8') !== '%PDF-') throw new Error('selftest failed: PPT PDF artifact does not have a PDF header');
|
|
2810
2795
|
const pptRenderReport = await readJson(path.join(pptMission.dir, PPT_RENDER_REPORT_ARTIFACT));
|
|
2811
2796
|
if (!pptRenderReport.passed || !pptRenderReport.design_policy_checks.every((check) => check.passed)) throw new Error('selftest failed: PPT render report did not pass design policy checks');
|
|
2797
|
+
const pptParallelReport = await readJson(path.join(pptMission.dir, PPT_PARALLEL_REPORT_ARTIFACT));
|
|
2798
|
+
if (!pptParallelReport.passed || pptParallelReport.parallel_group_count < 2 || !pptParallelReport.parallel_groups.some((group) => group.id === 'render_targets' && group.executed_in_parallel)) throw new Error('selftest failed: PPT parallel report did not record parallel build groups');
|
|
2812
2799
|
const pptCleanupReport = await readJson(path.join(pptMission.dir, PPT_CLEANUP_REPORT_ARTIFACT));
|
|
2813
2800
|
if (!pptCleanupReport.source_html_preserved || !pptCleanupReport.temp_cleanup_completed || pptCleanupReport.source_html_path !== PPT_HTML_ARTIFACT) throw new Error('selftest failed: PPT cleanup report did not preserve source HTML');
|
|
2814
2801
|
if (await exists(path.join(pptMission.dir, PPT_TEMP_DIR))) throw new Error('selftest failed: PPT temp directory was not cleaned');
|
|
@@ -2862,7 +2849,7 @@ async function selftest() {
|
|
|
2862
2849
|
if (!evalReport.candidate.wiki?.valid) throw new Error('selftest failed: wiki coordinate index invalid in eval');
|
|
2863
2850
|
if (evalReport.candidate.wiki?.voxel_schema !== 'sks.wiki-voxel.v1' || evalReport.candidate.wiki?.voxel_rows < 1) throw new Error('selftest failed: eval did not include voxel overlay metrics');
|
|
2864
2851
|
const harnessReport = harnessGrowthReport({});
|
|
2865
|
-
if (!harnessReport.forgetting.fixture.passed || !harnessReport.
|
|
2852
|
+
if (!harnessReport.forgetting.fixture.passed || !harnessReport.tmux.views.includes('Harness Experiments View') || !harnessReport.reliability.tool_error_taxonomy.includes('Unknown')) throw new Error('selftest failed: harness growth fixture incomplete');
|
|
2866
2853
|
const proofField = await proofFieldFixture();
|
|
2867
2854
|
if (!proofField.validation.ok || !validateProofFieldReport(proofField.report).ok) throw new Error('selftest failed: proof field report invalid');
|
|
2868
2855
|
if (!proofField.checks.route_cone_selected || !proofField.checks.cli_cone_selected || !proofField.checks.catastrophic_guard_present || !proofField.checks.negative_release_work_recorded || !proofField.checks.outcome_rubric_present || !proofField.checks.adversarial_lenses_present || !proofField.checks.simplicity_score_usable || !proofField.checks.execution_fast_lane_selected) throw new Error('selftest failed: proof field fixture checks incomplete');
|