sneakoscope 0.6.62 → 0.6.66

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
@@ -2,120 +2,415 @@
2
2
 
3
3
  ![](https://github.com/mandarange/Sneakoscope-Codex/raw/dev/docs/assets/sneakoscope-codex-logo.png)
4
4
 
5
- Codex CLI/App harness for `$` routes, cmux-native CLI workspaces, Team/Ralph/QA/Research, Context7, Honest Mode, DB safety, TriWiki, Codex App skills, and release readiness.
5
+ Sneakoscope Codex (`sks`, displayed as `ㅅㅋㅅ`) is a Codex CLI/App harness for repeatable agent workflows. It adds terminal commands, Codex App `$` prompt commands, cmux-native CLI workspaces, Team/Ralph/QA/Research routes, Context7 evidence checks, DB safety, TriWiki context tracking, Honest Mode, and release-readiness gates.
6
6
 
7
- Install: `npm i -g sneakoscope && sks bootstrap`
8
- Fallback: `npx -y -p sneakoscope sks bootstrap`
9
- Project: `npm i -D sneakoscope && npx sks setup --install-scope project`
7
+ ## Quick Start
10
8
 
11
- Discover: `sks commands`, `sks dollar-commands`, `sks usage <topic>`
12
- Check: `sks deps check`, `sks doctor --fix`, `sks selftest --mock`
9
+ Install globally, initialize the current project, then open the cmux runtime:
13
10
 
14
- ## What It Adds
11
+ ```sh
12
+ npm i -g sneakoscope
13
+ sks bootstrap
14
+ sks
15
+ ```
16
+
17
+ If you only want a one-shot run without keeping `sks` installed globally:
18
+
19
+ ```sh
20
+ npx -y -p sneakoscope sks bootstrap
21
+ ```
15
22
 
16
- Sneakoscope (`sks`, displayed as `ㅅㅋㅅ`) wraps Codex with a repeatable control surface:
23
+ For a repo-local install:
24
+
25
+ ```sh
26
+ npm i -D sneakoscope
27
+ npx sks setup --install-scope project
28
+ ```
29
+
30
+ Check that the install is usable:
31
+
32
+ ```sh
33
+ sks deps check
34
+ sks codex-app check
35
+ sks dollar-commands
36
+ sks selftest --mock
37
+ ```
38
+
39
+ ## What Sneakoscope Adds
17
40
 
18
41
  | Area | What it does |
19
42
  | --- | --- |
20
- | Codex App commands | Installs generated skills so `$Team`, `$DFix`, `$QA-LOOP`, `$Ralph`, `$DB`, `$Wiki`, `$Help`, and related routes are discoverable in prompt workflows. |
21
- | CLI commands | Provides `sks commands`, `sks dollar-commands`, `sks usage <topic>`, bootstrap, setup, doctor, deps, selftest, wiki, team, QA, Ralph, DB, and GX commands. |
22
- | cmux runtime | Opens Codex CLI through `sks`/`sks cmux`, exposes one-shot full-access high mode with `sks --mad --high`, and opens Team missions with cmux multi-line agent lanes when cmux is available. |
23
- | Team orchestration | Routes substantial code work through ambiguity removal, scouts, TriWiki refresh, debate, consensus, concrete runtime task graph/inboxes, implementation, review, integration, reflection, and Honest Mode. |
24
- | Ralph | Seals a decision contract up front, then continues without more user questions by using the agreed decision ladder. |
25
- | QA loop | Dogfoods UI/API behavior with safety boundaries, evidence capture, safe remediation, and focused rechecks. |
26
- | TriWiki | Keeps `.sneakoscope/wiki/context-pack.json` as the context SSOT, with refresh, pack, prune, validate, active attention ranking, and hydratable source-backed claims. |
27
- | Context7 | Requires current external library/API/framework docs for routes whose correctness depends on live package or platform behavior. |
28
- | DB safety | Treats SQL, migrations, Supabase, RLS, and destructive operations as high risk; defaults to inspection and guarded local/branch-safe migration work. |
29
- | Honest Mode | Finishes work with a claim/evidence pass that separates verified facts, unsupported claims, blocked checks, and not-applicable items. |
30
- | GX visual context | Generates deterministic visual context cartridges for structured visual review and drift checks. |
31
- | Research loops | Supports Research and AutoResearch workflows with hypotheses, experiments, falsification, novelty ledgers, SEO/GEO, and evidence-backed conclusions. |
32
- | Release hygiene | Checks versioning, changelog, package contents, tarball size, syntax, selftests, and dry-run packaging before publish. |
43
+ | CLI runtime | `sks`, `sks cmux`, and `sks --mad` open Codex CLI in a cmux workspace. |
44
+ | Codex App commands | Installs generated skills so `$Team`, `$DFix`, `$QA-LOOP`, `$Ralph`, `$DB`, `$Wiki`, `$Help`, and related routes are visible in prompt workflows. |
45
+ | Team orchestration | Runs substantial work through ambiguity handling, scouts, TriWiki refresh, debate, runtime task graphs, worker inboxes, implementation, review, cleanup, reflection, and Honest Mode. |
46
+ | QA loop | Dogfoods UI/API behavior with safety gates, Browser/Computer evidence, safe fixes, and rechecks. |
47
+ | Ralph | Clarifies once, seals a decision contract, then continues without repeatedly asking the user. |
48
+ | TriWiki | Maintains `.sneakoscope/wiki/context-pack.json` as the context SSOT with `attention.use_first` and `attention.hydrate_first`. |
49
+ | Context7 | Requires current docs for external packages, APIs, MCPs, SDKs, and framework/runtime behavior when correctness depends on current guidance. |
50
+ | DB safety | Treats SQL, migrations, Supabase, RLS, and destructive operations as high risk. |
51
+ | Release hygiene | Checks versioning, changelog, package contents, tarball size, syntax, selftests, and dry-run publishing. |
33
52
 
34
- ## Prompt `$` Commands
53
+ ## Requirements
35
54
 
36
- Use these inside Codex App or another agent prompt. They are prompt commands, not terminal commands.
55
+ - Node.js `>=20.11`
56
+ - npm
57
+ - Codex CLI for terminal workflows
58
+ - Codex App for app-facing workflows and first-party Browser Use / Computer Use parity
59
+ - cmux for the CLI-first runtime
60
+ - Context7 MCP for current-docs-gated routes
37
61
 
38
- | Prompt | Purpose |
39
- | --- | --- |
40
- | `$Team` | Default route for code-changing work and substantial implementation. |
41
- | `$From-Chat-IMG` | Team alias for chat screenshot plus original attachment intake. |
42
- | `$DFix` | Tiny design/content fixes: labels, copy, colors, spacing, translation. |
43
- | `$Answer` | Answer-only route when no implementation should start. |
44
- | `$SKS` | Setup, status, usage, and Sneakoscope workflow help. |
45
- | `$QA-LOOP` | UI/API dogfooding, safe fixes, and rechecks. |
46
- | `$Ralph` | Clarify once, seal a decision contract, then execute. |
47
- | `$Research` | Frontier-style research with hypotheses and falsification. |
48
- | `$AutoResearch` | Iterative improve-test-keep/discard optimization loop. |
49
- | `$DB` | Database and Supabase safety checks. |
50
- | `$MAD-SKS` | Explicit scoped DB authorization modifier; combine it with another `$` route when needed, widened Supabase MCP permissions last only for that invocation, and table deletion requires a short user confirmation timeout. |
51
- | `$GX` | Deterministic visual context generation and validation. |
52
- | `$Wiki` | TriWiki refresh, pack, prune, validate, and maintenance. |
53
- | `$Help` | Installed command and workflow explanation. |
54
-
55
- Run `sks dollar-commands` to verify the terminal and Codex App command surfaces agree.
56
-
57
- ## Terminal Examples
62
+ On macOS, `sks --mad` can install cmux through Homebrew when cmux is missing. You can also install it manually:
63
+
64
+ ```sh
65
+ brew tap manaflow-ai/cmux
66
+ brew install --cask cmux
67
+ ```
68
+
69
+ If the CLI is not on `PATH`, SKS also checks the app bundle path:
70
+
71
+ ```sh
72
+ /Applications/cmux.app/Contents/Resources/bin/cmux
73
+ /Applications/cmux.app/Contents/MacOS/cmux
74
+ ```
75
+
76
+ `sks --mad` is stricter than the normal runtime path:
77
+
78
+ - Checks npm for a newer `sneakoscope` before launch and asks whether to update when the terminal can answer y/n.
79
+ - Installs the latest Codex CLI with `npm i -g @openai/codex@latest` when it is missing and you approve or pass `--yes`.
80
+ - Installs or upgrades the latest cmux cask through Homebrew when cmux is missing or not launchable.
81
+ - Re-probes the real cmux binary after install instead of trusting Homebrew's success text alone.
82
+ - Wakes cmux and retries the socket probe; if the socket is broken, SKS attempts a cmux app restart during that explicit launch.
83
+
84
+ ## Installation
85
+
86
+ ### Global Install
87
+
88
+ Use this when you want `sks` available from any repo:
89
+
90
+ ```sh
91
+ npm i -g sneakoscope
92
+ sks bootstrap
93
+ ```
94
+
95
+ `sks bootstrap` initializes the current project, installs Codex App skills/hooks/config, checks Context7/Codex App/cmux readiness, and prints a ready status.
96
+
97
+ ### One-Shot Install
98
+
99
+ Use this when you do not want to keep a global install:
100
+
101
+ ```sh
102
+ npx -y -p sneakoscope sks bootstrap
103
+ ```
104
+
105
+ `npx` fetches the package into npm's cache and runs the binary for that command. This is useful for first-time setup or CI-style verification.
106
+
107
+ ### Project Install
108
+
109
+ Use this when a repo should pin Sneakoscope as a development dependency:
58
110
 
59
111
  ```sh
112
+ npm i -D sneakoscope
113
+ npx sks setup --install-scope project
114
+ ```
115
+
116
+ Project installs are useful when a team wants a repeatable harness version checked through `package-lock.json`.
117
+
118
+ ### Source Checkout
119
+
120
+ Use this when developing Sneakoscope itself:
121
+
122
+ ```sh
123
+ git clone https://github.com/mandarange/Sneakoscope-Codex.git
124
+ cd Sneakoscope-Codex
125
+ npm install
126
+ npm install -g .
127
+ sks --version
128
+ ```
129
+
130
+ ## Terminal CLI Usage
131
+
132
+ Use terminal commands when you want to inspect, set up, verify, or start a CLI-first workspace.
133
+
134
+ ### Discovery
135
+
136
+ ```sh
137
+ sks commands
60
138
  sks usage install
61
139
  sks usage team
62
- sks usage qa-loop
63
140
  sks usage codex-app
141
+ sks dollar-commands
142
+ sks --version
143
+ ```
144
+
145
+ ### Setup And Repair
146
+
147
+ ```sh
148
+ sks bootstrap
149
+ sks deps check
150
+ sks deps install cmux
151
+ sks codex-app check
152
+ sks doctor --fix
153
+ sks fix-path
154
+ ```
155
+
156
+ ### Open Codex CLI With cmux
157
+
158
+ ```sh
159
+ sks
64
160
  sks cmux check
65
- sks --mad --high
66
- sks setup --install-scope project
161
+ sks cmux status --once
162
+ ```
163
+
164
+ `sks` opens a cmux workspace for Codex CLI when running in an interactive terminal. `sks cmux check` is diagnostic and prints readiness without starting a workspace.
165
+
166
+ ### MAD cmux Workspace
167
+
168
+ ```sh
169
+ sks --mad
170
+ sks --mad --yes
171
+ ```
172
+
173
+ This creates/uses the `sks-mad-high` Codex profile for a one-shot full-access, high-reasoning cmux workspace with `approval_policy = "on-request"` and `approvals_reviewer = "auto_review"`. It is scoped to that explicit command and does not change normal SKS/DB safety defaults.
174
+
175
+ Before launching, SKS checks whether a newer `sneakoscope` exists on npm. In an interactive terminal it prompts:
176
+
177
+ ```text
178
+ SKS 0.x.y -> 0.x.z update before MAD launch? [Y/n]
179
+ ```
180
+
181
+ Answer `y` to install `sneakoscope@latest`, then rerun `sks --mad`. Answer `n` to continue with the current version. Use `--yes` to approve missing dependency installs automatically.
182
+
183
+ ### Team Missions
184
+
185
+ ```sh
186
+ sks team "implement this feature" executor:3 reviewer:1
187
+ sks team watch latest
188
+ sks team status latest
189
+ sks team log latest
190
+ ```
191
+
192
+ Team mode prepares the mission, records live events, compiles runtime tasks and worker inboxes, and opens cmux live lanes when cmux is available.
193
+
194
+ ### QA, Ralph, Research, DB, Wiki, GX
195
+
196
+ ```sh
197
+ sks qa-loop prepare "http://localhost:3000"
198
+ sks qa-loop run latest --max-cycles 2
199
+ sks ralph prepare "migrate this workflow without asking after prepare"
200
+ sks research prepare "evaluate this approach"
201
+ sks db scan --json
67
202
  sks wiki refresh
68
203
  sks wiki validate .sneakoscope/wiki/context-pack.json
69
- sks versioning status
204
+ sks gx init homepage
205
+ sks gx render homepage --format html
70
206
  ```
71
207
 
72
- Route examples:
208
+ ## Codex App Usage
209
+
210
+ Sneakoscope has two surfaces:
211
+
212
+ - Terminal commands such as `sks deps check`, `sks team "task"`, and `sks --mad`
213
+ - Codex App prompt commands such as `$Team`, `$DFix`, `$QA-LOOP`, and `$Wiki`
214
+
215
+ After installing, run:
73
216
 
74
217
  ```sh
75
- sks team "implement this" executor:3 reviewer:1
76
- sks team watch <mission-id>
77
- sks qa-loop prepare
78
- sks qa-loop run
79
- sks ralph prepare
80
- sks ralph run
81
- sks db scan
82
- sks gx init
218
+ sks bootstrap
219
+ sks codex-app check
220
+ sks dollar-commands
83
221
  ```
84
222
 
85
- ## Workflow Rules
223
+ Then open Codex App and use prompt commands directly in the chat. Examples:
86
224
 
87
- For code work, Sneakoscope defaults to Team. The normal flow is: remove ambiguity that can change scope or safety, read/validate TriWiki, consume `attention.use_first` for compact high-trust context, hydrate `attention.hydrate_first` from source before risky decisions, gather current source evidence, synthesize consensus, compile a concrete runtime task graph plus worker inboxes, implement bounded changes, refresh/validate context after meaningful findings, run relevant checks, then finish with reflection and Honest Mode.
225
+ ```text
226
+ $Team implement the checkout fix and verify it
227
+ $DFix change this label and spacing only
228
+ $QA-LOOP dogfood localhost:3000 and fix safe issues
229
+ $Ralph clarify once, then finish the migration without more questions
230
+ $Wiki refresh and validate the context pack
231
+ $DB inspect this migration for destructive risk
232
+ ```
88
233
 
89
- For tiny text/design edits use `$DFix`. For questions that should not change files use `$Answer`.
234
+ Generated app files include:
90
235
 
91
- ## Codex App Surface
236
+ | Path | Purpose |
237
+ | --- | --- |
238
+ | `.codex/SNEAKOSCOPE.md` | Codex App quick reference and route guidance. |
239
+ | `.agents/skills/` | Generated skill instructions for `$` commands. |
240
+ | `.codex/hooks.json` | Stop/finalization hooks for Honest Mode and completion summaries. |
241
+ | `.codex/config.toml` | Codex profiles, agents, and MCP configuration. |
242
+ | `.sneakoscope/` | Runtime state, missions, wiki packs, policies, and artifacts. |
92
243
 
93
- `sks bootstrap` and `sks setup` install `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, route instructions for `$` commands, and user-home skill state for first-install discoverability.
244
+ Use `sks dollar-commands` to confirm that terminal discovery and Codex App prompt commands agree.
94
245
 
95
- After install, check:
246
+ ## Prompt `$` Commands
247
+
248
+ Use these inside Codex App or another agent prompt. They are prompt commands, not terminal commands.
249
+
250
+ | Prompt | Use when |
251
+ | --- | --- |
252
+ | `$Team` | You want implementation, code changes, or substantial repo work. |
253
+ | `$From-Chat-IMG` | You have a chat screenshot plus original attachments and want each visible request mapped to work. |
254
+ | `$DFix` | You need a tiny design/content edit such as copy, label, color, spacing, or translation. |
255
+ | `$Answer` | You want an answer only and no implementation should start. |
256
+ | `$SKS` | You need setup, status, usage, or workflow help. |
257
+ | `$QA-LOOP` | You want UI/API dogfooding, safe fixes, and rechecks. |
258
+ | `$Ralph` | You want one prepare-time clarification pass, then no more user questions. |
259
+ | `$Research` | You need frontier-style research with hypotheses and falsification. |
260
+ | `$AutoResearch` | You want iterative improve/test/keep-or-discard optimization. |
261
+ | `$DB` | You need database, Supabase, migration, SQL, or MCP safety checks. |
262
+ | `$MAD-SKS` | You explicitly authorize a scoped high-risk DB permission modifier for the active invocation only. |
263
+ | `$GX` | You need deterministic visual context cartridges. |
264
+ | `$Wiki` | You want TriWiki refresh, pack, prune, validate, or maintenance. |
265
+ | `$Help` | You want installed command and workflow explanation. |
266
+
267
+ ## Common Workflows
268
+
269
+ ### First Install Checklist
96
270
 
97
271
  ```sh
272
+ npm i -g sneakoscope
273
+ sks bootstrap
274
+ sks deps check
275
+ sks codex-app check
98
276
  sks dollar-commands
99
- sks usage codex-app
277
+ sks selftest --mock
278
+ ```
279
+
280
+ ### Start A CLI Workspace
281
+
282
+ ```sh
283
+ sks cmux check
284
+ sks
100
285
  ```
101
286
 
102
- ## Release Checks
287
+ For the high-reasoning full-access profile:
103
288
 
104
- Before publish:
289
+ ```sh
290
+ sks --mad
291
+ ```
292
+
293
+ ### Use Codex App `$Team`
294
+
295
+ ```text
296
+ $Team implement the requested change, update docs if needed, and verify with the relevant tests
297
+ ```
298
+
299
+ Team mode records a mission under `.sneakoscope/missions/`, keeps a live transcript, uses TriWiki context, and finishes with evidence and Honest Mode.
300
+
301
+ ### Dogfood A UI Or API
302
+
303
+ ```sh
304
+ sks qa-loop prepare "http://localhost:3000"
305
+ sks qa-loop run latest --max-cycles 2
306
+ sks qa-loop status latest
307
+ ```
308
+
309
+ Use `$QA-LOOP` in Codex App when Browser Use or Computer Use evidence should be part of the workflow.
310
+
311
+ ### Refresh Context Before Risky Work
312
+
313
+ ```sh
314
+ sks wiki refresh
315
+ sks wiki validate .sneakoscope/wiki/context-pack.json
316
+ ```
317
+
318
+ TriWiki is the long-running context source of truth. It keeps compact high-trust recall in `attention.use_first` and source-hydration targets in `attention.hydrate_first`.
319
+
320
+ ## Safety Model
321
+
322
+ Sneakoscope intentionally treats these as high-risk:
323
+
324
+ - SQL and migrations
325
+ - Supabase MCP and RLS changes
326
+ - destructive filesystem operations
327
+ - user-global harness config
328
+ - published package/release state
329
+
330
+ By default, SKS favors inspection, local files, branch-safe changes, explicit confirmation for destructive DB operations, and completion claims backed by tests or artifacts.
331
+
332
+ ## Troubleshooting
333
+
334
+ ### `sks` points to an old version
335
+
336
+ ```sh
337
+ which sks
338
+ sks --version
339
+ node ./bin/sks.mjs --version
340
+ npm install -g .
341
+ ```
342
+
343
+ If the global command is stale, reinstall globally from the repo or from npm.
344
+
345
+ ### cmux is missing
346
+
347
+ ```sh
348
+ sks deps install cmux
349
+ sks cmux check
350
+ ```
351
+
352
+ `sks --mad` also attempts Homebrew installation or upgrade automatically on macOS when cmux is missing. If Homebrew reports the cask installed but the CLI still is not reachable, SKS checks the cmux app bundle paths directly, wakes the app, and retries the socket before reporting a remaining cmux app/socket issue.
353
+
354
+ ### Codex App tools are missing
355
+
356
+ ```sh
357
+ sks codex-app check
358
+ codex mcp list
359
+ ```
360
+
361
+ Codex App workflows need the app installed and the first-party Browser Use / Computer Use tools available for parity with QA and visual workflows.
362
+
363
+ ### Setup is blocked by another harness
364
+
365
+ ```sh
366
+ sks conflicts check
367
+ sks conflicts prompt
368
+ ```
369
+
370
+ OMX/DCodex conflicts intentionally block setup/doctor until the user approves cleanup.
371
+
372
+ ### The route is stuck or a final hook keeps reopening
373
+
374
+ ```sh
375
+ sks pipeline status --json
376
+ sks team watch latest
377
+ sks wiki validate .sneakoscope/wiki/context-pack.json
378
+ ```
379
+
380
+ Finalization requires real evidence, no unsupported critical claims, valid Team cleanup artifacts, reflection when required, and Honest Mode.
381
+
382
+ ## Development And Release
383
+
384
+ Run local checks:
385
+
386
+ ```sh
387
+ npm run repo-audit
388
+ npm run changelog:check
389
+ npm run packcheck
390
+ npm run selftest
391
+ npm run sizecheck
392
+ npm run release:check
393
+ ```
394
+
395
+ Dry-run publish:
105
396
 
106
397
  ```sh
107
398
  npm run publish:dry
108
399
  ```
109
400
 
110
- This runs repo audit, changelog check, syntax packcheck, mock selftest, sizecheck, and `npm pack --dry-run`. A dry run proves the local package is packable; npm account ownership or OTP can still block the real registry upload.
401
+ `publish:dry` proves the local package is packable. It does not prove npm ownership, OTP, or registry publish permission.
111
402
 
112
- ## Requirements
403
+ ## Documentation Style
113
404
 
114
- - Node.js `>=20.11`
115
- - npm
116
- - Codex CLI/App for app-facing workflows
117
- - cmux for the CLI-first runtime (`sks deps install cmux`)
118
- - Context7 MCP for current-docs-gated routes
405
+ This README follows a common open-source CLI shape:
406
+
407
+ - quick start first
408
+ - explicit install paths
409
+ - separate CLI and app/plugin usage
410
+ - command examples before internal architecture
411
+ - troubleshooting and release checks near the end
412
+
413
+ That shape mirrors how projects such as `rdme` and Vite separate quick start, setup/configuration, and CLI usage while keeping copy-ready commands visible.
119
414
 
120
415
  ## License
121
416
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.6.62",
4
+ "version": "0.6.66",
5
5
  "description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Ralph, AutoResearch, TriWiki, and Honest Mode.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
package/src/cli/main.mjs CHANGED
@@ -30,7 +30,7 @@ import { context7Evidence, evaluateStop, recordContext7Evidence, recordSubagentE
30
30
  import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
31
31
  import { appendTeamEvent, formatRoleCounts, initTeamLive, normalizeTeamSpec, parseTeamSpecArgs, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail } from '../core/team-live.mjs';
32
32
  import { CODEX_APP_DOCS_URL, codexAppIntegrationStatus, formatCodexAppStatus } from '../core/codex-app.mjs';
33
- import { buildCmuxLaunchPlan, defaultCmuxWorkspaceName, formatCmuxBanner, launchCmuxTeamView, launchCmuxUi, platformCmuxInstallHint, runCmuxStatus, sanitizeCmuxWorkspaceName, cmuxAvailable } from '../core/cmux-ui.mjs';
33
+ import { CMUX_BREW_COMMAND, CMUX_BREW_UPGRADE_COMMAND, buildCmuxLaunchPlan, defaultCmuxWorkspaceName, ensureCmuxInstalled, formatCmuxBanner, launchCmuxTeamView, launchCmuxUi, platformCmuxInstallHint, runCmuxStatus, sanitizeCmuxWorkspaceName, cmuxAvailable } from '../core/cmux-ui.mjs';
34
34
  import { autoReviewProfileName, autoReviewStatus, autoReviewSummary, enableAutoReview, disableAutoReview, enableMadHighProfile, madHighProfileName } from '../core/auto-review.mjs';
35
35
 
36
36
  const flag = (args, name) => args.includes(name);
@@ -116,7 +116,7 @@ Usage:
116
116
  sks bootstrap [--install-scope global|project] [--local-only] [--json]
117
117
  sks deps check|install [cmux|codex|context7|all] [--yes] [--json]
118
118
  sks codex-app
119
- sks --mad --high
119
+ sks --mad [--high]
120
120
  sks auto-review status|enable|start [--high]
121
121
  sks --Auto-review [--high]
122
122
  sks cmux [--workspace name]
@@ -389,13 +389,13 @@ async function ensureCodexCliTool({ skip = false } = {}) {
389
389
  const before = await getCodexInfo().catch(() => ({}));
390
390
  if (before.bin) return { status: 'present', bin: before.bin, version: before.version || null };
391
391
  const npmBin = await which('npm');
392
- if (!npmBin) return { status: 'failed', error: 'npm not found on PATH; install Codex CLI manually with npm i -g @openai/codex.' };
393
- const install = await runProcess(npmBin, ['i', '-g', '@openai/codex'], {
392
+ if (!npmBin) return { status: 'failed', error: 'npm not found on PATH; install Codex CLI manually with npm i -g @openai/codex@latest.' };
393
+ const install = await runProcess(npmBin, ['i', '-g', '@openai/codex@latest'], {
394
394
  timeoutMs: 120000,
395
395
  maxOutputBytes: 128 * 1024
396
396
  }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
397
397
  if (install.code !== 0) {
398
- return { status: 'failed', error: `${install.stderr || install.stdout || 'npm i -g @openai/codex failed'}`.trim() };
398
+ return { status: 'failed', error: `${install.stderr || install.stdout || 'npm i -g @openai/codex@latest failed'}`.trim() };
399
399
  }
400
400
  const after = await getCodexInfo().catch(() => ({}));
401
401
  return {
@@ -1044,13 +1044,75 @@ async function cmuxCommand(sub = 'start', args = []) {
1044
1044
  }
1045
1045
 
1046
1046
  async function madHighCommand(args = []) {
1047
- const cleanArgs = args.filter((arg) => !['--mad', '--MAD', '--mad-sks', '--high'].includes(arg));
1047
+ const cleanArgs = args.filter((arg) => !['--mad', '--MAD', '--mad-sks', '--high', '--no-auto-install-cmux'].includes(arg));
1048
+ if (flag(args, '--json')) {
1049
+ const profile = await enableMadHighProfile();
1050
+ return console.log(JSON.stringify(profile, null, 2));
1051
+ }
1052
+ const update = await maybePromptSksUpdateForMad(args);
1053
+ if (update.status === 'updated') {
1054
+ console.log(`SKS updated from ${PACKAGE_VERSION} to ${update.latest}. Rerun: sks --mad`);
1055
+ return;
1056
+ }
1057
+ if (update.status === 'failed') {
1058
+ console.error(`SKS update failed: ${update.error}`);
1059
+ process.exitCode = 1;
1060
+ return;
1061
+ }
1062
+ const deps = await ensureMadLaunchDependencies(args);
1063
+ if (!deps.ready) {
1064
+ console.error('SKS MAD launch blocked by missing dependencies.');
1065
+ for (const action of deps.actions) printDepsInstallAction(action);
1066
+ process.exitCode = 1;
1067
+ return;
1068
+ }
1048
1069
  const profile = await enableMadHighProfile();
1049
- if (flag(args, '--json')) return console.log(JSON.stringify(profile, null, 2));
1050
- console.log(`SKS MAD high profile ready: ${madHighProfileName()}`);
1051
- console.log('Scope: explicit cmux launch only; normal SKS/DB safety returns after this command.');
1070
+ console.log(`SKS MAD auto-review profile ready: ${madHighProfileName()}`);
1071
+ console.log('Scope: explicit cmux launch only; full access uses Codex auto_review approvals when approval prompts are raised.');
1052
1072
  const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${defaultCmuxWorkspaceName(process.cwd())}`));
1053
- return launchCmuxUi([...cleanArgs, '--workspace', workspace], { codexArgs: ['--profile', profile.profile_name] });
1073
+ return launchCmuxUi([...cleanArgs, '--workspace', workspace], {
1074
+ codexArgs: ['--profile', profile.profile_name],
1075
+ autoInstallCmux: !flag(args, '--no-auto-install-cmux'),
1076
+ conciseBlockers: true
1077
+ });
1078
+ }
1079
+
1080
+ async function maybePromptSksUpdateForMad(args = []) {
1081
+ if (flag(args, '--json') || flag(args, '--skip-update-check') || process.env.SKS_SKIP_UPDATE_CHECK === '1') return { status: 'skipped' };
1082
+ const latest = await npmPackageVersion('sneakoscope');
1083
+ if (!latest.version || compareVersions(latest.version, PACKAGE_VERSION) <= 0) return { status: 'current', latest: latest.version || null, error: latest.error || null };
1084
+ const command = 'npm i -g sneakoscope@latest';
1085
+ if (flag(args, '--yes') || flag(args, '-y')) return installSksLatest(command, latest.version);
1086
+ if (!canAskYesNo()) {
1087
+ console.log(`SKS update available: ${PACKAGE_VERSION} -> ${latest.version}. Run: ${command}`);
1088
+ return { status: 'available', latest: latest.version, command };
1089
+ }
1090
+ const answer = (await askPostinstallQuestion(`SKS ${PACKAGE_VERSION} -> ${latest.version} update before MAD launch? [Y/n] `)).trim();
1091
+ const yes = answer === '' || /^(y|yes|예|네|응)$/i.test(answer);
1092
+ if (!yes) return { status: 'skipped_by_user', latest: latest.version, command };
1093
+ return installSksLatest(command, latest.version);
1094
+ }
1095
+
1096
+ async function installSksLatest(command, latestVersion) {
1097
+ const npm = await which('npm').catch(() => null);
1098
+ if (!npm) return { status: 'failed', latest: latestVersion, command, error: 'npm not found on PATH' };
1099
+ const install = await runProcess(npm, ['i', '-g', 'sneakoscope@latest'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
1100
+ if (install.code !== 0) return { status: 'failed', latest: latestVersion, command, error: `${install.stderr || install.stdout || command + ' failed'}`.trim() };
1101
+ return { status: 'updated', latest: latestVersion, command };
1102
+ }
1103
+
1104
+ async function ensureMadLaunchDependencies(args = []) {
1105
+ const actions = [];
1106
+ if (!flag(args, '--skip-cli-tools')) {
1107
+ const codex = await getCodexInfo().catch(() => ({}));
1108
+ if (!codex.bin) actions.push(await installCodexDependency(args, { prompt: 'Codex CLI missing. Install latest Codex CLI with npm i -g @openai/codex@latest?' }));
1109
+ }
1110
+ if (!flag(args, '--no-auto-install-cmux')) {
1111
+ const cmux = await cmuxAvailable().catch(() => ({ ok: false }));
1112
+ if (!cmux.ok && !cmux.bin) actions.push(await installCmuxDependency(args));
1113
+ }
1114
+ const status = await depsStatus(await projectRoot());
1115
+ return { ready: Boolean(status.codex_cli.ok && (status.cmux.ok || status.cmux.bin)), actions, status };
1054
1116
  }
1055
1117
 
1056
1118
  async function deps(sub = 'check', args = []) {
@@ -1094,7 +1156,7 @@ async function depsStatus(root = null, opts = {}) {
1094
1156
  context7,
1095
1157
  browser_use: { ok: app.mcp.has_browser_use, cache: app.plugins.browser_use_cache },
1096
1158
  computer_use: { ok: app.mcp.has_computer_use, cache: app.plugins.computer_use_cache },
1097
- cmux: { ok: Boolean(cmux.ok), version: cmux.version || null, install_hint: cmux.ok ? null : platformCmuxInstallHint(), error: cmux.error || null },
1159
+ cmux: { ok: Boolean(cmux.ok), bin: cmux.bin || null, version: cmux.version || null, install_hint: cmux.ok ? null : platformCmuxInstallHint(), error: cmux.error || null },
1098
1160
  homebrew: process.platform === 'darwin' ? { ok: Boolean(brew), bin: brew, required_for_cmux_install: homebrewNeeded } : { ok: null, bin: null, required_for_cmux_install: false },
1099
1161
  next_actions: depsNextActions({ npmBin, globalBin, codex, app, context7, cmux, brew, nodeOk })
1100
1162
  };
@@ -1149,10 +1211,11 @@ async function depsInstall(args = []) {
1149
1211
  if (!status.ready) process.exitCode = 1;
1150
1212
  }
1151
1213
 
1152
- async function installCodexDependency(args = []) {
1214
+ async function installCodexDependency(args = [], opts = {}) {
1153
1215
  const before = await getCodexInfo().catch(() => ({}));
1154
1216
  if (before.bin) return { target: 'codex', status: 'present', bin: before.bin, version: before.version || null };
1155
- if (!await confirmInstall('Install Codex CLI with npm i -g @openai/codex?', args)) return { target: 'codex', status: 'needs_approval', command: 'npm i -g @openai/codex' };
1217
+ const command = 'npm i -g @openai/codex@latest';
1218
+ if (!await confirmInstall(opts.prompt || `Install Codex CLI with ${command}?`, args)) return { target: 'codex', status: 'needs_approval', command };
1156
1219
  return { target: 'codex', ...(await ensureCodexCliTool()) };
1157
1220
  }
1158
1221
 
@@ -1168,25 +1231,33 @@ async function installCmuxDependency(args = []) {
1168
1231
  if (before.ok) return { target: 'cmux', status: 'present', version: before.version || null };
1169
1232
  if (process.platform === 'darwin') {
1170
1233
  const brew = await which('brew').catch(() => null);
1171
- const command = 'brew tap manaflow-ai/cmux && brew install --cask cmux';
1234
+ const command = CMUX_BREW_COMMAND;
1172
1235
  if (!brew) return { target: 'cmux', status: 'homebrew_missing', command: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && ${command}` };
1173
1236
  if (flag(args, '--dry-run')) return { target: 'cmux', status: 'dry_run', command };
1174
- if (!await confirmInstall(`Install cmux with Homebrew (${command})?`, args)) return { target: 'cmux', status: 'needs_approval', command };
1175
- const tap = await runProcess(brew, ['tap', 'manaflow-ai/cmux'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 });
1176
- const run = tap.code === 0
1177
- ? await runProcess(brew, ['install', '--cask', 'cmux'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 })
1178
- : tap;
1179
- return { target: 'cmux', status: run.code === 0 ? 'installed' : 'failed', command, code: run.code, error: run.code === 0 ? null : `${run.stderr || run.stdout || 'brew install --cask cmux failed'}`.trim() };
1237
+ if (!await confirmInstall(`Install/update latest cmux with Homebrew (${command}; ${CMUX_BREW_UPGRADE_COMMAND} if already installed)?`, args)) return { target: 'cmux', status: 'needs_approval', command };
1238
+ const installed = await ensureCmuxInstalled({ autoInstall: true });
1239
+ return {
1240
+ target: 'cmux',
1241
+ status: installed.status,
1242
+ version: installed.cmux?.version || null,
1243
+ command,
1244
+ code: installed.code,
1245
+ error: installed.error || null
1246
+ };
1180
1247
  }
1181
1248
  return { target: 'cmux', status: 'manual_required', command: platformCmuxInstallHint() };
1182
1249
  }
1183
1250
 
1184
1251
  async function confirmInstall(question, args = []) {
1185
1252
  if (flag(args, '--yes') || flag(args, '-y')) return true;
1186
- if (!input.isTTY || !output.isTTY || process.env.CI === 'true') return false;
1253
+ if (!canAskYesNo()) return false;
1187
1254
  return /^(y|yes|예|네|응)$/i.test((await askPostinstallQuestion(`${question} [y/N] `)).trim());
1188
1255
  }
1189
1256
 
1257
+ function canAskYesNo() {
1258
+ return Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
1259
+ }
1260
+
1190
1261
  function printDepsInstallAction(action) {
1191
1262
  if (!action) return;
1192
1263
  console.log(`${action.target}: ${action.status}${action.version ? ` ${action.version}` : ''}`);
@@ -1759,6 +1830,8 @@ async function npmGlobalSksBin() {
1759
1830
  }
1760
1831
 
1761
1832
  async function npmPackageVersion(name) {
1833
+ const envName = `SKS_NPM_VIEW_${String(name || '').replace(/[^A-Za-z0-9]+/g, '_').toUpperCase()}_VERSION`;
1834
+ if (process.env[envName]) return { version: process.env[envName] };
1762
1835
  const npm = await which('npm').catch(() => null);
1763
1836
  if (!npm) return { error: 'npm not found' };
1764
1837
  const result = await runProcess(npm, ['view', name, 'version'], { timeoutMs: 5000, maxOutputBytes: 4096 });
@@ -2251,7 +2324,7 @@ async function selftest() {
2251
2324
  const madProfilePath = path.join(tmp, 'mad-codex-config.toml');
2252
2325
  const madProfile = await enableMadHighProfile({ configPath: madProfilePath });
2253
2326
  const madProfileText = await safeReadText(madProfilePath);
2254
- if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "never"') || !madProfileText.includes('model_reasoning_effort = "high"')) throw new Error('selftest failed: MAD high profile is not full-access high/fast');
2327
+ if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "on-request"') || !madProfileText.includes('approvals_reviewer = "auto_review"') || !madProfileText.includes('model_reasoning_effort = "high"')) throw new Error('selftest failed: MAD high profile is not full-access auto-review high');
2255
2328
  if (!isMadHighLaunch(['--mad', '--high']) || isMadHighLaunch(['db', '--mad'])) throw new Error('selftest failed: MAD high launch flag parsing is not top-level only');
2256
2329
  const guardBlocked = await checkHarnessModification(tmp, { tool_name: 'apply_patch', command: '*** Update File: .agents/skills/team/SKILL.md\n+tamper\n' });
2257
2330
  if (guardBlocked.action !== 'block') throw new Error('selftest failed: harness guard allowed skill tampering');
@@ -2646,11 +2719,11 @@ async function selftest() {
2646
2719
  const autoReviewEnabled = await enableAutoReview({ env: autoReviewEnv, high: true });
2647
2720
  if (!autoReviewEnabled.enabled || autoReviewEnabled.profile_name !== 'sks-auto-review-high' || !autoReviewEnabled.high_profile) throw new Error('selftest failed: auto-review high profile was not enabled');
2648
2721
  const autoReviewConfig = await safeReadText(path.join(autoReviewHome, '.codex', 'config.toml'));
2649
- if (!autoReviewConfig.includes('approvals_reviewer = "guardian_subagent"') || autoReviewConfig.includes('approvals_reviewer = "auto_review"') || !autoReviewConfig.includes('[profiles.sks-auto-review-high]')) throw new Error('selftest failed: auto-review config not written');
2722
+ if (!autoReviewConfig.includes('approvals_reviewer = "auto_review"') || autoReviewConfig.includes('approvals_reviewer = "guardian_subagent"') || !autoReviewConfig.includes('[profiles.sks-auto-review-high]')) throw new Error('selftest failed: auto-review config not written');
2650
2723
  const autoReviewDisabled = await disableAutoReview({ env: autoReviewEnv });
2651
2724
  if (autoReviewDisabled.enabled || autoReviewDisabled.approvals_reviewer !== 'user') throw new Error('selftest failed: auto-review disable did not restore user reviewer');
2652
2725
  const autoReviewDisabledConfig = await safeReadText(path.join(autoReviewHome, '.codex', 'config.toml'));
2653
- if (autoReviewDisabledConfig.includes('approvals_reviewer = "auto_review"')) throw new Error('selftest failed: auto-review disable left legacy invalid reviewer values');
2726
+ if (autoReviewDisabledConfig.includes('approvals_reviewer = "guardian_subagent"')) throw new Error('selftest failed: auto-review disable left legacy reviewer values');
2654
2727
  const analysisAgentExists = await exists(path.join(tmp, '.codex', 'agents', 'analysis-scout.toml'));
2655
2728
  if (!analysisAgentExists) throw new Error('selftest failed: analysis scout agent not installed');
2656
2729
  const teamAgentExists = await exists(path.join(tmp, '.codex', 'agents', 'team-consensus.toml'));
@@ -2,8 +2,8 @@ import os from 'node:os';
2
2
  import path from 'node:path';
3
3
  import { ensureDir, exists, readText, writeTextAtomic } from './fsx.mjs';
4
4
 
5
- export const AUTO_REVIEW_REVIEWER = 'guardian_subagent';
6
- export const LEGACY_AUTO_REVIEW_REVIEWER = 'auto_review';
5
+ export const AUTO_REVIEW_REVIEWER = 'auto_review';
6
+ export const LEGACY_AUTO_REVIEW_REVIEWER = 'guardian_subagent';
7
7
  export const AUTO_REVIEW_PROFILE = 'sks-auto-review';
8
8
  export const AUTO_REVIEW_HIGH_PROFILE = 'sks-auto-review-high';
9
9
  export const MAD_HIGH_PROFILE = 'sks-mad-high';
@@ -64,10 +64,12 @@ export async function enableMadHighProfile(opts = {}) {
64
64
  let next = upsertTable(current, `profiles.${MAD_HIGH_PROFILE}`, [
65
65
  `[profiles.${MAD_HIGH_PROFILE}]`,
66
66
  'model = "gpt-5.5"',
67
- 'approval_policy = "never"',
67
+ 'approval_policy = "on-request"',
68
+ `approvals_reviewer = "${AUTO_REVIEW_REVIEWER}"`,
68
69
  'sandbox_mode = "danger-full-access"',
69
70
  'model_reasoning_effort = "high"'
70
71
  ].join('\n'));
72
+ next = upsertAutoReviewPolicy(next);
71
73
  if (!next.endsWith('\n')) next += '\n';
72
74
  await writeTextAtomic(configPath, next);
73
75
  return {
@@ -75,7 +77,8 @@ export async function enableMadHighProfile(opts = {}) {
75
77
  profile_name: MAD_HIGH_PROFILE,
76
78
  launch_args: ['--profile', MAD_HIGH_PROFILE],
77
79
  sandbox_mode: 'danger-full-access',
78
- approval_policy: 'never',
80
+ approval_policy: 'on-request',
81
+ approvals_reviewer: AUTO_REVIEW_REVIEWER,
79
82
  model_reasoning_effort: 'high',
80
83
  scope: 'explicit_launch_only'
81
84
  };
@@ -111,7 +114,7 @@ export function autoReviewSummary(status = {}) {
111
114
  lines.push('Launch high mode with: sks --Auto-review --high');
112
115
  }
113
116
  if (status.legacy_invalid) {
114
- lines.push('', 'Legacy invalid reviewer value found: run sks auto-review enable or sks auto-review disable to rewrite Codex config.');
117
+ lines.push('', 'Legacy reviewer value found: run sks auto-review enable or sks auto-review disable to rewrite Codex config.');
115
118
  }
116
119
  return lines.join('\n');
117
120
  }
@@ -1,16 +1,22 @@
1
1
  import path from 'node:path';
2
+ import fsp from 'node:fs/promises';
2
3
  import { spawnSync } from 'node:child_process';
3
4
  import { exists, packageRoot, projectRoot, runProcess, sha256, which } from './fsx.mjs';
4
5
  import { getCodexInfo } from './codex-adapter.mjs';
5
6
  import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
6
7
 
7
8
  export const SKS_CMUX_LOGO = [
8
- '+----------------------+',
9
- '| ㅅㅋㅅ |',
10
- '| SKS cmux |',
11
- '+----------------------+'
9
+ ' _____ __ __ _____',
10
+ ' / ___// //_// ___/',
11
+ ' \\__ \\/ ,< \\__ \\ ㅅㅋㅅ',
12
+ ' ___/ / /| | ___/ /',
13
+ '/____/_/ |_|/____/',
14
+ 'Sneakoscope Codex cmux'
12
15
  ].join('\n');
13
16
 
17
+ export const CMUX_BREW_COMMAND = 'brew tap manaflow-ai/cmux && brew install --cask cmux';
18
+ export const CMUX_BREW_UPGRADE_COMMAND = 'brew tap manaflow-ai/cmux && brew upgrade --cask cmux';
19
+
14
20
  export function sanitizeCmuxWorkspaceName(input) {
15
21
  const base = String(input || 'sks').trim().replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
16
22
  return (base || 'sks').slice(0, 80);
@@ -29,9 +35,9 @@ export function shellEscape(value) {
29
35
  export function platformCmuxInstallHint() {
30
36
  if (process.platform !== 'darwin') return 'cmux is a native macOS app; install it on macOS 14+ from https://cmux.com or https://github.com/manaflow-ai/cmux.';
31
37
  return [
32
- 'brew tap manaflow-ai/cmux && brew install --cask cmux',
33
- 'then expose the CLI if needed:',
34
- 'sudo ln -sf "/Applications/cmux.app/Contents/Resources/bin/cmux" /usr/local/bin/cmux'
38
+ CMUX_BREW_COMMAND,
39
+ 'then run:',
40
+ 'sks cmux check'
35
41
  ].join(' ');
36
42
  }
37
43
 
@@ -40,17 +46,78 @@ export async function findCmuxBinary() {
40
46
  if (env && await exists(env)) return env;
41
47
  const onPath = await which('cmux').catch(() => null);
42
48
  if (onPath) return onPath;
43
- const appBin = '/Applications/cmux.app/Contents/Resources/bin/cmux';
44
- if (process.platform === 'darwin' && await exists(appBin)) return appBin;
49
+ for (const candidate of cmuxBinaryCandidates()) {
50
+ if (await exists(candidate)) return candidate;
51
+ }
45
52
  return null;
46
53
  }
47
54
 
55
+ export function cmuxBinaryCandidates() {
56
+ if (process.platform !== 'darwin') return [];
57
+ const envApps = String(process.env.SKS_CMUX_APP_PATHS || '')
58
+ .split(path.delimiter)
59
+ .map((entry) => entry.trim())
60
+ .filter(Boolean);
61
+ const appBundles = [
62
+ ...envApps,
63
+ '/Applications/cmux.app',
64
+ '/Applications/Cmux.app',
65
+ '/Applications/CMUX.app',
66
+ path.join(process.env.HOME || '', 'Applications', 'cmux.app'),
67
+ '/opt/homebrew/Caskroom/cmux/latest/cmux.app'
68
+ ].filter(Boolean);
69
+ const candidates = [];
70
+ for (const app of appBundles) {
71
+ candidates.push(
72
+ path.join(app, 'Contents', 'Resources', 'bin', 'cmux'),
73
+ path.join(app, 'Contents', 'MacOS', 'cmux')
74
+ );
75
+ }
76
+ candidates.push('/opt/homebrew/bin/cmux', '/usr/local/bin/cmux');
77
+ return Array.from(new Set(candidates));
78
+ }
79
+
48
80
  export async function cmuxAvailable() {
49
81
  const bin = await findCmuxBinary();
50
82
  if (!bin) return { ok: false, bin: null, version: null, error: 'cmux CLI not found' };
51
- const probe = await runProcess(bin, ['list-workspaces', '--json'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
83
+ const probe = await runProcess(bin, ['version'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
52
84
  const text = `${probe.stdout || ''}${probe.stderr || ''}`.trim();
53
- return { ok: probe.code === 0, bin, version: text || 'cmux CLI', error: probe.code === 0 ? null : text || 'cmux workspace probe failed' };
85
+ return { ok: probe.code === 0, bin, version: text || 'cmux CLI', error: probe.code === 0 ? null : text || 'cmux CLI probe failed' };
86
+ }
87
+
88
+ export async function ensureCmuxInstalled(opts = {}) {
89
+ const before = await cmuxAvailable().catch((err) => ({ ok: false, error: err.message || 'cmux probe failed' }));
90
+ if (before.ok) return { target: 'cmux', status: 'present', cmux: before, command: null };
91
+ if (opts.autoInstall === false || process.env.SKS_NO_CMUX_AUTO_INSTALL === '1') {
92
+ return { target: 'cmux', status: 'missing', cmux: before, command: CMUX_BREW_COMMAND, error: before.error || 'cmux CLI not found' };
93
+ }
94
+ if (process.platform !== 'darwin') {
95
+ return { target: 'cmux', status: 'manual_required', cmux: before, command: platformCmuxInstallHint(), error: before.error || 'cmux is macOS-only' };
96
+ }
97
+ const brew = await which('brew').catch(() => null);
98
+ if (!brew) {
99
+ return { target: 'cmux', status: 'homebrew_missing', cmux: before, command: CMUX_BREW_COMMAND, error: 'Homebrew is required for automatic cmux install' };
100
+ }
101
+ if (!opts.quiet) console.log('cmux CLI missing; installing/updating cmux with Homebrew...');
102
+ const tap = await runProcess(brew, ['tap', 'manaflow-ai/cmux'], { timeoutMs: 180000, maxOutputBytes: 128 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
103
+ const install = tap.code === 0
104
+ ? await installOrUpgradeCmuxCask(brew)
105
+ : tap;
106
+ let after = await cmuxAvailable().catch((err) => ({ ok: false, error: err.message || 'cmux probe failed after install' }));
107
+ if (!after.ok && after.bin) after = await wakeCmuxAndReprobe(after);
108
+ if (after.ok) return { target: 'cmux', status: 'installed', cmux: after, command: CMUX_BREW_COMMAND };
109
+ const installText = `${install.stderr || ''}\n${install.stdout || ''}`.trim();
110
+ const rawError = after.error || installText || 'brew install --cask cmux completed, but no working cmux CLI was found';
111
+ return { target: 'cmux', status: 'failed', cmux: after, command: CMUX_BREW_COMMAND, code: install.code, error: rawError };
112
+ }
113
+
114
+ async function installOrUpgradeCmuxCask(brew) {
115
+ const install = await runProcess(brew, ['install', '--cask', 'cmux'], { timeoutMs: 300000, maxOutputBytes: 256 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
116
+ if (install.code === 0) return install;
117
+ const text = `${install.stderr || ''}\n${install.stdout || ''}`;
118
+ if (!/already installed|to upgrade|brew upgrade/i.test(text)) return install;
119
+ const upgrade = await runProcess(brew, ['upgrade', '--cask', 'cmux'], { timeoutMs: 300000, maxOutputBytes: 256 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
120
+ return upgrade.code === 0 ? upgrade : install;
54
121
  }
55
122
 
56
123
  export function codexLaunchCommand(root, codexBin, codexArgs = []) {
@@ -110,7 +177,7 @@ export function formatCmuxBanner(status = null) {
110
177
  '',
111
178
  'CLI-first runtime:',
112
179
  ' sks open a cmux Codex CLI workspace',
113
- ' sks --mad --high open one-shot MAD-SKS high reasoning workspace',
180
+ ' sks --mad open one-shot MAD full-access auto-review workspace',
114
181
  ' sks team "task" prepare Team mission and cmux multi-line agent view',
115
182
  '',
116
183
  'Useful terminal commands:',
@@ -126,15 +193,29 @@ export function formatCmuxBanner(status = null) {
126
193
  export async function launchCmuxUi(args = [], opts = {}) {
127
194
  const rootArg = readOption(args, '--root', opts.root);
128
195
  const workspaceArg = readOption(args, '--workspace', readOption(args, '--session', opts.workspace || opts.session));
129
- const plan = await buildCmuxLaunchPlan({ ...opts, root: rootArg, workspace: workspaceArg });
196
+ let plan = await buildCmuxLaunchPlan({ ...opts, root: rootArg, workspace: workspaceArg });
197
+ let cmuxRepair = null;
198
+ if (!plan.ready && plan.cmux.bin && !plan.cmux.ok && !args.includes('--json')) {
199
+ const warmed = await wakeCmuxAndReprobe(plan.cmux);
200
+ if (warmed.ok) plan = await buildCmuxLaunchPlan({ ...opts, root: rootArg, workspace: workspaceArg, cmux: warmed });
201
+ }
202
+ if (!plan.ready && !plan.cmux.ok && opts.autoInstallCmux && !args.includes('--json')) {
203
+ cmuxRepair = await ensureCmuxInstalled({ autoInstall: true, quiet: Boolean(opts.quietAutoInstall || args.includes('--quiet')) });
204
+ if (cmuxRepair.cmux?.ok) plan = await buildCmuxLaunchPlan({ ...opts, root: rootArg, workspace: workspaceArg, cmux: cmuxRepair.cmux });
205
+ }
130
206
  if (args.includes('--json')) return { plan };
131
207
  if (!plan.ready && !args.includes('--status-only')) {
132
- console.log(formatCmuxBanner(plan.app));
133
- console.log('\nLaunch blocked:\n');
134
- for (const blocker of Array.from(new Set(plan.blockers))) console.log(`- ${blocker}`);
208
+ printCmuxLaunchBlocked(plan, { concise: opts.conciseBlockers, cmuxRepair });
135
209
  process.exitCode = 1;
136
210
  return { plan };
137
211
  }
212
+ const daemon = await ensureCmuxDaemonReady(plan.cmux);
213
+ if (!daemon.ok && !args.includes('--status-only')) {
214
+ const blocked = { ...plan, ready: false, cmux: { ...plan.cmux, ok: false, error: daemon.error || 'cmux app did not become ready' } };
215
+ printCmuxLaunchBlocked(blocked, { concise: opts.conciseBlockers, cmuxRepair });
216
+ process.exitCode = 1;
217
+ return { plan: blocked };
218
+ }
138
219
  if (!args.includes('--no-open')) await openCmuxApp().catch(() => null);
139
220
  const command = codexLaunchCommand(plan.root, plan.codex.bin, plan.codexArgs);
140
221
  const created = spawnSync(plan.cmux.bin, ['new-workspace', '--cwd', plan.root, '--command', command], { encoding: 'utf8', stdio: args.includes('--quiet') ? 'pipe' : 'inherit' });
@@ -149,10 +230,94 @@ export async function launchCmuxUi(args = [], opts = {}) {
149
230
  return { plan, created: true };
150
231
  }
151
232
 
233
+ function printCmuxLaunchBlocked(plan, opts = {}) {
234
+ if (opts.concise) {
235
+ console.error('SKS cmux launch blocked.');
236
+ if (!plan.cmux.ok) {
237
+ const repair = opts.cmuxRepair;
238
+ const installedButUnhealthy = Boolean(plan.cmux.bin);
239
+ const prefix = repair?.status
240
+ ? `cmux ${repair.status}`
241
+ : installedButUnhealthy
242
+ ? 'cmux app/socket unhealthy'
243
+ : 'cmux missing';
244
+ console.error(`- ${prefix}: ${repair?.error || plan.cmux.error || 'cmux CLI not found'}`);
245
+ console.error(`- ${installedButUnhealthy ? 'Repair command' : 'Install command'}: ${repair?.command || (installedButUnhealthy ? 'sks deps install cmux --yes' : CMUX_BREW_COMMAND)}`);
246
+ }
247
+ if (!plan.codex.bin) console.error('- Codex CLI missing. Install: npm i -g @openai/codex@latest, or set SKS_CODEX_BIN.');
248
+ return;
249
+ }
250
+ console.log(formatCmuxBanner(plan.app));
251
+ console.log('\nLaunch blocked:\n');
252
+ for (const blocker of Array.from(new Set(plan.blockers))) console.log(`- ${blocker}`);
253
+ }
254
+
152
255
  export async function openCmuxApp() {
153
256
  if (process.platform !== 'darwin') return { ok: false, reason: 'not_macos' };
154
257
  const run = await runProcess('open', ['-a', 'cmux'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
155
- return { ok: run.code === 0, stdout: run.stdout || '', stderr: run.stderr || '' };
258
+ if (run.code === 0) return { ok: true, stdout: run.stdout || '', stderr: run.stderr || '' };
259
+ for (const app of ['/Applications/cmux.app', '/Applications/Cmux.app']) {
260
+ if (!await exists(app)) continue;
261
+ const byPath = await runProcess('open', [app], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
262
+ if (byPath.code === 0) return { ok: true, stdout: byPath.stdout || '', stderr: byPath.stderr || '' };
263
+ }
264
+ return { ok: false, stdout: run.stdout || '', stderr: run.stderr || '' };
265
+ }
266
+
267
+ async function wakeCmuxAndReprobe(fallback = {}) {
268
+ return ensureCmuxDaemonReady(fallback);
269
+ }
270
+
271
+ async function ensureCmuxDaemonReady(cmux = {}) {
272
+ if (!cmux?.bin) return { ok: false, error: cmux?.error || 'cmux CLI not found' };
273
+ const first = await cmuxSocketProbe(cmux.bin);
274
+ if (first.ok) return { ...cmux, ok: true, error: null };
275
+ if (process.platform !== 'darwin') return first;
276
+ const opened = await openCmuxApp().catch(() => null);
277
+ if (!opened?.ok) return { ok: false, error: opened?.stderr || opened?.reason || first.error || 'cmux app launch failed' };
278
+ let last = first;
279
+ for (let i = 0; i < 8; i++) {
280
+ await new Promise((resolve) => setTimeout(resolve, 750));
281
+ last = await cmuxSocketProbe(cmux.bin);
282
+ if (last.ok) return { ...cmux, ok: true, error: null };
283
+ }
284
+ if (isRecoverableCmuxSocketError(last.error) && process.env.SKS_CMUX_NO_RESTART !== '1') {
285
+ await restartCmuxApp();
286
+ for (let i = 0; i < 8; i++) {
287
+ await new Promise((resolve) => setTimeout(resolve, 750));
288
+ last = await cmuxSocketProbe(cmux.bin);
289
+ if (last.ok) return { ...cmux, ok: true, error: null };
290
+ }
291
+ }
292
+ return { ok: false, error: last.error || 'cmux socket did not become ready' };
293
+ }
294
+
295
+ async function cmuxSocketProbe(bin) {
296
+ const probe = await runProcess(bin, ['list-workspaces', '--json'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
297
+ const text = `${probe.stdout || ''}${probe.stderr || ''}`.trim();
298
+ return { ok: probe.code === 0, error: probe.code === 0 ? null : text || 'cmux socket probe failed' };
299
+ }
300
+
301
+ function isRecoverableCmuxSocketError(error) {
302
+ return /socket|broken pipe|receive timeout|connection refused/i.test(String(error || ''));
303
+ }
304
+
305
+ async function restartCmuxApp() {
306
+ if (process.platform !== 'darwin') return { ok: false, reason: 'not_macos' };
307
+ const quit = await runProcess('osascript', ['-e', 'tell application "cmux" to quit'], { timeoutMs: 8000, maxOutputBytes: 16 * 1024 }).catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
308
+ if (quit.code !== 0) {
309
+ await runProcess('pkill', ['-TERM', '-f', '/Applications/cmux.app/Contents/MacOS/cmux'], { timeoutMs: 8000, maxOutputBytes: 16 * 1024 }).catch(() => null);
310
+ }
311
+ await new Promise((resolve) => setTimeout(resolve, 1500));
312
+ await removeStaleCmuxSocket().catch(() => null);
313
+ return openCmuxApp();
314
+ }
315
+
316
+ async function removeStaleCmuxSocket() {
317
+ const home = process.env.HOME;
318
+ if (!home) return;
319
+ const sock = path.join(home, 'Library', 'Application Support', 'cmux', 'cmux.sock');
320
+ await fsp.rm(sock, { force: true });
156
321
  }
157
322
 
158
323
  export async function launchCmuxTeamView({ root, missionId, plan = {}, promptFile = null, json = false } = {}) {
package/src/core/fsx.mjs CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
 
8
- export const PACKAGE_VERSION = '0.6.62';
8
+ export const PACKAGE_VERSION = '0.6.66';
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
@@ -404,10 +404,14 @@ export async function initProject(root, opts = {}) {
404
404
 
405
405
  await writeTextAtomic(path.join(root, '.codex', 'config.toml'), `[features]\ncodex_hooks = true\nmulti_agent = true\n\n[agents]\nmax_threads = 6\nmax_depth = 1\n\n${context7ConfigToml()}\n[agents.analysis_scout]\ndescription = "Read-only SKS scout."\nconfig_file = "./agents/analysis-scout.toml"\nnickname_candidates = ["Scout", "Mapper"]\n\n[agents.team_consensus]\ndescription = "SKS planning/debate agent."\nconfig_file = "./agents/team-consensus.toml"\nnickname_candidates = ["Consensus", "Atlas"]\n\n[agents.implementation_worker]\ndescription = "SKS bounded implementation worker."\nconfig_file = "./agents/implementation-worker.toml"\nnickname_candidates = ["Builder", "Mason"]\n\n[agents.db_safety_reviewer]\ndescription = "Read-only DB safety reviewer."\nconfig_file = "./agents/db-safety-reviewer.toml"\nnickname_candidates = ["Sentinel", "Ledger"]\n\n[agents.qa_reviewer]\ndescription = "Read-only QA reviewer."\nconfig_file = "./agents/qa-reviewer.toml"\nnickname_candidates = ["Verifier", "Scout"]\n\n[profiles.sks-task-medium]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "medium"\n\n[profiles.sks-logic-high]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-research-xhigh]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "xhigh"\n\n[profiles.sks-ralph]\nmodel = "gpt-5.5"\napproval_policy = "never"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-research]\nmodel = "gpt-5.5"\napproval_policy = "never"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "xhigh"\n\n[profiles.sks-team]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "high"\n\n[profiles.sks-mad-high]
406
406
  model = "gpt-5.5"
407
- approval_policy = "never"
407
+ approval_policy = "on-request"
408
+ approvals_reviewer = "auto_review"
408
409
  sandbox_mode = "danger-full-access"
409
410
  model_reasoning_effort = "high"
410
411
 
412
+ [auto_review]
413
+ policy = "Deny destructive database operations, credential exfiltration, persistent security weakening, broad file deletion, and writes outside the workspace unless explicitly authorized by the user."
414
+
411
415
  [profiles.sks-default]\nmodel = "gpt-5.5"\napproval_policy = "on-request"\nsandbox_mode = "workspace-write"\nmodel_reasoning_effort = "medium"\n`);
412
416
  created.push('.codex/config.toml');
413
417
 
@@ -334,7 +334,7 @@ export const COMMAND_CATALOG = [
334
334
  { name: 'deps', usage: 'sks deps check|install [cmux|codex|context7|all] [--yes]', description: 'Check or guided-install Node/npm PATH, Codex CLI/App, Context7, Browser Use, Computer Use, cmux, and Homebrew on macOS.' },
335
335
  { name: 'codex-app', usage: 'sks codex-app [check|open]', description: 'Check Codex App install and first-party MCP/plugin readiness, then show app setup files and examples.' },
336
336
  { name: 'cmux', usage: 'sks cmux [check|status] [--workspace name]', description: 'Open the SKS cmux runtime with the ㅅㅋㅅ ASCII status pane and Codex CLI.' },
337
- { name: 'mad-high', usage: 'sks --mad --high', description: 'Open a one-shot cmux Codex CLI workspace with the SKS MAD high full-access profile.' },
337
+ { name: 'mad', usage: 'sks --mad [--high]', description: 'Open a one-shot cmux Codex CLI workspace with the SKS MAD full-access auto-review profile.' },
338
338
  { name: 'auto-review', usage: 'sks auto-review status|enable|start [--high] | sks --Auto-review --high', description: 'Enable Codex automatic approval review and launch SKS cmux with the auto-review profile.' },
339
339
  { name: 'dollar-commands', usage: 'sks dollar-commands [--json]', description: 'List Codex App $ commands such as $DFix and $Team.' },
340
340
  { name: 'dfix', usage: 'sks dfix', description: 'Explain $DFix ultralight design/content fix mode.' },