sneakoscope 0.7.41 → 0.7.43
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 +4 -4
- package/package.json +1 -1
- package/src/cli/install-helpers.mjs +38 -2
- package/src/cli/main.mjs +13 -7
- package/src/core/auto-review.mjs +3 -3
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +14 -2
- package/src/core/ppt.mjs +221 -28
- package/src/core/routes.mjs +1 -0
- package/src/core/tmux-ui.mjs +122 -8
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ sks selftest --mock
|
|
|
42
42
|
|
|
43
43
|
| Area | What it does |
|
|
44
44
|
| --- | --- |
|
|
45
|
-
| CLI runtime | Bare `sks` opens or reuses the default tmux Codex CLI workspace. `sks tmux open` remains the explicit form for session/workspace flags, and `sks --mad` launches the high-reasoning
|
|
45
|
+
| CLI runtime | Bare `sks` opens or reuses the default tmux Codex CLI workspace. `sks tmux open` remains the explicit form for session/workspace flags, and `sks --mad` launches the explicit full-access high-reasoning profile. |
|
|
46
46
|
| Codex App commands | Installs generated skills so `$Team`, `$From-Chat-IMG`, `$DFix`, `$QA-LOOP`, `$PPT`, `$Goal`, `$DB`, `$Wiki`, `$Help`, and related routes are visible in prompt workflows. |
|
|
47
47
|
| OpenClaw agents | Generates an OpenClaw skill package so OpenClaw agents can attach `sneakoscope-codex`, enable the `shell` tool, and discover/use SKS commands from the target repo root. |
|
|
48
48
|
| Pipeline plans | Writes `pipeline-plan.json` for stateful routes so the runtime lane, kept stages, skipped stages, verification commands, and no-unrequested-fallback invariant are visible with `sks pipeline plan`. |
|
|
@@ -166,7 +166,7 @@ sks tmux check
|
|
|
166
166
|
sks tmux status --once
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
Bare `sks` creates or reuses the default named tmux session for Codex CLI and attaches to it in an interactive terminal. By default it launches Codex in the SKS fast-high runtime (`--model gpt-5.5 -c model_reasoning_effort="high"`). Override with `SKS_CODEX_MODEL`, `SKS_CODEX_REASONING`,
|
|
169
|
+
Bare `sks` creates or reuses the default named tmux session for Codex CLI and attaches to it in an interactive terminal. By default it launches Codex in the SKS fast-high runtime (`--model gpt-5.5 -c model_reasoning_effort="high"`) with a short animated SKS ASCII intro. Override with `SKS_CODEX_MODEL`, `SKS_CODEX_REASONING`, disable the default model profile with `SKS_CODEX_FAST_HIGH=0`, or disable the intro animation with `SKS_TMUX_LOGO_ANIMATION=0`. Use `sks tmux open` when you need explicit `--workspace` / `--session` flags, `sks tmux check` for readiness without launching, and `sks help` for CLI help. Use `--no-attach` or `SKS_TMUX_NO_AUTO_ATTACH=1` when you only want SKS to create/reuse the session and print the manual attach command.
|
|
170
170
|
|
|
171
171
|
Before opening tmux, SKS checks the installed Codex CLI against npm `@openai/codex@latest`. If a newer version exists, it asks `Y/n`; answering `y` updates automatically with `npm i -g @openai/codex@latest` and then opens tmux with the updated Codex CLI.
|
|
172
172
|
|
|
@@ -183,7 +183,7 @@ Bare `sks` asks this before opening Codex when codex-lb is not configured:
|
|
|
183
183
|
Authenticate and route Codex through codex-lb? [y/N]
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
Answering `y` asks for the hosted domain and API key, writes `~/.codex/config.toml`, stores the key in `~/.codex/sks-codex-lb.env` with mode `0600`, and sources that env file before launching Codex in tmux. When codex-lb is configured from this prompt, SKS opens a fresh tmux session for that launch so the new key is loaded by the Codex process immediately. The generated provider config follows the codex-lb README's Codex CLI API-key setup:
|
|
186
|
+
Answering `y` asks for the hosted domain and API key, writes `~/.codex/config.toml`, stores the key in `~/.codex/sks-codex-lb.env` with mode `0600`, syncs Codex CLI API-key auth through `codex login --with-api-key`, and sources that env file before launching Codex in tmux. When codex-lb is configured from this prompt, SKS opens a fresh tmux session for that launch so the new key is loaded by the Codex process immediately. The generated provider config follows the codex-lb README's Codex CLI API-key setup:
|
|
187
187
|
|
|
188
188
|
```toml
|
|
189
189
|
model_provider = "codex-lb"
|
|
@@ -204,7 +204,7 @@ sks --mad
|
|
|
204
204
|
sks --mad --yes
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
This creates/uses the `sks-mad-high` Codex profile for a one-shot full-access, high-reasoning tmux session with `
|
|
207
|
+
This creates/uses the `sks-mad-high` Codex profile for a one-shot full-access, high-reasoning tmux session with `sandbox_mode = "danger-full-access"` and `approval_policy = "never"`, then launches Codex with `--sandbox danger-full-access --ask-for-approval never` and attaches to the session in an interactive terminal. It is scoped to that explicit command and does not change normal SKS/DB safety defaults. Repeat launches reuse the same named SKS MAD tmux session.
|
|
208
208
|
|
|
209
209
|
MAD does not disable the pipeline contract: stages, executors, reviewers, and auto-review policy still must not invent unrequested fallback implementation code. If the requested path cannot be implemented, SKS should block with evidence rather than add substitute behavior.
|
|
210
210
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.43",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -143,7 +143,8 @@ export async function configureCodexLb(opts = {}) {
|
|
|
143
143
|
await writeTextAtomic(envPath, `export CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`);
|
|
144
144
|
await fsp.chmod(envPath, 0o600).catch(() => {});
|
|
145
145
|
process.env.CODEX_LB_API_KEY = apiKey;
|
|
146
|
-
|
|
146
|
+
const codexLogin = await syncCodexApiKeyLogin(apiKey, { home });
|
|
147
|
+
return { ok: true, status: 'configured', config_path: configPath, env_path: envPath, base_url: baseUrl, env_key: 'CODEX_LB_API_KEY', codex_login: codexLogin };
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
export async function codexLbStatus(opts = {}) {
|
|
@@ -172,7 +173,11 @@ export async function maybePromptCodexLbSetupForLaunch(args = [], opts = {}) {
|
|
|
172
173
|
if (args.includes('--json') || args.includes('--skip-codex-lb') || process.env.SKS_SKIP_CODEX_LB_PROMPT === '1') return { status: 'skipped' };
|
|
173
174
|
if (!canAskYesNo()) return { status: 'non_interactive' };
|
|
174
175
|
const status = await codexLbStatus(opts);
|
|
175
|
-
if (status.ok)
|
|
176
|
+
if (status.ok) {
|
|
177
|
+
const codexLogin = await ensureCodexLbLoginFromEnv(status, opts);
|
|
178
|
+
if (codexLogin.status === 'synced') console.log('codex-lb auth synced with Codex CLI.');
|
|
179
|
+
return { status: 'present', ...status, codex_login: codexLogin };
|
|
180
|
+
}
|
|
176
181
|
const useCodexLb = (await askPostinstallQuestion('\nAuthenticate and route Codex through codex-lb? [y/N] ')).trim();
|
|
177
182
|
if (!/^(y|yes|예|네|응)$/i.test(useCodexLb)) return { status: 'continued_to_codex' };
|
|
178
183
|
const host = (await askPostinstallQuestion('codex-lb host domain [http://127.0.0.1:2455]: ')).trim() || 'http://127.0.0.1:2455';
|
|
@@ -183,6 +188,28 @@ export async function maybePromptCodexLbSetupForLaunch(args = [], opts = {}) {
|
|
|
183
188
|
return configured;
|
|
184
189
|
}
|
|
185
190
|
|
|
191
|
+
async function ensureCodexLbLoginFromEnv(status = {}, opts = {}) {
|
|
192
|
+
const home = opts.home || process.env.HOME || os.homedir();
|
|
193
|
+
const envPath = opts.envPath || status.env_path || codexLbEnvPath(home);
|
|
194
|
+
const apiKey = parseCodexLbEnvKey(await readText(envPath, ''));
|
|
195
|
+
if (!apiKey) return { ok: false, status: 'missing_env_key' };
|
|
196
|
+
return syncCodexApiKeyLogin(apiKey, { ...opts, home });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function syncCodexApiKeyLogin(apiKey, opts = {}) {
|
|
200
|
+
const home = opts.home || process.env.HOME || os.homedir();
|
|
201
|
+
const codexHome = opts.codexHome || path.join(home, '.codex');
|
|
202
|
+
const codexBin = opts.codexBin || (await getCodexInfo().catch(() => ({}))).bin || await which('codex').catch(() => null);
|
|
203
|
+
if (!codexBin) return { ok: false, status: 'codex_missing' };
|
|
204
|
+
await ensureDir(codexHome);
|
|
205
|
+
const env = { HOME: home, CODEX_HOME: codexHome, CODEX_LB_API_KEY: apiKey };
|
|
206
|
+
const current = await runProcess(codexBin, ['login', 'status'], { env, timeoutMs: 10000, maxOutputBytes: 8192 });
|
|
207
|
+
if (current.code === 0 && !/not logged in/i.test(`${current.stdout}\n${current.stderr}`)) return { ok: true, status: 'present' };
|
|
208
|
+
const login = await runProcess(codexBin, ['login', '--with-api-key'], { input: `${apiKey}\n`, env, timeoutMs: 15000, maxOutputBytes: 8192 });
|
|
209
|
+
if (login.code === 0) return { ok: true, status: 'synced' };
|
|
210
|
+
return { ok: false, status: 'login_failed', error: (login.stderr || login.stdout || 'codex login failed').trim() };
|
|
211
|
+
}
|
|
212
|
+
|
|
186
213
|
function upsertCodexLbConfig(text = '', baseUrl) {
|
|
187
214
|
let next = upsertTopLevelTomlString(text, 'model_provider', 'codex-lb');
|
|
188
215
|
const block = [
|
|
@@ -235,6 +262,15 @@ function shellSingleQuote(value) {
|
|
|
235
262
|
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
236
263
|
}
|
|
237
264
|
|
|
265
|
+
function parseCodexLbEnvKey(text = '') {
|
|
266
|
+
const match = String(text || '').match(/^\s*(?:export\s+)?CODEX_LB_API_KEY\s*=\s*(.+?)\s*$/m);
|
|
267
|
+
if (!match) return '';
|
|
268
|
+
const raw = match[1].trim();
|
|
269
|
+
if (raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1).replace(/'\\''/g, "'");
|
|
270
|
+
if (raw.startsWith('"') && raw.endsWith('"')) return raw.slice(1, -1).replace(/\\"/g, '"');
|
|
271
|
+
return raw;
|
|
272
|
+
}
|
|
273
|
+
|
|
238
274
|
function escapeRegExp(value) {
|
|
239
275
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
240
276
|
}
|
package/src/cli/main.mjs
CHANGED
|
@@ -1005,11 +1005,11 @@ async function madHighCommand(args = []) {
|
|
|
1005
1005
|
return;
|
|
1006
1006
|
}
|
|
1007
1007
|
const profile = await enableMadHighProfile();
|
|
1008
|
-
console.log(`SKS MAD
|
|
1009
|
-
console.log('Scope: explicit tmux launch only;
|
|
1008
|
+
console.log(`SKS MAD full-access profile ready: ${madHighProfileName()}`);
|
|
1009
|
+
console.log('Scope: explicit tmux launch only; Codex opens with danger-full-access sandbox and approval_policy=never.');
|
|
1010
1010
|
const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', `sks-mad-${defaultTmuxSessionName(process.cwd())}`));
|
|
1011
1011
|
return launchTmuxUi([...cleanArgs, '--workspace', workspace], {
|
|
1012
|
-
codexArgs:
|
|
1012
|
+
codexArgs: profile.launch_args,
|
|
1013
1013
|
autoInstallTmux: !flag(args, '--no-auto-install-tmux'),
|
|
1014
1014
|
conciseBlockers: true
|
|
1015
1015
|
});
|
|
@@ -1927,6 +1927,7 @@ async function selftest() {
|
|
|
1927
1927
|
if (postinstallConflictPrompt.code !== 0 || !String(postinstallConflictPrompt.stdout || '').includes('Goal: completely remove the conflicting Codex harnesses')) throw new Error('selftest failed: interactive postinstall prompt did not print cleanup prompt');
|
|
1928
1928
|
const postinstallSetupTmp = tmpdir();
|
|
1929
1929
|
await writeJsonAtomic(path.join(postinstallSetupTmp, 'package.json'), { name: 'postinstall-setup-smoke', version: '0.0.0' });
|
|
1930
|
+
await ensureDir(path.join(postinstallSetupTmp, '.git'));
|
|
1930
1931
|
const postinstallSetup = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'postinstall'], { cwd: postinstallSetupTmp, env: { INIT_CWD: postinstallSetupTmp, HOME: path.join(postinstallSetupTmp, 'home'), SKS_SKIP_POSTINSTALL_SHIM: '1', SKS_SKIP_POSTINSTALL_CONTEXT7: '1', SKS_SKIP_POSTINSTALL_GETDESIGN: '1', SKS_SKIP_CLI_TOOLS: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
|
|
1931
1932
|
if (postinstallSetup.code !== 0) throw new Error(`selftest failed: postinstall setup exited ${postinstallSetup.code}: ${postinstallSetup.stderr}`);
|
|
1932
1933
|
if (await exists(path.join(postinstallSetupTmp, '.agents', 'skills', 'agent-team', 'SKILL.md'))) throw new Error('selftest failed: postinstall installed deprecated agent-team fallback skill');
|
|
@@ -1964,6 +1965,7 @@ async function selftest() {
|
|
|
1964
1965
|
if (!String(postinstallNoMarker.stdout || '').includes('no project marker found; auto-running global SKS runtime bootstrap')) throw new Error('selftest failed: no-marker postinstall did not report global runtime bootstrap');
|
|
1965
1966
|
if (!(await exists(path.join(postinstallNoMarkerGlobalRoot, '.sneakoscope', 'manifest.json')))) throw new Error('selftest failed: no-marker postinstall did not bootstrap global runtime root');
|
|
1966
1967
|
if (await exists(path.join(postinstallNoMarkerCwd, '.sneakoscope'))) throw new Error('selftest failed: no-marker postinstall polluted install cwd');
|
|
1968
|
+
if (await exists(path.join(postinstallNoMarkerGlobalRoot, '.gitignore'))) throw new Error('selftest failed: global runtime bootstrap without project git wrote shared .gitignore');
|
|
1967
1969
|
const bootstrapJsonTmp = tmpdir();
|
|
1968
1970
|
await writeJsonAtomic(path.join(bootstrapJsonTmp, 'package.json'), { name: 'bootstrap-json-smoke', version: '0.0.0' });
|
|
1969
1971
|
const bootstrapJson = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'bootstrap', '--json'], { cwd: bootstrapJsonTmp, env: { HOME: path.join(bootstrapJsonTmp, 'home'), SKS_SKIP_POSTINSTALL_GLOBAL_SKILLS: '1', SKS_SKIP_CLI_TOOLS: '1' }, timeoutMs: 30000, maxOutputBytes: 256 * 1024 });
|
|
@@ -1987,9 +1989,9 @@ async function selftest() {
|
|
|
1987
1989
|
const madProfilePath = path.join(tmp, 'mad-codex-config.toml');
|
|
1988
1990
|
const madProfile = await enableMadHighProfile({ configPath: madProfilePath });
|
|
1989
1991
|
const madProfileText = await safeReadText(madProfilePath);
|
|
1990
|
-
if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "
|
|
1992
|
+
if (madProfile.profile_name !== 'sks-mad-high' || !madProfileText.includes('sandbox_mode = "danger-full-access"') || !madProfileText.includes('approval_policy = "never"') || !madProfileText.includes('approvals_reviewer = "auto_review"') || !madProfile.launch_args.includes('--sandbox') || !madProfile.launch_args.includes('danger-full-access') || !madProfile.launch_args.includes('--ask-for-approval') || !madProfile.launch_args.includes('never') || !madProfileText.includes('model_reasoning_effort = "high"') || !madProfileText.includes('unrequested fallback implementation code')) throw new Error('selftest failed: MAD high profile is not Codex full-access high with fallback-code guard');
|
|
1991
1993
|
if (!isMadHighLaunch(['--mad', '--high']) || isMadHighLaunch(['db', '--mad'])) throw new Error('selftest failed: MAD high launch flag parsing is not top-level only');
|
|
1992
|
-
const workspacePlan = { session: 'sks-mad-selftest', root: tmp, codexArgs:
|
|
1994
|
+
const workspacePlan = { session: 'sks-mad-selftest', root: tmp, codexArgs: madProfile.launch_args };
|
|
1993
1995
|
const tmuxSyntax = runTmuxLaunchPlanSyntaxCheck(workspacePlan);
|
|
1994
1996
|
if (!tmuxSyntax.ok || !tmuxSyntax.command.includes('tmux attach-session -t sks-mad-selftest')) throw new Error('selftest failed: MAD tmux attach plan is not stable by session name');
|
|
1995
1997
|
const tmuxOpenArgs = buildTmuxOpenArgs(workspacePlan);
|
|
@@ -2007,9 +2009,11 @@ async function selftest() {
|
|
|
2007
2009
|
const codexLbSetupJson = JSON.parse(codexLbSetup.stdout);
|
|
2008
2010
|
const codexLbConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
2009
2011
|
const codexLbEnv = await safeReadText(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'));
|
|
2010
|
-
|
|
2012
|
+
const codexLbAuth = await safeReadText(path.join(codexLbHome, '.codex', 'auth.json'));
|
|
2013
|
+
if (!codexLbSetupJson.ok || codexLbSetupJson.base_url !== 'https://lb.example.test/backend-api/codex' || !codexLbConfig.includes('model_provider = "codex-lb"') || !codexLbConfig.includes('[model_providers.codex-lb]') || !codexLbEnv.includes("CODEX_LB_API_KEY='sk-test'") || !codexLbAuth.includes('"auth_mode": "apikey"')) throw new Error('selftest failed: codex-lb setup did not write provider config, env key, and Codex API-key auth');
|
|
2011
2014
|
const codexLbLaunch = codexLaunchCommand(tmp, 'codex', []);
|
|
2012
2015
|
if (!codexLbLaunch.includes('sks-codex-lb.env')) throw new Error('selftest failed: tmux launch command does not source codex-lb env file');
|
|
2016
|
+
if (!codexLbLaunch.includes('SKS_TMUX_LOGO_ANIMATION') || !codexLbLaunch.includes('SNEAKOSCOPE CODEX')) throw new Error('selftest failed: tmux launch command does not include the animated SKS logo intro');
|
|
2013
2017
|
if (!shouldAutoAttachTmux(['--mad'], {}, { stdin: { isTTY: true }, stdout: { isTTY: true } })) throw new Error('selftest failed: MAD tmux launch does not auto-attach in an interactive terminal');
|
|
2014
2018
|
if (shouldAutoAttachTmux(['--mad', '--json'], {}, { stdin: { isTTY: true }, stdout: { isTTY: true } })) throw new Error('selftest failed: MAD tmux json mode should not auto-attach');
|
|
2015
2019
|
if (shouldAutoAttachTmux(['--mad', '--no-attach'], {}, { stdin: { isTTY: true }, stdout: { isTTY: true } })) throw new Error('selftest failed: MAD tmux --no-attach should remain print-only');
|
|
@@ -2624,6 +2628,7 @@ async function selftest() {
|
|
|
2624
2628
|
if (!codexConfigText.includes('multi_agent = true')) throw new Error('selftest failed: multi_agent not enabled');
|
|
2625
2629
|
if (!hasContext7ConfigText(codexConfigText)) throw new Error('selftest failed: Context7 MCP not configured');
|
|
2626
2630
|
if (!codexConfigText.includes('[profiles.sks-task-low]') || !codexConfigText.includes('[profiles.sks-task-medium]') || !codexConfigText.includes('[profiles.sks-logic-high]') || !codexConfigText.includes('[profiles.sks-fast-high]') || !codexConfigText.includes('[profiles.sks-research-xhigh]') || !codexConfigText.includes('[profiles.sks-mad-high]')) throw new Error('selftest failed: GPT-5.5 reasoning profiles not configured');
|
|
2631
|
+
if (!/\[profiles\.sks-mad-high\][\s\S]*?approval_policy = "never"[\s\S]*?sandbox_mode = "danger-full-access"/.test(codexConfigText)) throw new Error('selftest failed: generated sks-mad-high profile is not full access');
|
|
2627
2632
|
if (!codexConfigText.includes('[agents.analysis_scout]')) throw new Error('selftest failed: analysis_scout agent not configured');
|
|
2628
2633
|
if (!codexConfigText.includes('[agents.team_consensus]')) throw new Error('selftest failed: team_consensus agent not configured');
|
|
2629
2634
|
const preservedConfigTmp = tmpdir();
|
|
@@ -2631,7 +2636,7 @@ async function selftest() {
|
|
|
2631
2636
|
await writeTextAtomic(path.join(preservedConfigTmp, '.codex', 'config.toml'), '[features]\nfast_mode_ui = true\n\n[user.fast_mode]\nvisible = true\n');
|
|
2632
2637
|
await initProject(preservedConfigTmp, {});
|
|
2633
2638
|
const preservedConfig = await safeReadText(path.join(preservedConfigTmp, '.codex', 'config.toml'));
|
|
2634
|
-
if (!preservedConfig.includes('fast_mode_ui = true') || !preservedConfig.includes('[user.fast_mode]') || !preservedConfig.includes('visible = true')) throw new Error('selftest failed: Codex config merge dropped
|
|
2639
|
+
if (!preservedConfig.includes('fast_mode_ui = true') || !preservedConfig.includes('[user.fast_mode]') || !preservedConfig.includes('visible = true') || !preservedConfig.includes('enabled = true') || !preservedConfig.includes('default_profile = "sks-fast-high"')) throw new Error('selftest failed: Codex config merge dropped or failed to enable Fast mode settings');
|
|
2635
2640
|
if (!preservedConfig.includes('codex_hooks = true') || !preservedConfig.includes('[profiles.sks-fast-high]')) throw new Error('selftest failed: Codex config merge did not add SKS managed settings');
|
|
2636
2641
|
const autoReviewHome = path.join(tmp, 'auto-review-home');
|
|
2637
2642
|
const autoReviewEnv = { HOME: autoReviewHome };
|
|
@@ -3032,6 +3037,7 @@ async function selftest() {
|
|
|
3032
3037
|
if (!pptHtml.includes('<html') || pptHtml.includes('gradient')) throw new Error('selftest failed: PPT HTML artifact missing or over-designed');
|
|
3033
3038
|
const pptStyleTokens = await readJson(path.join(pptMission.dir, 'ppt-style-tokens.json'));
|
|
3034
3039
|
if (pptStyleTokens.design_policy?.design_ssot?.authority !== DESIGN_SYSTEM_SSOT.authority_file || !pptStyleTokens.design_policy?.source_inputs?.some((entry) => entry.url === AWESOME_DESIGN_MD_REFERENCE.url && entry.role === 'source_input_for_ssot') || !pptStyleTokens.design_policy?.anti_generic_ai_style) throw new Error('selftest failed: PPT style tokens missing fused design SSOT/source-input anti-generic policy');
|
|
3040
|
+
if (!pptStyleTokens.design_policy?.design_reference_selection?.primary?.id?.startsWith('awesome-design-md:') || !pptStyleTokens.design_policy?.design_reference_selection?.selected_sources?.length || !pptStyleTokens.layout?.composition || !pptStyleTokens.layout?.treatment) throw new Error('selftest failed: PPT style tokens did not select and apply a concrete awesome-design-md reference profile');
|
|
3035
3041
|
if (JSON.stringify(pptStyleTokens.design_policy?.pipeline_allowlist?.required_skills || []) !== JSON.stringify(PPT_PIPELINE_SKILL_ALLOWLIST) || !pptStyleTokens.design_policy?.pipeline_allowlist?.ignore_installed_out_of_pipeline_skills || !(pptStyleTokens.design_policy?.pipeline_allowlist?.ignored_design_skills_even_if_installed || []).includes('design-artifact-expert') || !/AI-like/.test(pptStyleTokens.design_policy?.pipeline_allowlist?.anti_ai_design_goal || '')) throw new Error('selftest failed: PPT style tokens missing skill/MCP allowlist enforcement');
|
|
3036
3042
|
const audienceScript = pptHtml.match(/id="ppt-audience-strategy">([^<]+)<\/script>/);
|
|
3037
3043
|
if (!audienceScript) throw new Error('selftest failed: PPT HTML missing audience strategy script data');
|
package/src/core/auto-review.mjs
CHANGED
|
@@ -64,7 +64,7 @@ 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 = "never"',
|
|
68
68
|
`approvals_reviewer = "${AUTO_REVIEW_REVIEWER}"`,
|
|
69
69
|
'sandbox_mode = "danger-full-access"',
|
|
70
70
|
'model_reasoning_effort = "high"'
|
|
@@ -75,9 +75,9 @@ export async function enableMadHighProfile(opts = {}) {
|
|
|
75
75
|
return {
|
|
76
76
|
config_path: configPath,
|
|
77
77
|
profile_name: MAD_HIGH_PROFILE,
|
|
78
|
-
launch_args: ['--profile', MAD_HIGH_PROFILE],
|
|
78
|
+
launch_args: ['--profile', MAD_HIGH_PROFILE, '--sandbox', 'danger-full-access', '--ask-for-approval', 'never'],
|
|
79
79
|
sandbox_mode: 'danger-full-access',
|
|
80
|
-
approval_policy: '
|
|
80
|
+
approval_policy: 'never',
|
|
81
81
|
approvals_reviewer: AUTO_REVIEW_REVIEWER,
|
|
82
82
|
model_reasoning_effort: 'high',
|
|
83
83
|
scope: 'explicit_launch_only'
|
package/src/core/fsx.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.7.
|
|
8
|
+
export const PACKAGE_VERSION = '0.7.43';
|
|
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
|
@@ -113,8 +113,9 @@ export async function initProject(root, opts = {}) {
|
|
|
113
113
|
'.sneakoscope/state', '.sneakoscope/missions', '.sneakoscope/db', '.sneakoscope/bus', '.sneakoscope/hproof', '.sneakoscope/db', '.sneakoscope/wiki', '.sneakoscope/skills', '.sneakoscope/memory/q0_raw', '.sneakoscope/memory/q1_evidence', '.sneakoscope/memory/q2_facts', '.sneakoscope/memory/q3_tags', '.sneakoscope/memory/q4_bits', '.sneakoscope/gx/cartridges', '.sneakoscope/model/fingerprints', '.sneakoscope/genome/candidates', '.sneakoscope/trajectories/raw', '.sneakoscope/locks', '.sneakoscope/tmp', '.sneakoscope/arenas', '.sneakoscope/reports', '.codex', '.codex/agents', '.agents/skills'
|
|
114
114
|
];
|
|
115
115
|
for (const d of dirs) await ensureDir(path.join(root, d));
|
|
116
|
+
const sharedIgnoreWanted = !localOnly && await shouldWriteSharedGitIgnore(root, installScope);
|
|
116
117
|
const localExclude = localOnly ? await ensureLocalOnlyGitExclude(root) : null;
|
|
117
|
-
const sharedIgnore =
|
|
118
|
+
const sharedIgnore = sharedIgnoreWanted ? await ensureSharedGitIgnore(root) : null;
|
|
118
119
|
if (localExclude?.path) created.push(`${path.relative(root, localExclude.path)} local-only excludes`);
|
|
119
120
|
if (sharedIgnore?.changed) created.push(`${path.relative(root, sharedIgnore.path)} SKS generated files ignore`);
|
|
120
121
|
|
|
@@ -429,6 +430,10 @@ function mergeManagedCodexConfigToml(existingContent = '') {
|
|
|
429
430
|
let next = String(existingContent || '').trimEnd();
|
|
430
431
|
next = upsertTomlTableKey(next, 'features', 'codex_hooks = true');
|
|
431
432
|
next = upsertTomlTableKey(next, 'features', 'multi_agent = true');
|
|
433
|
+
next = upsertTomlTableKey(next, 'features', 'fast_mode_ui = true');
|
|
434
|
+
next = upsertTomlTableKey(next, 'user.fast_mode', 'visible = true');
|
|
435
|
+
next = upsertTomlTableKey(next, 'user.fast_mode', 'enabled = true');
|
|
436
|
+
next = upsertTomlTableKey(next, 'user.fast_mode', 'default_profile = "sks-fast-high"');
|
|
432
437
|
next = upsertTomlTableKey(next, 'agents', 'max_threads = 6');
|
|
433
438
|
next = upsertTomlTableKey(next, 'agents', 'max_depth = 1');
|
|
434
439
|
for (const block of managedCodexConfigBlocks()) {
|
|
@@ -452,7 +457,7 @@ function managedCodexConfigBlocks() {
|
|
|
452
457
|
{ table: 'profiles.sks-research-xhigh', text: profileConfigBlock('sks-research-xhigh', 'xhigh') },
|
|
453
458
|
{ table: 'profiles.sks-research', text: profileConfigBlock('sks-research', 'xhigh', { approval: 'never' }) },
|
|
454
459
|
{ table: 'profiles.sks-team', text: profileConfigBlock('sks-team', 'high') },
|
|
455
|
-
{ table: 'profiles.sks-mad-high', text: profileConfigBlock('sks-mad-high', 'high', { sandbox: 'danger-full-access', approvalsReviewer: 'auto_review' }) },
|
|
460
|
+
{ table: 'profiles.sks-mad-high', text: profileConfigBlock('sks-mad-high', 'high', { approval: 'never', sandbox: 'danger-full-access', approvalsReviewer: 'auto_review' }) },
|
|
456
461
|
{
|
|
457
462
|
table: 'auto_review',
|
|
458
463
|
text: '[auto_review]\npolicy = "Deny destructive database operations, credential exfiltration, persistent security weakening, broad file deletion, writes outside the workspace, and unrequested fallback implementation code unless explicitly authorized by the user or sealed decision contract."'
|
|
@@ -613,6 +618,13 @@ async function ensureSharedGitIgnore(root) {
|
|
|
613
618
|
return { path: ignorePath, patterns, changed: true };
|
|
614
619
|
}
|
|
615
620
|
|
|
621
|
+
async function shouldWriteSharedGitIgnore(root, installScope) {
|
|
622
|
+
if (normalizeInstallScope(installScope) === 'project') return true;
|
|
623
|
+
if (await exists(path.join(root, '.git'))) return true;
|
|
624
|
+
if (await exists(path.join(root, '.gitignore'))) return true;
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
|
|
616
628
|
async function ensureLocalOnlyGitExclude(root) {
|
|
617
629
|
const gitDir = await resolveGitDir(root);
|
|
618
630
|
if (!gitDir) return { path: null, patterns: [] };
|
package/src/core/ppt.mjs
CHANGED
|
@@ -16,6 +16,99 @@ export const PPT_CLEANUP_REPORT_ARTIFACT = 'ppt-cleanup-report.json';
|
|
|
16
16
|
export const PPT_PARALLEL_REPORT_ARTIFACT = 'ppt-parallel-report.json';
|
|
17
17
|
export const PPT_TEMP_DIR = 'ppt-tmp';
|
|
18
18
|
|
|
19
|
+
const PPT_DESIGN_REFERENCE_PROFILES = Object.freeze([
|
|
20
|
+
{
|
|
21
|
+
id: 'awesome-design-md:ibm',
|
|
22
|
+
name: 'IBM Carbon enterprise',
|
|
23
|
+
source_url: 'https://raw.githubusercontent.com/VoltAgent/awesome-design-md/main/design-md/ibm/DESIGN.md',
|
|
24
|
+
source_summary: 'enterprise Carbon-style system: white surfaces, charcoal text, IBM Blue as the single accent, flat square tiles, thin rules, no shadow',
|
|
25
|
+
keywords: ['enterprise', 'b2b', 'investor', 'vc', 'strategy', 'proposal', 'board', 'finance', 'risk', 'compliance', '운영', '투자', '의사결정', '리스크', '전략'],
|
|
26
|
+
tokens: {
|
|
27
|
+
bg: '#ffffff',
|
|
28
|
+
text: '#161616',
|
|
29
|
+
muted: '#525252',
|
|
30
|
+
primary: '#0f62fe',
|
|
31
|
+
accent: '#393939',
|
|
32
|
+
surface: '#f4f4f4',
|
|
33
|
+
rule: '#e0e0e0',
|
|
34
|
+
display_px: 64,
|
|
35
|
+
body_px: 28,
|
|
36
|
+
caption_px: 15,
|
|
37
|
+
line_height: 1.36,
|
|
38
|
+
radius_px: 2,
|
|
39
|
+
treatment: 'flat_thin_rules_no_shadow',
|
|
40
|
+
composition: 'enterprise_evidence_grid',
|
|
41
|
+
mono_label: 'uppercase technical labels, sparse blue accent, source-visible rows'
|
|
42
|
+
},
|
|
43
|
+
applied_rules: [
|
|
44
|
+
'use white/charcoal enterprise canvas',
|
|
45
|
+
'reserve IBM Blue for one decision/action accent',
|
|
46
|
+
'prefer thin rules and square evidence rows over decorative cards',
|
|
47
|
+
'avoid shadows, gradients, and ornamental surfaces'
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'awesome-design-md:vercel',
|
|
52
|
+
name: 'Vercel developer infrastructure',
|
|
53
|
+
source_url: 'https://raw.githubusercontent.com/VoltAgent/awesome-design-md/main/design-md/vercel/DESIGN.md',
|
|
54
|
+
source_summary: 'developer-infrastructure minimalism: white canvas, near-black type, shadow-as-border, mono technical labels, functional blue/red/pink workflow accents',
|
|
55
|
+
keywords: ['developer', 'devtools', 'api', 'sdk', 'cloud', 'infra', 'saas', 'technical', 'codex', 'ai', 'agent', '배포', '개발자', '기술', '자동화'],
|
|
56
|
+
tokens: {
|
|
57
|
+
bg: '#ffffff',
|
|
58
|
+
text: '#171717',
|
|
59
|
+
muted: '#4d4d4d',
|
|
60
|
+
primary: '#0072f5',
|
|
61
|
+
accent: '#ff5b4f',
|
|
62
|
+
surface: '#fafafa',
|
|
63
|
+
rule: '#ebebeb',
|
|
64
|
+
display_px: 66,
|
|
65
|
+
body_px: 28,
|
|
66
|
+
caption_px: 14,
|
|
67
|
+
line_height: 1.34,
|
|
68
|
+
radius_px: 8,
|
|
69
|
+
treatment: 'shadow_as_border_minimal_depth',
|
|
70
|
+
composition: 'technical_pipeline_grid',
|
|
71
|
+
mono_label: 'mono labels, workflow accent only when it clarifies sequence'
|
|
72
|
+
},
|
|
73
|
+
applied_rules: [
|
|
74
|
+
'use near-black text on a white technical canvas',
|
|
75
|
+
'show structure through shadow-as-border or one-pixel rules',
|
|
76
|
+
'use mono labels for sources and technical evidence',
|
|
77
|
+
'keep color functional rather than decorative'
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'awesome-design-md:linear',
|
|
82
|
+
name: 'Linear precision operations',
|
|
83
|
+
source_url: 'https://github.com/VoltAgent/awesome-design-md',
|
|
84
|
+
source_summary: 'ultra-minimal precise product-management system: restrained neutral surfaces, exact spacing, one controlled purple accent',
|
|
85
|
+
keywords: ['roadmap', 'product', 'ops', 'workflow', 'issue', 'planning', 'productivity', '운영', '워크플로우', '프로덕트', '계획'],
|
|
86
|
+
tokens: {
|
|
87
|
+
bg: '#f7f8fb',
|
|
88
|
+
text: '#101114',
|
|
89
|
+
muted: '#5f6673',
|
|
90
|
+
primary: '#5e6ad2',
|
|
91
|
+
accent: '#26a69a',
|
|
92
|
+
surface: '#ffffff',
|
|
93
|
+
rule: '#dfe3ea',
|
|
94
|
+
display_px: 62,
|
|
95
|
+
body_px: 27,
|
|
96
|
+
caption_px: 14,
|
|
97
|
+
line_height: 1.38,
|
|
98
|
+
radius_px: 6,
|
|
99
|
+
treatment: 'precise_subtle_product_grid',
|
|
100
|
+
composition: 'operational_decision_matrix',
|
|
101
|
+
mono_label: 'compact status labels, dense but quiet operations layout'
|
|
102
|
+
},
|
|
103
|
+
applied_rules: [
|
|
104
|
+
'use a quiet operational canvas with dense hierarchy',
|
|
105
|
+
'keep the purple accent sparse and semantic',
|
|
106
|
+
'make comparison rows easy to scan',
|
|
107
|
+
'avoid marketing-style hero composition'
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
]);
|
|
111
|
+
|
|
19
112
|
export const PPT_REQUIRED_GATE_FIELDS = Object.freeze([
|
|
20
113
|
'clarification_contract_sealed',
|
|
21
114
|
'audience_strategy_sealed',
|
|
@@ -282,6 +375,13 @@ export function buildPptStoryboard(contract = {}, audience = buildPptAudienceStr
|
|
|
282
375
|
|
|
283
376
|
export function buildPptStyleTokens(contract = {}) {
|
|
284
377
|
const korean = /[ㄱ-ㅎ가-힣]/.test(`${contract.prompt || ''} ${JSON.stringify(contract.answers || {})}`);
|
|
378
|
+
const reference = selectPptDesignReference(contract);
|
|
379
|
+
const refTokens = reference.applied_token_profile.color;
|
|
380
|
+
const fontStack = korean
|
|
381
|
+
? '"Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Noto Sans KR", sans-serif'
|
|
382
|
+
: reference.primary.id.endsWith(':ibm')
|
|
383
|
+
? '"IBM Plex Sans", -apple-system, BlinkMacSystemFont, "SF Pro Display", Inter, Arial, sans-serif'
|
|
384
|
+
: '-apple-system, BlinkMacSystemFont, "SF Pro Display", Inter, "Helvetica Neue", Arial, sans-serif';
|
|
285
385
|
return {
|
|
286
386
|
schema_version: 1,
|
|
287
387
|
created_at: nowIso(),
|
|
@@ -294,23 +394,31 @@ export function buildPptStyleTokens(contract = {}) {
|
|
|
294
394
|
gutter_px: 24
|
|
295
395
|
},
|
|
296
396
|
color: {
|
|
297
|
-
bg:
|
|
298
|
-
text:
|
|
299
|
-
muted:
|
|
300
|
-
primary:
|
|
301
|
-
accent:
|
|
302
|
-
surface:
|
|
303
|
-
rule:
|
|
397
|
+
bg: refTokens.bg,
|
|
398
|
+
text: refTokens.text,
|
|
399
|
+
muted: refTokens.muted,
|
|
400
|
+
primary: refTokens.primary,
|
|
401
|
+
accent: refTokens.accent,
|
|
402
|
+
surface: refTokens.surface,
|
|
403
|
+
rule: refTokens.rule
|
|
304
404
|
},
|
|
305
405
|
typography: {
|
|
306
406
|
language: korean ? 'ko' : 'en',
|
|
307
|
-
font_stack:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
407
|
+
font_stack: fontStack,
|
|
408
|
+
display_px: refTokens.display_px,
|
|
409
|
+
body_px: refTokens.body_px,
|
|
410
|
+
caption_px: refTokens.caption_px,
|
|
411
|
+
line_height: korean ? Math.max(1.4, refTokens.line_height) : refTokens.line_height,
|
|
412
|
+
letter_spacing: 0
|
|
413
|
+
},
|
|
414
|
+
layout: {
|
|
415
|
+
composition: refTokens.composition,
|
|
416
|
+
treatment: refTokens.treatment,
|
|
417
|
+
radius_px: refTokens.radius_px,
|
|
418
|
+
rule_px: 1,
|
|
419
|
+
source_rail: true,
|
|
420
|
+
evidence_grid: true,
|
|
421
|
+
mono_label: refTokens.mono_label
|
|
314
422
|
},
|
|
315
423
|
design_policy: {
|
|
316
424
|
priority: 'information_first',
|
|
@@ -328,8 +436,9 @@ export function buildPptStyleTokens(contract = {}) {
|
|
|
328
436
|
authority: DESIGN_SYSTEM_SSOT.authority_file,
|
|
329
437
|
builder_prompt: DESIGN_SYSTEM_SSOT.builder_prompt,
|
|
330
438
|
route_local_artifact: PPT_STYLE_TOKENS_ARTIFACT,
|
|
331
|
-
rule: 'PPT style tokens are a route-local projection of the design SSOT; source inputs are fused here
|
|
439
|
+
rule: 'PPT style tokens are a route-local projection of the design SSOT; source inputs are selected, fused, and applied here rather than kept as independent authorities.'
|
|
332
440
|
},
|
|
441
|
+
design_reference_selection: reference,
|
|
333
442
|
source_inputs: [
|
|
334
443
|
{
|
|
335
444
|
id: GETDESIGN_REFERENCE.id,
|
|
@@ -343,31 +452,112 @@ export function buildPptStyleTokens(contract = {}) {
|
|
|
343
452
|
}
|
|
344
453
|
],
|
|
345
454
|
avoid: ['over-designed decoration', 'ornamental gradients', 'nested cards', 'low-contrast gray body text', 'excessive motion or effects'],
|
|
346
|
-
detail_strategy: ['precise spacing', 'clear hierarchy', 'thin rules', 'disciplined alignment', 'subtle accent color only when it clarifies meaning'],
|
|
347
|
-
anti_generic_ai_style: 'prevent AI-like design: select a concrete
|
|
455
|
+
detail_strategy: ['precise spacing', 'clear hierarchy', 'thin rules', 'disciplined alignment', 'visible source rails', 'subtle accent color only when it clarifies meaning'],
|
|
456
|
+
anti_generic_ai_style: 'prevent AI-like design: select and apply a concrete awesome-design-md reference profile before styling; do not default to generic cards, gradients, vague SaaS visuals, oversized decoration, or unsupported image-like flourishes',
|
|
348
457
|
image_policy: 'use images only when they improve comprehension; prefer Codex App built-in image generation via https://developers.openai.com/codex/app/features#image-generation when generated assets are needed'
|
|
349
458
|
}
|
|
350
459
|
};
|
|
351
460
|
}
|
|
352
461
|
|
|
462
|
+
export function selectPptDesignReference(contract = {}) {
|
|
463
|
+
const text = cleanText(`${contract.prompt || ''} ${JSON.stringify(contract.answers || {})}`).toLowerCase();
|
|
464
|
+
const scored = PPT_DESIGN_REFERENCE_PROFILES.map((profile) => {
|
|
465
|
+
const score = profile.keywords.reduce((sum, keyword) => sum + (text.includes(String(keyword).toLowerCase()) ? 1 : 0), 0);
|
|
466
|
+
return { profile, score };
|
|
467
|
+
}).sort((a, b) => b.score - a.score);
|
|
468
|
+
const primary = scored[0]?.score > 0 ? scored[0].profile : PPT_DESIGN_REFERENCE_PROFILES[0];
|
|
469
|
+
const secondary = scored.find((entry) => entry.profile.id !== primary.id && entry.score > 0)?.profile || PPT_DESIGN_REFERENCE_PROFILES.find((entry) => entry.id !== primary.id);
|
|
470
|
+
return {
|
|
471
|
+
source: AWESOME_DESIGN_MD_REFERENCE.url,
|
|
472
|
+
selection_method: 'keyword_match_against_sealed_ppt_contract',
|
|
473
|
+
primary: {
|
|
474
|
+
id: primary.id,
|
|
475
|
+
name: primary.name,
|
|
476
|
+
source_url: primary.source_url,
|
|
477
|
+
source_summary: primary.source_summary,
|
|
478
|
+
applied_rules: primary.applied_rules
|
|
479
|
+
},
|
|
480
|
+
secondary: secondary ? {
|
|
481
|
+
id: secondary.id,
|
|
482
|
+
name: secondary.name,
|
|
483
|
+
source_url: secondary.source_url,
|
|
484
|
+
source_summary: secondary.source_summary,
|
|
485
|
+
applied_rules: secondary.applied_rules.slice(0, 2)
|
|
486
|
+
} : null,
|
|
487
|
+
selected_sources: [primary, secondary].filter(Boolean).map((profile) => ({
|
|
488
|
+
id: profile.id,
|
|
489
|
+
name: profile.name,
|
|
490
|
+
source_url: profile.source_url,
|
|
491
|
+
role: profile.id === primary.id ? 'primary_style_reference' : 'secondary_guardrail_reference'
|
|
492
|
+
})),
|
|
493
|
+
applied_token_profile: {
|
|
494
|
+
color: primary.tokens,
|
|
495
|
+
composition: primary.tokens.composition,
|
|
496
|
+
treatment: primary.tokens.treatment
|
|
497
|
+
},
|
|
498
|
+
selection_reason: scored[0]?.score > 0
|
|
499
|
+
? `matched ${scored[0].score} contract keyword(s) to ${primary.name}`
|
|
500
|
+
: `no strong domain match; defaulted to ${primary.name} for restrained business presentation output`
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
353
504
|
export function buildPptHtml({ contract = {}, audience, sourceLedger, storyboard, styleTokens }) {
|
|
354
505
|
const title = escapeHtml(storyboard.title);
|
|
506
|
+
const referenceName = escapeHtml(styleTokens.design_policy?.design_reference_selection?.primary?.name || 'selected design reference');
|
|
507
|
+
const audienceRaw = escapeHtml(audience?.audience_profile?.raw || 'Audience context');
|
|
508
|
+
const stpRaw = escapeHtml(audience?.stp?.raw || 'STP context');
|
|
509
|
+
const decisionRaw = escapeHtml(audience?.decision_context?.raw || storyboard.thesis || '');
|
|
510
|
+
const surfaceRule = styleTokens.layout?.treatment === 'shadow_as_border_minimal_depth'
|
|
511
|
+
? `box-shadow: 0 0 0 1px ${styleTokens.color.rule}; border: 0;`
|
|
512
|
+
: `border: 1px solid ${styleTokens.color.rule}; box-shadow: none;`;
|
|
355
513
|
const css = `@page { size: 16in 9in; margin: 0; }
|
|
356
514
|
* { box-sizing: border-box; }
|
|
357
515
|
body { margin: 0; background: ${styleTokens.color.bg}; color: ${styleTokens.color.text}; font-family: ${styleTokens.typography.font_stack}; }
|
|
358
|
-
.page { width: 100vw; min-height: 100vh; page-break-after: always; padding:
|
|
359
|
-
.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
.
|
|
363
|
-
|
|
516
|
+
.page { width: 100vw; min-height: 100vh; page-break-after: always; padding: 64px 88px 54px; display: grid; grid-template-rows: auto 1fr auto; gap: 34px; }
|
|
517
|
+
.topline { display: grid; grid-template-columns: 1fr auto; align-items: end; border-bottom: 1px solid ${styleTokens.color.rule}; padding-bottom: 14px; }
|
|
518
|
+
.kicker { color: ${styleTokens.color.primary}; font-size: ${styleTokens.typography.caption_px}px; font-weight: 600; letter-spacing: 0; text-transform: uppercase; }
|
|
519
|
+
.reference { color: ${styleTokens.color.muted}; font-size: ${styleTokens.typography.caption_px}px; letter-spacing: 0; }
|
|
520
|
+
.content { display: grid; grid-template-columns: minmax(0, 6fr) minmax(320px, 4fr); gap: 58px; align-items: center; }
|
|
521
|
+
h1 { margin: 0; font-size: ${styleTokens.typography.display_px}px; line-height: 1.08; letter-spacing: 0; max-width: 1040px; font-weight: 600; }
|
|
522
|
+
p { margin: 0; color: ${styleTokens.color.muted}; font-size: ${styleTokens.typography.body_px}px; line-height: ${styleTokens.typography.line_height}; max-width: 920px; }
|
|
523
|
+
.claim { display: grid; gap: 26px; }
|
|
524
|
+
.evidence { ${surfaceRule} border-radius: ${styleTokens.layout.radius_px}px; background: ${styleTokens.color.surface}; display: grid; }
|
|
525
|
+
.evidence-row { padding: 22px 24px; border-bottom: 1px solid ${styleTokens.color.rule}; }
|
|
526
|
+
.evidence-row:last-child { border-bottom: 0; }
|
|
527
|
+
.label { color: ${styleTokens.color.primary}; font-size: ${styleTokens.typography.caption_px}px; font-weight: 600; letter-spacing: 0; text-transform: uppercase; margin-bottom: 8px; }
|
|
528
|
+
.value { color: ${styleTokens.color.text}; font-size: 20px; line-height: 1.42; }
|
|
529
|
+
.source { display: grid; grid-template-columns: 1fr auto; gap: 24px; color: ${styleTokens.color.muted}; font-size: ${styleTokens.typography.caption_px}px; border-top: 1px solid ${styleTokens.color.rule}; padding-top: 14px; }
|
|
530
|
+
.accent { width: 64px; height: 3px; background: ${styleTokens.color.accent}; }`;
|
|
364
531
|
const pages = storyboard.pages.map((page) => `<section class="page">
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
<
|
|
368
|
-
|
|
532
|
+
<header class="topline">
|
|
533
|
+
<div class="kicker">${escapeHtml(page.kind)} / ${page.number}</div>
|
|
534
|
+
<div class="reference">${referenceName}</div>
|
|
535
|
+
</header>
|
|
536
|
+
<main class="content">
|
|
537
|
+
<div class="claim">
|
|
538
|
+
<div class="accent"></div>
|
|
539
|
+
<h1>${escapeHtml(page.claim)}</h1>
|
|
540
|
+
<p>${escapeHtml(page.support)}</p>
|
|
541
|
+
</div>
|
|
542
|
+
<aside class="evidence" aria-label="decision evidence">
|
|
543
|
+
<div class="evidence-row">
|
|
544
|
+
<div class="label">Audience</div>
|
|
545
|
+
<div class="value">${audienceRaw}</div>
|
|
546
|
+
</div>
|
|
547
|
+
<div class="evidence-row">
|
|
548
|
+
<div class="label">STP</div>
|
|
549
|
+
<div class="value">${stpRaw}</div>
|
|
550
|
+
</div>
|
|
551
|
+
<div class="evidence-row">
|
|
552
|
+
<div class="label">Decision</div>
|
|
553
|
+
<div class="value">${decisionRaw}</div>
|
|
554
|
+
</div>
|
|
555
|
+
</aside>
|
|
556
|
+
</main>
|
|
557
|
+
<div class="source">
|
|
558
|
+
<span>Sources: ${escapeHtml((page.source_ids || []).join(', ') || 'none')}</span>
|
|
559
|
+
<span>${escapeHtml(styleTokens.layout?.composition || 'presentation-grid')}</span>
|
|
369
560
|
</div>
|
|
370
|
-
<div class="source">Sources: ${escapeHtml((page.source_ids || []).join(', ') || 'none')}</div>
|
|
371
561
|
</section>`).join('\n');
|
|
372
562
|
return `<!doctype html>
|
|
373
563
|
<html lang="${styleTokens.typography.language}">
|
|
@@ -495,6 +685,9 @@ export function buildPptRenderReport({ contract = {}, audience, sourceLedger, st
|
|
|
495
685
|
{ id: 'restrained_detail', passed: styleTokens.design_policy?.visual_style === 'simple_restrained_detailed' },
|
|
496
686
|
{ id: 'design_ssot_declared', passed: styleTokens.design_policy?.design_ssot?.authority === DESIGN_SYSTEM_SSOT.authority_file },
|
|
497
687
|
{ id: 'curated_design_md_input_fused', passed: (styleTokens.design_policy?.source_inputs || []).some((entry) => entry.url === AWESOME_DESIGN_MD_REFERENCE.url && entry.role === 'source_input_for_ssot') },
|
|
688
|
+
{ id: 'concrete_design_reference_selected', passed: Boolean(styleTokens.design_policy?.design_reference_selection?.primary?.id && styleTokens.design_policy?.design_reference_selection?.selected_sources?.length) },
|
|
689
|
+
{ id: 'reference_rules_applied_to_tokens', passed: Boolean(styleTokens.layout?.composition && styleTokens.layout?.treatment && styleTokens.design_policy?.design_reference_selection?.applied_token_profile) },
|
|
690
|
+
{ id: 'html_uses_reference_layout', passed: typeof html === 'string' && html.includes('decision evidence') && html.includes(styleTokens.layout?.composition || 'presentation-grid') },
|
|
498
691
|
{ id: 'ppt_skill_allowlist_enforced', passed: JSON.stringify(styleTokens.design_policy?.pipeline_allowlist?.required_skills || []) === JSON.stringify([...PPT_PIPELINE_SKILL_ALLOWLIST]) },
|
|
499
692
|
{ id: 'out_of_pipeline_design_skills_ignored', passed: styleTokens.design_policy?.pipeline_allowlist?.ignore_installed_out_of_pipeline_skills === true && (styleTokens.design_policy?.pipeline_allowlist?.ignored_design_skills_even_if_installed || []).includes('design-artifact-expert') },
|
|
500
693
|
{ id: 'ppt_mcp_allowlist_scoped', passed: (styleTokens.design_policy?.pipeline_allowlist?.allowed_mcp_servers || []).every((entry) => entry.mcp === 'context7' && /external_documentation/.test(entry.condition || '')) },
|
package/src/core/routes.mjs
CHANGED
|
@@ -679,6 +679,7 @@ export function subagentExecutionPolicyText(route, prompt = '') {
|
|
|
679
679
|
}
|
|
680
680
|
return [
|
|
681
681
|
'Subagent policy: REQUIRED for code-changing or execution work in this route.',
|
|
682
|
+
'The selected SKS route itself authorizes route-owned worker/reviewer subagents; the user does not need to separately ask for subagents when the default Team pipeline is active.',
|
|
682
683
|
'Before editing, the parent orchestrator must visibly state the SKS route, split independent write scopes, and spawn worker/reviewer subagents whenever the tools are available.',
|
|
683
684
|
'Run workers in parallel only with disjoint ownership. The parent owns integration, verification, and final evidence.',
|
|
684
685
|
'If subagent tools are unavailable or the work cannot be safely split, record that as explicit subagent evidence before editing.',
|
package/src/core/tmux-ui.mjs
CHANGED
|
@@ -6,14 +6,116 @@ import { getCodexInfo } from './codex-adapter.mjs';
|
|
|
6
6
|
import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
|
|
7
7
|
|
|
8
8
|
export const SKS_TMUX_LOGO = [
|
|
9
|
-
'
|
|
10
|
-
' /
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
9
|
+
' _______ __ __ _______',
|
|
10
|
+
' / _____/ / /_/ / / _____/|',
|
|
11
|
+
' / /____ / __ / / /____ | |',
|
|
12
|
+
' \\____ \\ / / / / \\____ \\| |',
|
|
13
|
+
' ____/ / /_/ /_/ ____/ /| | | |',
|
|
14
|
+
'/_____/ /_/\\__/ /_____/ |_|/ /',
|
|
15
|
+
' \\_____\\ \\_\\ \\_\\ \\_____\\|_| /',
|
|
16
|
+
' SNEAKOSCOPE CODEX'
|
|
15
17
|
].join('\n');
|
|
16
18
|
|
|
19
|
+
const SKS_TMUX_LOGO_FRAMES = [
|
|
20
|
+
[
|
|
21
|
+
' ||',
|
|
22
|
+
' || .',
|
|
23
|
+
' || .:',
|
|
24
|
+
' || .::',
|
|
25
|
+
' || .:::',
|
|
26
|
+
' ||.::::',
|
|
27
|
+
' SKS'
|
|
28
|
+
].join('\n'),
|
|
29
|
+
[
|
|
30
|
+
' //||',
|
|
31
|
+
' .// || ..',
|
|
32
|
+
' // || ..::',
|
|
33
|
+
' // || ..::::',
|
|
34
|
+
' // || ..::::',
|
|
35
|
+
' // ||::::',
|
|
36
|
+
' S K S'
|
|
37
|
+
].join('\n'),
|
|
38
|
+
[
|
|
39
|
+
' ______ __ ______',
|
|
40
|
+
' . / ___/| . / /__ . / ___/|',
|
|
41
|
+
' / / /__ | | / //_/ / / /__ | |',
|
|
42
|
+
' / /\\__ \\ | | / ,< / /\\__ \\ | |',
|
|
43
|
+
' / /___/ / | | / /| | / /___/ / | |',
|
|
44
|
+
' /_/_____/ |/ /_/ |_| /_/_____/ |/',
|
|
45
|
+
' SNEAKOSCOPE'
|
|
46
|
+
].join('\n'),
|
|
47
|
+
[
|
|
48
|
+
' _______ __ __ _______',
|
|
49
|
+
' . / _____/ . / /_/ / . / _____/|',
|
|
50
|
+
' / / /____ / __ / / / /____ | |',
|
|
51
|
+
' / /\\____ \\ / / / / / /\\____ \\| |',
|
|
52
|
+
' / /_____/ / /_/ /_/ / /_____/ /| |',
|
|
53
|
+
' /_/______/ /_/\\__/ /_/______/ |_|/',
|
|
54
|
+
' SNEAKOSCOPE'
|
|
55
|
+
].join('\n'),
|
|
56
|
+
[
|
|
57
|
+
' _____ __ __ _____',
|
|
58
|
+
' / ___/ / /_/ / / ___/| .',
|
|
59
|
+
' / /__ / __ / / /__ | | ::',
|
|
60
|
+
' \\__ \\ / / / / \\__ \\| | :::',
|
|
61
|
+
' ___/ / /_/ /_/ ___/ /| | | :::',
|
|
62
|
+
'/____/ /_/\\__/ /____/ |_|/::',
|
|
63
|
+
' \\___\\ \\_\\ \\_\\ \\____\\|_|:',
|
|
64
|
+
' SNEAKOSCOPE'
|
|
65
|
+
].join('\n'),
|
|
66
|
+
[
|
|
67
|
+
' _______ __ __ _______',
|
|
68
|
+
' / _____/ / /_/ / / _____/|',
|
|
69
|
+
' / /____ / __ / / /____ | |',
|
|
70
|
+
' \\____ \\ / / / / \\____ \\| |',
|
|
71
|
+
' ____/ / /_/ /_/ ____/ /| | | |',
|
|
72
|
+
'/_____/ /_/\\__/ /_____/ |_|/ /',
|
|
73
|
+
' \\_____\\ \\_\\ \\_\\ \\_____\\|_| /',
|
|
74
|
+
' SNEAKOSCOPE CODEX'
|
|
75
|
+
].join('\n'),
|
|
76
|
+
[
|
|
77
|
+
' _______ __ _______',
|
|
78
|
+
' |\\_____ \\ . / /_. |\\_____ \\',
|
|
79
|
+
' | |____\\ \\ / __/ | |____\\ \\',
|
|
80
|
+
' | |\\____\\ \\/ / / | |\\____\\ \\',
|
|
81
|
+
' | | |___/ /_/ /__ | | |___/ /',
|
|
82
|
+
' \\|_|/____/\\__/__/ \\|_|/____/',
|
|
83
|
+
' SNEAKOSCOPE'
|
|
84
|
+
].join('\n'),
|
|
85
|
+
[
|
|
86
|
+
' ||\\\\',
|
|
87
|
+
' .. || \\\\.',
|
|
88
|
+
' ..:: || \\\\',
|
|
89
|
+
' ..:::: || \\\\',
|
|
90
|
+
':::: || \\\\',
|
|
91
|
+
' || \\\\',
|
|
92
|
+
' S K S'
|
|
93
|
+
].join('\n'),
|
|
94
|
+
[
|
|
95
|
+
' ||',
|
|
96
|
+
' . ||',
|
|
97
|
+
' .:: ||',
|
|
98
|
+
' .:::: ||',
|
|
99
|
+
' .:::: ||',
|
|
100
|
+
' .:::: ||',
|
|
101
|
+
' SKS'
|
|
102
|
+
].join('\n'),
|
|
103
|
+
SKS_TMUX_LOGO
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const SKS_TMUX_LOGO_ANIMATION_STEPS = Object.freeze([
|
|
107
|
+
{ frame: 0, color: '39', bold: false, delay: '0.045' },
|
|
108
|
+
{ frame: 1, color: '39', bold: false, delay: '0.045' },
|
|
109
|
+
{ frame: 2, color: '45', bold: false, delay: '0.05' },
|
|
110
|
+
{ frame: 3, color: '51', bold: false, delay: '0.055' },
|
|
111
|
+
{ frame: 4, color: '51', bold: true, delay: '0.07' },
|
|
112
|
+
{ frame: 5, color: '51', bold: true, delay: '0.07' },
|
|
113
|
+
{ frame: 6, color: '45', bold: false, delay: '0.05' },
|
|
114
|
+
{ frame: 7, color: '39', bold: false, delay: '0.045' },
|
|
115
|
+
{ frame: 8, color: '39', bold: false, delay: '0.045' },
|
|
116
|
+
{ frame: 9, color: '51', bold: true, delay: '0.16' }
|
|
117
|
+
]);
|
|
118
|
+
|
|
17
119
|
export const DEFAULT_SKS_CODEX_MODEL = 'gpt-5.5';
|
|
18
120
|
export const DEFAULT_SKS_CODEX_REASONING = 'high';
|
|
19
121
|
|
|
@@ -103,8 +205,7 @@ export function tmuxStatusKind(tmux = {}) {
|
|
|
103
205
|
export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
104
206
|
const extraArgs = Array.isArray(codexArgs) ? codexArgs : [];
|
|
105
207
|
return [
|
|
106
|
-
|
|
107
|
-
`printf '%s\\n' ${shellEscape(SKS_TMUX_LOGO)}`,
|
|
208
|
+
sksLogoIntroCommand(),
|
|
108
209
|
`printf '\\nProject: %s\\n' ${shellEscape(root)}`,
|
|
109
210
|
'printf \'Runtime: tmux session for Codex CLI\\n\'',
|
|
110
211
|
'printf \'Prompt: use canonical $ commands, for example $Team or $QA-LOOP\\n\\n\'',
|
|
@@ -114,6 +215,19 @@ export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
|
114
215
|
].join('; ');
|
|
115
216
|
}
|
|
116
217
|
|
|
218
|
+
export function sksLogoIntroCommand() {
|
|
219
|
+
const staticLogo = `clear; printf '\\033[1;38;5;51m%s\\033[0m\\n' ${shellEscape(SKS_TMUX_LOGO)}`;
|
|
220
|
+
const animated = SKS_TMUX_LOGO_ANIMATION_STEPS.flatMap((step) => {
|
|
221
|
+
const style = `${step.bold ? '1;' : ''}38;5;${step.color}`;
|
|
222
|
+
return [
|
|
223
|
+
'clear',
|
|
224
|
+
`printf '\\033[${style}m%s\\033[0m\\n' ${shellEscape(SKS_TMUX_LOGO_FRAMES[step.frame])}`,
|
|
225
|
+
`sleep ${step.delay}`
|
|
226
|
+
];
|
|
227
|
+
}).join('; ');
|
|
228
|
+
return `if [ "\${SKS_TMUX_LOGO_ANIMATION:-1}" = "0" ]; then ${staticLogo}; else ${animated}; fi`;
|
|
229
|
+
}
|
|
230
|
+
|
|
117
231
|
function terminalTitleCommand(title = '') {
|
|
118
232
|
return `printf '\\033]0;%s\\007' ${shellEscape(String(title || '').slice(0, 80))}`;
|
|
119
233
|
}
|