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 +367 -72
- package/package.json +1 -1
- package/src/cli/main.mjs +97 -24
- package/src/core/auto-review.mjs +8 -5
- package/src/core/cmux-ui.mjs +182 -17
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +5 -1
- package/src/core/routes.mjs +1 -1
package/README.md
CHANGED
|
@@ -2,120 +2,415 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Codex CLI/App harness for `$`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
| Ralph |
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
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
|
-
##
|
|
53
|
+
## Requirements
|
|
35
54
|
|
|
36
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
66
|
-
|
|
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
|
|
204
|
+
sks gx init homepage
|
|
205
|
+
sks gx render homepage --format html
|
|
70
206
|
```
|
|
71
207
|
|
|
72
|
-
|
|
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
|
|
76
|
-
sks
|
|
77
|
-
sks
|
|
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
|
-
|
|
223
|
+
Then open Codex App and use prompt commands directly in the chat. Examples:
|
|
86
224
|
|
|
87
|
-
|
|
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
|
-
|
|
234
|
+
Generated app files include:
|
|
90
235
|
|
|
91
|
-
|
|
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
|
-
|
|
244
|
+
Use `sks dollar-commands` to confirm that terminal discovery and Codex App prompt commands agree.
|
|
94
245
|
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
287
|
+
For the high-reasoning full-access profile:
|
|
103
288
|
|
|
104
|
-
|
|
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
|
-
|
|
401
|
+
`publish:dry` proves the local package is packable. It does not prove npm ownership, OTP, or registry publish permission.
|
|
111
402
|
|
|
112
|
-
##
|
|
403
|
+
## Documentation Style
|
|
113
404
|
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
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.
|
|
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
|
-
|
|
1050
|
-
console.log(
|
|
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], {
|
|
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
|
-
|
|
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 =
|
|
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
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
:
|
|
1179
|
-
|
|
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 (!
|
|
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 = "
|
|
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 = "
|
|
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 = "
|
|
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'));
|
package/src/core/auto-review.mjs
CHANGED
|
@@ -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 = '
|
|
6
|
-
export const LEGACY_AUTO_REVIEW_REVIEWER = '
|
|
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 = "
|
|
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: '
|
|
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
|
|
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
|
}
|
package/src/core/cmux-ui.mjs
CHANGED
|
@@ -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
|
-
'
|
|
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
|
-
|
|
33
|
-
'then
|
|
34
|
-
'
|
|
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
|
|
44
|
-
|
|
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, ['
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 = "
|
|
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
|
|
package/src/core/routes.mjs
CHANGED
|
@@ -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
|
|
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.' },
|