sneakoscope 0.7.42 → 0.7.44
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 +3 -3
- package/package.json +1 -1
- package/src/cli/main.mjs +18 -8
- 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/pipeline.mjs +96 -4
- package/src/core/ppt.mjs +221 -28
- package/src/core/questions.mjs +9 -2
- package/src/core/routes.mjs +1 -0
- package/src/core/tmux-ui.mjs +113 -29
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"`) with a
|
|
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 static SKS 3D ASCII intro inside tmux; the animated intro is reserved for non-tmux unauthenticated Codex launches and can be disabled with `SKS_TMUX_LOGO_ANIMATION=0`. Override the runtime with `SKS_CODEX_MODEL`, `SKS_CODEX_REASONING`, or disable the default model profile with `SKS_CODEX_FAST_HIGH=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
|
|
|
@@ -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.44",
|
|
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",
|
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);
|
|
@@ -2405,7 +2407,8 @@ async function selftest() {
|
|
|
2405
2407
|
if (!hookKoreanSksContext.includes('Route: $Team')) throw new Error('selftest failed: Korean implementation prompt did not promote to Team route');
|
|
2406
2408
|
if (hookKoreanSksContext.includes('SKS answer-only pipeline active')) throw new Error('selftest failed: Korean implementation prompt still used answer-only pipeline');
|
|
2407
2409
|
const hookKoreanSksState = await readJson(stateFile(hookKoreanSksTmp), {});
|
|
2408
|
-
if (hookKoreanSksState.phase !== '
|
|
2410
|
+
if (hookKoreanSksState.phase !== 'TEAM_PARALLEL_ANALYSIS_SCOUTING' || hookKoreanSksState.implementation_allowed !== true || !hookKoreanSksState.ambiguity_gate_passed || !hookKoreanSksState.team_plan_ready) throw new Error('selftest failed: Korean Team auto-seal did not materialize Team');
|
|
2411
|
+
if (!(await exists(path.join(missionDir(hookKoreanSksTmp, hookKoreanSksState.mission_id), 'team-plan.json')))) throw new Error('selftest failed: Korean Team auto-seal did not write team-plan.json');
|
|
2409
2412
|
const hookPaymentTeamTmp = tmpdir();
|
|
2410
2413
|
await initProject(hookPaymentTeamTmp, {});
|
|
2411
2414
|
const hookPaymentTeamPayload = JSON.stringify({ cwd: hookPaymentTeamTmp, prompt: '$Team 결제 재시도 정책과 로그인 세션 만료 버그 수정 executor:2 reviewer:1 user:1' });
|
|
@@ -2416,9 +2419,10 @@ async function selftest() {
|
|
|
2416
2419
|
if (!hookPaymentTeamContext.includes('Ambiguity gate auto-sealed')) throw new Error('selftest failed: predictable payment/auth Team prompt did not auto-seal');
|
|
2417
2420
|
if (hookPaymentTeamContext.includes('PAYMENT_RETRY_POLICY') || hookPaymentTeamContext.includes('AUTH_PROTOCOL_CHANGE_ALLOWED')) throw new Error('selftest failed: predictable payment/auth policy defaults were asked instead of inferred');
|
|
2418
2421
|
const hookPaymentTeamState = await readJson(stateFile(hookPaymentTeamTmp), {});
|
|
2419
|
-
if (hookPaymentTeamState.phase !== '
|
|
2422
|
+
if (hookPaymentTeamState.phase !== 'TEAM_PARALLEL_ANALYSIS_SCOUTING' || hookPaymentTeamState.implementation_allowed !== true || !hookPaymentTeamState.ambiguity_gate_passed || !hookPaymentTeamState.team_plan_ready) throw new Error('selftest failed: predictable payment/auth Team did not materialize after auto-seal');
|
|
2420
2423
|
const hookPaymentTeamSchema = await readJson(path.join(missionDir(hookPaymentTeamTmp, hookPaymentTeamState.mission_id), 'required-answers.schema.json'));
|
|
2421
2424
|
if (hookPaymentTeamSchema.slots.length !== 0 || hookPaymentTeamSchema.inferred_answers?.PAYMENT_RETRY_POLICY === undefined || hookPaymentTeamSchema.inferred_answers?.AUTH_SESSION_EXPIRED_BEHAVIOR === undefined) throw new Error('selftest failed: predictable payment/auth defaults were not recorded as inferred answers');
|
|
2425
|
+
if (!(await exists(path.join(missionDir(hookPaymentTeamTmp, hookPaymentTeamState.mission_id), 'team-plan.json')))) throw new Error('selftest failed: predictable payment/auth Team auto-seal did not write team-plan.json');
|
|
2422
2426
|
const hookTeamTmp = tmpdir();
|
|
2423
2427
|
await initProject(hookTeamTmp, {});
|
|
2424
2428
|
const hookTeamPayload = JSON.stringify({ cwd: hookTeamTmp, prompt: '$Team 발표자료 만들어줘 executor:2 reviewer:1 user:1' });
|
|
@@ -2626,6 +2630,7 @@ async function selftest() {
|
|
|
2626
2630
|
if (!codexConfigText.includes('multi_agent = true')) throw new Error('selftest failed: multi_agent not enabled');
|
|
2627
2631
|
if (!hasContext7ConfigText(codexConfigText)) throw new Error('selftest failed: Context7 MCP not configured');
|
|
2628
2632
|
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');
|
|
2633
|
+
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');
|
|
2629
2634
|
if (!codexConfigText.includes('[agents.analysis_scout]')) throw new Error('selftest failed: analysis_scout agent not configured');
|
|
2630
2635
|
if (!codexConfigText.includes('[agents.team_consensus]')) throw new Error('selftest failed: team_consensus agent not configured');
|
|
2631
2636
|
const preservedConfigTmp = tmpdir();
|
|
@@ -2633,7 +2638,7 @@ async function selftest() {
|
|
|
2633
2638
|
await writeTextAtomic(path.join(preservedConfigTmp, '.codex', 'config.toml'), '[features]\nfast_mode_ui = true\n\n[user.fast_mode]\nvisible = true\n');
|
|
2634
2639
|
await initProject(preservedConfigTmp, {});
|
|
2635
2640
|
const preservedConfig = await safeReadText(path.join(preservedConfigTmp, '.codex', 'config.toml'));
|
|
2636
|
-
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
|
|
2641
|
+
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');
|
|
2637
2642
|
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');
|
|
2638
2643
|
const autoReviewHome = path.join(tmp, 'auto-review-home');
|
|
2639
2644
|
const autoReviewEnv = { HOME: autoReviewHome };
|
|
@@ -2988,6 +2993,10 @@ async function selftest() {
|
|
|
2988
2993
|
if (buttonUxSlotIds.length) throw new Error(`selftest failed: clear small UI work should auto-seal, got ${buttonUxSlotIds.join(',')}`);
|
|
2989
2994
|
if (buttonUxSchema.inferred_answers.UI_STATE_BEHAVIOR !== 'infer_from_task_context_and_existing_design_system; preserve existing loading/error/empty/retry behavior unless explicitly requested; add only standard states required by the touched surface') throw new Error('selftest failed: UI state default inference missing');
|
|
2990
2995
|
if (buttonUxSchema.inferred_answers.VISUAL_REGRESSION_REQUIRED !== 'yes_if_available') throw new Error('selftest failed: visual regression default inference missing');
|
|
2996
|
+
const predictableAuthCliSchema = buildQuestionSchema('회전 아스키 아트는 제일 처음 인증 안됐을때만 codex cli처럼 애니메이션으로 보이게 하고 tmux에서는 정적 3d 아스키 아트로 보여줘');
|
|
2997
|
+
const predictableAuthCliSlotIds = predictableAuthCliSchema.slots.map((s) => s.id);
|
|
2998
|
+
if (predictableAuthCliSlotIds.length) throw new Error(`selftest failed: clear auth-worded CLI rendering work should auto-seal, got ${predictableAuthCliSlotIds.join(',')}`);
|
|
2999
|
+
if (!predictableAuthCliSchema.inferred_answers.RISK_BOUNDARY?.includes('no destructive commands or live data writes')) throw new Error('selftest failed: predictable auth-worded CLI work did not infer conservative risk boundary');
|
|
2991
3000
|
const vagueSchema = buildQuestionSchema('뭔가 개선해줘');
|
|
2992
3001
|
const vagueSlotIds = vagueSchema.slots.map((s) => s.id);
|
|
2993
3002
|
if (!vagueSlotIds.includes('INTENT_TARGET') || vagueSlotIds.includes('GOAL_PRECISE') || vagueSlotIds.includes('ACCEPTANCE_CRITERIA')) throw new Error(`selftest failed: vague work should ask dynamic intent questions only, got ${vagueSlotIds.join(',')}`);
|
|
@@ -3034,6 +3043,7 @@ async function selftest() {
|
|
|
3034
3043
|
if (!pptHtml.includes('<html') || pptHtml.includes('gradient')) throw new Error('selftest failed: PPT HTML artifact missing or over-designed');
|
|
3035
3044
|
const pptStyleTokens = await readJson(path.join(pptMission.dir, 'ppt-style-tokens.json'));
|
|
3036
3045
|
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');
|
|
3046
|
+
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');
|
|
3037
3047
|
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');
|
|
3038
3048
|
const audienceScript = pptHtml.match(/id="ppt-audience-strategy">([^<]+)<\/script>/);
|
|
3039
3049
|
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.44';
|
|
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/pipeline.mjs
CHANGED
|
@@ -504,10 +504,14 @@ async function prepareClarificationGate(root, route, task, required, opts = {})
|
|
|
504
504
|
if (schema.slots.length === 0) {
|
|
505
505
|
await writeJsonAtomic(path.join(dir, 'answers.json'), schema.inferred_answers || {});
|
|
506
506
|
const result = await sealContract(dir, mission);
|
|
507
|
-
const
|
|
507
|
+
const materialized = result.ok && route?.id === 'Team'
|
|
508
|
+
? await materializeAutoSealedTeam(root, id, dir, route, task, result.contract?.sealed_hash || null)
|
|
509
|
+
: {};
|
|
510
|
+
const effectiveTask = materialized.prompt || task;
|
|
511
|
+
const plan = await writePipelinePlan(dir, { missionId: id, route, task: effectiveTask, required, ambiguity: { required: true, slots: 0, auto_sealed: result.ok, passed: result.ok, contract_hash: result.contract?.sealed_hash || null } });
|
|
508
512
|
await appendJsonl(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'route.clarification.auto_sealed', route: route.id, slots: 0, ok: result.ok });
|
|
509
|
-
await setCurrent(root, routeState(id, route, result.ok ? `${route.mode}_CLARIFICATION_CONTRACT_SEALED` : `${route.mode}_CLARIFICATION_AWAITING_ANSWERS`, required, {
|
|
510
|
-
prompt:
|
|
513
|
+
await setCurrent(root, routeState(id, route, result.ok ? (materialized.phase || `${route.mode}_CLARIFICATION_CONTRACT_SEALED`) : `${route.mode}_CLARIFICATION_AWAITING_ANSWERS`, required, {
|
|
514
|
+
prompt: effectiveTask,
|
|
511
515
|
questions_allowed: false,
|
|
512
516
|
implementation_allowed: result.ok,
|
|
513
517
|
clarification_required: false,
|
|
@@ -517,8 +521,10 @@ async function prepareClarificationGate(root, route, task, required, opts = {})
|
|
|
517
521
|
pipeline_plan_ready: validatePipelinePlan(plan).ok,
|
|
518
522
|
pipeline_plan_path: PIPELINE_PLAN_ARTIFACT,
|
|
519
523
|
original_stop_gate: route.stopGate,
|
|
520
|
-
stop_gate: route.stopGate
|
|
524
|
+
stop_gate: route.stopGate,
|
|
525
|
+
...(materialized.state || {})
|
|
521
526
|
}));
|
|
527
|
+
const materializedLine = materialized.phase ? `\nTeam runtime artifacts were materialized immediately; state advanced to ${materialized.phase}.` : '';
|
|
522
528
|
return {
|
|
523
529
|
route,
|
|
524
530
|
additionalContext: `${promptPipelineContext(task, route)}
|
|
@@ -528,6 +534,7 @@ Mission: ${id}
|
|
|
528
534
|
Decision contract: .sneakoscope/missions/${id}/decision-contract.json
|
|
529
535
|
Resolved answers: .sneakoscope/missions/${id}/resolved-answers.json
|
|
530
536
|
Pipeline plan: .sneakoscope/missions/${id}/${PIPELINE_PLAN_ARTIFACT}
|
|
537
|
+
${materializedLine}
|
|
531
538
|
Next atomic action: continue the original route lifecycle with the sealed decision-contract.json.`
|
|
532
539
|
};
|
|
533
540
|
}
|
|
@@ -581,6 +588,91 @@ function applyMadSksAuthorizationToSchema(schema = {}) {
|
|
|
581
588
|
return schema;
|
|
582
589
|
}
|
|
583
590
|
|
|
591
|
+
async function materializeAutoSealedTeam(root, id, dir, route, task, contractHash = null) {
|
|
592
|
+
const spec = parseTeamSpecText(task);
|
|
593
|
+
const cleanTask = spec.prompt || task;
|
|
594
|
+
const fromChatImgRequired = hasFromChatImgSignal(cleanTask);
|
|
595
|
+
const { agentSessions, roleCounts, roster } = spec;
|
|
596
|
+
const plan = {
|
|
597
|
+
schema_version: 1,
|
|
598
|
+
mission_id: id,
|
|
599
|
+
task: cleanTask,
|
|
600
|
+
agent_session_count: agentSessions,
|
|
601
|
+
default_agent_session_count: 3,
|
|
602
|
+
role_counts: roleCounts,
|
|
603
|
+
session_policy: `Use at most ${agentSessions} subagent sessions at a time; the parent orchestrator is not counted.`,
|
|
604
|
+
bundle_size: roster.bundle_size,
|
|
605
|
+
roster,
|
|
606
|
+
contract_hash: contractHash,
|
|
607
|
+
team_model: {
|
|
608
|
+
phases: ['parallel_analysis_scouts', 'triwiki_stage_refresh', 'debate_team', 'runtime_task_graph', 'development_team', 'review'],
|
|
609
|
+
analysis_team: `Read-only parallel scouting with exactly ${roster.bundle_size} analysis_scout_N agents.`,
|
|
610
|
+
debate_team: `Read-only role debate with exactly ${roster.bundle_size} participants.`,
|
|
611
|
+
development_team: `Fresh parallel development bundle with exactly ${roster.bundle_size} executor_N developers implementing disjoint slices.`
|
|
612
|
+
},
|
|
613
|
+
context_tracking: triwikiContextTracking(),
|
|
614
|
+
team_runtime: teamRuntimePlanMetadata(),
|
|
615
|
+
phases: [
|
|
616
|
+
{ id: 'team_roster_confirmation', goal: `Materialize the Team roster from default SKS counts or explicit user counts, write team-roster.json, and surface role counts ${formatRoleCounts(roleCounts)}.`, agents: ['parent_orchestrator'], output: 'team-roster.json' },
|
|
617
|
+
{ id: 'parallel_analysis_scouting', goal: `Read TriWiki context, then spawn exactly ${roster.bundle_size} read-only analysis_scout_N agents in parallel. ${fromChatImgRequired ? `From-Chat-IMG active: ${CODEX_COMPUTER_USE_ONLY_POLICY}` : 'From-Chat-IMG inactive: do not assume ordinary images are chat captures.'}`, agents: roster.analysis_team.map((agent) => agent.id), max_parallel_subagents: agentSessions, write_policy: 'read-only' },
|
|
618
|
+
{ id: 'triwiki_refresh', goal: `Refresh or pack TriWiki and run ${triwikiContextTracking().validate_command}.`, agents: ['parent_orchestrator'], output: '.sneakoscope/wiki/context-pack.json' },
|
|
619
|
+
{ id: 'planning_debate', goal: 'Run read-only planning debate, map constraints and implementation slices, then seal one objective.', agents: roster.debate_team.map((agent) => agent.id) },
|
|
620
|
+
{ id: 'runtime_task_graph_compile', goal: `Compile the agreed Team plan into ${TEAM_GRAPH_ARTIFACT}, ${TEAM_RUNTIME_TASKS_ARTIFACT}, ${TEAM_DECOMPOSITION_ARTIFACT}, and ${TEAM_INBOX_DIR}.`, agents: ['parent_orchestrator'], output: [TEAM_GRAPH_ARTIFACT, TEAM_RUNTIME_TASKS_ARTIFACT, TEAM_DECOMPOSITION_ARTIFACT, TEAM_INBOX_DIR] },
|
|
621
|
+
{ id: 'parallel_implementation', goal: `Close debate agents, then spawn a fresh ${roster.bundle_size}-person executor development team with non-overlapping write ownership.`, agents: roster.development_team.map((agent) => agent.id) },
|
|
622
|
+
{ id: 'review_integration', goal: 'Review, integrate, verify, and record evidence before final.', agents: roster.validation_team.map((agent) => agent.id) },
|
|
623
|
+
{ id: 'session_cleanup', goal: `Close or account for Team subagent sessions and write ${TEAM_SESSION_CLEANUP_ARTIFACT}.`, agents: ['parent_orchestrator'] }
|
|
624
|
+
],
|
|
625
|
+
live_visibility: {
|
|
626
|
+
markdown: 'team-live.md',
|
|
627
|
+
transcript: 'team-transcript.jsonl',
|
|
628
|
+
dashboard: 'team-dashboard.json',
|
|
629
|
+
tmux: 'CLI Team entrypoints open tmux live lanes for the visible Team agent budget when tmux is available.',
|
|
630
|
+
commands: ['sks team status latest', 'sks team log latest', 'sks team tail latest', 'sks team watch latest', 'sks team lane latest --agent <name> --follow']
|
|
631
|
+
},
|
|
632
|
+
required_artifacts: ['team-roster.json', 'team-analysis.md', ...(fromChatImgRequired ? [FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT] : []), 'team-consensus.md', ...teamRuntimeRequiredArtifacts(), 'team-review.md', 'team-gate.json', TEAM_SESSION_CLEANUP_ARTIFACT, 'reflection.md', 'reflection-gate.json', 'team-live.md', 'team-transcript.jsonl', 'team-dashboard.json', '.sneakoscope/wiki/context-pack.json', 'context7-evidence.jsonl']
|
|
633
|
+
};
|
|
634
|
+
await writeJsonAtomic(path.join(dir, 'team-plan.json'), plan);
|
|
635
|
+
await writeJsonAtomic(path.join(dir, 'team-roster.json'), { schema_version: 1, mission_id: id, role_counts: roleCounts, agent_sessions: agentSessions, bundle_size: roster.bundle_size, roster, confirmed: true, source: 'auto_sealed_team_spec' });
|
|
636
|
+
const contextTracking = triwikiContextTracking();
|
|
637
|
+
await writeTextAtomic(path.join(dir, 'team-workflow.md'), `# SKS Team Workflow\n\nTask: ${cleanTask}\n\nAgent session budget: ${agentSessions}\nBundle size: ${roster.bundle_size}\nRole counts: ${formatRoleCounts(roleCounts)}\nContext tracking: ${contextTracking.ssot} SSOT, ${contextTracking.default_pack}.\n\nAuto-sealed ambiguity gate: no user question was required. Continue directly with Team scouting, debate, runtime task graph, implementation, review, cleanup, reflection, and Honest Mode.\n`);
|
|
638
|
+
await initTeamLive(id, dir, cleanTask, { agentSessions, roleCounts, roster });
|
|
639
|
+
const runtime = await writeTeamRuntimeArtifacts(dir, plan, { contractHash });
|
|
640
|
+
await writeMemorySweepReport(root, dir, { missionId: id }).catch(() => null);
|
|
641
|
+
await writeSkillForgeReport(dir, { mission_id: id, route: 'team', task_signature: cleanTask }).catch(() => null);
|
|
642
|
+
await writeMistakeMemoryReport(dir, { mission_id: id, route: 'team', task: cleanTask }).catch(() => null);
|
|
643
|
+
await writeCodeStructureReport(root, dir, { missionId: id, exception: 'Team auto-seal records split-review risk; extraction happens only when the mission scope includes the touched file.' }).catch(() => null);
|
|
644
|
+
await writeJsonAtomic(path.join(dir, 'team-gate.json'), {
|
|
645
|
+
passed: false,
|
|
646
|
+
team_roster_confirmed: true,
|
|
647
|
+
analysis_artifact: false,
|
|
648
|
+
triwiki_refreshed: false,
|
|
649
|
+
triwiki_validated: false,
|
|
650
|
+
consensus_artifact: false,
|
|
651
|
+
...runtime.gate_fields,
|
|
652
|
+
implementation_team_fresh: false,
|
|
653
|
+
review_artifact: false,
|
|
654
|
+
integration_evidence: false,
|
|
655
|
+
session_cleanup: false,
|
|
656
|
+
context7_evidence: false,
|
|
657
|
+
...(fromChatImgRequired ? { from_chat_img_required: true, from_chat_img_request_coverage: false } : {}),
|
|
658
|
+
contract_hash: contractHash
|
|
659
|
+
});
|
|
660
|
+
await appendJsonl(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'team.materialized_after_auto_sealed_ambiguity_gate', route: route.id, bundle_size: roster.bundle_size, agent_sessions: agentSessions });
|
|
661
|
+
return {
|
|
662
|
+
phase: 'TEAM_PARALLEL_ANALYSIS_SCOUTING',
|
|
663
|
+
prompt: cleanTask,
|
|
664
|
+
state: {
|
|
665
|
+
agent_sessions: agentSessions,
|
|
666
|
+
role_counts: roleCounts,
|
|
667
|
+
team_roster_confirmed: true,
|
|
668
|
+
team_plan_ready: true,
|
|
669
|
+
team_graph_ready: runtime.ok,
|
|
670
|
+
team_live_ready: true,
|
|
671
|
+
from_chat_img_required: fromChatImgRequired
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
584
676
|
async function prepareTeam(root, route, task, required) {
|
|
585
677
|
const spec = parseTeamSpecText(task);
|
|
586
678
|
const cleanTask = spec.prompt || task;
|
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/questions.mjs
CHANGED
|
@@ -131,6 +131,10 @@ function promptHasRisk(lower) {
|
|
|
131
131
|
return /(운영|production|prod|live|배포|publish|release|결제|payment|billing|auth|인증|보안|security|db|database|supabase|postgres|sql|schema|migration|마이그레이션|삭제|delete|drop|truncate|reset|권한|permission|credential|secret)/.test(lower);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
function promptNeedsExplicitRiskBoundary(lower) {
|
|
135
|
+
return /(drop|truncate|wipe|reset\s+(?:db|database)|delete\s+all|all-row|전체\s*(?:삭제|초기화)|운영\s*(?:db|데이터|삭제|쓰기|변경)|production\s*(?:db|database|delete|write|mutation)|prod\s*(?:db|database|delete|write|mutation)|credential|secret|api\s*key|토큰\s*(?:노출|삭제|교체)|권한\s*(?:상승|확대)|permission\s*(?:escalation|widening))/.test(lower);
|
|
136
|
+
}
|
|
137
|
+
|
|
134
138
|
function promptHasContextTarget(text, lower) {
|
|
135
139
|
return promptHasTarget(text, lower)
|
|
136
140
|
|| /https?:\/\/\S+/.test(text)
|
|
@@ -146,8 +150,11 @@ export function buildAmbiguityAssessment(prompt, explicitAnswers = {}) {
|
|
|
146
150
|
const acceptance = promptHasExplicitAcceptance(lower) || hasAnswer(explicitAnswers.ACCEPTANCE_CRITERIA) || hasAnswer(explicitAnswers.SUCCESS_CRITERIA_OR_ACCEPTANCE);
|
|
147
151
|
const risk = promptHasRisk(lower);
|
|
148
152
|
const contextTarget = promptHasContextTarget(text, lower) || hasAnswer(explicitAnswers.CODEBASE_CONTEXT_TARGET);
|
|
153
|
+
const actionable = target && action && !underspecified;
|
|
154
|
+
const hardRiskNeedsBoundary = promptNeedsExplicitRiskBoundary(lower);
|
|
149
155
|
const predictableSafetyDefault = /(재시도|retry|세션\s*만료|session\s*expired|session\s*expiry|token\s*expired)/.test(lower);
|
|
150
|
-
const
|
|
156
|
+
const predictableImplementationBoundary = actionable && risk && !hardRiskNeedsBoundary;
|
|
157
|
+
const hasPolicy = hasAnswer(explicitAnswers.RISK_BOUNDARY) || hasAnswer(explicitAnswers.RISK_AND_BOUNDARY) || predictableSafetyDefault || predictableImplementationBoundary || /(하지\s*마|금지|no\s+|never|묻지|보존|preserve|safe|안전|검증|approval|승인|알아서|판단|추론|infer|default|기본)/.test(lower);
|
|
151
158
|
const hasMultipleChoiceRisk = /(\bor\b|또는|아니면|선택|둘 중|여러|multiple|대안)/.test(lower) && !/(알아서|판단|infer|추론|default|기본)/.test(lower);
|
|
152
159
|
|
|
153
160
|
const goalClarity = underspecified ? (target || action ? 0.45 : 0.2) : (target && action ? 0.9 : target || action ? 0.62 : 0.25);
|
|
@@ -169,7 +176,7 @@ export function buildAmbiguityAssessment(prompt, explicitAnswers = {}) {
|
|
|
169
176
|
const unresolved = [];
|
|
170
177
|
if (components.goal.clarity_score < CLARITY_FLOORS.goal) unresolved.push('intent_target_or_required_outcome');
|
|
171
178
|
if (components.success.clarity_score < CLARITY_FLOORS.success && (!target || !action || risk)) unresolved.push('success_criteria_or_acceptance');
|
|
172
|
-
if (components.constraints.clarity_score < CLARITY_FLOORS.constraints || hasMultipleChoiceRisk) unresolved.push('risk_boundary_or_choice');
|
|
179
|
+
if ((components.constraints.clarity_score < CLARITY_FLOORS.constraints && hardRiskNeedsBoundary) || hasMultipleChoiceRisk) unresolved.push('risk_boundary_or_choice');
|
|
173
180
|
if (components.context.clarity_score < CLARITY_FLOORS.context) unresolved.push('codebase_context_target');
|
|
174
181
|
const uniqueUnresolved = [...new Set(unresolved)];
|
|
175
182
|
return {
|
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,34 +6,113 @@ 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
|
|
|
17
19
|
const SKS_TMUX_LOGO_FRAMES = [
|
|
18
20
|
[
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
21
|
+
' ||',
|
|
22
|
+
' ||',
|
|
23
|
+
' ||',
|
|
24
|
+
' ||',
|
|
25
|
+
' ||',
|
|
26
|
+
' ||',
|
|
27
|
+
' SKS',
|
|
28
|
+
' SNEAKOSCOPE CODEX'
|
|
25
29
|
].join('\n'),
|
|
26
30
|
[
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
31
|
+
' //||',
|
|
32
|
+
' // || .',
|
|
33
|
+
' // || .:',
|
|
34
|
+
' // || .::',
|
|
35
|
+
' // || .:::',
|
|
36
|
+
' // ||.::::',
|
|
37
|
+
' S K S',
|
|
38
|
+
' SNEAKOSCOPE CODEX'
|
|
39
|
+
].join('\n'),
|
|
40
|
+
[
|
|
41
|
+
' _______ __ __ _______',
|
|
42
|
+
' / _____/| / /_/ /| / _____/|',
|
|
43
|
+
' / /____| | / __ / | / /____| |',
|
|
44
|
+
' \\____ \\ | / / / /| | \\____ \\ |',
|
|
45
|
+
' ____/ / | |/_/ /_/ / | |____/ / | |',
|
|
46
|
+
'/_____/ |//_/\\__/ / |//_____/ |/',
|
|
47
|
+
'\\_____\\___/ \\_\\ \\_\\/___/ \\_____\\___/',
|
|
48
|
+
' SNEAKOSCOPE CODEX'
|
|
49
|
+
].join('\n'),
|
|
50
|
+
[
|
|
51
|
+
' _______ __ __ _______',
|
|
52
|
+
' / _____/ / /_/ / / _____/|',
|
|
53
|
+
' / /____ / __ / / /____ | |',
|
|
54
|
+
' \\____ \\ / / / / \\____ \\| |',
|
|
55
|
+
' ____/ / /_/ /_/ / ____/ / | |',
|
|
56
|
+
'/_____/ /_/\\__/ /_____/ |/',
|
|
57
|
+
' \\_____\\ \\_\\ \\_\\ \\_____\\___/',
|
|
58
|
+
' SNEAKOSCOPE CODEX'
|
|
59
|
+
].join('\n'),
|
|
60
|
+
[
|
|
61
|
+
' _______ __ __ _______',
|
|
62
|
+
' / _____/| / /_/ /| / _____/|',
|
|
63
|
+
' / /____| | / __ / | / /____| |',
|
|
64
|
+
' \\____ \\ | / / / /| | \\____ \\ |',
|
|
65
|
+
' ____/ / | |/_/ /_/ / | |____/ / | |',
|
|
66
|
+
'/_____/ |//_/\\__/ / |//_____/ |/',
|
|
67
|
+
'\\_____\\___/ \\_\\ \\_\\/___/ \\_____\\___/',
|
|
68
|
+
' SNEAKOSCOPE CODEX'
|
|
69
|
+
].join('\n'),
|
|
70
|
+
[
|
|
71
|
+
' _______ __ __ _______',
|
|
72
|
+
' |\\_____ \\ / /_/ / |\\_____ \\',
|
|
73
|
+
' | |____\\ \\/ __ / | |____\\ \\',
|
|
74
|
+
' | |\\____\\/ / / / | |\\____\\ \\',
|
|
75
|
+
' | | |___/ /_/ /__ | | |___/ /',
|
|
76
|
+
' \\|_|/____/\\__/__/ \\|_|/____/',
|
|
77
|
+
' S K S',
|
|
78
|
+
' SNEAKOSCOPE CODEX'
|
|
79
|
+
].join('\n'),
|
|
80
|
+
[
|
|
81
|
+
' ||\\\\',
|
|
82
|
+
' . || \\\\',
|
|
83
|
+
' ::. || \\\\',
|
|
84
|
+
' ::::. || \\\\',
|
|
85
|
+
' ::::::. || \\\\',
|
|
86
|
+
' :::::::::|| \\\\',
|
|
87
|
+
' S K S',
|
|
88
|
+
' SNEAKOSCOPE CODEX'
|
|
89
|
+
].join('\n'),
|
|
90
|
+
[
|
|
91
|
+
' ||',
|
|
92
|
+
' ||',
|
|
93
|
+
' ||',
|
|
94
|
+
' ||',
|
|
95
|
+
' ||',
|
|
96
|
+
' ||',
|
|
97
|
+
' SKS',
|
|
98
|
+
' SNEAKOSCOPE CODEX'
|
|
33
99
|
].join('\n'),
|
|
34
100
|
SKS_TMUX_LOGO
|
|
35
101
|
];
|
|
36
102
|
|
|
103
|
+
const SKS_TMUX_LOGO_ANIMATION_STEPS = Object.freeze([
|
|
104
|
+
{ frame: 0, color: '39', bold: false, delay: '0.045' },
|
|
105
|
+
{ frame: 1, color: '39', bold: false, delay: '0.045' },
|
|
106
|
+
{ frame: 2, color: '45', bold: false, delay: '0.05' },
|
|
107
|
+
{ frame: 3, color: '51', bold: false, delay: '0.055' },
|
|
108
|
+
{ frame: 4, color: '51', bold: true, delay: '0.07' },
|
|
109
|
+
{ frame: 5, color: '51', bold: true, delay: '0.07' },
|
|
110
|
+
{ frame: 6, color: '45', bold: false, delay: '0.05' },
|
|
111
|
+
{ frame: 7, color: '39', bold: false, delay: '0.045' },
|
|
112
|
+
{ frame: 8, color: '39', bold: false, delay: '0.045' },
|
|
113
|
+
{ frame: 9, color: '51', bold: true, delay: '0.16' }
|
|
114
|
+
]);
|
|
115
|
+
|
|
37
116
|
export const DEFAULT_SKS_CODEX_MODEL = 'gpt-5.5';
|
|
38
117
|
export const DEFAULT_SKS_CODEX_REASONING = 'high';
|
|
39
118
|
|
|
@@ -123,7 +202,7 @@ export function tmuxStatusKind(tmux = {}) {
|
|
|
123
202
|
export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
124
203
|
const extraArgs = Array.isArray(codexArgs) ? codexArgs : [];
|
|
125
204
|
return [
|
|
126
|
-
sksLogoIntroCommand(),
|
|
205
|
+
sksLogoIntroCommand(codexBin),
|
|
127
206
|
`printf '\\nProject: %s\\n' ${shellEscape(root)}`,
|
|
128
207
|
'printf \'Runtime: tmux session for Codex CLI\\n\'',
|
|
129
208
|
'printf \'Prompt: use canonical $ commands, for example $Team or $QA-LOOP\\n\\n\'',
|
|
@@ -133,20 +212,25 @@ export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
|
133
212
|
].join('; ');
|
|
134
213
|
}
|
|
135
214
|
|
|
136
|
-
export function sksLogoIntroCommand() {
|
|
215
|
+
export function sksLogoIntroCommand(codexBin = 'codex') {
|
|
137
216
|
const staticLogo = `clear; printf '\\033[1;38;5;51m%s\\033[0m\\n' ${shellEscape(SKS_TMUX_LOGO)}`;
|
|
217
|
+
const authenticatedCheck = `${shellEscape(codexBin)} login status >/dev/null 2>&1`;
|
|
138
218
|
const animated = [
|
|
139
219
|
'clear',
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
220
|
+
'trap \'printf "\\033[0m\\033[?25h"\' EXIT INT TERM',
|
|
221
|
+
`printf '\\033[?25l'`,
|
|
222
|
+
...SKS_TMUX_LOGO_ANIMATION_STEPS.flatMap((step) => {
|
|
223
|
+
const style = `${step.bold ? '1;' : ''}38;5;${step.color}`;
|
|
224
|
+
return [
|
|
225
|
+
`printf '\\033[H\\033[J\\033[${style}m%s\\033[0m\\n' ${shellEscape(SKS_TMUX_LOGO_FRAMES[step.frame])}`,
|
|
226
|
+
`sleep ${step.delay}`
|
|
227
|
+
];
|
|
228
|
+
}),
|
|
229
|
+
`printf '\\033[H\\033[J\\033[1;38;5;51m%s\\033[0m\\n' ${shellEscape(SKS_TMUX_LOGO)}`,
|
|
230
|
+
`printf '\\033[?25h'`,
|
|
231
|
+
'trap - EXIT INT TERM'
|
|
148
232
|
].join('; ');
|
|
149
|
-
return `if [ "\${SKS_TMUX_LOGO_ANIMATION:-1}" = "0" ]; then ${staticLogo}; else ${animated}; fi`;
|
|
233
|
+
return `if [ -n "\${TMUX:-}" ] || [ "\${SKS_TMUX_LOGO_ANIMATION:-1}" = "0" ] || ${authenticatedCheck}; then ${staticLogo}; else ${animated}; fi`;
|
|
150
234
|
}
|
|
151
235
|
|
|
152
236
|
function terminalTitleCommand(title = '') {
|