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/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 { buildWarpLaunchConfigYaml, buildWarpLaunchPlan, buildWarpOpenArgs, isWarpShellSession, runWarpLaunchConfigSyntaxCheck, warpOpenLaunchDecision, warpReadiness, warpStatusKind, defaultWarpWorkspaceName, formatWarpBanner, launchWarpTeamView, launchWarpUi, platformWarpInstallHint, runWarpStatus, sanitizeWarpWorkspaceName, teamLaneStyle, writeWarpLaunchConfig } from '../core/warp-ui.mjs';
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 === 'warp') return !sub || String(sub).startsWith('--') ? warpCommand('check', tail) : warpCommand(sub, rest);
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 [warp|codex|context7|all] [--yes] [--json]
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 warp open [--workspace name]
159
- sks warp status [--once]
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-warp [mission-id|latest]
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 warpCommand(sub = 'start', args = []) {
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 runWarpStatus(action === 'banner' ? ['--once', ...args] : args);
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 buildWarpLaunchPlan({ root, session: readOption(args, '--session', null) });
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(formatWarpBanner(plan.app));
892
+ console.log(formatTmuxBanner(plan.app));
889
893
  console.log('');
890
- console.log(`warp: ${plan.warp.ok ? 'ok' : 'missing'} ${plan.warp.version || ''}`.trim());
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 launchWarpUi(args);
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 warp open|start|check|status|banner [--workspace name]');
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-warp'].includes(arg));
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 warp launch only; full access uses Codex auto_review approvals when approval prompts are raised.');
936
- const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${defaultWarpWorkspaceName(process.cwd())}`));
937
- return launchWarpUi([...cleanArgs, '--workspace', workspace], {
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
- autoInstallWarp: !flag(args, '--no-auto-install-warp'),
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-warp')) {
976
- const warp = await warpReadiness().catch(() => ({ ok: false }));
977
- if (!warp.ok) actions.push(await installWarpDependency(args));
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.warp.ok), actions, 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 [warp|codex|context7|all] [--yes] [--json]');
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 warp = opts.warp || await warpReadiness().catch((err) => ({ ok: false, version: null, error: err.message }));
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' && !warp.ok;
1016
+ const homebrewNeeded = process.platform === 'darwin' && !tmux.ok;
1013
1017
  return {
1014
1018
  root,
1015
- ready: Boolean(nodeOk && npmBin && globalBin && codex.bin && context7.ok && warp.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
- warp: { ok: Boolean(warp.ok), app: warp.app || null, cli: warp.cli || null, version: warp.version || null, launch_config_dir: warp.launch_config_dir || null, uri_scheme: warp.uri_scheme || null, install_hint: warp.ok ? null : platformWarpInstallHint(), error: warp.error || null },
1025
- homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew, required_for_warp_install: homebrewNeeded } : { ok: null, bin: null, required_for_warp_install: false },
1026
- next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7, warp, brew, nodeOk })
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, warp, brew, nodeOk }) {
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 (!warp.ok) out.push(process.platform === 'darwin' && !brew ? 'Install Warp from https://www.warp.dev/download, or install Homebrew then run: sks deps install warp' : 'Run: sks deps install warp');
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(`warp: ${warpStatusKind(status.warp)} ${status.warp.version || status.warp.error || ''}`.trimEnd());
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', 'warp'] : [target];
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('warp')) actions.push(await installWarpDependency(args));
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 installWarpDependency(args = []) {
1095
- const before = await warpReadiness().catch(() => ({ ok: false }));
1096
- if (before.ok) return { target: 'warp', status: 'present', version: before.version || null, app: before.app || null, cli: before.cli || null };
1097
- const command = process.platform === 'darwin' ? 'brew install --cask warp' : platformWarpInstallHint();
1098
- if (flag(args, '--dry-run')) return { target: 'warp', status: 'dry_run', command };
1099
- return { target: 'warp', status: 'manual_required', command, error: before.error || 'Warp app not found' };
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 || sanitizeWarpWorkspaceName(`${profile}-${defaultWarpWorkspaceName(process.cwd())}`);
1150
- return launchWarpUi([...cleanArgs, '--session', session], { codexArgs: ['--profile', profile] });
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 warp check', '',
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 warp check', ' sks dollar-commands', '', `Topics: ${USAGE_TOPICS}`],
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 warp.'],
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 [warp|codex|context7|all] [--yes]', '', 'warp on macOS uses Homebrew only after approval.'],
1223
- warp: ['warp', '', ' sks warp open', ' sks warp check', ' sks warp status --once', ' sks deps install warp', '', 'Warp launch is explicit. Running bare `sks` prints help and never opens Warp by itself.'],
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-warp latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
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 Warp cockpit views.'],
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, warp: cliTools.warp });
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.warp.ok);
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
- warp: deps.warp,
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(`warp: ${deps.warp.ok ? 'ok' : 'missing'}${deps.warp.version ? ` ${deps.warp.version}` : ''}`);
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)}; warp ${warpStatusKind(cliTools.warp)} ${cliTools.warp.version || cliTools.warp.error || ''}`.trimEnd());
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.warp.ok) console.log(`\nwarp ${warpStatusKind(cliTools.warp)}. Install: ${cliTools.warp.install_hint}`);
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 warpStatus = await warpReadiness().catch((err) => ({ ok: false, version: null, error: err.message }));
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: { warp: { ok: Boolean(warpStatus.ok), app: warpStatus.app || null, cli: warpStatus.cli || null, version: warpStatus.version || null, launch_config_dir: warpStatus.launch_config_dir || null, uri_scheme: warpStatus.uri_scheme || null, install_hint: warpStatus.ok ? null : platformWarpInstallHint(), error: warpStatus.error || null } },
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.warp.ok && result.harness_guard.ok && result.versioning.ok && result.db_guard.ok && result.codex_app.ok && result.skills.ok && result.global_skills.ok;
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(`warp: ${warpStatusKind(result.runtime.warp)} ${result.runtime.warp.version || result.runtime.warp.error || ''}`.trimEnd());
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.warp.ok) console.log('Warp missing. Run: sks deps install warp');
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.warp.ok) blocked.push(['Warp is missing', 'sks deps install warp']);
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 || !('warp' in depsResult) || !('homebrew' in depsResult)) throw new Error('selftest failed: deps check json missing expected fields');
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 = { workspace: 'sks-mad-selftest', root: tmp, codexArgs: ['--profile', 'sks-mad-high'] };
1849
- const warpLaunchYaml = buildWarpLaunchConfigYaml({ ...workspacePlan, command: 'codex', title: 'sks-mad-selftest' }, [{ cwd: tmp, command: 'codex --profile sks-mad-high', focused: true }]);
1850
- const warpSyntax = runWarpLaunchConfigSyntaxCheck(warpLaunchYaml);
1851
- if (!warpSyntax.ok || !warpLaunchYaml.includes('name: "sks-mad-selftest"') || !warpLaunchYaml.includes('commands:')) throw new Error('selftest failed: MAD Warp launch configuration was not generated with name and command');
1852
- const warpOpenArgs = buildWarpOpenArgs(workspacePlan);
1853
- if (!warpOpenArgs.includes('open') || !warpOpenArgs.some((arg) => String(arg).includes('warp://launch/sks-mad-selftest.yaml'))) throw new Error('selftest failed: MAD Warp launch URI is not stable by workspace name');
1854
- if (!isWarpShellSession({ TERM_PROGRAM: 'WarpTerminal' })) throw new Error('selftest failed: Warp shell session env was not detected');
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 openWarpFlagParsed = parseTeamCreateArgs(['--open-warp', '작업']);
2668
- if (openWarpFlagParsed.prompt !== '작업') throw new Error('selftest failed: team --open-warp leaked into prompt');
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 warpTeam = await launchWarpTeamView({ root: tmp, missionId: teamId, plan: roleTeamPlan, json: true });
2675
- if (!warpTeam.agents?.length || !warpTeam.agents.some((entry) => entry.agent === 'analysis_scout_1') || !warpTeam.agents.every((entry) => String(entry.command || '').includes('team lane') && String(entry.command || '').includes('--agent'))) throw new Error('selftest failed: Team warp view did not expose agent live lanes');
2676
- if (!warpTeam.overview?.command?.includes('team watch') || !warpTeam.lanes?.some((entry) => entry.role === 'overview') || !warpTeam.lanes?.some((entry) => entry.agent === 'analysis_scout_1')) throw new Error('selftest failed: Team warp view did not expose orchestration overview plus agent lanes');
2677
- if (teamLaneStyle('analysis_scout_1').role !== 'scout' || teamLaneStyle('executor_1').role !== 'execution' || teamLaneStyle('reviewer_1').role !== 'review') throw new Error('selftest failed: Team warp role palette did not classify lane roles');
2678
- if (!String(warpTeam.cleanup_policy || '').includes('mark-complete') || !warpTeam.lanes.every((entry) => entry.style?.color && entry.title)) throw new Error('selftest failed: Team warp view did not expose color/title metadata and cleanup policy');
2679
- if (!warpTeam.launch_uri?.includes(encodeURIComponent(`sks-team-${teamId}.yaml`))) throw new Error('selftest failed: Team warp launch URI is not named for visibility');
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.warp.views.includes('Harness Experiments View') || !harnessReport.reliability.tool_error_taxonomy.includes('Unknown')) throw new Error('selftest failed: harness growth fixture incomplete');
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');