sneakoscope 0.7.26 → 0.7.33

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
@@ -11,11 +11,10 @@ Install globally, then run `sks` from either a project or any global shell locat
11
11
  ```sh
12
12
  npm i -g sneakoscope
13
13
  sks root
14
- sks bootstrap
15
14
  sks
16
15
  ```
17
16
 
18
- `sks root` tells you whether SKS found a project root or is using the per-user global runtime root. Outside a repo/project marker, runtime commands such as `sks`, `sks deps check`, `sks pipeline status`, and `sks team ...` use that global root instead of writing `.sneakoscope` into the random current directory.
17
+ `npm i -g sneakoscope` automatically refreshes the `sks` command shim, global Codex App `$` skills, and SKS bootstrap surface. When the install is run from a project, postinstall bootstraps that project. When it is run outside a repo/project marker, postinstall bootstraps the per-user global runtime root instead of writing `.sneakoscope` into a random current directory. `sks root` tells you which root SKS will use.
19
18
 
20
19
  If you only want a one-shot run without keeping `sks` installed globally:
21
20
 
@@ -43,7 +42,7 @@ sks selftest --mock
43
42
 
44
43
  | Area | What it does |
45
44
  | --- | --- |
46
- | CLI runtime | `sks tmux open` and `sks --mad` explicitly launch Codex CLI with tmux; bare `sks` only prints help/readiness surfaces. |
45
+ | CLI runtime | Bare `sks` opens or reuses the default tmux Codex CLI workspace. `sks tmux open` remains the explicit form for session/workspace flags, and `sks --mad` launches the high-reasoning auto-review profile. |
47
46
  | Codex App commands | Installs generated skills so `$Team`, `$From-Chat-IMG`, `$DFix`, `$QA-LOOP`, `$PPT`, `$Goal`, `$DB`, `$Wiki`, `$Help`, and related routes are visible in prompt workflows. |
48
47
  | OpenClaw agents | Generates an OpenClaw skill package so OpenClaw agents can attach `sneakoscope-codex`, enable the `shell` tool, and discover/use SKS commands from the target repo root. |
49
48
  | Pipeline plans | Writes `pipeline-plan.json` for stateful routes so the runtime lane, kept stages, skipped stages, verification commands, and no-unrequested-fallback invariant are visible with `sks pipeline plan`. |
@@ -75,9 +74,9 @@ Install tmux from [tmux.dev/download](https://www.tmux.dev/download). On macOS,
75
74
  brew install tmux
76
75
  ```
77
76
 
78
- `sks --mad` is stricter than the normal runtime path:
77
+ The default `sks` runtime checks npm for newer `sneakoscope` and `@openai/codex` versions before opening tmux and prompts to update when the terminal can answer y/n. If you approve the Codex CLI update, SKS installs `@openai/codex@latest` and opens tmux with the version visible on PATH. `sks --mad` is stricter than the normal runtime path:
79
78
 
80
- - Checks npm for a newer `sneakoscope` before launch and asks whether to update when the terminal can answer y/n.
79
+ - Checks npm for newer `sneakoscope` and `@openai/codex` versions before launch and asks whether to update when the terminal can answer y/n.
81
80
  - Installs the latest Codex CLI with `npm i -g @openai/codex@latest` when it is missing and you approve or pass `--yes`.
82
81
  - Requires tmux 3.x or newer before opening the session.
83
82
  - Creates or reuses a named detached tmux session, splits panes, and prints the attach command.
@@ -91,10 +90,9 @@ Use this when you want `sks` available from any repo:
91
90
  ```sh
92
91
  npm i -g sneakoscope
93
92
  sks root
94
- sks bootstrap
95
93
  ```
96
94
 
97
- `sks` commands work even when no project root is present. Project-aware commands use the nearest `.sneakoscope`, `.dcodex`, or `.git` root; if none exists, SKS uses a per-user global runtime root. `sks bootstrap` still initializes the current project when you want project-local hooks, skills, and TriWiki state.
95
+ `sks` commands work even when no project root is present. Project-aware commands use the nearest `.sneakoscope`, `.dcodex`, or `.git` root; if none exists, SKS uses a per-user global runtime root. Global npm install/upgrade automatically bootstraps the current project when a project marker is present, otherwise it bootstraps the global runtime root. Run `sks bootstrap` manually only when you intentionally want to initialize or repair the current project after install.
98
96
 
99
97
  Project setup writes shared `.gitignore` entries for generated SKS files: `.sneakoscope/`, `.codex/`, `.agents/`, and managed `AGENTS.md`. Setup, doctor repair, and npm postinstall refreshes also compare the previous SKS generated-file manifest with the current package templates and prune stale SKS-generated legacy skills or agent files while preserving user-owned custom skills. Use `sks setup --local-only` when you want those excludes kept only in `.git/info/exclude`.
100
98
 
@@ -162,12 +160,15 @@ sks fix-path
162
160
  ### Open Codex CLI With tmux
163
161
 
164
162
  ```sh
163
+ sks
165
164
  sks tmux open
166
165
  sks tmux check
167
166
  sks tmux status --once
168
167
  ```
169
168
 
170
- `sks tmux open` creates or reuses a named tmux session for Codex CLI only when that is explicitly requested. `sks` and `sks tmux check` are diagnostic/help surfaces and do not start a workspace.
169
+ Bare `sks` creates or reuses the default named tmux session for Codex CLI. Use `sks tmux open` when you need explicit `--workspace` / `--session` flags, `sks tmux check` for readiness without launching, and `sks help` for CLI help.
170
+
171
+ Before opening tmux, SKS checks the installed Codex CLI against npm `@openai/codex@latest`. If a newer version exists, it asks `Y/n`; answering `y` updates automatically with `npm i -g @openai/codex@latest` and then opens tmux with the updated Codex CLI.
171
172
 
172
173
  ### MAD tmux Launch
173
174
 
@@ -331,10 +332,14 @@ agents:
331
332
  coding-agent:
332
333
  tools:
333
334
  - shell
335
+ env:
336
+ SKS_OPENCLAW: "1"
334
337
  skills:
335
338
  - sneakoscope-codex
336
339
  ```
337
340
 
341
+ `SKS_OPENCLAW=1` tells SKS that commands are running from OpenClaw. In that mode, SKS auto-approves update/install prompts such as the Codex CLI update check before tmux launch, instead of waiting for a human `Y/n` response.
342
+
338
343
  Then prompt the OpenClaw agent from the target repo root:
339
344
 
340
345
  ```text
@@ -344,11 +349,11 @@ Run sks root, inspect AGENTS.md, then use the SKS Team route to implement this f
344
349
  Useful commands for OpenClaw agents:
345
350
 
346
351
  ```sh
347
- sks root
348
- sks commands
349
- sks dollar-commands
350
- sks deps check
351
- sks proof-field scan --intent "small CLI change" --changed src/cli/main.mjs
352
+ SKS_OPENCLAW=1 sks root
353
+ SKS_OPENCLAW=1 sks commands
354
+ SKS_OPENCLAW=1 sks dollar-commands
355
+ SKS_OPENCLAW=1 sks deps check
356
+ SKS_OPENCLAW=1 sks proof-field scan --intent "small CLI change" --changed src/cli/main.mjs
352
357
  ```
353
358
 
354
359
  If OpenClaw runs the skill inside a sandbox, grant shell execution only for the trusted local workspace. Database, Supabase, migration, and destructive filesystem work should still follow the repo's SKS safety route and require explicit write scope.
@@ -393,9 +398,11 @@ sks selftest --mock
393
398
 
394
399
  ```sh
395
400
  sks tmux check
396
- sks tmux open
401
+ sks
397
402
  ```
398
403
 
404
+ `sks tmux open` is the equivalent explicit launch form when you want to pass tmux session flags.
405
+
399
406
  For the high-reasoning full-access profile:
400
407
 
401
408
  ```sh
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.7.26",
4
+ "version": "0.7.33",
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",
@@ -3,7 +3,7 @@ import os from 'node:os';
3
3
  import fsp from 'node:fs/promises';
4
4
  import readline from 'node:readline/promises';
5
5
  import { stdin as input, stdout as output } from 'node:process';
6
- import { ensureDir, exists, packageRoot, runProcess, which, writeTextAtomic } from '../core/fsx.mjs';
6
+ import { ensureDir, exists, globalSksRoot, packageRoot, runProcess, which, writeTextAtomic } from '../core/fsx.mjs';
7
7
  import { getCodexInfo } from '../core/codex-adapter.mjs';
8
8
  import { formatHarnessConflictReport, llmHarnessCleanupPrompt, scanHarnessConflicts } from '../core/harness-conflicts.mjs';
9
9
  import { installSkills } from '../core/init.mjs';
@@ -83,15 +83,20 @@ function shouldAskPostinstallQuestion() {
83
83
  export async function postinstallBootstrapDecision(root) {
84
84
  if (process.env.SKS_POSTINSTALL_NO_BOOTSTRAP === '1') return { run: false, reason: 'SKS_POSTINSTALL_NO_BOOTSTRAP=1' };
85
85
  if (process.env.SKS_POSTINSTALL_BOOTSTRAP === '0') return { run: false, reason: 'SKS_POSTINSTALL_BOOTSTRAP=0' };
86
- const candidate = await isProjectSetupCandidate(path.resolve(root || process.cwd()));
87
- if (!candidate && process.env.SKS_POSTINSTALL_BOOTSTRAP !== '1') return { run: false, reason: 'no project marker found in install cwd' };
88
- if (process.env.SKS_POSTINSTALL_BOOTSTRAP === '1') return { run: true, reason: 'forced by SKS_POSTINSTALL_BOOTSTRAP=1' };
89
- return { run: true, reason: 'auto-running sks setup --bootstrap --install-scope global --force' };
86
+ const installRoot = path.resolve(root || process.cwd());
87
+ const candidate = await isProjectSetupCandidate(installRoot);
88
+ const target = candidate ? installRoot : globalSksRoot();
89
+ if (process.env.SKS_POSTINSTALL_BOOTSTRAP === '1') return { run: true, target, reason: 'forced by SKS_POSTINSTALL_BOOTSTRAP=1' };
90
+ if (candidate) return { run: true, target, reason: 'auto-running sks setup --bootstrap --install-scope global --force' };
91
+ return { run: true, target, reason: 'no project marker found; auto-running global SKS runtime bootstrap' };
90
92
  }
91
93
 
92
94
  async function runPostinstallBootstrap(root, bootstrap) {
93
95
  const previousCwd = process.cwd();
94
- process.chdir(path.resolve(root || previousCwd));
96
+ const decision = await postinstallBootstrapDecision(root);
97
+ const target = path.resolve(decision.target || root || previousCwd);
98
+ await ensureDir(target);
99
+ process.chdir(target);
95
100
  try {
96
101
  await bootstrap(['--from-postinstall', '--install-scope', 'global', '--force']);
97
102
  } finally {
@@ -254,6 +259,88 @@ export async function ensureCodexCliTool({ skip = false } = {}) {
254
259
  };
255
260
  }
256
261
 
262
+ export async function maybePromptCodexUpdateForLaunch(args = [], opts = {}) {
263
+ if (hasFlag(args, '--json') || hasFlag(args, '--skip-cli-tools') || hasFlag(args, '--skip-codex-update') || process.env.SKS_SKIP_CODEX_UPDATE === '1') return { status: 'skipped' };
264
+ const latest = await npmPackageVersion('@openai/codex');
265
+ const codex = await getCodexInfo().catch(() => ({}));
266
+ const current = codexCliVersionNumber(codex.version);
267
+ const command = 'npm i -g @openai/codex@latest';
268
+ const label = opts.label || 'tmux launch';
269
+ const missing = !codex.bin;
270
+ const updateAvailable = Boolean(latest.version && current && compareVersions(latest.version, current) > 0);
271
+ if (!missing && !updateAvailable) return { status: 'current', latest: latest.version || null, current, bin: codex.bin || null, error: latest.error || null };
272
+ const prompt = missing
273
+ ? `Codex CLI missing. Install @openai/codex${latest.version ? ` ${latest.version}` : '@latest'} before ${label}? [Y/n] `
274
+ : `Codex CLI ${current} -> ${latest.version} update before ${label}? [Y/n] `;
275
+ if (shouldAutoApproveInstall(args)) return installCodexLatest(command, latest.version, current);
276
+ if (!canAskYesNo()) {
277
+ const reason = missing ? 'Codex CLI missing' : `Codex CLI update available: ${current} -> ${latest.version}`;
278
+ console.log(`${reason}. Run: ${command}`);
279
+ return { status: missing ? 'missing' : 'available', latest: latest.version || null, current, command, bin: codex.bin || null };
280
+ }
281
+ const answer = (await askPostinstallQuestion(prompt)).trim();
282
+ const yes = answer === '' || /^(y|yes|예|네|응)$/i.test(answer);
283
+ if (!yes) return { status: 'skipped_by_user', latest: latest.version || null, current, command, bin: codex.bin || null };
284
+ return installCodexLatest(command, latest.version, current);
285
+ }
286
+
287
+ export function shouldAutoApproveInstall(args = [], env = process.env) {
288
+ return hasFlag(args, '--yes') || hasFlag(args, '-y') || isOpenClawRuntime(env);
289
+ }
290
+
291
+ function canAskYesNo() {
292
+ return Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
293
+ }
294
+
295
+ function hasFlag(args = [], name) {
296
+ return args.includes(name);
297
+ }
298
+
299
+ function isOpenClawRuntime(env = process.env) {
300
+ return ['SKS_OPENCLAW', 'OPENCLAW', 'OPENCLAW_AGENT', 'OPENCLAW_RUN_ID', 'OPENCLAW_SESSION_ID']
301
+ .some((key) => /^(1|true|yes|y)$/i.test(String(env[key] || '').trim()));
302
+ }
303
+
304
+ async function installCodexLatest(command, latestVersion, previousVersion = null) {
305
+ const npm = await which('npm').catch(() => null);
306
+ if (!npm) return { status: 'failed', latest: latestVersion || null, previous: previousVersion || null, command, error: 'npm not found on PATH' };
307
+ const install = await runProcess(npm, ['i', '-g', '@openai/codex@latest'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
308
+ if (install.code !== 0) return { status: 'failed', latest: latestVersion || null, previous: previousVersion || null, command, error: `${install.stderr || install.stdout || command + ' failed'}`.trim() };
309
+ const after = await getCodexInfo().catch(() => ({}));
310
+ const afterVersion = codexCliVersionNumber(after.version);
311
+ if (!after.bin) return { status: 'updated_not_reflected', latest: latestVersion || null, previous: previousVersion || null, version: afterVersion || null, command, error: 'npm completed, but codex is not on PATH. Restart the shell or set SKS_CODEX_BIN.' };
312
+ if (latestVersion && afterVersion && compareVersions(afterVersion, latestVersion) < 0) {
313
+ return { status: 'updated_not_reflected', latest: latestVersion, previous: previousVersion || null, version: afterVersion, bin: after.bin, command, error: `npm completed, but PATH still resolves Codex CLI ${afterVersion}; expected ${latestVersion}.` };
314
+ }
315
+ console.log(`Codex CLI ready: ${previousVersion || 'missing'} -> ${after.version || after.bin}`);
316
+ return { status: previousVersion ? 'updated' : 'installed', latest: latestVersion || null, previous: previousVersion || null, version: afterVersion || null, raw_version: after.version || null, bin: after.bin || null, command };
317
+ }
318
+
319
+ function codexCliVersionNumber(versionText = '') {
320
+ const match = String(versionText || '').match(/(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
321
+ return match ? match[1] : null;
322
+ }
323
+
324
+ async function npmPackageVersion(name) {
325
+ const envName = `SKS_NPM_VIEW_${String(name || '').replace(/[^A-Za-z0-9]+/g, '_').toUpperCase()}_VERSION`;
326
+ if (process.env[envName]) return { version: process.env[envName] };
327
+ const npm = await which('npm').catch(() => null);
328
+ if (!npm) return { error: 'npm not found' };
329
+ const result = await runProcess(npm, ['view', name, 'version'], { timeoutMs: 5000, maxOutputBytes: 4096 });
330
+ if (result.code !== 0) return { error: `${result.stderr || result.stdout || 'npm view failed'}`.trim() };
331
+ return { version: result.stdout.trim().split(/\s+/).pop() };
332
+ }
333
+
334
+ function compareVersions(a, b) {
335
+ const pa = String(a || '').split(/[.-]/).map((x) => Number.parseInt(x, 10) || 0);
336
+ const pb = String(b || '').split(/[.-]/).map((x) => Number.parseInt(x, 10) || 0);
337
+ for (let i = 0; i < Math.max(pa.length, pb.length, 3); i++) {
338
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
339
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
340
+ }
341
+ return 0;
342
+ }
343
+
257
344
  async function isProjectSetupCandidate(root) {
258
345
  const markers = ['package.json', '.git', 'AGENTS.md', '.codex', '.sneakoscope'];
259
346
  for (const marker of markers) {
package/src/cli/main.mjs CHANGED
@@ -60,7 +60,7 @@ import { OPENCLAW_SKILL_NAME, installOpenClawSkill } from '../core/openclaw.mjs'
60
60
  import { buildTmuxLaunchPlan, buildTmuxOpenArgs, createTmuxSession, isTmuxShellSession, runTmuxLaunchPlanSyntaxCheck, tmuxReadiness, tmuxStatusKind, defaultTmuxSessionName, formatTmuxBanner, launchTmuxTeamView, launchTmuxUi, platformTmuxInstallHint, runTmuxStatus, sanitizeTmuxSessionName, teamLaneStyle } from '../core/tmux-ui.mjs';
61
61
  import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
62
62
  import { context7Command } from './context7-command.mjs';
63
- import { askPostinstallQuestion, checkContext7, checkRequiredSkills, ensureCodexCliTool, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, globalCodexSkillsRoot, postinstall, postinstallBootstrapDecision } from './install-helpers.mjs';
63
+ import { askPostinstallQuestion, checkContext7, checkRequiredSkills, ensureCodexCliTool, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureRelatedCliTools, ensureSksCommandDuringInstall, globalCodexSkillsRoot, maybePromptCodexUpdateForLaunch, postinstall, postinstallBootstrapDecision, shouldAutoApproveInstall } from './install-helpers.mjs';
64
64
  import { buildTeamPlan, codeStructureCommand, dbCommand, defaultBeta, defaultVGraph, evalCommand, gcCommand, goalCommand, gxCommand, harnessCommand, hproofCommand, memoryCommand, migrateWikiContextPack, parseTeamCreateArgs, perfCommand, profileCommand, projectWikiClaims, proofFieldCommand, qaLoopCommand, quickstartCommand, researchCommand, skillDreamCommand, statsCommand, team, teamWorkflowMarkdown, validateArtifactsCommand, wikiCommand, wikiVoxelRowCount, writeWikiContextPack } from './maintenance-commands.mjs';
65
65
  import { openClawCommand } from './openclaw-command.mjs';
66
66
 
@@ -82,7 +82,7 @@ export async function main(args) {
82
82
  if (isAutoReviewFlag(args[0])) return autoReviewCommand('start', args.slice(1));
83
83
  const [cmd, sub, ...rest] = args;
84
84
  const tail = sub === undefined ? [] : [sub, ...rest];
85
- if (!cmd) return help();
85
+ if (!cmd) return defaultTmuxCommand();
86
86
  if (cmd === '--help' || cmd === '-h') return help();
87
87
  if (cmd === '--version' || cmd === '-v' || cmd === 'version') return version();
88
88
  if (cmd === 'tmux') return !sub || String(sub).startsWith('--') ? tmuxCommand('check', tail) : tmuxCommand(sub, rest);
@@ -100,6 +100,26 @@ export async function main(args) {
100
100
  process.exitCode = 1;
101
101
  }
102
102
 
103
+ async function defaultTmuxCommand(args = []) {
104
+ const update = await maybePromptSksUpdateForLaunch(args, { label: 'default tmux launch' });
105
+ if (update.status === 'updated') {
106
+ console.log(`SKS updated from ${PACKAGE_VERSION} to ${update.latest}. Rerun: sks`);
107
+ return;
108
+ }
109
+ if (update.status === 'failed') {
110
+ console.error(`SKS update failed: ${update.error}`);
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ const codexUpdate = await maybePromptCodexUpdateForLaunch(args, { label: 'default tmux launch' });
115
+ if (codexUpdate.status === 'failed' || codexUpdate.status === 'updated_not_reflected') {
116
+ console.error(`Codex CLI update failed: ${codexUpdate.error || 'updated version was not visible on PATH'}`);
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ return launchTmuxUi(args, { conciseBlockers: true });
121
+ }
122
+
103
123
  function help(args = []) {
104
124
  const topic = args[0];
105
125
  if (topic) return usage([topic]);
@@ -107,6 +127,7 @@ function help(args = []) {
107
127
  Sneakoscope Codex
108
128
 
109
129
  Usage:
130
+ sks
110
131
  sks help [topic]
111
132
  sks version
112
133
  sks update-check [--json]
@@ -873,6 +894,12 @@ async function tmuxCommand(sub = 'start', args = []) {
873
894
  return;
874
895
  }
875
896
  if (['start', 'attach', 'connect', 'open'].includes(action)) {
897
+ const codexUpdate = await maybePromptCodexUpdateForLaunch(args, { label: 'tmux launch' });
898
+ if (codexUpdate.status === 'failed' || codexUpdate.status === 'updated_not_reflected') {
899
+ console.error(`Codex CLI update failed: ${codexUpdate.error || 'updated version was not visible on PATH'}`);
900
+ process.exitCode = 1;
901
+ return;
902
+ }
876
903
  const result = await launchTmuxUi(args);
877
904
  if (flag(args, '--json')) console.log(JSON.stringify(result, null, 2));
878
905
  return;
@@ -887,7 +914,7 @@ async function madHighCommand(args = []) {
887
914
  const profile = await enableMadHighProfile();
888
915
  return console.log(JSON.stringify(profile, null, 2));
889
916
  }
890
- const update = await maybePromptSksUpdateForMad(args);
917
+ const update = await maybePromptSksUpdateForLaunch(args, { label: 'MAD launch' });
891
918
  if (update.status === 'updated') {
892
919
  console.log(`SKS updated from ${PACKAGE_VERSION} to ${update.latest}. Rerun: sks --mad`);
893
920
  return;
@@ -897,6 +924,12 @@ async function madHighCommand(args = []) {
897
924
  process.exitCode = 1;
898
925
  return;
899
926
  }
927
+ const codexUpdate = await maybePromptCodexUpdateForLaunch(args, { label: 'MAD launch' });
928
+ if (codexUpdate.status === 'failed' || codexUpdate.status === 'updated_not_reflected') {
929
+ console.error(`Codex CLI update failed: ${codexUpdate.error || 'updated version was not visible on PATH'}`);
930
+ process.exitCode = 1;
931
+ return;
932
+ }
900
933
  const deps = await ensureMadLaunchDependencies(args);
901
934
  if (!deps.ready) {
902
935
  console.error('SKS MAD launch blocked by missing dependencies.');
@@ -915,18 +948,19 @@ async function madHighCommand(args = []) {
915
948
  });
916
949
  }
917
950
 
918
- async function maybePromptSksUpdateForMad(args = []) {
951
+ async function maybePromptSksUpdateForLaunch(args = [], opts = {}) {
919
952
  if (flag(args, '--json') || flag(args, '--skip-update-check') || process.env.SKS_SKIP_UPDATE_CHECK === '1') return { status: 'skipped' };
920
953
  const latest = await npmPackageVersion('sneakoscope');
921
954
  const currentPackage = await effectivePackageVersion();
922
955
  if (!latest.version || compareVersions(latest.version, currentPackage) <= 0) return { status: 'current', latest: latest.version || null, error: latest.error || null };
923
956
  const command = 'npm i -g sneakoscope@latest';
924
- if (flag(args, '--yes') || flag(args, '-y')) return installSksLatest(command, latest.version);
957
+ if (shouldAutoApproveInstall(args)) return installSksLatest(command, latest.version);
925
958
  if (!canAskYesNo()) {
926
959
  console.log(`SKS update available: ${currentPackage} -> ${latest.version}. Run: ${command}`);
927
960
  return { status: 'available', latest: latest.version, command };
928
961
  }
929
- const answer = (await askPostinstallQuestion(`SKS ${currentPackage} -> ${latest.version} update before MAD launch? [Y/n] `)).trim();
962
+ const label = opts.label || 'launch';
963
+ const answer = (await askPostinstallQuestion(`SKS ${currentPackage} -> ${latest.version} update before ${label}? [Y/n] `)).trim();
930
964
  const yes = answer === '' || /^(y|yes|예|네|응)$/i.test(answer);
931
965
  if (!yes) return { status: 'skipped_by_user', latest: latest.version, command };
932
966
  return installSksLatest(command, latest.version);
@@ -1074,7 +1108,7 @@ async function installTmuxDependency(args = []) {
1074
1108
  }
1075
1109
 
1076
1110
  async function confirmInstall(question, args = []) {
1077
- if (flag(args, '--yes') || flag(args, '-y')) return true;
1111
+ if (shouldAutoApproveInstall(args)) return true;
1078
1112
  if (!canAskYesNo()) return false;
1079
1113
  return /^(y|yes|예|네|응)$/i.test((await askPostinstallQuestion(`${question} [y/N] `)).trim());
1080
1114
  }
@@ -1119,6 +1153,12 @@ async function autoReviewCommand(sub = 'status', args = []) {
1119
1153
  const status = await enableAutoReview({ high });
1120
1154
  if (flag(args, '--json')) return console.log(JSON.stringify(status, null, 2));
1121
1155
  console.log(`SKS Auto-Review enabled: ${profile}`);
1156
+ const codexUpdate = await maybePromptCodexUpdateForLaunch(args, { label: 'auto-review tmux launch' });
1157
+ if (codexUpdate.status === 'failed' || codexUpdate.status === 'updated_not_reflected') {
1158
+ console.error(`Codex CLI update failed: ${codexUpdate.error || 'updated version was not visible on PATH'}`);
1159
+ process.exitCode = 1;
1160
+ return;
1161
+ }
1122
1162
  const sessionArg = readOption(cleanArgs, '--session', null);
1123
1163
  const session = sessionArg || sanitizeTmuxSessionName(`${profile}-${defaultTmuxSessionName(process.cwd())}`);
1124
1164
  return launchTmuxUi([...cleanArgs, '--session', session], { codexArgs: ['--profile', profile] });
@@ -1179,6 +1219,7 @@ Codex App prompt commands:
1179
1219
  ${formatDollarCommandsCompact(' ')}
1180
1220
 
1181
1221
  Examples:
1222
+ sks
1182
1223
  sks setup
1183
1224
  sneakoscope setup
1184
1225
  sks commands
@@ -1194,7 +1235,7 @@ function usage(args = []) {
1194
1235
  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.'],
1195
1236
  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.'],
1196
1237
  deps: ['Dependencies', '', ' sks deps check [--json]', ' sks deps install [tmux|codex|context7|all] [--yes]', '', 'tmux on macOS uses Homebrew only after approval.'],
1197
- tmux: ['tmux', '', ' sks tmux open', ' sks tmux check', ' sks tmux status --once', ' sks deps install tmux', '', 'tmux launch is explicit. Running bare `sks` prints help and never opens tmux by itself.'],
1238
+ tmux: ['tmux', '', ' sks', ' sks tmux open', ' sks tmux check', ' sks tmux status --once', ' sks deps install tmux', '', 'Running bare `sks` opens or reuses the default tmux Codex CLI session. Before launch, SKS checks npm @openai/codex@latest and prompts Y/n when the installed Codex CLI is missing or outdated. Use `sks tmux open` when you need explicit session/workspace flags, and `sks help` for CLI help.'],
1198
1239
  openclaw: ['OpenClaw', '', ' sks openclaw install', ' sks openclaw path', ' sks openclaw print SKILL.md', '', 'Installs an OpenClaw skill package under ~/.openclaw/skills/sneakoscope-codex so OpenClaw agents can attach skills: [sneakoscope-codex] with the shell tool and call local SKS commands from a project root.'],
1199
1240
  team: ['Team', '', ' sks team "task" executor:5 reviewer:2 user:1', ' sks team watch latest', ' sks team lane latest --agent analysis_scout_1 --follow', ' sks team message latest --from analysis_scout_1 --to executor_1 --message "handoff note"', ' sks team cleanup-tmux latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
1200
1241
  'qa-loop': ['QA-LOOP', '', ' sks qa-loop prepare "QA this app"', ' sks qa-loop answer <MISSION_ID> answers.json', ' sks qa-loop run <MISSION_ID> --max-cycles 8', '', 'Report: YYYY-MM-DD-v<version>-qa-report.md'],
@@ -1844,6 +1885,16 @@ async function selftest() {
1844
1885
  if (oldNoBootstrap === undefined) delete process.env.SKS_POSTINSTALL_NO_BOOTSTRAP;
1845
1886
  else process.env.SKS_POSTINSTALL_NO_BOOTSTRAP = oldNoBootstrap;
1846
1887
  if (noBootstrapDecision.run || noBootstrapDecision.reason !== 'SKS_POSTINSTALL_NO_BOOTSTRAP=1') throw new Error('selftest failed: postinstall bootstrap opt-out decision');
1888
+ const postinstallNoMarkerTmp = tmpdir();
1889
+ const postinstallNoMarkerHome = path.join(postinstallNoMarkerTmp, 'home');
1890
+ const postinstallNoMarkerCwd = path.join(postinstallNoMarkerTmp, 'cwd');
1891
+ const postinstallNoMarkerGlobalRoot = path.join(postinstallNoMarkerTmp, 'global-root');
1892
+ await ensureDir(postinstallNoMarkerCwd);
1893
+ const postinstallNoMarker = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'postinstall'], { cwd: postinstallNoMarkerCwd, env: { INIT_CWD: postinstallNoMarkerCwd, HOME: postinstallNoMarkerHome, SKS_GLOBAL_ROOT: postinstallNoMarkerGlobalRoot, SKS_SKIP_POSTINSTALL_SHIM: '1', SKS_SKIP_POSTINSTALL_CONTEXT7: '1', SKS_SKIP_POSTINSTALL_GETDESIGN: '1', SKS_SKIP_CLI_TOOLS: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
1894
+ if (postinstallNoMarker.code !== 0) throw new Error(`selftest failed: no-marker postinstall bootstrap exited ${postinstallNoMarker.code}: ${postinstallNoMarker.stderr}`);
1895
+ if (!String(postinstallNoMarker.stdout || '').includes('no project marker found; auto-running global SKS runtime bootstrap')) throw new Error('selftest failed: no-marker postinstall did not report global runtime bootstrap');
1896
+ if (!(await exists(path.join(postinstallNoMarkerGlobalRoot, '.sneakoscope', 'manifest.json')))) throw new Error('selftest failed: no-marker postinstall did not bootstrap global runtime root');
1897
+ if (await exists(path.join(postinstallNoMarkerCwd, '.sneakoscope'))) throw new Error('selftest failed: no-marker postinstall polluted install cwd');
1847
1898
  const bootstrapJsonTmp = tmpdir();
1848
1899
  await writeJsonAtomic(path.join(bootstrapJsonTmp, 'package.json'), { name: 'bootstrap-json-smoke', version: '0.0.0' });
1849
1900
  const bootstrapJson = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'bootstrap', '--json'], { cwd: bootstrapJsonTmp, env: { HOME: path.join(bootstrapJsonTmp, 'home'), SKS_SKIP_POSTINSTALL_GLOBAL_SKILLS: '1', SKS_SKIP_CLI_TOOLS: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
@@ -1876,6 +1927,39 @@ async function selftest() {
1876
1927
  if (tmuxOpenArgs.join(' ') !== 'attach-session -t sks-mad-selftest') throw new Error('selftest failed: MAD tmux attach args are not stable by session name');
1877
1928
  if (!isTmuxShellSession({ TMUX: '/tmp/tmux-501/default,1,0' })) throw new Error('selftest failed: tmux shell session env was not detected');
1878
1929
  if (tmuxStatusKind({ ok: false, bin: null }) !== 'missing') throw new Error('selftest failed: missing tmux was not labeled missing');
1930
+ const bareDefault = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs')], {
1931
+ cwd: globalCwd,
1932
+ env: { SKS_GLOBAL_ROOT: globalRuntimeRoot, SKS_NPM_VIEW_SNEAKOSCOPE_VERSION: PACKAGE_VERSION, PATH: '' },
1933
+ timeoutMs: 15000,
1934
+ maxOutputBytes: 64 * 1024
1935
+ });
1936
+ if (bareDefault.code !== 1 || !String(bareDefault.stderr || '').includes('SKS tmux launch blocked') || String(bareDefault.stdout || '').includes('Usage:')) throw new Error('selftest failed: bare sks did not route to default tmux launch');
1937
+ const fakeCodexBin = path.join(tmp, 'fake-codex-bin');
1938
+ await ensureDir(fakeCodexBin);
1939
+ const fakeCodexPath = path.join(fakeCodexBin, 'codex');
1940
+ await writeTextAtomic(fakeCodexPath, '#!/bin/sh\necho "codex-cli 0.1.0"\n');
1941
+ await fsp.chmod(fakeCodexPath, 0o755);
1942
+ const codexUpdatePrompt = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs')], {
1943
+ cwd: globalCwd,
1944
+ env: { SKS_GLOBAL_ROOT: globalRuntimeRoot, SKS_NPM_VIEW_SNEAKOSCOPE_VERSION: PACKAGE_VERSION, SKS_NPM_VIEW__OPENAI_CODEX_VERSION: '99.0.0', PATH: fakeCodexBin },
1945
+ timeoutMs: 15000,
1946
+ maxOutputBytes: 64 * 1024
1947
+ });
1948
+ if (!String(codexUpdatePrompt.stdout || '').includes('Codex CLI update available: 0.1.0 -> 99.0.0') || String(codexUpdatePrompt.stdout || '').includes('Usage:')) throw new Error('selftest failed: bare sks did not recommend Codex CLI update before tmux launch');
1949
+ const openClawAutoBin = path.join(tmp, 'openclaw-auto-bin');
1950
+ await ensureDir(openClawAutoBin);
1951
+ const openClawCodexPath = path.join(openClawAutoBin, 'codex');
1952
+ await writeTextAtomic(openClawCodexPath, '#!/bin/sh\necho "codex-cli 0.1.0"\n');
1953
+ await writeTextAtomic(path.join(openClawAutoBin, 'npm'), '#!/bin/sh\nDIR="${0%/*}"\nif [ "$1" = "i" ]; then\n printf \'#!/bin/sh\\necho "codex-cli 99.0.0"\\n\' > "$DIR/codex"\n chmod +x "$DIR/codex"\n exit 0\nfi\necho "unexpected npm $*" >&2\nexit 1\n');
1954
+ await fsp.chmod(openClawCodexPath, 0o755);
1955
+ await fsp.chmod(path.join(openClawAutoBin, 'npm'), 0o755);
1956
+ const openClawAutoUpdate = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs')], {
1957
+ cwd: globalCwd,
1958
+ env: { SKS_GLOBAL_ROOT: globalRuntimeRoot, SKS_OPENCLAW: '1', SKS_NPM_VIEW_SNEAKOSCOPE_VERSION: PACKAGE_VERSION, SKS_NPM_VIEW__OPENAI_CODEX_VERSION: '99.0.0', PATH: openClawAutoBin },
1959
+ timeoutMs: 15000,
1960
+ maxOutputBytes: 64 * 1024
1961
+ });
1962
+ if (!String(openClawAutoUpdate.stdout || '').includes('Codex CLI ready: 0.1.0 -> codex-cli 99.0.0')) throw new Error('selftest failed: OpenClaw mode did not auto-approve Codex CLI update before tmux launch');
1879
1963
  const guardBlocked = await checkHarnessModification(tmp, { tool_name: 'apply_patch', command: '*** Update File: .agents/skills/team/SKILL.md\n+tamper\n' });
1880
1964
  if (guardBlocked.action !== 'block') throw new Error('selftest failed: harness guard allowed skill tampering');
1881
1965
  const setupBlocked = await checkHarnessModification(tmp, { command: 'sks setup --force' });
@@ -2018,6 +2102,9 @@ async function selftest() {
2018
2102
  if (!promptPipelineSkillExists) throw new Error('selftest failed: prompt pipeline skill not installed');
2019
2103
  const promptPipelineText = await safeReadText(path.join(tmp, '.agents', 'skills', 'prompt-pipeline', 'SKILL.md'));
2020
2104
  if (!promptPipelineText.includes('TriWiki context-tracking SSOT')) throw new Error('selftest failed: prompt pipeline missing TriWiki context-tracking SSOT');
2105
+ if (!promptPipelineText.includes('Codex App pipeline activation:') || !promptPipelineText.includes('sks hook user-prompt-submit') || !promptPipelineText.includes('hookSpecificOutput.additionalContext')) throw new Error('selftest failed: prompt pipeline missing Codex App pipeline activation fallback');
2106
+ const teamSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'team', 'SKILL.md'));
2107
+ if (!teamSkillText.includes('Codex App pipeline activation:') || !teamSkillText.includes('sks pipeline status') || !teamSkillText.includes('mission/pipeline artifacts')) throw new Error('selftest failed: Team skill missing pipeline activation fallback');
2021
2108
  if (!promptPipelineText.includes('before every route stage') || !promptPipelineText.includes('sks wiki refresh')) throw new Error('selftest failed: prompt pipeline missing per-stage TriWiki policy');
2022
2109
  if (!promptPipelineText.includes('single design decision authority') || !promptPipelineText.includes('imagegen') || !promptPipelineText.includes('getdesign-reference') || !promptPipelineText.includes(AWESOME_DESIGN_MD_REFERENCE.url) || !promptPipelineText.includes('not parallel authorities')) throw new Error('selftest failed: prompt pipeline missing design SSOT/source-input routing');
2023
2110
  if (!promptPipelineText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL)) throw new Error('selftest failed: prompt pipeline missing Codex App image generation policy');
@@ -2073,9 +2160,9 @@ async function selftest() {
2073
2160
  const openClawSkillText = await safeReadText(path.join(openClawResult.target_dir, 'SKILL.md'));
2074
2161
  const openClawManifestText = await safeReadText(path.join(openClawResult.target_dir, 'manifest.yaml'));
2075
2162
  const openClawConfigText = await safeReadText(path.join(openClawResult.target_dir, 'openclaw-agent-config.example.yaml'));
2076
- if (!openClawSkillText.includes('sks root') || !openClawSkillText.includes('$Team') || !openClawSkillText.includes('OpenClaw agent must have the built-in `shell` tool enabled')) throw new Error('selftest failed: OpenClaw skill missing SKS agent guidance');
2163
+ if (!openClawSkillText.includes('sks root') || !openClawSkillText.includes('$Team') || !openClawSkillText.includes('OpenClaw agent must have the built-in `shell` tool enabled') || !openClawSkillText.includes('SKS_OPENCLAW=1')) throw new Error('selftest failed: OpenClaw skill missing SKS agent guidance');
2077
2164
  if (!openClawManifestText.includes('generated_by: sneakoscope') || !openClawManifestText.includes(`version: ${PACKAGE_VERSION}`)) throw new Error('selftest failed: OpenClaw manifest missing generated marker or version');
2078
- if (!openClawConfigText.includes(`- ${OPENCLAW_SKILL_NAME}`) || !openClawConfigText.includes('- shell')) throw new Error('selftest failed: OpenClaw agent config example missing skill or shell tool');
2165
+ if (!openClawConfigText.includes(`- ${OPENCLAW_SKILL_NAME}`) || !openClawConfigText.includes('- shell') || !openClawConfigText.includes('SKS_OPENCLAW')) throw new Error('selftest failed: OpenClaw agent config example missing skill, shell tool, or OpenClaw env');
2079
2166
  const registryDollarCommands = DOLLAR_COMMANDS.map((c) => c.command);
2080
2167
  const manifest = await readJson(path.join(tmp, '.sneakoscope', 'manifest.json'));
2081
2168
  const policy = await readJson(path.join(tmp, '.sneakoscope', 'policy.json'));
@@ -2151,7 +2238,7 @@ async function selftest() {
2151
2238
  if (String(hookUpdateCurrentContext).includes('Update SKS now') || String(hookUpdateCurrentContext).includes('Skip update for this conversation')) throw new Error('selftest failed: hook prompted for update even though installed SKS is current');
2152
2239
  const hookUpdateCurrentState = await readJson(path.join(hookUpdateCurrentTmp, '.sneakoscope', 'state', 'update-check.json'), {});
2153
2240
  if (hookUpdateCurrentState.pending_offer) throw new Error('selftest failed: current installed SKS left a pending update offer');
2154
- if (hookUpdateCurrentState.current !== '9.9.9' || hookUpdateCurrentState.runtime_current !== PACKAGE_VERSION || hookUpdateCurrentState.installed_current !== '9.9.9') throw new Error('selftest failed: hook did not record effective installed SKS version');
2241
+ if (hookUpdateCurrentState.current !== '9.9.9' || hookUpdateCurrentState.runtime_current !== PACKAGE_VERSION || hookUpdateCurrentState.installed_current !== '9.9.9') throw new Error(`selftest failed: hook did not record effective installed SKS version: ${JSON.stringify({ expected: { current: '9.9.9', runtime_current: PACKAGE_VERSION, installed_current: '9.9.9' }, actual: hookUpdateCurrentState })}`);
2155
2242
  const hookUpdatePendingTmp = tmpdir();
2156
2243
  await initProject(hookUpdatePendingTmp, {});
2157
2244
  await writeJsonAtomic(path.join(hookUpdatePendingTmp, '.sneakoscope', 'state', 'update-check.json'), {
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.26';
8
+ export const PACKAGE_VERSION = '0.7.33';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
 
package/src/core/init.mjs CHANGED
@@ -569,8 +569,8 @@ function codexAppQuickReference(scope, commandPrefix) {
569
569
  `Runtime root: ${commandPrefix} root shows whether SKS is using the nearest project root or the per-user global SKS runtime root; outside any project marker, runtime commands use the global root instead of writing .sneakoscope into the current random directory.`,
570
570
  `Context Tracking: TriWiki SSOT. Before each route phase read only the latest coordinate+voxel overlay pack at .sneakoscope/wiki/context-pack.json; coordinate-only legacy packs are invalid. Use attention.use_first for compact high-trust recall and hydrate attention.hydrate_first from source before risky/lower-trust decisions. During every stage hydrate low-trust claims from source/hash/RGBA anchors; after changes run ${commandPrefix} wiki refresh or pack; before handoff/final run ${commandPrefix} wiki validate .sneakoscope/wiki/context-pack.json.`,
571
571
  stackCurrentDocsPolicyText(commandPrefix),
572
- `Team tmux view: ${commandPrefix} team "task" prepares live watch/lane commands without opening tmux by default; add --open-tmux when you explicitly want a named tmux session with an overview watch pane plus color-coded split per-agent lanes; ${commandPrefix} team lane latest --agent analysis_scout_1 --follow shows one agent's status, assigned runtime tasks, recent agent events, direct messages, and fallback global tail; ${commandPrefix} team message latest --from analysis_scout_1 --to executor_1 --message "handoff note" mirrors bounded agent communication into transcript/lane panes; ${commandPrefix} team cleanup-tmux latest marks the SKS session record complete and asks follow panes to show a cleanup summary then stop.`,
573
- `Runtime: open Codex App once, then run ${commandPrefix} bootstrap, ${commandPrefix} deps check, or ${commandPrefix} tmux open when you explicitly want a tmux/Codex CLI launch.`,
572
+ `Team tmux view: ${commandPrefix} team "task" prepares live watch/lane commands without opening a Team tmux view by default; add --open-tmux when you explicitly want a named Team tmux session with an overview watch pane plus color-coded split per-agent lanes; ${commandPrefix} team lane latest --agent analysis_scout_1 --follow shows one agent's status, assigned runtime tasks, recent agent events, direct messages, and fallback global tail; ${commandPrefix} team message latest --from analysis_scout_1 --to executor_1 --message "handoff note" mirrors bounded agent communication into transcript/lane panes; ${commandPrefix} team cleanup-tmux latest marks the SKS session record complete and asks follow panes to show a cleanup summary then stop.`,
573
+ `Runtime: open Codex App once, then run ${commandPrefix} bootstrap and ${commandPrefix} deps check. Bare ${commandPrefix} opens or reuses the default tmux/Codex CLI session; before launch it checks npm @openai/codex@latest and prompts Y/n when the installed Codex CLI is missing or outdated. ${commandPrefix} tmux open is the explicit form for session/workspace flags.`,
574
574
  `Guard: generated harness files are immutable outside the engine source repo; check ${commandPrefix} guard check; conflicts use ${commandPrefix} conflicts prompt with human approval.`
575
575
  ].join('\n') + '\n';
576
576
  }
@@ -680,8 +680,11 @@ async function removeStaleGeneratedSkillsFromManifest(root, skillNames) {
680
680
  function enrichSkillContent(name, content) {
681
681
  if (!['sks', 'answer', 'wiki', 'team', 'qa-loop', 'ppt', 'computer-use', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'reflection', 'prompt-pipeline', 'pipeline-runner', 'context7-docs', 'turbo-context-pack', 'hproof-evidence-bind'].includes(name)) return content;
682
682
  const text = String(content || '').trimEnd();
683
- if (text.includes('TriWiki context-tracking SSOT')) return text;
684
- return `${text}
683
+ const activation = pipelineActivationText(name);
684
+ if (text.includes('TriWiki context-tracking SSOT')) {
685
+ return activation && !text.includes('Codex App pipeline activation:') ? `${text}\n\n${activation}` : text;
686
+ }
687
+ return `${text}${activation ? `\n\n${activation}` : ''}
685
688
 
686
689
  Context tracking:
687
690
  - Ask only ambiguity that can change scope, safety, behavior, or acceptance; infer the rest from TriWiki/current code and seal answers before execution.
@@ -692,6 +695,16 @@ Context tracking:
692
695
  `;
693
696
  }
694
697
 
698
+ function pipelineActivationText(name) {
699
+ const stateful = new Set(['sks', 'team', 'qa-loop', 'ppt', 'computer-use', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'prompt-pipeline', 'pipeline-runner']);
700
+ if (!stateful.has(name)) return '';
701
+ return `Codex App pipeline activation:
702
+ - If the SKS UserPromptSubmit hook already injected route context, follow that context.
703
+ - If no SKS hook context is visible in the current turn, immediately run \`sks hook user-prompt-submit\` from the target repo root with JSON input containing the current prompt, for example: \`printf '%s' '{"prompt":"<user prompt>","cwd":"<repo root>"}' | sks hook user-prompt-submit\`.
704
+ - Use the returned \`hookSpecificOutput.additionalContext\` as authoritative route context before answering or editing. If it creates a mission, continue through \`sks pipeline status\`, \`sks pipeline plan\`, and \`sks pipeline answer\` as directed.
705
+ - Do not treat this skill text alone as completion of the SKS route; a stateful SKS route must materialize mission/pipeline artifacts or explicitly report why the hook could not run.`;
706
+ }
707
+
695
708
  async function writeSkillMetadata(dir, name) {
696
709
  const effort = ['computer-use', 'computer-use-fast', 'cu'].includes(name)
697
710
  ? 'low'
@@ -85,6 +85,8 @@ Use this skill when the user asks an OpenClaw agent to work in a codebase that u
85
85
 
86
86
  The OpenClaw agent must have the built-in \`shell\` tool enabled. Prefer running commands in the target repository root.
87
87
 
88
+ Set \`SKS_OPENCLAW=1\` for SKS shell commands. In OpenClaw mode, SKS treats update/install prompts as automatically approved so \`sks\` can update \`sneakoscope\` or \`@openai/codex\` before launching tmux without pausing for a human \`Y/n\` answer.
89
+
88
90
  ## Core Commands
89
91
 
90
92
  - \`${sksCommand} root\` checks whether SKS is using a project root or the global runtime root.
@@ -97,7 +99,7 @@ The OpenClaw agent must have the built-in \`shell\` tool enabled. Prefer running
97
99
 
98
100
  ## Agent Operating Rules
99
101
 
100
- 1. Before substantive work, run \`${sksCommand} root\` and inspect the repository's \`AGENTS.md\` if present.
102
+ 1. Before substantive work, run \`SKS_OPENCLAW=1 ${sksCommand} root\` and inspect the repository's \`AGENTS.md\` if present.
101
103
  2. For implementation, prefer the repository's requested SKS route. General code work normally routes to \`$Team\`; tiny design or copy edits can use \`$DFix\`; UI/browser dogfood uses \`$QA-LOOP\`; database or Supabase work uses \`$DB\`.
102
104
  3. Do not invent fallback implementation code when the requested SKS path is blocked. Report the blocker with command output and source paths.
103
105
  4. For database, migration, and Supabase tasks, default to read-only inspection unless the user explicitly authorizes a write/migration scope.
@@ -118,6 +120,8 @@ Version: ${version}
118
120
 
119
121
  This OpenClaw skill lets an OpenClaw agent discover and use Sneakoscope Codex through the local \`${sksCommand}\` command.
120
122
 
123
+ OpenClaw agents should set \`SKS_OPENCLAW=1\` when running SKS commands. That mode auto-approves SKS dependency/update prompts, including the Codex CLI update preflight before tmux launch.
124
+
121
125
  ## Install
122
126
 
123
127
  \`\`\`sh
@@ -155,6 +159,8 @@ function openClawAgentConfigExample({ skillName }) {
155
159
  - terminal
156
160
  tools:
157
161
  - shell
162
+ env:
163
+ SKS_OPENCLAW: "1"
158
164
  skills:
159
165
  - ${skillName}
160
166
  `;
@@ -457,7 +457,7 @@ export const COMMAND_CATALOG = [
457
457
  { name: 'deps', usage: 'sks deps check|install [tmux|codex|context7|all] [--yes]', description: 'Check or guided-install Node/npm PATH, Codex CLI/App, Context7, Browser Use, Computer Use, tmux, and Homebrew on macOS.' },
458
458
  { name: 'codex-app', usage: 'sks codex-app [check|open]', description: 'Check Codex App install and first-party MCP/plugin readiness, then show app setup files and examples.' },
459
459
  { name: 'openclaw', usage: 'sks openclaw install|path|print [--dir path] [--force] [--json]', description: 'Generate an OpenClaw skill package so OpenClaw agents can discover and use local SKS workflows.' },
460
- { name: 'tmux', usage: 'sks tmux open|check|status [--workspace name]', description: 'Explicitly open the SKS tmux runtime, or check/status without launching tmux.' },
460
+ { name: 'tmux', usage: 'sks | sks tmux open|check|status [--workspace name]', description: 'Open the default SKS tmux runtime with bare sks, or use tmux subcommands for explicit launch/check/status.' },
461
461
  { name: 'mad', usage: 'sks --mad [--high]', description: 'Open a one-shot tmux Codex CLI workspace with the SKS MAD full-access auto-review profile.' },
462
462
  { name: 'auto-review', usage: 'sks auto-review status|enable|start [--high] | sks --Auto-review --high', description: 'Enable Codex automatic approval review and launch SKS tmux with the auto-review profile.' },
463
463
  { name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DFix and $Team.' },
@@ -207,7 +207,8 @@ export function formatTmuxBanner(status = null) {
207
207
  ' $DFix $Answer $SKS $Team $QA-LOOP $PPT $Goal $Research $AutoResearch $DB $GX $Wiki $Help',
208
208
  '',
209
209
  'CLI-first runtime:',
210
- ' sks tmux open open or attach a tmux Codex CLI session',
210
+ ' sks open or attach the default tmux Codex CLI session',
211
+ ' sks tmux open open or attach a tmux Codex CLI session with explicit flags',
211
212
  ' sks --mad open one-shot MAD full-access auto-review tmux session',
212
213
  ' sks team "task" prepare Team mission and tmux multi-pane live view',
213
214
  '',