sneakoscope 0.7.59 → 0.7.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -241,7 +241,7 @@ By default, Team missions keep at least five QA/reviewer lanes active. Use expli
241
241
 
242
242
  Team mode prepares the mission, records live events, compiles runtime tasks and worker inboxes, writes schema-backed effort/work-order/dashboard artifacts, and opens a named tmux Team session with split live lanes by default when tmux is available. Use `--no-open-tmux` for artifact-only mission creation. The default terminal output stays compact: mission id, agent count, role count, tmux status, watch command, and artifact directory. `sks team dashboard` renders the cockpit panes for mission overview, agent lanes, task DAG, QA/dogfood, artifacts/evidence, and performance.
243
243
 
244
- The tmux Team launch is a live orchestration screen in one tmux window: the first pane follows `sks team watch <mission-id> --follow` as the mission overview, and neighboring split panes follow individual `sks team lane <mission-id> --agent <name> --follow` views. Pane headers show only mission, lane, phase, follow command, and cleanup command. SKS gives lanes role-specific colors, labels, and terminal titles, so scouts, planning/debate voices, executors, reviewers, and safety lanes are visually distinct while detailed evidence is mirrored into `team-transcript.jsonl`, `team-live.md`, and `team-dashboard.json`.
244
+ The tmux Team launch is a live orchestration screen in one tmux window: the first pane follows `sks team watch <mission-id> --follow` as the mission overview, and neighboring split panes follow individual `sks team lane <mission-id> --agent <name> --follow` views. Pane headers show only mission, lane, phase, follow command, and cleanup command. SKS sets the Team window to `window-size latest`, installs `client-attached` and `client-resized` hooks, reapplies `resize-window -A`, preserves `window-size latest`, and recalculates the tiled layout so split panes keep fitting when Warp or another terminal is resized. SKS gives lanes role-specific colors, labels, and terminal titles, so scouts, planning/debate voices, executors, reviewers, and safety lanes are visually distinct while detailed evidence is mirrored into `team-transcript.jsonl`, `team-live.md`, and `team-dashboard.json`.
245
245
 
246
246
  Team roster and runtime artifacts now include per-agent Fast reasoning metadata. Simple bounded Team lanes can use low reasoning, tool-heavy runtime/CLI/tmux work uses medium, and knowledge, current-docs, safety, DB, release, commit, or research-heavy lanes use high or xhigh as appropriate instead of opening every scout at high.
247
247
 
@@ -283,7 +283,7 @@ SKS no longer starts from a fixed checklist such as `GOAL_PRECISE` and `ACCEPTAN
283
283
 
284
284
  The design borrows two useful ideas from external planning systems without copying their route weight: Ouroboros-style ambiguity thresholds decide whether the prompt is clear enough to proceed, while Prometheus/Hyperplan-style adversarial lenses challenge framing, remove unnecessary surface, demand evidence, test integration risk, and consider a simpler alternative before Team work starts.
285
285
 
286
- `sks skill-dream` keeps generated skill complexity bounded without doing a heavy evaluation on every prompt. Route use writes compact counters to `.sneakoscope/skills/dream-state.json`; after the configured count/cooldown threshold, or when you run `sks skill-dream run`, SKS scans `.agents/skills` and writes `.sneakoscope/reports/skill-dream-latest.json` with keep, merge, prune, and improvement candidates. The report is intentionally advisory: deleting or merging skills requires explicit approval.
286
+ `sks skill-dream` keeps generated skill complexity bounded without doing a heavy evaluation on every prompt. Route use writes compact counters to `.sneakoscope/skills/dream-state.json`; after the configured 10-route-event threshold and cooldown, or when you run `sks skill-dream run`, SKS scans `.agents/skills` and writes `.sneakoscope/reports/skill-dream-latest.json` with keep, merge, prune, and improvement candidates. The report is intentionally advisory: deleting or merging skills requires explicit approval.
287
287
 
288
288
  `sks goal` and `$Goal` only prepare/control the native `/goal` persistence bridge. They do not replace Team, QA, DB, or other implementation routes; use the selected execution route for the actual work and verification. Context7 is only needed for Goal when external API/library documentation becomes relevant.
289
289
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.7.59",
4
+ "version": "0.7.62",
5
5
  "description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
@@ -89,5 +89,8 @@
89
89
  "mcp-safety",
90
90
  "db-guardian"
91
91
  ],
92
- "license": "MIT"
92
+ "license": "MIT",
93
+ "dependencies": {
94
+ "figlet": "^1.11.0"
95
+ }
93
96
  }
@@ -307,10 +307,10 @@ export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
307
307
  export function normalizeCodexFastModeUiConfig(text = '') {
308
308
  let next = removeLegacyTopLevelCodexModeLocks(text);
309
309
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
310
- next = removeTomlTableKey(next, 'features', 'codex_hooks');
310
+ next = removeTomlTableKey(next, 'features', 'hooks');
311
311
  next = upsertTopLevelTomlString(next, 'model', 'gpt-5.5');
312
312
  next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
313
- next = upsertTomlTableKey(next, 'features', 'hooks = true');
313
+ next = upsertTomlTableKey(next, 'features', 'codex_hooks = true');
314
314
  next = upsertTomlTableKey(next, 'features', 'fast_mode = true');
315
315
  next = upsertTomlTableKey(next, 'features', 'fast_mode_ui = true');
316
316
  next = upsertTomlTableKey(next, 'user.fast_mode', 'visible = true');
@@ -993,7 +993,7 @@ export async function selftestCodexLb(tmp) {
993
993
  if (codexLbNotConfigured.code !== 0 || String(codexLbNotConfigured.stdout || '').includes('codex-lb auth:')) throw new Error('selftest failed: postinstall should stay quiet when codex-lb is not configured');
994
994
  const codexLbStatusText = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'codex-lb', 'status'], { cwd: tmp, env: codexLbEnvForSelftest, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
995
995
  if (!String(codexLbStatusText.stdout || '').includes('Repair auth: sks codex-lb repair')) throw new Error('selftest failed: codex-lb status did not advertise repair command');
996
- if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('service_tier = "fast"') || !codexLbConfig.includes('hooks = true') || codexLbConfig.includes('codex_hooks = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !codexLbConfig.includes('default_profile = "sks-fast-high"') || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig)) throw new Error('selftest failed: codex-lb setup did not preserve Codex App Fast mode defaults, force GPT-5.5, or migrate the hooks feature flag');
996
+ if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('service_tier = "fast"') || !codexLbConfig.includes('codex_hooks = true') || hasLegacyHooksFeatureFlag(codexLbConfig) || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !codexLbConfig.includes('default_profile = "sks-fast-high"') || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig)) throw new Error('selftest failed: codex-lb setup did not preserve Codex App Fast mode defaults, force GPT-5.5, or migrate the hooks feature flag');
997
997
  const codexLbLaunch = codexLaunchCommand(tmp, 'codex', []);
998
998
  if (!codexLbLaunch.includes('sks-codex-lb.env')) throw new Error('selftest failed: tmux launch command does not source codex-lb env file');
999
999
  if (!codexLbLaunch.includes("'--model' 'gpt-5.5'")) throw new Error('selftest failed: tmux launch command without args did not force GPT-5.5');
@@ -1006,3 +1006,17 @@ export async function selftestCodexLb(tmp) {
1006
1006
  function hasTopLevelCodexModeLock(text = '') {
1007
1007
  return /(^|\n)\s*model\s*=\s*"codex-lb"\s*(\n|$)/.test(text) || /(^|\n)\s*model_provider\s*=\s*"openai"\s*(\n|$)/.test(text);
1008
1008
  }
1009
+
1010
+ function hasLegacyHooksFeatureFlag(text = '') {
1011
+ const lines = String(text || '').split('\n');
1012
+ const start = lines.findIndex((line) => line.trim() === '[features]');
1013
+ if (start === -1) return false;
1014
+ let end = lines.length;
1015
+ for (let i = start + 1; i < lines.length; i += 1) {
1016
+ if (/^\s*\[.+\]\s*$/.test(lines[i])) {
1017
+ end = i;
1018
+ break;
1019
+ }
1020
+ }
1021
+ return lines.slice(start + 1, end).some((line) => /^\s*hooks\s*=/.test(line));
1022
+ }
package/src/cli/main.mjs CHANGED
@@ -75,7 +75,7 @@ import { GOAL_WORKFLOW_ARTIFACT } from '../core/goal-workflow.mjs';
75
75
  import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
76
76
  import { codexAppRemoteControlCommand } from './codex-app-command.mjs';
77
77
  import { OPENCLAW_SKILL_NAME, installOpenClawSkill } from '../core/openclaw.mjs';
78
- import { buildTmuxLaunchPlan, buildTmuxOpenArgs, codexLaunchCommand, createTmuxSession, defaultCodexLaunchArgs, isTmuxShellSession, runTmuxLaunchPlanSyntaxCheck, shouldAutoAttachTmux, tmuxReadiness, tmuxStatusKind, defaultTmuxSessionName, formatTmuxBanner, launchMadTmuxUi, launchTmuxTeamView, launchTmuxUi, platformTmuxInstallHint, runTmuxStatus, sanitizeTmuxSessionName, teamLaneStyle } from '../core/tmux-ui.mjs';
78
+ import { buildTmuxLaunchPlan, buildTmuxOpenArgs, codexLaunchCommand, createTmuxSession, defaultCodexLaunchArgs, isTmuxShellSession, runTmuxLaunchPlanSyntaxCheck, shouldAutoAttachTmux, sksAsciiLogo, tmuxReadiness, tmuxStatusKind, defaultTmuxSessionName, formatTmuxBanner, launchMadTmuxUi, launchTmuxTeamView, launchTmuxUi, platformTmuxInstallHint, runTmuxStatus, sanitizeTmuxSessionName, teamLaneStyle } from '../core/tmux-ui.mjs';
79
79
  import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
80
80
  import { context7Command } from './context7-command.mjs';
81
81
  import { askPostinstallQuestion, checkContext7, checkRequiredSkills, codexLbStatus, configureCodexLb, ensureCodexCliTool, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, ensureTmuxCliTool, globalCodexSkillsRoot, maybePromptCodexLbSetupForLaunch, maybePromptCodexUpdateForLaunch, postinstall, postinstallBootstrapDecision, repairCodexLbAuth, selftestCodexLb, shouldAutoApproveInstall } from './install-helpers.mjs';
@@ -156,8 +156,7 @@ function codexLbImmediateLaunchOpts(args = [], lb = {}, opts = {}) {
156
156
  function help(args = []) {
157
157
  const topic = args[0];
158
158
  if (topic) return usage([topic]);
159
- console.log(`ㅅㅋㅅ
160
- Sneakoscope Codex
159
+ console.log(`${sksAsciiLogo()}
161
160
 
162
161
  Usage:
163
162
  sks
@@ -272,7 +271,7 @@ async function wizard(args = []) {
272
271
  if (!shouldShowWizard() && !flag(args, '--force')) return help();
273
272
  const rl = readline.createInterface({ input, output });
274
273
  try {
275
- console.log('ㅅㅋㅅ Setup UI\n');
274
+ console.log(`${sksAsciiLogo()}\nSetup UI\n`);
276
275
  const currentPackage = await effectivePackageVersion();
277
276
  console.log(`Current package: ${currentPackage}`);
278
277
  const latest = await npmPackageVersion('sneakoscope');
@@ -340,7 +339,7 @@ async function updateCheck(args = []) {
340
339
  error: latest.error || null
341
340
  };
342
341
  if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
343
- console.log('ㅅㅋㅅ Update Check');
342
+ console.log(`${sksAsciiLogo()}\nUpdate Check`);
344
343
  console.log(`Current: ${result.current}`);
345
344
  console.log(`Latest: ${result.latest || 'unknown'}`);
346
345
  console.log(`Update: ${result.update_available ? 'available' : 'not needed'}`);
@@ -352,7 +351,7 @@ const DOLLAR_DEFAULT_PIPELINE_TEXT = 'Default pipeline: direct answers -> $Answe
352
351
 
353
352
  function commands(args = []) {
354
353
  if (flag(args, '--json')) return console.log(JSON.stringify({ aliases: ['sks', 'sneakoscope'], dollar_commands: DOLLAR_COMMANDS, app_skill_aliases: DOLLAR_COMMAND_ALIASES, commands: COMMAND_CATALOG }, null, 2));
355
- console.log('ㅅㅋㅅ Commands\n');
354
+ console.log(`${sksAsciiLogo()}\nCommands\n`);
356
355
  console.log('Aliases: sks, sneakoscope\n');
357
356
  const width = Math.max(...COMMAND_CATALOG.map((c) => c.usage.length));
358
357
  for (const c of COMMAND_CATALOG) console.log(`${c.usage.padEnd(width)} ${c.description}`);
@@ -375,7 +374,7 @@ async function rootCommand(args = []) {
375
374
  using_global_root: !project
376
375
  };
377
376
  if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
378
- console.log('SKS Root\n');
377
+ console.log(`${sksAsciiLogo()}\nRoot\n`);
379
378
  console.log(`Mode: ${result.mode}`);
380
379
  console.log(`Active root: ${active}`);
381
380
  console.log(`Project: ${project || 'none'}`);
@@ -385,7 +384,7 @@ async function rootCommand(args = []) {
385
384
 
386
385
  function dollarCommands(args = []) {
387
386
  if (flag(args, '--json')) return console.log(JSON.stringify({ dollar_commands: DOLLAR_COMMANDS, app_skill_aliases: DOLLAR_COMMAND_ALIASES }, null, 2));
388
- console.log('ㅅㅋㅅ $ Commands\n');
387
+ console.log(`${sksAsciiLogo()}\n$ Commands\n`);
389
388
  console.log('Use these inside Codex App or another agent prompt. Shells treat $ as variable syntax, so these are prompt commands, not terminal commands.\n');
390
389
  console.log(formatDollarCommandsDetailed());
391
390
  console.log(`\nCanonical Codex App picker skills: ${DOLLAR_COMMAND_ALIASES.map((x) => x.app_skill).join(', ')}`);
@@ -1406,7 +1405,8 @@ async function codexAppHelp(args = []) {
1406
1405
  const status = await codexAppIntegrationStatus();
1407
1406
  const skills = await codexAppSkillReadiness();
1408
1407
  console.log([
1409
- 'ㅅㅋㅅ Codex App', '',
1408
+ sksAsciiLogo(), '',
1409
+ 'Codex App', '',
1410
1410
  formatCodexAppStatus(status), '',
1411
1411
  `Skills: project=${skills.project.ok ? 'ok' : `missing ${skills.project.missing.length}`} global=${skills.global.ok ? 'ok' : `missing ${skills.global.missing.length}`}`, '',
1412
1412
  'Setup:', ' sks bootstrap', ' sks deps check', ' sks codex-app check', ' sks codex-app remote-control --status', ' sks tmux check', '',
@@ -1417,7 +1417,9 @@ async function codexAppHelp(args = []) {
1417
1417
  }
1418
1418
 
1419
1419
  function aliases() {
1420
- console.log(`ㅅㅋㅅ Aliases
1420
+ console.log(`${sksAsciiLogo()}
1421
+
1422
+ Aliases
1421
1423
 
1422
1424
  Binary aliases:
1423
1425
  sks
@@ -1443,7 +1445,7 @@ Examples:
1443
1445
  function usage(args = []) {
1444
1446
  const topic = String(args[0] || 'overview').toLowerCase();
1445
1447
  const blocks = {
1446
- 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}`],
1448
+ overview: [sksAsciiLogo(), '', '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}`],
1447
1449
  install: ['Install', '', '1. Global install:', ' npm i -g sneakoscope', '', '2. Bootstrap and check dependencies:', ' sks bootstrap', ' sks deps check', '', '3. Confirm Codex App commands:', ' sks codex-app check', ' sks dollar-commands', '', '4. Optional codex-lb key setup for CLI sks runs:', ' sks codex-lb setup --host <domain> --api-key <key>', ' sks codex-lb repair', ' sks', '', 'Fallback:', ' npx -y -p sneakoscope sks root', '', 'Project:', ' npm i -D sneakoscope', ' npx sks setup --install-scope project'],
1448
1450
  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.'],
1449
1451
  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.'],
@@ -1589,7 +1591,7 @@ async function setup(args) {
1589
1591
  next: ['sks context7 check', 'sks selftest --mock', 'sks doctor', 'sks commands']
1590
1592
  };
1591
1593
  if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
1592
- console.log('ㅅㅋㅅ Setup\n');
1594
+ console.log(`${sksAsciiLogo()}\nSetup\n`);
1593
1595
  console.log(`Project: ${root}`);
1594
1596
  console.log(`Install: ${install.ok ? 'ok' : 'missing'} ${install.scope} (${install.command_prefix})`);
1595
1597
  console.log(`CLI tools: Codex ${formatCodexCliToolStatus(cliTools.codex)}; tmux ${tmuxStatusKind(cliTools.tmux)} ${cliTools.tmux.version || cliTools.tmux.error || ''}`.trimEnd());
@@ -1713,7 +1715,7 @@ async function doctor(args) {
1713
1715
  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;
1714
1716
  if (result.harness_conflicts.hard_block) process.exitCode = 1;
1715
1717
  if (flag(args, '--json')) return console.log(JSON.stringify(result, null, 2));
1716
- console.log('ㅅㅋㅅ Doctor\n');
1718
+ console.log(`${sksAsciiLogo()}\nDoctor\n`);
1717
1719
  console.log(`Node: ${nodeOk ? 'ok' : 'fail'} ${process.version}`);
1718
1720
  console.log(`Project: ${root}`);
1719
1721
  console.log(`Codex: ${codex.bin ? 'ok' : 'missing'} ${codex.version || ''}`);
@@ -1779,7 +1781,7 @@ async function init(args) {
1779
1781
  const localOnly = flag(args, '--local-only');
1780
1782
  const globalCommand = await globalSksCommand();
1781
1783
  const res = await initProject(root, { force: flag(args, '--force'), installScope, globalCommand, localOnly });
1782
- console.log(`Initialized ㅅㅋㅅ in ${root}`);
1784
+ console.log(`Initialized SKS in ${root}`);
1783
1785
  console.log(`Install scope: ${installScope} (${sksCommandPrefix(installScope, { globalCommand })})`);
1784
1786
  if (localOnly) console.log('Git mode: local-only (.git/info/exclude)');
1785
1787
  else console.log('Git mode: shared .gitignore');
@@ -1920,6 +1922,20 @@ function hasTopLevelCodexModeLock(text = '') {
1920
1922
  return (Boolean(model) && model !== 'gpt-5.5') || /^model_reasoning_effort\s*=/m.test(top);
1921
1923
  }
1922
1924
 
1925
+ function hasLegacyHooksFeatureFlag(text = '') {
1926
+ const lines = String(text || '').split('\n');
1927
+ const start = lines.findIndex((line) => line.trim() === '[features]');
1928
+ if (start === -1) return false;
1929
+ let end = lines.length;
1930
+ for (let i = start + 1; i < lines.length; i += 1) {
1931
+ if (/^\s*\[.+\]\s*$/.test(lines[i])) {
1932
+ end = i;
1933
+ break;
1934
+ }
1935
+ }
1936
+ return lines.slice(start + 1, end).some((line) => /^\s*hooks\s*=/.test(line));
1937
+ }
1938
+
1923
1939
  async function resolveMissionId(root, arg) { return (!arg || arg === 'latest') ? findLatestMission(root) : arg; }
1924
1940
  function readMaxCycles(args, fallback) {
1925
1941
  const i = args.indexOf('--max-cycles');
@@ -2460,6 +2476,13 @@ async function selftest() {
2460
2476
  const camelHookGuardResult = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'pre-tool'], { cwd: tmp, input: camelHookGuardPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
2461
2477
  const camelHookGuardJson = JSON.parse(camelHookGuardResult.stdout);
2462
2478
  if (camelHookGuardJson.decision !== 'block') throw new Error('selftest failed: hook did not block camelCase Codex tool payload');
2479
+ await setCurrent(tmp, { mode: 'QALOOP', phase: 'QALOOP_RUNNING_NO_QUESTIONS', route: 'QALoop', implementation_allowed: true });
2480
+ const codexGitPermissionResult = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'permission-request'], { cwd: tmp, input: JSON.stringify({ cwd: tmp, command: 'git push origin dev', action: 'Codex App Git Actions Push' }), env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
2481
+ const codexGitPermissionJson = JSON.parse(codexGitPermissionResult.stdout);
2482
+ if (codexGitPermissionJson.hookSpecificOutput?.decision?.behavior === 'deny') throw new Error('selftest failed: Codex App git push permission was denied during no-question mode');
2483
+ const codexGitForcePermissionResult = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'permission-request'], { cwd: tmp, input: JSON.stringify({ cwd: tmp, command: 'git push --force origin dev', action: 'Codex App Git Actions Push' }), env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
2484
+ const codexGitForcePermissionJson = JSON.parse(codexGitForcePermissionResult.stdout);
2485
+ if (codexGitForcePermissionJson.hookSpecificOutput?.decision?.behavior !== 'deny') throw new Error('selftest failed: force-push permission should stay denied during no-question mode');
2463
2486
  if (new Set(DOLLAR_COMMANDS.map((c) => c.command)).size !== DOLLAR_COMMANDS.length) throw new Error('selftest failed: duplicate dollar commands');
2464
2487
  if (!DOLLAR_COMMAND_ALIASES.some((alias) => alias.canonical === '$QA-LOOP' && alias.app_skill === '$qa-loop')) throw new Error('selftest failed: $QA-LOOP picker skill missing');
2465
2488
  if (!DOLLAR_COMMAND_ALIASES.some((alias) => alias.canonical === '$Team' && alias.app_skill === '$from-chat-img')) throw new Error('selftest failed: $From-Chat-IMG picker skill missing');
@@ -2916,7 +2939,7 @@ async function selftest() {
2916
2939
  if (!wikiJson.systemMessage?.includes('wiki refresh')) throw new Error('selftest failed: Wiki route missing system message');
2917
2940
  const codexConfigText = await safeReadText(path.join(tmp, '.codex', 'config.toml'));
2918
2941
  if (!codexConfigText.includes('multi_agent = true')) throw new Error('selftest failed: multi_agent not enabled');
2919
- if (!codexConfigText.includes('hooks = true') || codexConfigText.includes('codex_hooks = true')) throw new Error('selftest failed: Codex hooks feature flag not migrated to hooks');
2942
+ if (!codexConfigText.includes('codex_hooks = true') || hasLegacyHooksFeatureFlag(codexConfigText)) throw new Error('selftest failed: Codex hooks feature flag not aligned with current codex_hooks setting');
2920
2943
  if (!hasContext7ConfigText(codexConfigText)) throw new Error('selftest failed: Context7 MCP not configured');
2921
2944
  if (!codexConfigText.includes('[profiles.sks-task-low]') || !codexConfigText.includes('[profiles.sks-task-medium]') || !codexConfigText.includes('[profiles.sks-logic-high]') || !codexConfigText.includes('[profiles.sks-fast-high]') || !codexConfigText.includes('[profiles.sks-research-xhigh]') || !codexConfigText.includes('[profiles.sks-mad-high]')) throw new Error('selftest failed: GPT-5.5 reasoning profiles not configured');
2922
2945
  if (!/\[profiles\.sks-mad-high\][\s\S]*?approval_policy = "never"[\s\S]*?sandbox_mode = "danger-full-access"/.test(codexConfigText)) throw new Error('selftest failed: generated sks-mad-high profile is not full access');
@@ -2924,12 +2947,12 @@ async function selftest() {
2924
2947
  if (!codexConfigText.includes('[agents.team_consensus]')) throw new Error('selftest failed: team_consensus agent not configured');
2925
2948
  const preservedConfigTmp = tmpdir();
2926
2949
  await ensureDir(path.join(preservedConfigTmp, '.codex'));
2927
- await writeTextAtomic(path.join(preservedConfigTmp, '.codex', 'config.toml'), 'model = "gpt-5.5"\nmodel_reasoning_effort = "high"\nservice_tier = "fast"\n\n[notice]\nfast_default_opt_out = true\nkeep = true\n\n[features]\ncodex_hooks = true\nfast_mode_ui = true\n\n[user.fast_mode]\nvisible = true\n');
2950
+ await writeTextAtomic(path.join(preservedConfigTmp, '.codex', 'config.toml'), 'model = "gpt-5.5"\nmodel_reasoning_effort = "high"\nservice_tier = "fast"\n\n[notice]\nfast_default_opt_out = true\nkeep = true\n\n[features]\nhooks = true\nfast_mode_ui = true\n\n[user.fast_mode]\nvisible = true\n');
2928
2951
  await initProject(preservedConfigTmp, {});
2929
2952
  const preservedConfig = await safeReadText(path.join(preservedConfigTmp, '.codex', 'config.toml'));
2930
2953
  if (!/^model = "gpt-5\.5"/m.test(preservedConfig) || !preservedConfig.includes('service_tier = "fast"') || !preservedConfig.includes('fast_mode = true') || !preservedConfig.includes('fast_mode_ui = true') || !preservedConfig.includes('[user.fast_mode]') || !preservedConfig.includes('visible = true') || !preservedConfig.includes('enabled = true') || !preservedConfig.includes('default_profile = "sks-fast-high"') || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(preservedConfig)) throw new Error('selftest failed: Codex config merge dropped or failed to enable Fast mode defaults and GPT-5.5');
2931
2954
  if (preservedConfig.includes('fast_default_opt_out = true') || !preservedConfig.includes('keep = true')) throw new Error('selftest failed: Codex config merge did not remove stale Fast opt-out notice while preserving other notice keys');
2932
- if (!preservedConfig.includes('hooks = true') || preservedConfig.includes('codex_hooks = true') || !preservedConfig.includes('[profiles.sks-fast-high]')) throw new Error('selftest failed: Codex config merge did not add SKS managed settings or remove the legacy hooks flag');
2955
+ if (!preservedConfig.includes('codex_hooks = true') || hasLegacyHooksFeatureFlag(preservedConfig) || !preservedConfig.includes('[profiles.sks-fast-high]')) throw new Error('selftest failed: Codex config merge did not add current SKS hook settings or remove the legacy hooks flag');
2933
2956
  if (hasTopLevelCodexModeLock(preservedConfig)) throw new Error('selftest failed: Codex config merge left top-level legacy model/reasoning locks that hide Fast mode UI');
2934
2957
  const autoReviewHome = path.join(tmp, 'auto-review-home');
2935
2958
  const autoReviewEnv = { HOME: autoReviewHome };
@@ -3146,7 +3169,7 @@ async function selftest() {
3146
3169
  await writeJsonAtomic(path.join(teamDir, 'team-plan.json'), teamPlan);
3147
3170
  if (teamPlan.agent_session_count !== 5) throw new Error('selftest failed: team default sessions not 5');
3148
3171
  if (teamPlan.role_counts.executor !== 3 || teamPlan.role_counts.user !== 1 || teamPlan.role_counts.reviewer !== 5) throw new Error('selftest failed: team default role counts invalid');
3149
- if (teamPlan.codex_config_required?.features?.hooks !== true || teamPlan.codex_config_required?.features?.codex_hooks === true) throw new Error('selftest failed: team plan Codex config still uses legacy hooks feature flag');
3172
+ if (teamPlan.codex_config_required?.features?.codex_hooks !== true || teamPlan.codex_config_required?.features?.hooks === true) throw new Error('selftest failed: team plan Codex config still uses legacy hooks feature flag');
3150
3173
  if (!teamPlan.review_gate?.passed || teamPlan.review_gate.required_reviewer_lanes !== 5) throw new Error('selftest failed: team review policy gate did not pass default plan');
3151
3174
  if (teamPlan.codex_config_required?.service_tier !== 'fast' || teamPlan.reasoning?.service_tier !== 'fast') throw new Error('selftest failed: team plan did not require Fast service tier');
3152
3175
  if (!teamPlan.goal_continuation?.enabled || teamPlan.goal_continuation?.mode !== 'ambient_codex_native_goal_overlay') throw new Error('selftest failed: Team plan did not include ambient Goal continuation');
@@ -3218,7 +3241,7 @@ async function selftest() {
3218
3241
  await ensureDir(fakeTmuxDir);
3219
3242
  const fakeTmuxLog = path.join(fakeTmuxDir, 'tmux.log');
3220
3243
  const fakeTmuxBin = path.join(fakeTmuxDir, 'tmux');
3221
- await writeTextAtomic(fakeTmuxBin, `#!/usr/bin/env node\nconst fs = require('node:fs');\nconst log = process.env.SKS_FAKE_TMUX_LOG;\nif (log) fs.appendFileSync(log, process.argv.slice(2).join(' ') + '\\n');\nconst cmd = process.argv[2];\nif (cmd === 'has-session') process.exit(0);\nif (cmd === 'kill-session') process.exit(0);\nif (cmd === 'new-session') { console.log('%1'); process.exit(0); }\nif (cmd === 'split-window') { console.log('%2'); process.exit(0); }\nprocess.exit(0);\n`);
3244
+ await writeTextAtomic(fakeTmuxBin, `#!/usr/bin/env node\nconst fs = require('node:fs');\nconst log = process.env.SKS_FAKE_TMUX_LOG;\nif (log) fs.appendFileSync(log, process.argv.slice(2).join(' ') + '\\n');\nconst cmd = process.argv[2];\nif (cmd === 'has-session') process.exit(0);\nif (cmd === 'kill-session') process.exit(0);\nif (cmd === 'new-session') { console.log('%1'); process.exit(0); }\nif (cmd === 'split-window') { console.log('%2'); process.exit(0); }\nif (cmd === 'list-windows') { console.log('@1'); process.exit(0); }\nprocess.exit(0);\n`);
3222
3245
  await fsp.chmod(fakeTmuxBin, 0o755);
3223
3246
  const previousFakeTmuxLog = process.env.SKS_FAKE_TMUX_LOG;
3224
3247
  process.env.SKS_FAKE_TMUX_LOG = fakeTmuxLog;
@@ -3228,6 +3251,8 @@ async function selftest() {
3228
3251
  ], { recreate: true });
3229
3252
  const fakeTmuxLogText = await safeReadText(fakeTmuxLog);
3230
3253
  if (!recreatedTmux.ok || !fakeTmuxLogText.includes('kill-session -t sks-existing-selftest') || !fakeTmuxLogText.includes('new-session') || !fakeTmuxLogText.includes('split-window')) throw new Error('selftest failed: tmux recreate did not replace stale existing session with split panes');
3254
+ if (!recreatedTmux.dynamic_resize?.enabled || !fakeTmuxLogText.includes('list-windows -t sks-existing-selftest -F #{window_id}') || !fakeTmuxLogText.includes('set-window-option -t @1 window-size latest') || !fakeTmuxLogText.includes('set-hook -t sks-existing-selftest client-resized') || !fakeTmuxLogText.includes('resize-window -t @1 -A')) throw new Error('selftest failed: tmux dynamic resize hooks were not installed for split panes');
3255
+ if (recreatedTmux.layout !== 'tiled' || Number(recreatedTmux.initial_size?.width || 0) < 120 || Number(recreatedTmux.initial_size?.height || 0) < 36) throw new Error('selftest failed: tmux dynamic resize metadata missing normalized initial size/layout');
3231
3256
  await writeTextAtomic(fakeTmuxLog, '');
3232
3257
  const madCockpit = await launchMadTmuxUi(['--workspace', 'sks-mad-selftest-ui', '--no-attach'], { root: tmp, tmux: { ok: true, bin: fakeTmuxBin, version: '3.4' }, codex: { bin: process.execPath }, app: { ok: true, guidance: [] }, missionId: 'M-MAD-SELFTEST' });
3233
3258
  const madTmuxLogText = await safeReadText(fakeTmuxLog);
@@ -3655,6 +3680,6 @@ async function selftest() {
3655
3680
  const gc = await enforceRetention(tmp, { dryRun: true });
3656
3681
  if (!gc.report.exists) throw new Error('selftest failed: storage report');
3657
3682
  if (!gc.actions.some((action) => action.action === 'remove_from_chat_img_temp_triwiki')) throw new Error('selftest failed: From-Chat-IMG temporary TriWiki retention action missing');
3658
- console.log('ㅅㅋㅅ selftest passed.');
3683
+ console.log(`${sksAsciiLogo()}\nselftest passed.`);
3659
3684
  console.log(`temp: ${tmp}`);
3660
3685
  }
@@ -1778,7 +1778,7 @@ export function buildTeamPlan(id, prompt, opts = {}) {
1778
1778
  reasoning: teamReasoningPolicy(prompt, roster),
1779
1779
  codex_config_required: {
1780
1780
  service_tier: 'fast',
1781
- features: { multi_agent: true, hooks: true },
1781
+ features: { multi_agent: true, codex_hooks: true },
1782
1782
  agents: { max_threads: 6, max_depth: 1 },
1783
1783
  custom_agents_dir: '.codex/agents'
1784
1784
  },
package/src/core/fsx.mjs CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
 
8
- export const PACKAGE_VERSION = '0.7.59';
8
+ export const PACKAGE_VERSION = '0.7.62';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
 
@@ -281,6 +281,7 @@ async function hookPermission(root, state, payload, noQuestion) {
281
281
  if (clarificationGateLocked(state) && !clarificationAnswerToolAllowed(payload)) {
282
282
  return { decision: 'deny', permissionDecision: 'deny', reason: clarificationPauseBlockReason(state) };
283
283
  }
284
+ if (noQuestion && looksLikeUserGitAction(payload)) return { continue: true };
284
285
  if (!noQuestion) return { continue: true };
285
286
  return {
286
287
  decision: 'deny',
@@ -289,6 +290,25 @@ async function hookPermission(root, state, payload, noQuestion) {
289
290
  };
290
291
  }
291
292
 
293
+ function looksLikeUserGitAction(payload = {}) {
294
+ const command = extractCommand(payload);
295
+ const haystack = [
296
+ command,
297
+ payload.action,
298
+ payload.intent,
299
+ payload.operation,
300
+ payload.permission,
301
+ payload.description,
302
+ payload.message,
303
+ payload.tool_name,
304
+ payload.toolName
305
+ ].filter(Boolean).join(' ');
306
+ if (/\b(?:reset\s+--hard|clean\s+-[^\s]*f|checkout\s+--|restore\s+|rm\s+|push\s+--force|push\s+-[^\s]*f)\b/i.test(command)) return false;
307
+ if (/\bcodex\b[\s_-]*(?:app\s*)?(?:git\s*)?(?:action|commit|push|pr)\b/i.test(haystack)) return true;
308
+ if (!/^\s*git\s+/i.test(command)) return false;
309
+ return /\bgit\s+(?:status|diff|add|commit|push|branch|remote|rev-parse|log)\b/i.test(command);
310
+ }
311
+
292
312
  function clarificationGateLocked(state = {}) {
293
313
  if (isBlockingClarificationAwaiting(state)) return true;
294
314
  return Boolean(
package/src/core/init.mjs CHANGED
@@ -7,7 +7,7 @@ import { isHarnessSourceProject, writeHarnessGuardPolicy } from './harness-guard
7
7
  import { repairSksGeneratedArtifacts } from './harness-conflicts.mjs';
8
8
  import { installVersionGitHook } from './version-manager.mjs';
9
9
  import { MIN_TEAM_REVIEWER_LANES, MIN_TEAM_REVIEW_POLICY_TEXT } from './team-review-policy.mjs';
10
- import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_REQUIRED_POLICY, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_COMMAND_ALIASES, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_DESIGN_REFERENCES, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, RESERVED_CODEX_PLUGIN_SKILL_NAMES, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, imageUxReviewPipelinePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
10
+ import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_REQUIRED_POLICY, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_COMMAND_ALIASES, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_DESIGN_REFERENCES, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, RESERVED_CODEX_PLUGIN_SKILL_NAMES, SOLUTION_SCOUT_SKILL_NAME, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, imageUxReviewPipelinePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, solutionScoutPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
11
11
  import { SKILL_DREAM_POLICY, skillDreamPolicyText } from './skill-forge.mjs';
12
12
 
13
13
  const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
@@ -94,7 +94,7 @@ function isSksManagedHook(hook) {
94
94
  return hook.type === 'command' && /\bhook\s+(?:user-prompt-submit|pre-tool|post-tool|permission-request|stop)\b/.test(command) && /\b(?:sks|sneakoscope|sks\.mjs)\b/.test(command);
95
95
  }
96
96
 
97
- const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Before substantive work, SKS checks npm for a newer package. If newer, ask update-now vs skip-for-this-conversation.\n- Versioning is managed by the SKS pre-commit hook; check `sks versioning status`. Bypass only with `SKS_DISABLE_VERSIONING=1`.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: analysis scouts, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, minimum five-lane Team review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for UI/browser/visual tasks: skip Team debate and upfront TriWiki loops, use Codex Computer Use directly, then refresh/validate TriWiki and run Honest Mode at final closeout.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Team completion requires at least five independent reviewer/QA validation lanes before integration or final, even when a prompt requests fewer reviewers. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is Direct Fix: only tiny copy/config/docs/labels/spacing/translation/simple mechanical edits, bypassing the main pipeline, Team, TriWiki/TriFix/reflection recording, and persistent route state; it still uses a one-line DFix-specific Honest check before final. Broad implementation stays on `$Team`, while UI design specifics follow the relevant design/UI route rules. `$PPT` is the restrained, information-first HTML/PDF presentation route and must seal delivery context, audience profile, STP, decision context, and 3+ pain-point/solution/aha mappings before design/render work. It must avoid over-designed visuals, carry detail through hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents, preserve editable source HTML under `source-html/`, record `ppt-parallel-report.json`, and clean PPT-only temporary build files before completion. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md` as the only design decision SSOT. If missing, create it through `design-system-builder` from `docs/Design-Sys-Prompt.md`; getdesign.md, getdesign-reference, and curated DESIGN.md examples from https://github.com/VoltAgent/awesome-design-md are source inputs to fuse into that SSOT or route-local style tokens, not parallel design authorities. Image/logo/raster assets use `imagegen`, which must prefer official Codex App built-in image generation via `$imagegen` / `gpt-image-2` before API generation and must not be replaced by placeholder SVG/HTML/CSS, prose-only reviews, or fabricated files when generated raster evidence is required.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n- Skill dreaming stays lightweight: route use records JSON counters in `.sneakoscope/skills/dream-state.json`, and full skill inventory/recommendation runs only after the configured count/cooldown threshold. Reports are recommendation-only; deleting or merging skills needs explicit user approval.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$ppt`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
97
+ const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Before substantive work, SKS checks npm for a newer package. If newer, ask update-now vs skip-for-this-conversation.\n- Versioning is managed by the SKS pre-commit hook; check `sks versioning status`. Bypass only with `SKS_DISABLE_VERSIONING=1`.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: analysis scouts, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, minimum five-lane Team review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for UI/browser/visual tasks: skip Team debate and upfront TriWiki loops, use Codex Computer Use directly, then refresh/validate TriWiki and run Honest Mode at final closeout.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Team completion requires at least five independent reviewer/QA validation lanes before integration or final, even when a prompt requests fewer reviewers. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is Direct Fix: only tiny copy/config/docs/labels/spacing/translation/simple mechanical edits, bypassing the main pipeline, Team, TriWiki/TriFix/reflection recording, and persistent route state; it still uses a one-line DFix-specific Honest check before final. Broad implementation stays on `$Team`, while UI design specifics follow the relevant design/UI route rules. `$PPT` is the restrained, information-first HTML/PDF presentation route and must seal delivery context, audience profile, STP, decision context, and 3+ pain-point/solution/aha mappings before design/render work. It must avoid over-designed visuals, carry detail through hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents, preserve editable source HTML under `source-html/`, record `ppt-parallel-report.json`, and clean PPT-only temporary build files before completion. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md` as the only design decision SSOT. If missing, create it through `design-system-builder` from `docs/Design-Sys-Prompt.md`; getdesign.md, getdesign-reference, and curated DESIGN.md examples from https://github.com/VoltAgent/awesome-design-md are source inputs to fuse into that SSOT or route-local style tokens, not parallel design authorities. Image/logo/raster assets use `imagegen`, which must prefer official Codex App built-in image generation via `$imagegen` / `gpt-image-2` before API generation and must not be replaced by placeholder SVG/HTML/CSS, prose-only reviews, or fabricated files when generated raster evidence is required.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n- Skill dreaming stays lightweight: route use records JSON counters in `.sneakoscope/skills/dream-state.json`, and full skill inventory/recommendation runs only after the configured 10-route-event threshold and cooldown. Reports are recommendation-only; deleting or merging skills needs explicit user approval.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$ppt`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
98
98
 
99
99
  function agentsBlockText() {
100
100
  return AGENTS_BLOCK
@@ -440,10 +440,10 @@ function installPolicy(scope, commandPrefix) {
440
440
  function mergeManagedCodexConfigToml(existingContent = '') {
441
441
  let next = removeLegacyTopLevelCodexModeLocks(String(existingContent || '').trimEnd());
442
442
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
443
- next = removeTomlTableKey(next, 'features', 'codex_hooks');
443
+ next = removeTomlTableKey(next, 'features', 'hooks');
444
444
  next = upsertTopLevelTomlString(next, 'model', 'gpt-5.5');
445
445
  next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
446
- next = upsertTomlTableKey(next, 'features', 'hooks = true');
446
+ next = upsertTomlTableKey(next, 'features', 'codex_hooks = true');
447
447
  next = upsertTomlTableKey(next, 'features', 'multi_agent = true');
448
448
  next = upsertTomlTableKey(next, 'features', 'fast_mode = true');
449
449
  next = upsertTomlTableKey(next, 'features', 'fast_mode_ui = true');
@@ -746,7 +746,7 @@ export async function installSkills(root) {
746
746
  'answer': `---\nname: answer\ndescription: Answer-only research route for ordinary questions that should not start implementation.\n---\n\nUse for explanations, comparisons, status, facts, source-backed research, or docs guidance. Use repo/TriWiki first for project-local facts; hydrate low-trust claims from source. Browse or use Context7 for current external package/API/framework/MCP docs. End with a concise answer summary plus Honest Mode; do not create missions, subagents, or file edits.\n`,
747
747
  'sks': `---\nname: sks\ndescription: General Sneakoscope Codex command route for $SKS or $sks usage, setup, status, and workflow help.\n---\n\nUse local SKS commands: bootstrap, deps, commands, quickstart, codex-app, context7, guard, conflicts, reasoning, wiki, pipeline status, pipeline plan, skill-dream. Promote code-changing work to Team unless Answer/DFix/Help/Wiki/safety route fits. Surface route/guard/scope, use TriWiki, do not edit installed harness files outside this engine repo, and require human-approved conflict cleanup. ${skillDreamPolicyText()}\n`,
748
748
  'wiki': `---\nname: wiki\ndescription: Dollar-command route for $Wiki TriWiki refresh, pack, validate, and prune commands.\n---\n\nUse for $Wiki or Korean wiki-refresh requests. Refresh/update/갱신: run sks wiki refresh, then validate .sneakoscope/wiki/context-pack.json. Pack: run sks wiki pack, then validate. Prune/clean/정리: use sks wiki refresh --prune, or sks wiki prune --dry-run for inspection. Report claims, anchors, trust, attention.use_first/hydrate_first, validation, and blockers. Do not start ambiguity-gated implementation, subagents, or unrelated work.\n`,
749
- 'team': `---\nname: team\ndescription: SKS Team orchestration for $Team/code work; $From-Chat-IMG is the explicit chat-image alias.\n---\n\nUse for $Team/code work. Auto-seal the route contract from prompt, TriWiki/current-code defaults, and conservative policy; do not surface a prequestion sheet. Read pipeline-plan.json or run sks pipeline plan to see the runtime lane, kept/skipped stages, and verification before implementation. Write team-roster.json; team-gate.json needs team_roster_confirmed=true. executor:N means N scouts, N debate voices, then fresh N executors. ${MIN_TEAM_REVIEW_POLICY_TEXT} After consensus, compile team-graph.json, team-runtime-tasks.json, team-decomposition-report.json, and team-inbox/ so worker handoff uses concrete runtime task ids with role/path/domain/lane hints. Refresh/validate TriWiki before debate, implementation, review, and final; consume attention.use_first and hydrate attention.hydrate_first before risky decisions. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${skillDreamPolicyText()} Log events and use sks team message for bounded inter-agent communication in transcript/lane panes. Color-coded tmux lanes distinguish overview/scout/planning/execution/review/safety sessions in one tmux window using split panes when tmux is available. $Team/$team plus sks --mad uses the MAD-SKS permission gate module: live server work, normal DB writes, Supabase MCP writes, direct SQL, schema cleanup, and needed migrations are open for the active invocation; only catastrophic DB wipe/all-row/project-management guards remain. End with cleanup-tmux or a cleanup event so follow panes show cleanup and stop; pass team-session-cleanup.json, then reflection and Honest Mode. Parent integrates/verifies.\n\n${chatCaptureIntakeText()}\n`,
749
+ 'team': `---\nname: team\ndescription: SKS Team orchestration for $Team/code work; $From-Chat-IMG is the explicit chat-image alias.\n---\n\nUse for $Team/code work. Auto-seal the route contract from prompt, TriWiki/current-code defaults, and conservative policy; do not surface a prequestion sheet. Read pipeline-plan.json or run sks pipeline plan to see the runtime lane, kept/skipped stages, and verification before implementation. Write team-roster.json; team-gate.json needs team_roster_confirmed=true. executor:N means N scouts, N debate voices, then fresh N executors. ${MIN_TEAM_REVIEW_POLICY_TEXT} After consensus, compile team-graph.json, team-runtime-tasks.json, team-decomposition-report.json, and team-inbox/ so worker handoff uses concrete runtime task ids with role/path/domain/lane hints. Refresh/validate TriWiki before debate, implementation, review, and final; consume attention.use_first and hydrate attention.hydrate_first before risky decisions. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${solutionScoutPolicyText('fix this broken behavior')} ${skillDreamPolicyText()} Log events and use sks team message for bounded inter-agent communication in transcript/lane panes. Color-coded tmux lanes distinguish overview/scout/planning/execution/review/safety sessions in one tmux window using split panes when tmux is available. $Team/$team plus sks --mad uses the MAD-SKS permission gate module: live server work, normal DB writes, Supabase MCP writes, direct SQL, schema cleanup, and needed migrations are open for the active invocation; only catastrophic DB wipe/all-row/project-management guards remain. End with cleanup-tmux or a cleanup event so follow panes show cleanup and stop; pass team-session-cleanup.json, then reflection and Honest Mode. Parent integrates/verifies.\n\n${chatCaptureIntakeText()}\n`,
750
750
  'from-chat-img': `---\nname: from-chat-img\ndescription: Explicit $From-Chat-IMG Team alias for chat screenshot plus attachment analysis.\n---\n\nUse only for From-Chat-IMG/$From-Chat-IMG. It enters the normal Team pipeline. Treat uploads as chat screenshot plus originals. Use Codex Computer Use visual inspection when available, list requirements first, match regions to attachments with confidence, write ${FROM_CHAT_IMG_COVERAGE_ARTIFACT}, ${FROM_CHAT_IMG_CHECKLIST_ARTIFACT}, ${FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT}, and ${FROM_CHAT_IMG_QA_LOOP_ARTIFACT}, then continue Team gates, review, reflection, and Honest Mode. ${CODEX_COMPUTER_USE_ONLY_POLICY} The ledger must account for every visible customer request, screenshot image region, and separate attachment; ${FROM_CHAT_IMG_CHECKLIST_ARTIFACT} must have a checked item for each request, image-region/attachment match, work item, scoped QA-LOOP, and verification step; ${FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT} stores temporary TriWiki-backed session context with expires_after_sessions=${FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS}. ${FROM_CHAT_IMG_QA_LOOP_ARTIFACT} must prove QA-LOOP ran over the exact customer-request work-order range after implementation, with every work item covered, post-fix verification complete, and zero unresolved findings. team-gate.json cannot pass From-Chat-IMG completion until unresolved_items is empty, every checklist box is checked, and scoped_qa_loop_completed=true.\n`,
751
751
  'qa-loop': `---\nname: qa-loop\ndescription: $QA-LOOP dogfoods UI/API as human proxy with safety gates, Codex Computer Use-only UI evidence, safe fixes, rechecks, and a QA report.\n---\n\nUse only $QA-LOOP. Infer scope, target, mutation policy, and login boundary from the prompt plus TriWiki/current-code defaults; do not surface a prequestion sheet. Credentials are runtime-only; never save secrets. UI-level E2E needs official Codex Computer Use evidence or must be marked unverified; Chrome MCP, Browser Use, Playwright, Selenium, Puppeteer, and other browser automation do not satisfy UI/browser verification. Deployed targets are read-only; destructive removal is forbidden. After answer/run, dogfood real flows, apply safe contract-allowed code/test/docs fixes, recheck, and do not pass qa-gate.json with unresolved findings or without post_fix_verification_complete. Finish qa-ledger, date/version report, gate, completion summary, and Honest Mode.\n`,
752
752
  'ppt': `---\nname: ppt\ndescription: $PPT information-first HTML/PDF presentation pipeline with inferred STP, audience, pain-point, format, research, design-system, and verification contract.\n---\n\nUse only when the user invokes $PPT or asks to create a presentation, deck, slides, pitch deck, proposal deck, HTML presentation, or PDF presentation artifact. Before artifact work, auto-seal presentation-specific answers from prompt, TriWiki/current-code defaults, and conservative policy: delivery context, target audience profile including role/average age/job/industry/topic familiarity/decision power, STP strategy, decision context and objections, and 3+ pain-point to solution mappings with expected aha moments. Do not surface a prequestion sheet. Presentation design must be simple, restrained, and information-first: avoid over-designed decoration, ornamental gradients, nested cards, and effects that compete with the message. Design detail should be embedded through typography hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents. ${pptPipelineAllowlistPolicyText()} Use design.md as the only design decision SSOT. If design.md is missing, use docs/Design-Sys-Prompt.md plus getdesign-reference and curated DESIGN.md examples from ${AWESOME_DESIGN_MD_REFERENCE.url} only as source inputs, then fuse them into route-local PPT style tokens with a recorded design_ssot instead of treating references as parallel authorities. If generated image assets or slide visual critique are needed, use Codex App $imagegen/gpt-image-2 only when that asset/review need is explicitly sealed in the $PPT contract; direct API fallback, placeholder files, and prose-only substitutes do not satisfy the route gate. ${CODEX_IMAGEGEN_REQUIRED_POLICY} Use web or Context7 evidence only when external facts/libraries/current docs are required by the PPT contract, record verified claims in ppt-fact-ledger.json, record generated image asset plans/results/blockers in ppt-image-asset-ledger.json, then create the PDF plus editable source HTML under source-html/, keep independent strategy/render/file-write phases parallel where inputs allow, record ppt-parallel-report.json, run the bounded ppt-review-policy/ppt-review-ledger/ppt-iteration-report loop, and verify readability, overlap, format fit, source coverage, export state, unsupported-claim status, image-asset completion, review-loop termination, and temporary build files cleanup. Finish with reflection and Honest Mode.\n`,
@@ -759,7 +759,8 @@ export async function installSkills(root) {
759
759
  'mad-sks': `---\nname: mad-sks\ndescription: Explicit high-risk authorization modifier for $MAD-SKS scoped Supabase MCP DB permission widening.\n---\n\nUse only when the user explicitly invokes $MAD-SKS or top-level sks --mad. It can be combined with another route, such as $MAD-SKS $Team or $DB ... $MAD-SKS; in that case the other command remains the primary workflow and MAD-SKS is only the temporary permission grant. The widened permission applies only while the active mission gate is open, must be deactivated when the task ends, and opens live server work, Supabase MCP database writes, column/schema cleanup, direct execute SQL, migration application when required, and normal targeted DB writes. Keep only catastrophic safeguards: whole database/schema/table removal, truncate, all-row delete/update, reset, dangerous project/branch management, credential exfiltration, persistent security weakening, and unrequested fallback implementation remain blocked. Do not carry MAD-SKS permission into later prompts or routes. The permission profile is centralized in src/core/permission-gates.mjs so skill/hook/MCP-style gates share one decision function.\n`,
760
760
  'gx': `---\nname: gx\ndescription: Dollar-command route for $GX or $gx deterministic GX visual context cartridges.\n---\n\nUse when the user invokes $GX/$gx or asks for architecture/context visualization through SKS. Prefer sks gx init, render, validate, drift, and snapshot. vgraph.json remains the source of truth.\n`,
761
761
  'help': `---\nname: help\ndescription: Dollar-command route for $Help or $help explaining installed SKS commands and workflows.\n---\n\nUse when the user invokes $Help/$help or asks what commands exist. Prefer concise output from sks commands, sks usage <topic>, sks quickstart, sks aliases, and sks codex-app.\n`,
762
- 'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline for execution prompts; Answer and DFix bypass it.\n---\n\nClassify intent: Answer only for real questions; question-shaped implicit instructions, complaints, and mandatory-policy statements route to Team. DFix handles Direct Fix work: tiny copy/config/docs/labels/spacing/translation/simple mechanical edits; code and broad implementation default to Team unless safety/research/GX route fits. Infer goal, target, constraints, acceptance, risk, and smallest safe route from prompt, TriWiki/current-code defaults, and conservative SKS policy. Do not surface a prequestion sheet. Materialize pipeline-plan.json for the runtime lane, kept/skipped stages, no-fallback invariant, and verification; inspect with sks pipeline plan, adding --proof-field when changed files are known. Code work surfaces route/guard/scopes, materializes team-roster.json from default or explicit counts before implementation, compiles concrete Team runtime graph/inbox artifacts after consensus, and parent owns integration/tests/Context7/Honest Mode. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${skillDreamPolicyText()}\n\n${chatCaptureIntakeText()}\n\nDesign: non-PPT UI/UX reads design.md; if missing use design-system-builder; use imagegen for image/logo/raster, and imagegen must prefer Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) before API generation. ${CODEX_IMAGEGEN_REQUIRED_POLICY} For UI/UX review/audit requests that mention image generation, gpt-image-2, callouts, or annotated review images, route to $Image-UX-Review/$UX-Review and require generated annotated review image evidence before issue extraction; do not satisfy that route with text-only critique. For $PPT, ${pptPipelineAllowlistPolicyText()} ${getdesignReferencePolicyText()} TriWiki context-tracking SSOT: .sneakoscope/wiki/context-pack.json; read only the latest coordinate+voxel overlay pack before every route stage, run sks wiki refresh/pack after changes, validate before handoffs/final.\n`,
762
+ 'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline for execution prompts; Answer and DFix bypass it.\n---\n\nClassify intent: Answer only for real questions; question-shaped implicit instructions, complaints, and mandatory-policy statements route to Team. DFix handles Direct Fix work: tiny copy/config/docs/labels/spacing/translation/simple mechanical edits; code and broad implementation default to Team unless safety/research/GX route fits. Infer goal, target, constraints, acceptance, risk, and smallest safe route from prompt, TriWiki/current-code defaults, and conservative SKS policy. Do not surface a prequestion sheet. Materialize pipeline-plan.json for the runtime lane, kept/skipped stages, no-fallback invariant, and verification; inspect with sks pipeline plan, adding --proof-field when changed files are known. Code work surfaces route/guard/scopes, materializes team-roster.json from default or explicit counts before implementation, compiles concrete Team runtime graph/inbox artifacts after consensus, and parent owns integration/tests/Context7/Honest Mode. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${solutionScoutPolicyText('fix this broken behavior')} ${skillDreamPolicyText()}\n\n${chatCaptureIntakeText()}\n\nDesign: non-PPT UI/UX reads design.md; if missing use design-system-builder; use imagegen for image/logo/raster, and imagegen must prefer Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) before API generation. ${CODEX_IMAGEGEN_REQUIRED_POLICY} For UI/UX review/audit requests that mention image generation, gpt-image-2, callouts, or annotated review images, route to $Image-UX-Review/$UX-Review and require generated annotated review image evidence before issue extraction; do not satisfy that route with text-only critique. For $PPT, ${pptPipelineAllowlistPolicyText()} ${getdesignReferencePolicyText()} TriWiki context-tracking SSOT: .sneakoscope/wiki/context-pack.json; read only the latest coordinate+voxel overlay pack before every route stage, run sks wiki refresh/pack after changes, validate before handoffs/final.\n`,
763
+ [SOLUTION_SCOUT_SKILL_NAME]: `---\nname: ${SOLUTION_SCOUT_SKILL_NAME}\ndescription: Web-similarity scout hook for SKS problem-solving and repair requests.\n---\n\n${solutionScoutPolicyText('fix this broken behavior')}\n\nUse this as a pipeline hook, not as a standalone route: when a user asks to solve, fix, repair, troubleshoot, or investigate broken behavior, search first for similar resolution cases, summarize the useful patterns with sources, then combine them with current repo evidence before editing. If browsing is unavailable, mark the external scout unverified and continue with local evidence only.\n`,
763
764
  'reasoning-router': `---\nname: reasoning-router\ndescription: Temporary SKS reasoning-effort routing for every command and pipeline route.\n---\n\nmedium: simple copy/color/discovery/setup/mechanical edits. high: logic, safety, architecture, DB, orchestration, refactor, multi-file work. xhigh: research, AutoResearch, falsification, benchmarks, SEO/GEO, open-ended discovery, and From-Chat-IMG image work-order analysis. Routing is temporary; return to default after the gate. Inspect with sks reasoning and sks pipeline status.\n`,
764
765
  'pipeline-runner': `---\nname: pipeline-runner\ndescription: Execute SKS dollar-command routes as stateful pipelines with mission artifacts, route gates, Context7 evidence, temporary reasoning routing, reflection, and Honest Mode.\n---\n\nEvery $ command is a route. Use current.json, mission artifacts, and pipeline-plan.json as the execution plan: it records the lane, skipped stages, kept stages, verification, and no-unrequested-fallback invariant. Use temporary reasoning, TriWiki before stages, source hydration, Context7 when required, Team cleanup before reflection, reflection for full routes, and completion summary plus Honest Mode before final. Surface guard/scopes, record evidence, refresh/pack/validate TriWiki, and check sks pipeline status/resume/plan. ${speedLanePolicyText()} ${skillDreamPolicyText()}\n`,
765
766
  'context7-docs': `---\nname: context7-docs\ndescription: Enforce Context7 MCP documentation evidence for SKS routes that depend on external libraries, frameworks, APIs, MCPs, package managers, DB SDKs, or generated docs.\n---\n\nWhen required, resolve-library-id, then query-docs for the resolved id. Legacy get-library-docs evidence is accepted. Prefer sks context7 tools/resolve/docs/evidence and finish only after both evidence stages exist. Check setup with sks context7 check.\n`,
@@ -18,7 +18,7 @@ import { writeQaLoopArtifacts } from './qa-loop.mjs';
18
18
  import { IMAGE_UX_REVIEW_GATE_ARTIFACT, IMAGE_UX_REVIEW_POLICY_ARTIFACT, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT, IMAGE_UX_REVIEW_REQUIRED_GATE_FIELDS, writeImageUxReviewRouteArtifacts } from './image-ux-review.mjs';
19
19
  import { SPEED_LANE_POLICY } from './proof-field.mjs';
20
20
  import { permissionGateSummary } from './permission-gates.mjs';
21
- import { CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_REQUIRED_POLICY, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, chatCaptureIntakeText, context7RequirementText, dollarCommand, evidenceMentionsForbiddenBrowserAutomation, getdesignReferencePolicyText, hasFromChatImgSignal, hasMadSksSignal, imageUxReviewPipelinePolicyText, noUnrequestedFallbackCodePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stripDollarCommand, stripMadSksSignal, stripVisibleDecisionAnswerBlocks, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
21
+ import { CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_REQUIRED_POLICY, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, SOLUTION_SCOUT_STAGE_ID, chatCaptureIntakeText, context7RequirementText, dollarCommand, evidenceMentionsForbiddenBrowserAutomation, getdesignReferencePolicyText, hasFromChatImgSignal, hasMadSksSignal, imageUxReviewPipelinePolicyText, looksLikeProblemSolvingRequest, noUnrequestedFallbackCodePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, solutionScoutPolicyText, speedLanePolicyText, stripDollarCommand, stripMadSksSignal, stripVisibleDecisionAnswerBlocks, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
22
22
  import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from './team-dag.mjs';
23
23
  import { formatAgentReasoning, formatRoleCounts, initTeamLive, parseTeamSpecText, teamReasoningPolicy } from './team-live.mjs';
24
24
  import { evaluateTeamReviewPolicyGate, MIN_TEAM_REVIEWER_LANES, MIN_TEAM_REVIEW_POLICY_TEXT, teamReviewPolicy } from './team-review-policy.mjs';
@@ -50,6 +50,7 @@ const QUESTION_GATE_ROUTES = new Set(['QALoop', 'PPT']);
50
50
  const LIGHTWEIGHT_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki']);
51
51
  const FULL_ROUTE_STAGES = Object.freeze([
52
52
  'route_classification',
53
+ SOLUTION_SCOUT_STAGE_ID,
53
54
  'skill_dream_counter',
54
55
  'ambiguity_gate',
55
56
  'pipeline_plan',
@@ -234,6 +235,9 @@ function buildPipelineStages(route, task, ambiguity, lane, context7Required) {
234
235
  }
235
236
 
236
237
  function optionalStage(route, task, ambiguity, context7Required, id) {
238
+ if (id === SOLUTION_SCOUT_STAGE_ID && !looksLikeProblemSolvingRequest(task)) return { skip: true, notApplicable: true, reason: 'no_problem_solving_signal' };
239
+ if (id === SOLUTION_SCOUT_STAGE_ID && ['Answer', 'Help', 'Wiki'].includes(route?.id)) return { skip: true, notApplicable: true, reason: 'route_not_code_repair' };
240
+ if (id === SOLUTION_SCOUT_STAGE_ID) return { skip: false, reason: 'problem_solving_request_requires_web_similarity_scout' };
237
241
  if (id === 'ambiguity_gate' && ambiguity?.required === false) return { skip: true, notApplicable: true, reason: 'ambiguity_gate_not_required_for_entrypoint' };
238
242
  if (id === 'ambiguity_gate' && CLARIFICATION_BYPASS_ROUTES.has(route?.id)) return { skip: true, notApplicable: true, reason: 'route_bypasses_clarification' };
239
243
  if (id === 'context7_evidence' && !context7Required) return { skip: true, notApplicable: true, reason: 'context7_not_required_by_route' };
@@ -252,9 +256,16 @@ function planVerification(route, proof) {
252
256
  }
253
257
 
254
258
  function planNextActions(route, task, ambiguity, lane) {
255
- if (ambiguity.required && !ambiguity.passed) return ['auto-seal execution contract from inferred answers', 'continue with decision-contract.json'];
259
+ if (ambiguity.required && !ambiguity.passed) {
260
+ return [
261
+ 'auto-seal execution contract from inferred answers',
262
+ ...(looksLikeProblemSolvingRequest(task) ? ['run Solution Scout web search for similar fixes before editing'] : []),
263
+ 'continue with decision-contract.json'
264
+ ];
265
+ }
256
266
  const actions = ['read pipeline-plan.json before work', 'execute kept stages only', 'run listed verification'];
257
267
  if (!lane.fast_lane_allowed && routeRequiresSubagents(route, task)) actions.splice(1, 0, 'materialize full Team artifacts before implementation');
268
+ if (looksLikeProblemSolvingRequest(task)) actions.splice(1, 0, 'run Solution Scout web search for similar fixes before editing');
258
269
  actions.push('refresh/validate TriWiki when required', 'finish with completion summary and Honest Mode');
259
270
  return actions;
260
271
  }
@@ -283,6 +294,7 @@ export function promptPipelineContext(prompt, route = null) {
283
294
  'Default execution routing: general implementation/code-changing prompts promote to Team so the normal path is parallel analysis, TriWiki refresh, debate/consensus, then fresh parallel executors. Answer, DFix, Help, Wiki maintenance, and safety-specific routes are intentional exceptions.',
284
295
  'Stance: infer the user intent aggressively from rough wording, local context, TriWiki, and conservative defaults; do not surface prequestion sheets before work.',
285
296
  subagentExecutionPolicyText(route, cleanPrompt),
297
+ solutionScoutPolicyText(cleanPrompt),
286
298
  noUnrequestedFallbackCodePolicyText(),
287
299
  outcomeRubricPolicyText(),
288
300
  speedLanePolicyText(),
@@ -1,4 +1,25 @@
1
1
  const REFLECTION_SKILL_NAME = 'reflection';
2
+ export const SOLUTION_SCOUT_SKILL_NAME = 'solution-scout';
3
+ export const SOLUTION_SCOUT_STAGE_ID = 'solution_scout';
4
+
5
+ export function looksLikeProblemSolvingRequest(prompt = '') {
6
+ const text = String(prompt || '').trim();
7
+ if (!text) return false;
8
+ const problemCue = /(문제|오류|에러|버그|고장|깨짐|실패|안\s*(?:됨|돼|되|나옴|보임|돌아|먹)|작동\s*안|해결|고쳐|수정|복구|troubleshoot|not\s+working|broken|bug|error|failure|fails?|crash|fix|repair|resolve|solve)/i.test(text);
9
+ const actionCue = /(해줘|해달|해라|되게|찾아|검색|기반|수정|진행|apply|implement|fix|repair|resolve|solve|troubleshoot|patch|update|change)/i.test(text);
10
+ return problemCue && actionCue;
11
+ }
12
+
13
+ export function solutionScoutPolicyText(prompt = '') {
14
+ if (!looksLikeProblemSolvingRequest(prompt)) return '';
15
+ return [
16
+ 'Solution Scout hook: this prompt looks like a problem-solving or repair request.',
17
+ 'Before code edits, run a short web search for similar error reports, bug fixes, docs notes, or prior resolution patterns using the concrete symptom, stack, package, and error text from the repo.',
18
+ 'Prefer primary sources and official docs for package/API behavior; use Context7 when the fix depends on a library, SDK, MCP, package manager, or generated documentation.',
19
+ 'Summarize the relevant external patterns in 2-3 bullets, then design the local SKS fix from current code/tests plus those patterns. Do not copy a workaround blindly.',
20
+ 'If web search is unavailable or the issue is fully local and trivial, state that the external-similarity search is unverified and continue from local evidence only.'
21
+ ].join('\n');
22
+ }
2
23
  export const FROM_CHAT_IMG_COVERAGE_ARTIFACT = 'from-chat-img-coverage-ledger.json';
3
24
  export const FROM_CHAT_IMG_WORK_ORDER_ARTIFACT = 'from-chat-img-work-order.md';
4
25
  export const FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT = 'from-chat-img-source-inventory.json';
@@ -12,7 +33,7 @@ export const CODEX_COMPUTER_USE_EVIDENCE_SOURCE = 'codex_computer_use';
12
33
  export const CODEX_IMAGEGEN_EVIDENCE_SOURCE = 'codex_app_imagegen_gpt_image_2';
13
34
  export const CODEX_APP_IMAGE_GENERATION_DOC_URL = 'https://developers.openai.com/codex/app/features#image-generation';
14
35
  export const OPENAI_IMAGE_GENERATION_DOC_URL = 'https://developers.openai.com/api/docs/guides/image-generation';
15
- export const CODEX_COMPUTER_USE_ONLY_POLICY = 'Pipeline UI/browser verification and visual inspection must use Codex Computer Use only. Do not use Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or any other browser automation substitute; if Codex Computer Use is unavailable, mark the UI/browser evidence unverified instead of substituting another tool. In Codex App prompts, invoke @Computer or @AppName in a new thread when live Computer Use tools are needed; SKS hooks and skills can require the policy but cannot attach missing host tools to an already-started turn.';
36
+ export const CODEX_COMPUTER_USE_ONLY_POLICY = 'Pipeline UI/browser verification and visual inspection must use Codex Computer Use only. Do not use or install Playwright packages, Chrome MCP, Browser Use, Selenium, Puppeteer, or any other browser automation substitute; if Codex Computer Use is unavailable, mark the UI/browser evidence unverified instead of substituting another tool. In Codex App prompts, invoke @Computer or @AppName in a new thread when live Computer Use tools are needed; SKS hooks and skills can require the policy but cannot attach missing host tools to an already-started turn.';
16
37
  export const CODEX_IMAGEGEN_REQUIRED_POLICY = 'Pipeline image generation, raster asset creation/editing, and generated image-review evidence must use real Codex App imagegen/$imagegen with gpt-image-2 when that evidence is required. Do not substitute placeholder SVG/HTML/CSS, prose-only critique, stock-like stand-ins, manually fabricated files, or missing-output ledgers for requested/generated raster assets or required generated review images. If imagegen/gpt-image-2 is unavailable, record the blocker and mark the image asset or review evidence unverified instead of passing the gate. In Codex App prompts, invoke $imagegen when live image generation is needed; SKS hooks and skills can require the policy but cannot attach missing host image-generation tools to an already-started turn.';
17
38
  export const RESERVED_CODEX_PLUGIN_SKILL_NAMES = Object.freeze(['computer-use', 'browser', 'browser-use']);
18
39
  export const FORBIDDEN_BROWSER_AUTOMATION_RE = /\b(playwright|chrome\s+mcp|browser\s+use|selenium|puppeteer)\b/i;
@@ -106,6 +127,7 @@ export function imageUxReviewPipelinePolicyText() {
106
127
  export const RECOMMENDED_SKILLS = [
107
128
  'reasoning-router',
108
129
  'pipeline-runner',
130
+ 'solution-scout',
109
131
  'context7-docs',
110
132
  'seo-geo-optimizer',
111
133
  'autoresearch-loop',
@@ -717,8 +739,8 @@ export function looksLikeDirectWorkRequest(prompt = '') {
717
739
  return looksLikeCodeChangingWork(text)
718
740
  || looksLikeChatCaptureRequest(text)
719
741
  || looksLikeQuestionShapedDirective(text)
720
- || /(작업|파이프라인|구현|수정|변경|추가|적용|반영|처리|수행|검수|설치|리드미|README).*(해줘|해달|해라|해야|되게|줘야|줘야지|달라)/i.test(text)
721
- || /(진행해|수행해|작업해|처리해|적용해|반영해|검수해|고쳐줘|바꿔줘|만들어줘|해줘야|해줘야지|해달라|해야지|되게 해|install|run|execute|test|deploy|commit|push)/i.test(text);
742
+ || /(작업|파이프라인|구현|수정|변경|추가|적용|반영|처리|수행|검수|설치|해결|리드미|README).*(해줘|해달|해라|해야|되게|줘야|줘야지|달라)/i.test(text)
743
+ || /(진행해|수행해|작업해|처리해|적용해|반영해|검수해|고쳐줘|바꿔줘|해결해줘|만들어줘|해줘야|해줘야지|해달라|해야지|되게 해|install|run|execute|test|deploy|commit|push)/i.test(text);
722
744
  }
723
745
 
724
746
  export function routeNeedsContext7(route, prompt = '') {
@@ -748,7 +770,7 @@ export function reflectionRequiredForRoute(route) {
748
770
  }
749
771
 
750
772
  export function looksLikeCodeChangingWork(prompt = '') {
751
- return /\b(implement|build|make|add|edit|modify|change|fix|refactor|rewrite|migrate|create|delete|remove|rename|update|patch|코드|구현|개발|수정|변경|추가|삭제|고쳐|바꿔|리팩터|마이그레이션)\b/i.test(String(prompt || ''));
773
+ return /\b(implement|build|make|add|edit|modify|change|fix|refactor|rewrite|migrate|create|delete|remove|rename|update|patch|코드|구현|개발|수정|변경|추가|삭제|해결|고쳐|바꿔|리팩터|마이그레이션)\b/i.test(String(prompt || ''));
752
774
  }
753
775
 
754
776
  export function looksLikeExecutionWork(prompt = '') {
@@ -8,7 +8,7 @@ export const SKILL_DREAM_POLICY = Object.freeze({
8
8
  schema_version: 1,
9
9
  state_path: '.sneakoscope/skills/dream-state.json',
10
10
  latest_report_path: '.sneakoscope/reports/skill-dream-latest.json',
11
- min_events_between_runs: 12,
11
+ min_events_between_runs: 10,
12
12
  min_interval_hours: 24,
13
13
  max_events_retained: 160,
14
14
  max_skill_lines_before_compression: 80,
@@ -39,7 +39,7 @@ const MERGE_GROUPS = [
39
39
  ];
40
40
 
41
41
  export function skillDreamPolicyText() {
42
- return 'Skill dreaming policy: record only cheap route/skill usage counters in `.sneakoscope/skills/dream-state.json`; do not evaluate every conversation. Run `sks skill-dream run` or the automatic due check only after the configured event count and cooldown. Reports are recommendation-only: keep/merge/prune/improve candidates may update future generated skill wording, but skill deletion or merge requires explicit user approval.';
42
+ return 'Skill dreaming policy: record only cheap route/skill usage counters in `.sneakoscope/skills/dream-state.json`; do not evaluate every conversation. Run `sks skill-dream run` or the automatic due check only after the configured event count and cooldown, defaulting to one due check every 10 route events subject to cooldown. Reports are recommendation-only: keep/merge/prune/improve candidates may update future generated skill wording, but skill deletion or merge requires explicit user approval.';
43
43
  }
44
44
 
45
45
  export function createSkillCandidate(opts = {}) {
@@ -1,119 +1,48 @@
1
1
  import path from 'node:path';
2
2
  import fsp from 'node:fs/promises';
3
3
  import { spawnSync } from 'node:child_process';
4
- import { exists, nowIso, packageRoot, readJson, runProcess, sha256, sksRoot, which, writeJsonAtomic } from './fsx.mjs';
4
+ import figlet from 'figlet';
5
+ import { exists, nowIso, PACKAGE_VERSION, packageRoot, readJson, runProcess, sha256, sksRoot, which, writeJsonAtomic } from './fsx.mjs';
5
6
  import { getCodexInfo } from './codex-adapter.mjs';
6
7
  import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
7
8
  import { REQUIRED_CODEX_MODEL, forceGpt55CodexArgs } from './codex-model-guard.mjs';
8
9
  import { MIN_TEAM_REVIEWER_LANES } from './team-review-policy.mjs';
9
10
  import { appendTeamEvent } from './team-live.mjs';
10
11
 
11
- export const SKS_TMUX_LOGO = [
12
- ' _______ __ __ _______',
13
- ' / _____/| / /_/ /| / _____/|',
14
- ' / /____| | / __ / | / /____| |',
15
- ' \\____ \\ | / / / /| | \\____ \\ |',
16
- ' ____/ / | |/_/ /_/ / | |____/ / | |',
17
- '/_____/ |//_/\\__/ / |//_____/ |/',
18
- '\\_____\\___/ \\_\\ \\_\\/___/ \\_____\\___/',
19
- ' SNEAKOSCOPE CODEX'
20
- ].join('\n');
12
+ const SKS_FIGLET_FONT = 'Standard';
13
+
14
+ function trimFiglet(text = '') {
15
+ return String(text || '')
16
+ .split(/\r?\n/)
17
+ .map((line) => line.replace(/\s+$/g, ''))
18
+ .join('\n')
19
+ .replace(/\n+$/g, '');
20
+ }
21
+
22
+ export function sksAsciiLogo(opts = {}) {
23
+ const version = opts.version || PACKAGE_VERSION;
24
+ const subtitle = opts.subtitle || 'SNEAKOSCOPE CODEX';
25
+ let logo = '';
26
+ try {
27
+ logo = figlet.textSync('SKS', {
28
+ font: SKS_FIGLET_FONT,
29
+ horizontalLayout: 'fitted',
30
+ verticalLayout: 'default'
31
+ });
32
+ } catch {
33
+ logo = ' ____ _ __ ____\n / ___| | |/ // ___|\n \\___ \\ | \' / \\___ \\\n ___) || . \\ ___) |\n |____/ |_|\\_\\|____/';
34
+ }
35
+ return `${trimFiglet(logo)}\n${subtitle} v${version}`;
36
+ }
37
+
38
+ export const SKS_TMUX_LOGO = sksAsciiLogo();
21
39
 
22
40
  const SKS_TMUX_LOGO_FRAMES = [
23
- [
24
- ' ||',
25
- ' ||',
26
- ' ||',
27
- ' ||',
28
- ' ||',
29
- ' ||',
30
- ' SKS',
31
- ' SNEAKOSCOPE CODEX'
32
- ].join('\n'),
33
- [
34
- ' //||',
35
- ' // || .',
36
- ' // || .:',
37
- ' // || .::',
38
- ' // || .:::',
39
- ' // ||.::::',
40
- ' S K S',
41
- ' SNEAKOSCOPE CODEX'
42
- ].join('\n'),
43
- [
44
- ' _______ __ __ _______',
45
- ' / _____/| / /_/ /| / _____/|',
46
- ' / /____| | / __ / | / /____| |',
47
- ' \\____ \\ | / / / /| | \\____ \\ |',
48
- ' ____/ / | |/_/ /_/ / | |____/ / | |',
49
- '/_____/ |//_/\\__/ / |//_____/ |/',
50
- '\\_____\\___/ \\_\\ \\_\\/___/ \\_____\\___/',
51
- ' SNEAKOSCOPE CODEX'
52
- ].join('\n'),
53
- [
54
- ' _______ __ __ _______',
55
- ' / _____/ / /_/ / / _____/|',
56
- ' / /____ / __ / / /____ | |',
57
- ' \\____ \\ / / / / \\____ \\| |',
58
- ' ____/ / /_/ /_/ / ____/ / | |',
59
- '/_____/ /_/\\__/ /_____/ |/',
60
- ' \\_____\\ \\_\\ \\_\\ \\_____\\___/',
61
- ' SNEAKOSCOPE CODEX'
62
- ].join('\n'),
63
- [
64
- ' _______ __ __ _______',
65
- ' / _____/| / /_/ /| / _____/|',
66
- ' / /____| | / __ / | / /____| |',
67
- ' \\____ \\ | / / / /| | \\____ \\ |',
68
- ' ____/ / | |/_/ /_/ / | |____/ / | |',
69
- '/_____/ |//_/\\__/ / |//_____/ |/',
70
- '\\_____\\___/ \\_\\ \\_\\/___/ \\_____\\___/',
71
- ' SNEAKOSCOPE CODEX'
72
- ].join('\n'),
73
- [
74
- ' _______ __ __ _______',
75
- ' |\\_____ \\ / /_/ / |\\_____ \\',
76
- ' | |____\\ \\/ __ / | |____\\ \\',
77
- ' | |\\____\\/ / / / | |\\____\\ \\',
78
- ' | | |___/ /_/ /__ | | |___/ /',
79
- ' \\|_|/____/\\__/__/ \\|_|/____/',
80
- ' S K S',
81
- ' SNEAKOSCOPE CODEX'
82
- ].join('\n'),
83
- [
84
- ' ||\\\\',
85
- ' . || \\\\',
86
- ' ::. || \\\\',
87
- ' ::::. || \\\\',
88
- ' ::::::. || \\\\',
89
- ' :::::::::|| \\\\',
90
- ' S K S',
91
- ' SNEAKOSCOPE CODEX'
92
- ].join('\n'),
93
- [
94
- ' ||',
95
- ' ||',
96
- ' ||',
97
- ' ||',
98
- ' ||',
99
- ' ||',
100
- ' SKS',
101
- ' SNEAKOSCOPE CODEX'
102
- ].join('\n'),
103
41
  SKS_TMUX_LOGO
104
42
  ];
105
43
 
106
44
  const SKS_TMUX_LOGO_ANIMATION_STEPS = Object.freeze([
107
- { frame: 0, color: '39', bold: false, delay: '0.045' },
108
- { frame: 1, color: '39', bold: false, delay: '0.045' },
109
- { frame: 2, color: '45', bold: false, delay: '0.05' },
110
- { frame: 3, color: '51', bold: false, delay: '0.055' },
111
- { frame: 4, color: '51', bold: true, delay: '0.07' },
112
- { frame: 5, color: '51', bold: true, delay: '0.07' },
113
- { frame: 6, color: '45', bold: false, delay: '0.05' },
114
- { frame: 7, color: '39', bold: false, delay: '0.045' },
115
- { frame: 8, color: '39', bold: false, delay: '0.045' },
116
- { frame: 9, color: '51', bold: true, delay: '0.16' }
45
+ { frame: 0, color: '51', bold: true, delay: '0.16' }
117
46
  ]);
118
47
 
119
48
  export const DEFAULT_SKS_CODEX_MODEL = REQUIRED_CODEX_MODEL;
@@ -360,7 +289,7 @@ export function formatTmuxBanner(status = null) {
360
289
  const lines = [
361
290
  SKS_TMUX_LOGO,
362
291
  '',
363
- 'ㅅㅋㅅ tmux runtime',
292
+ 'SKS tmux runtime',
364
293
  '',
365
294
  'Canonical prompt commands:',
366
295
  ' $DFix $Answer $SKS $Team $QA-LOOP $PPT $Goal $Research $AutoResearch $DB $GX $Wiki $Help',
@@ -391,11 +320,62 @@ function paneId(stdout = '') {
391
320
  return id.startsWith('%') ? id : null;
392
321
  }
393
322
 
323
+ function currentTerminalDimensions(opts = {}) {
324
+ const width = Number(opts.width || opts.detachedWidth || process.stdout?.columns || process.env.COLUMNS || 0);
325
+ const height = Number(opts.height || opts.detachedHeight || process.stdout?.rows || process.env.LINES || 0);
326
+ return {
327
+ width: String(Math.max(120, Number.isFinite(width) && width > 0 ? width : 120)),
328
+ height: String(Math.max(36, Number.isFinite(height) && height > 0 ? height : 36))
329
+ };
330
+ }
331
+
394
332
  async function hasTmuxSession(bin, session) {
395
333
  const run = await tmuxRun(bin, ['has-session', '-t', session], { timeoutMs: 5000 });
396
334
  return run.code === 0;
397
335
  }
398
336
 
337
+ async function tmuxWindowTarget(bin, session) {
338
+ const fallback = sanitizeTmuxSessionName(session);
339
+ const run = await tmuxRun(bin, ['list-windows', '-t', fallback, '-F', '#{window_id}'], { timeoutMs: 5000, maxOutputBytes: 4096 });
340
+ if (run.code !== 0) return fallback;
341
+ const windowId = String(run.stdout || '').split(/\r?\n/).map((line) => line.trim()).find((line) => /^@\d+$/.test(line));
342
+ return windowId || fallback;
343
+ }
344
+
345
+ function tmuxLayoutName(value = 'tiled') {
346
+ const layout = String(value || 'tiled').trim();
347
+ return /^(tiled|even-horizontal|even-vertical|main-horizontal|main-horizontal-mirrored|main-vertical|main-vertical-mirrored)$/.test(layout)
348
+ ? layout
349
+ : 'tiled';
350
+ }
351
+
352
+ async function enableTmuxDynamicResize(tmuxBin, session, opts = {}) {
353
+ const layout = tmuxLayoutName(opts.layout || 'tiled');
354
+ const safeSession = sanitizeTmuxSessionName(session);
355
+ const target = await tmuxWindowTarget(tmuxBin, safeSession);
356
+ const relayout = `resize-window -t ${target} -A; set-window-option -t ${target} window-size latest; select-layout -t ${target} ${layout}; select-layout -t ${target} -E; set-window-option -t ${target} window-size latest`;
357
+ const commands = [
358
+ ['set-window-option', '-t', target, 'window-size', 'latest'],
359
+ ['set-window-option', '-t', target, 'aggressive-resize', 'on'],
360
+ ['set-hook', '-t', safeSession, 'client-attached', relayout],
361
+ ['set-hook', '-t', safeSession, 'client-resized', relayout],
362
+ ['resize-window', '-t', target, '-A'],
363
+ ['set-window-option', '-t', target, 'window-size', 'latest'],
364
+ ['select-layout', '-t', target, layout],
365
+ ['select-layout', '-t', target, '-E'],
366
+ ['set-window-option', '-t', target, 'window-size', 'latest']
367
+ ];
368
+ const applied = [];
369
+ const failed = [];
370
+ for (const args of commands) {
371
+ const run = await tmuxRun(tmuxBin, args, { timeoutMs: 5000 });
372
+ const command = [path.basename(tmuxBin), ...args].join(' ');
373
+ if (run.code === 0) applied.push(command);
374
+ else failed.push({ command, stderr: run.stderr || run.stdout || 'tmux command failed' });
375
+ }
376
+ return { enabled: failed.length === 0, layout, applied, failed };
377
+ }
378
+
399
379
  export function buildTmuxOpenArgs(plan = {}) {
400
380
  return ['attach-session', '-t', sanitizeTmuxSessionName(plan.session || plan.workspace || defaultTmuxSessionName(plan.root))];
401
381
  }
@@ -445,19 +425,19 @@ export async function createTmuxSession(plan = {}, panes = [], opts = {}) {
445
425
  }
446
426
  }
447
427
  const first = normalizedPanes[0] || { cwd: root, command: 'pwd' };
448
- const detachedWidth = String(Math.max(120, Number(opts.width || opts.detachedWidth) || 180));
449
- const detachedHeight = String(Math.max(36, Number(opts.height || opts.detachedHeight) || 48));
450
- const create = await tmuxRun(tmuxBin, ['new-session', '-d', '-x', detachedWidth, '-y', detachedHeight, '-s', session, '-c', path.resolve(first.cwd || root), '-n', 'sks', '-P', '-F', '#{pane_id}', first.command || 'pwd']);
428
+ const dimensions = currentTerminalDimensions(opts);
429
+ const layout = tmuxLayoutName(opts.layout || 'tiled');
430
+ const create = await tmuxRun(tmuxBin, ['new-session', '-d', '-x', dimensions.width, '-y', dimensions.height, '-s', session, '-c', path.resolve(first.cwd || root), '-n', 'sks', '-P', '-F', '#{pane_id}', first.command || 'pwd']);
451
431
  if (create.code !== 0) return { ok: false, session, panes: [], stderr: create.stderr || create.stdout || 'tmux new-session failed' };
452
432
  const created = [{ pane_id: paneId(create.stdout), role: first.role || 'overview', title: first.title || 'overview' }];
453
433
  for (const pane of normalizedPanes.slice(1)) {
454
434
  const split = await tmuxRun(tmuxBin, ['split-window', '-t', session, pane.vertical ? '-v' : '-h', '-d', '-P', '-F', '#{pane_id}', '-c', path.resolve(pane.cwd || root), pane.command || 'pwd']);
455
435
  if (split.code !== 0) return { ok: false, session, panes: created, stderr: split.stderr || split.stdout || 'tmux split-window failed' };
456
436
  created.push({ pane_id: paneId(split.stdout), role: pane.role || 'lane', title: pane.title || null });
457
- await tmuxRun(tmuxBin, ['select-layout', '-t', session, opts.layout || 'tiled']).catch(() => null);
437
+ await tmuxRun(tmuxBin, ['select-layout', '-t', session, layout]).catch(() => null);
458
438
  }
459
- await tmuxRun(tmuxBin, ['select-layout', '-t', session, opts.layout || 'tiled']).catch(() => null);
460
- return { ok: true, reused: false, session, panes: created, attach_command: `tmux attach-session -t ${session}` };
439
+ const dynamic_resize = await enableTmuxDynamicResize(tmuxBin, session, { layout });
440
+ return { ok: true, reused: false, session, panes: created, attach_command: `tmux attach-session -t ${session}`, layout, initial_size: dimensions, dynamic_resize };
461
441
  }
462
442
 
463
443
  export async function launchTmuxUi(args = [], opts = {}) {
@@ -593,6 +573,9 @@ export async function launchTmuxTeamView({ root, missionId, plan = {}, promptFil
593
573
  mode: 'single_window_split_panes',
594
574
  window: 'sks',
595
575
  layout: 'tiled',
576
+ dynamic_resize: true,
577
+ window_size: 'latest',
578
+ resize_hooks: ['client-attached', 'client-resized'],
596
579
  live_updates: true,
597
580
  panes_show: ['overview', 'scout', 'planning', 'execution', 'review', 'safety'],
598
581
  user_attach_command: launch.attach_command