sneakoscope 0.7.50 → 0.7.51
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 -2
- package/package.json +1 -1
- package/src/cli/main.mjs +123 -4
- package/src/core/fsx.mjs +1 -1
- package/src/core/image-ux-review.mjs +298 -0
- package/src/core/init.mjs +16 -5
- package/src/core/pipeline.mjs +84 -1
- package/src/core/routes.mjs +37 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Sneakoscope Codex (`sks`, displayed as `ㅅㅋㅅ`) is a Codex CLI/App harness for repeatable agent workflows. It adds terminal commands, Codex App `$` prompt commands, tmux-native CLI workspaces, Team/QA/Research routes, inspectable pipeline plans, a maximum-speed Computer Use lane, a fast Goal bridge for native `/goal` persistence, Context7 evidence checks, DB safety, TriWiki context tracking, design-system SSOT routing, lightweight skill dreaming, Honest Mode, and release-readiness gates.
|
|
5
|
+
Sneakoscope Codex (`sks`, displayed as `ㅅㅋㅅ`) is a Codex CLI/App harness for repeatable agent workflows. It adds terminal commands, Codex App `$` prompt commands, tmux-native CLI workspaces, Team/QA/Research routes, inspectable pipeline plans, a maximum-speed Computer Use lane, an imagegen/gpt-image-2 UI/UX review route, a fast Goal bridge for native `/goal` persistence, Context7 evidence checks, DB safety, TriWiki context tracking, design-system SSOT routing, lightweight skill dreaming, Honest Mode, and release-readiness gates.
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
@@ -43,7 +43,7 @@ sks selftest --mock
|
|
|
43
43
|
| Area | What it does |
|
|
44
44
|
| --- | --- |
|
|
45
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
|
-
| 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. `sks codex-app remote-control` wraps Codex CLI 0.130.0+ headless remote control without falling back to older app-server internals. |
|
|
46
|
+
| Codex App commands | Installs generated skills so `$Team`, `$From-Chat-IMG`, `$DFix`, `$QA-LOOP`, `$PPT`, `$Image-UX-Review`, `$UX-Review`, `$Goal`, `$DB`, `$Wiki`, `$Help`, and related routes are visible in prompt workflows. `sks codex-app remote-control` wraps Codex CLI 0.130.0+ headless remote control without falling back to older app-server internals. |
|
|
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`. |
|
|
49
49
|
| Team orchestration | Runs substantial work through score-based ambiguity handling, scouts, TriWiki refresh, debate, runtime task graphs, worker inboxes, implementation, review, cleanup, reflection, and Honest Mode; narrow work should use Proof Field evidence to skip unrelated pipeline work instead of expanding Team. |
|
|
@@ -51,6 +51,7 @@ sks selftest --mock
|
|
|
51
51
|
| From-Chat-IMG | Turns chat screenshots plus original attachments into source-bound work orders, then requires scoped QA evidence before completion. |
|
|
52
52
|
| QA loop | Dogfoods UI/API behavior with safety gates, Codex Computer Use-only UI evidence, safe fixes, and rechecks. |
|
|
53
53
|
| PPT pipeline | Uses `$PPT` for simple, restrained, information-first HTML/PDF presentation artifacts, first asking delivery context, audience profile, STP strategy, decision context, and 3+ pain-point to solution/aha mappings before source research, design-system work, HTML/PDF export, and render QA. Independent strategy/render/file-write phases run in parallel where inputs allow and are recorded in `ppt-parallel-report.json`; editable source HTML is preserved under `source-html/`, PPT-only temporary build files are cleaned after completion, installed skills/MCPs outside the `$PPT` allowlist are ignored, generated image assets may use `$imagegen` only when sealed in the contract, and `ppt-style-tokens.json` records the design SSOT plus fused source inputs. |
|
|
54
|
+
| Image UX Review | Uses `$Image-UX-Review` / `$UX-Review` for UI/UX audits where source screenshots are first turned into generated annotated review images through Codex App `$imagegen`/`gpt-image-2`; those generated images are then read back into `image-ux-issue-ledger.json`, optional requested fixes are rechecked, and text-only screenshot critique cannot pass `image-ux-review-gate.json`. |
|
|
54
55
|
| Computer Use fast lane | Uses `$Computer-Use` / `$CU` for UI/browser/visual work that needs maximum speed: skip Team debate and upfront TriWiki loops, use Codex Computer Use directly, then refresh/validate TriWiki and run Honest Mode at final closeout. |
|
|
55
56
|
| Goal | Provides a fast SKS bridge overlay for Codex native persisted `/goal` create, pause, resume, and clear controls; implementation continues through the selected SKS execution route. |
|
|
56
57
|
| TriWiki voxels | Maintains `.sneakoscope/wiki/context-pack.json` as the context SSOT with coordinate anchors, voxel metadata, `attention.use_first`, `attention.hydrate_first`, and prompt-bound mistake recall ledgers. |
|
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.51",
|
|
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
|
@@ -41,6 +41,15 @@ import {
|
|
|
41
41
|
writePptBuildArtifacts,
|
|
42
42
|
writePptRouteArtifacts
|
|
43
43
|
} from '../core/ppt.mjs';
|
|
44
|
+
import {
|
|
45
|
+
IMAGE_UX_REVIEW_GATE_ARTIFACT,
|
|
46
|
+
IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
47
|
+
IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT,
|
|
48
|
+
IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT,
|
|
49
|
+
IMAGE_UX_REVIEW_POLICY_ARTIFACT,
|
|
50
|
+
IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT,
|
|
51
|
+
writeImageUxReviewRouteArtifacts
|
|
52
|
+
} from '../core/image-ux-review.mjs';
|
|
44
53
|
import { contextCapsule } from '../core/triwiki-attention.mjs';
|
|
45
54
|
import { rgbaKey, rgbaToWikiCoord, validateWikiCoordinateIndex } from '../core/wiki-coordinate.mjs';
|
|
46
55
|
import { ALLOWED_REASONING_EFFORTS, AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, COMMAND_CATALOG, DESIGN_SYSTEM_SSOT, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, FROM_CHAT_IMG_VISUAL_MAP_ARTIFACT, FROM_CHAT_IMG_WORK_ORDER_ARTIFACT, GETDESIGN_REFERENCE, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_SKILLS, ROUTES, USAGE_TOPICS, context7ConfigToml, hasContext7ConfigText, hasFromChatImgSignal, looksLikeAnswerOnlyRequest, noUnrequestedFallbackCodePolicyText, reflectionRequiredForRoute, reasoningInstruction, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stackCurrentDocsPolicy, triwikiContextTracking } from '../core/routes.mjs';
|
|
@@ -100,7 +109,7 @@ export async function main(args) {
|
|
|
100
109
|
if (String(cmd).toLowerCase() === 'dfix') return dfixHelp();
|
|
101
110
|
const handlers = {
|
|
102
111
|
postinstall: () => postinstall({ bootstrap }), wizard: () => wizard(tail), ui: () => wizard(tail), 'update-check': () => updateCheck(tail), help: () => help(tail), commands: () => commands(tail), usage: () => usage(tail), root: () => rootCommand(tail), quickstart: () => quickstartCommand(), 'codex-app': () => codexAppHelp(tail), 'codex-lb': () => codexLbCommand(sub, rest), auth: () => codexLbCommand(sub, rest), openclaw: () => openClawCommand(tail), bootstrap: () => bootstrap(tail), deps: () => deps(sub, rest),
|
|
103
|
-
'qa-loop': () => qaLoopCommand(sub, rest), ppt: () => pptCommand(sub, rest), context7: () => context7Command(sub, rest), pipeline: () => pipeline(sub, rest), guard: () => guard(sub, rest), conflicts: () => conflicts(sub, rest), versioning: () => versioning(sub, rest), reasoning: () => reasoningCommand(tail), aliases: () => aliases(), setup: () => setup(tail), 'fix-path': () => fixPath(tail), doctor: () => doctor(tail), init: () => init(tail), selftest: () => selftest(tail),
|
|
112
|
+
'qa-loop': () => qaLoopCommand(sub, rest), ppt: () => pptCommand(sub, rest), 'image-ux-review': () => imageUxReviewCommand(sub, rest), 'ux-review': () => imageUxReviewCommand(sub, rest), 'visual-review': () => imageUxReviewCommand(sub, rest), 'ui-ux-review': () => imageUxReviewCommand(sub, rest), context7: () => context7Command(sub, rest), pipeline: () => pipeline(sub, rest), guard: () => guard(sub, rest), conflicts: () => conflicts(sub, rest), versioning: () => versioning(sub, rest), reasoning: () => reasoningCommand(tail), aliases: () => aliases(), setup: () => setup(tail), 'fix-path': () => fixPath(tail), doctor: () => doctor(tail), init: () => init(tail), selftest: () => selftest(tail),
|
|
104
113
|
goal: () => goalCommand(sub, rest), research: () => researchCommand(sub, rest), hook: () => emitHook(sub), profile: () => profileCommand(sub, rest), hproof: () => hproofCommand(sub, rest), 'validate-artifacts': () => validateArtifactsCommand(tail), perf: () => perfCommand(sub, rest), 'proof-field': () => proofFieldCommand(sub, rest), 'skill-dream': () => skillDreamCommand(sub, rest), 'code-structure': () => codeStructureCommand(sub, rest), memory: () => memoryCommand(sub, rest), gx: () => gxCommand(sub, rest),
|
|
105
114
|
team: () => team(tail), db: () => dbCommand(sub, rest), eval: () => evalCommand(sub, rest), harness: () => harnessCommand(sub, rest), wiki: () => wikiCommand(sub, rest), gc: () => gcCommand(tail), stats: () => statsCommand(tail)
|
|
106
115
|
};
|
|
@@ -329,7 +338,7 @@ async function updateCheck(args = []) {
|
|
|
329
338
|
if (result.update_available) console.log('Run: npm i -g sneakoscope');
|
|
330
339
|
}
|
|
331
340
|
|
|
332
|
-
const DOLLAR_DEFAULT_PIPELINE_TEXT = 'Default pipeline: questions -> $Answer, small design/content -> $DFix, presentation/PDF artifacts -> $PPT, Computer Use UI/browser speed work -> $Computer-Use, code -> $Team. Use $From-Chat-IMG only for chat screenshot plus original attachments. Use $MAD-SKS only as an explicit scoped DB authorization modifier that can be combined with another $ route. No route may invent unrequested fallback implementation code.';
|
|
341
|
+
const DOLLAR_DEFAULT_PIPELINE_TEXT = 'Default pipeline: questions -> $Answer, small design/content -> $DFix, presentation/PDF artifacts -> $PPT, image-generation UI/UX reviews -> $Image-UX-Review/$UX-Review, Computer Use UI/browser speed work -> $Computer-Use, code -> $Team. Use $From-Chat-IMG only for chat screenshot plus original attachments. Use $MAD-SKS only as an explicit scoped DB authorization modifier that can be combined with another $ route. No route may invent unrequested fallback implementation code.';
|
|
333
342
|
|
|
334
343
|
function commands(args = []) {
|
|
335
344
|
if (flag(args, '--json')) return console.log(JSON.stringify({ aliases: ['sks', 'sneakoscope'], dollar_commands: DOLLAR_COMMANDS, app_skill_aliases: DOLLAR_COMMAND_ALIASES, commands: COMMAND_CATALOG }, null, 2));
|
|
@@ -477,6 +486,66 @@ async function pptCommand(sub = 'status', args = []) {
|
|
|
477
486
|
throw new Error(`Unknown ppt command: ${action}`);
|
|
478
487
|
}
|
|
479
488
|
|
|
489
|
+
async function imageUxReviewCommand(sub = 'status', args = []) {
|
|
490
|
+
const root = await sksRoot();
|
|
491
|
+
const action = sub || 'status';
|
|
492
|
+
if (action === 'help' || action === '--help' || action === '-h') {
|
|
493
|
+
console.log(`SKS Image UX Review
|
|
494
|
+
|
|
495
|
+
Prompt commands:
|
|
496
|
+
$Image-UX-Review <target>
|
|
497
|
+
$UX-Review <target>
|
|
498
|
+
|
|
499
|
+
Inspect artifacts:
|
|
500
|
+
sks image-ux-review status latest --json
|
|
501
|
+
|
|
502
|
+
Core loop:
|
|
503
|
+
source UI screenshot -> $imagegen/gpt-image-2 generated annotated review image -> image-ux-issue-ledger.json -> optional requested fixes -> changed-screen recheck
|
|
504
|
+
`);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (action !== 'status') throw new Error(`Unknown image-ux-review command: ${action}`);
|
|
508
|
+
const missionArg = args.find((arg) => !String(arg).startsWith('--')) || 'latest';
|
|
509
|
+
const id = await resolveMissionId(root, missionArg);
|
|
510
|
+
if (!id) throw new Error('Usage: sks image-ux-review status <mission-id|latest> [--json]');
|
|
511
|
+
const { dir } = await loadMission(root, id);
|
|
512
|
+
const gate = await readJson(path.join(dir, IMAGE_UX_REVIEW_GATE_ARTIFACT), null);
|
|
513
|
+
const policy = await readJson(path.join(dir, IMAGE_UX_REVIEW_POLICY_ARTIFACT), null);
|
|
514
|
+
const inventory = await readJson(path.join(dir, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT), null);
|
|
515
|
+
const generatedReviewLedger = await readJson(path.join(dir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT), null);
|
|
516
|
+
const issueLedger = await readJson(path.join(dir, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT), null);
|
|
517
|
+
const iterationReport = await readJson(path.join(dir, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT), null);
|
|
518
|
+
const status = {
|
|
519
|
+
ok: Boolean(gate?.passed),
|
|
520
|
+
mission_id: id,
|
|
521
|
+
gate,
|
|
522
|
+
policy,
|
|
523
|
+
inventory,
|
|
524
|
+
generated_review_ledger: generatedReviewLedger,
|
|
525
|
+
issue_ledger: issueLedger,
|
|
526
|
+
iteration_report: iterationReport,
|
|
527
|
+
files: {
|
|
528
|
+
policy: path.join(dir, IMAGE_UX_REVIEW_POLICY_ARTIFACT),
|
|
529
|
+
inventory: path.join(dir, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT),
|
|
530
|
+
generated_review_ledger: path.join(dir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT),
|
|
531
|
+
issue_ledger: path.join(dir, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT),
|
|
532
|
+
iteration_report: path.join(dir, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT),
|
|
533
|
+
gate: path.join(dir, IMAGE_UX_REVIEW_GATE_ARTIFACT)
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
if (flag(args, '--json')) return console.log(JSON.stringify(status, null, 2));
|
|
537
|
+
console.log('SKS Image UX Review status\n');
|
|
538
|
+
console.log(`Mission: ${id}`);
|
|
539
|
+
console.log(`Gate: ${status.ok ? 'passed' : 'not passed'}`);
|
|
540
|
+
console.log(`Policy: ${path.relative(root, status.files.policy)}`);
|
|
541
|
+
console.log(`Screens: ${path.relative(root, status.files.inventory)}`);
|
|
542
|
+
console.log(`Images: ${path.relative(root, status.files.generated_review_ledger)}`);
|
|
543
|
+
console.log(`Issues: ${path.relative(root, status.files.issue_ledger)}`);
|
|
544
|
+
console.log(`Loop: ${path.relative(root, status.files.iteration_report)}`);
|
|
545
|
+
console.log(`Gate: ${path.relative(root, status.files.gate)}`);
|
|
546
|
+
if (gate?.blockers?.length) console.log(`Blockers:${' '.repeat(1)}${gate.blockers.join(', ')}`);
|
|
547
|
+
}
|
|
548
|
+
|
|
480
549
|
async function pipeline(sub = 'status', args = []) {
|
|
481
550
|
const root = await sksRoot();
|
|
482
551
|
const action = sub || 'status';
|
|
@@ -754,6 +823,25 @@ async function materializeAfterPipelineAnswer(root, id, dir, mission, route, rou
|
|
|
754
823
|
}
|
|
755
824
|
};
|
|
756
825
|
}
|
|
826
|
+
if (route?.id === 'ImageUXReview') {
|
|
827
|
+
await writeImageUxReviewRouteArtifacts(dir, contract);
|
|
828
|
+
await appendJsonlBounded(path.join(dir, 'events.jsonl'), {
|
|
829
|
+
ts: nowIso(),
|
|
830
|
+
type: 'image_ux_review.materialized',
|
|
831
|
+
route: route.id,
|
|
832
|
+
gate: IMAGE_UX_REVIEW_GATE_ARTIFACT,
|
|
833
|
+
generated_review_ledger: IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT
|
|
834
|
+
});
|
|
835
|
+
return {
|
|
836
|
+
phase: 'IMAGE_UX_REVIEW_READY',
|
|
837
|
+
prompt: routeContext.task || mission.prompt || '',
|
|
838
|
+
state: {
|
|
839
|
+
image_ux_review_gate_ready: true,
|
|
840
|
+
image_ux_review_policy_ready: true,
|
|
841
|
+
...madSksState
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
}
|
|
757
845
|
if (route?.id !== 'Team') return Object.keys(madSksState).length ? { state: madSksState } : {};
|
|
758
846
|
const spec = parseTeamSpecText(routeContext.task || mission.prompt || '');
|
|
759
847
|
const prompt = spec.prompt || routeContext.task || mission.prompt || '';
|
|
@@ -1476,6 +1564,7 @@ function usage(args = []) {
|
|
|
1476
1564
|
team: ['Team', '', ' sks team "task" executor:5 reviewer:6 user:1', ' sks team watch latest', ' sks team lane latest --agent analysis_scout_1 --follow', ' sks team message latest --from analysis_scout_1 --to executor_1 --message "handoff note"', ' sks team cleanup-tmux latest', '', '$Team runs questions -> contract -> scouts -> TriWiki attention -> debate -> runtime graph/inbox -> fresh executors -> review -> cleanup -> reflection -> Honest.'],
|
|
1477
1565
|
'qa-loop': ['QA-LOOP', '', ' sks qa-loop prepare "QA this app"', ' sks qa-loop answer <MISSION_ID> answers.json', ' sks qa-loop run <MISSION_ID> --max-cycles 8', '', 'Report: YYYY-MM-DD-v<version>-qa-report.md'],
|
|
1478
1566
|
ppt: ['PPT', '', ' $PPT 투자자용 피치덱을 HTML 기반 PDF로 만들어줘', ' $PPT 우리 SaaS 소개자료 만들어줘', ' sks ppt build latest --json', ' sks ppt status latest --json', '', '$PPT asks delivery context, audience profile, STP strategy, decision context, and 3+ pain-point/solution/aha mappings before source research, design-system work, HTML/PDF export, render QA, fact-ledger validation, and bounded review-loop validation. Independent strategy/render/file-write phases run in parallel where inputs allow and are recorded in ppt-parallel-report.json. The visual system must stay simple, restrained, and information-first; editable source HTML is kept under source-html/, PPT-only temporary build files are cleaned, and installed skills/MCPs outside the $PPT allowlist are ignored. Design uses getdesign-reference plus the built-in PPT design pipeline; imagegen/gpt-image-2 and Context7 are conditional only when the sealed PPT contract needs raster assets, slide visual critique, or current external docs. Missing required image-review evidence blocks instead of being simulated.'],
|
|
1567
|
+
'image-ux-review': ['Image UX Review', '', ' $Image-UX-Review localhost 화면을 이미지 생성 리뷰 루프로 검수해줘', ' $UX-Review 이 스크린샷을 gpt-image-2 콜아웃 리뷰로 분석하고 고쳐줘', ' sks image-ux-review status latest --json', '', '$Image-UX-Review captures or receives source UI screenshots, runs Codex App $imagegen/gpt-image-2 to create generated annotated review images with numbered callouts, then extracts those generated images into image-ux-issue-ledger.json. Text-only screenshot critique cannot pass image-ux-review-gate.json; missing generated review images remain an explicit blocker.'],
|
|
1479
1568
|
goal: ['Goal', '', ' sks goal create "task"', ' sks goal status latest', ' sks goal pause latest', ' sks goal resume latest', ' sks goal clear latest'],
|
|
1480
1569
|
'codex-app': ['Codex App', '', ' sks bootstrap', ' sks codex-app check', ' sks codex-app remote-control --status', ' sks dollar-commands', ' cat .codex/SNEAKOSCOPE.md'],
|
|
1481
1570
|
dollar: ['Dollar Commands', '', formatDollarCommandsCompact(' '), '', 'Terminal: sks dollar-commands [--json]'],
|
|
@@ -2442,11 +2531,13 @@ async function selftest() {
|
|
|
2442
2531
|
if (fromChatImgSkillText.includes('Computer Use/browser visual inspection')) throw new Error('selftest failed: from-chat-img skill still allows browser visual inspection wording');
|
|
2443
2532
|
const fromChatImgSkillMeta = await safeReadText(path.join(tmp, '.agents', 'skills', 'from-chat-img', 'agents', 'openai.yaml'));
|
|
2444
2533
|
if (!fromChatImgSkillMeta.includes('model_reasoning_effort: xhigh')) throw new Error('selftest failed: from-chat-img skill metadata is not xhigh');
|
|
2445
|
-
for (const supportSkill of ['reasoning-router', 'pipeline-runner', 'context7-docs', 'seo-geo-optimizer', 'reflection', 'design-system-builder', 'design-ui-editor', 'getdesign-reference', 'imagegen']) {
|
|
2534
|
+
for (const supportSkill of ['reasoning-router', 'pipeline-runner', 'context7-docs', 'seo-geo-optimizer', 'reflection', 'design-system-builder', 'design-ui-editor', 'getdesign-reference', 'imagegen', 'image-ux-review', 'visual-review', 'ui-ux-review']) {
|
|
2446
2535
|
if (!(await exists(path.join(tmp, '.agents', 'skills', supportSkill, 'SKILL.md')))) throw new Error(`selftest failed: ${supportSkill} skill not installed`);
|
|
2447
2536
|
}
|
|
2448
2537
|
const imagegenSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'imagegen', 'SKILL.md'));
|
|
2449
2538
|
if (!imagegenSkillText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL) || !imagegenSkillText.includes('$imagegen') || !imagegenSkillText.includes('gpt-image-2') || !imagegenSkillText.includes('OPENAI_API_KEY')) throw new Error('selftest failed: imagegen skill missing official Codex App image generation priority');
|
|
2539
|
+
const imageUxReviewSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'image-ux-review', 'SKILL.md'));
|
|
2540
|
+
if (!imageUxReviewSkillText.includes('gpt-image-2') || !imageUxReviewSkillText.includes('$imagegen') || !imageUxReviewSkillText.includes('generated annotated review image') || !imageUxReviewSkillText.includes('Text-only screenshot critique cannot satisfy this route') || !imageUxReviewSkillText.includes(IMAGE_UX_REVIEW_GATE_ARTIFACT) || !imageUxReviewSkillText.includes(IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT)) throw new Error('selftest failed: image-ux-review skill missing gpt-image-2 generated-image review gate guidance');
|
|
2450
2541
|
const getdesignSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'getdesign-reference', 'SKILL.md')); if (!getdesignSkillText.includes(AWESOME_DESIGN_MD_REFERENCE.url) || !getdesignSkillText.includes('only design decision SSOT') || !getdesignSkillText.includes('source inputs')) throw new Error('selftest failed: getdesign-reference skill missing design SSOT source-input guidance');
|
|
2451
2542
|
const designSystemBuilderSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'design-system-builder', 'SKILL.md')); if (!designSystemBuilderSkillText.includes(AWESOME_DESIGN_MD_REFERENCE.url) || !designSystemBuilderSkillText.includes('Fuse those inputs into one design.md SSOT') || !designSystemBuilderSkillText.includes('competing authorities')) throw new Error('selftest failed: design-system-builder skill missing fused design SSOT guidance');
|
|
2452
2543
|
const designSysPromptText = await safeReadText(path.join(packageRoot(), 'docs', 'Design-Sys-Prompt.md')); if (!designSysPromptText.includes('Design SSOT contract') || !designSysPromptText.includes('builder prompt') || !designSysPromptText.includes('not a competing design authority')) throw new Error('selftest failed: Design-Sys-Prompt missing design SSOT contract');
|
|
@@ -2462,7 +2553,9 @@ async function selftest() {
|
|
|
2462
2553
|
if (new Set(DOLLAR_COMMANDS.map((c) => c.command)).size !== DOLLAR_COMMANDS.length) throw new Error('selftest failed: duplicate dollar commands');
|
|
2463
2554
|
if (!DOLLAR_COMMAND_ALIASES.some((alias) => alias.canonical === '$QA-LOOP' && alias.app_skill === '$qa-loop')) throw new Error('selftest failed: $QA-LOOP picker skill missing');
|
|
2464
2555
|
if (!DOLLAR_COMMAND_ALIASES.some((alias) => alias.canonical === '$Team' && alias.app_skill === '$from-chat-img')) throw new Error('selftest failed: $From-Chat-IMG picker skill missing');
|
|
2556
|
+
if (!DOLLAR_COMMAND_ALIASES.some((alias) => alias.canonical === '$Image-UX-Review' && alias.app_skill === '$visual-review')) throw new Error('selftest failed: $Image-UX-Review picker alias missing');
|
|
2465
2557
|
if (!DOLLAR_COMMANDS.some((entry) => entry.command === '$From-Chat-IMG')) throw new Error('selftest failed: $From-Chat-IMG missing from dollar command list');
|
|
2558
|
+
if (!DOLLAR_COMMANDS.some((entry) => entry.command === '$Image-UX-Review') || !DOLLAR_COMMANDS.some((entry) => entry.command === '$UX-Review')) throw new Error('selftest failed: Image UX Review missing from dollar command list');
|
|
2466
2559
|
if (DOLLAR_COMMAND_ALIASES.some((alias) => ['$agent-team', '$qaloop', '$wiki-refresh', '$wikirefresh'].includes(alias.app_skill))) throw new Error('selftest failed: duplicate picker aliases still present');
|
|
2467
2560
|
if (routePrompt('$agent-team run specialists')) throw new Error('selftest failed: deprecated $agent-team route still resolved');
|
|
2468
2561
|
if (routePrompt('$QA-LOOP run UI E2E')?.id !== 'QALoop' || routePrompt('$QALoop deployed smoke')) throw new Error('selftest failed: QA-LOOP route is not standardized to $QA-LOOP');
|
|
@@ -2470,6 +2563,12 @@ async function selftest() {
|
|
|
2470
2563
|
if (routePrompt('$MAD-SKS Supabase MCP main 작업')?.id !== 'MadSKS') throw new Error('selftest failed: $MAD-SKS route did not resolve');
|
|
2471
2564
|
if (routePrompt('$MAD-SKS $Team Supabase MCP main 작업')?.id !== 'Team') throw new Error('selftest failed: $MAD-SKS did not compose with $Team');
|
|
2472
2565
|
if (routePrompt('$DB Supabase 점검 $MAD-SKS')?.id !== 'DB') throw new Error('selftest failed: trailing $MAD-SKS changed primary route');
|
|
2566
|
+
const imageUxRoute = routePrompt('$Image-UX-Review localhost 화면 검수');
|
|
2567
|
+
if (imageUxRoute?.id !== 'ImageUXReview') throw new Error('selftest failed: $Image-UX-Review did not route to ImageUXReview');
|
|
2568
|
+
if (routePrompt('$UX-Review 스크린샷 gpt-image-2 콜아웃 리뷰')?.id !== 'ImageUXReview') throw new Error('selftest failed: $UX-Review did not route to ImageUXReview');
|
|
2569
|
+
if (routePrompt('UI UX를 gpt-image-2 이미지 생성 콜아웃으로 리뷰해줘')?.id !== 'ImageUXReview') throw new Error('selftest failed: image-generation UI/UX review prompt did not route to ImageUXReview');
|
|
2570
|
+
if (routeRequiresSubagents(imageUxRoute, '$Image-UX-Review localhost 화면 검수')) throw new Error('selftest failed: ImageUXReview route should not require subagents');
|
|
2571
|
+
if (!reflectionRequiredForRoute(imageUxRoute)) throw new Error('selftest failed: ImageUXReview route should require reflection');
|
|
2473
2572
|
const madStandaloneTmp = tmpdir();
|
|
2474
2573
|
await initProject(madStandaloneTmp, {});
|
|
2475
2574
|
const madStandalonePayload = JSON.stringify({ cwd: madStandaloneTmp, prompt: '$MAD-SKS main 권한 열어줘' });
|
|
@@ -2498,7 +2597,8 @@ async function selftest() {
|
|
|
2498
2597
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$Team')) throw new Error('selftest failed: dollar-commands missing Team default routing guidance');
|
|
2499
2598
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$From-Chat-IMG')) throw new Error('selftest failed: dollar-commands missing From-Chat-IMG guidance');
|
|
2500
2599
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$MAD-SKS')) throw new Error('selftest failed: dollar-commands missing MAD-SKS scoped override guidance');
|
|
2501
|
-
if (!
|
|
2600
|
+
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$Image-UX-Review')) throw new Error('selftest failed: dollar-commands missing Image UX Review guidance');
|
|
2601
|
+
if (!COMMAND_CATALOG.some((c) => c.name === 'context7') || !COMMAND_CATALOG.some((c) => c.name === 'pipeline') || !COMMAND_CATALOG.some((c) => c.name === 'qa-loop') || !COMMAND_CATALOG.some((c) => c.name === 'image-ux-review') || !COMMAND_CATALOG.some((c) => c.name === 'root') || !COMMAND_CATALOG.some((c) => c.name === 'openclaw')) throw new Error('selftest failed: context7/pipeline/qa-loop/image-ux-review/root/openclaw commands missing from catalog');
|
|
2502
2602
|
const openClawTmp = tmpdir();
|
|
2503
2603
|
const openClawResult = await installOpenClawSkill({ targetDir: path.join(openClawTmp, 'skills', OPENCLAW_SKILL_NAME) });
|
|
2504
2604
|
if (!openClawResult.ok) throw new Error(`selftest failed: OpenClaw skill install blocked: ${openClawResult.reason}`);
|
|
@@ -2529,6 +2629,25 @@ async function selftest() {
|
|
|
2529
2629
|
const hookGoalTmp = tmpdir();
|
|
2530
2630
|
await initProject(hookGoalTmp, {});
|
|
2531
2631
|
const hookBin = path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
2632
|
+
const hookImageUxTmp = tmpdir();
|
|
2633
|
+
await initProject(hookImageUxTmp, {});
|
|
2634
|
+
const hookImageUxPayload = JSON.stringify({ cwd: hookImageUxTmp, prompt: '$Image-UX-Review localhost 화면을 gpt-image-2 콜아웃 리뷰로 검수해줘' });
|
|
2635
|
+
const hookImageUxResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookImageUxTmp, input: hookImageUxPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2636
|
+
if (hookImageUxResult.code !== 0) throw new Error(`selftest failed: $Image-UX-Review hook exited ${hookImageUxResult.code}: ${hookImageUxResult.stderr}`);
|
|
2637
|
+
const hookImageUxJson = JSON.parse(hookImageUxResult.stdout);
|
|
2638
|
+
const imageUxContext = hookImageUxJson.hookSpecificOutput?.additionalContext || '';
|
|
2639
|
+
if (!imageUxContext.includes('$Image-UX-Review route prepared') || !imageUxContext.includes('Codex App $imagegen/gpt-image-2')) throw new Error('selftest failed: $Image-UX-Review hook did not prepare imagegen loop context');
|
|
2640
|
+
const hookImageUxState = await readJson(stateFile(hookImageUxTmp), {});
|
|
2641
|
+
if (hookImageUxState.mode !== 'IMAGE_UX_REVIEW' || hookImageUxState.stop_gate !== IMAGE_UX_REVIEW_GATE_ARTIFACT || hookImageUxState.subagents_required !== false || hookImageUxState.reflection_required !== true) throw new Error('selftest failed: $Image-UX-Review hook did not set direct image UX review state');
|
|
2642
|
+
const imageUxMissionDir = missionDir(hookImageUxTmp, hookImageUxState.mission_id);
|
|
2643
|
+
const imageUxGate = await readJson(path.join(imageUxMissionDir, IMAGE_UX_REVIEW_GATE_ARTIFACT));
|
|
2644
|
+
const imageUxGeneratedLedger = await readJson(path.join(imageUxMissionDir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT));
|
|
2645
|
+
if (imageUxGate.passed || imageUxGate.imagegen_review_images_generated || !imageUxGate.blockers?.includes('source_screenshots_not_captured_yet') || !imageUxGate.blockers?.includes('no_source_screenshots_for_imagegen_review')) throw new Error('selftest failed: Image UX review gate did not block missing source/generated review images');
|
|
2646
|
+
if (imageUxGeneratedLedger.provider?.model !== 'gpt-image-2' || imageUxGeneratedLedger.passed) throw new Error('selftest failed: Image UX generated review ledger did not record required gpt-image-2 blocker state');
|
|
2647
|
+
const imageUxStatusResult = await runProcess(process.execPath, [hookBin, 'image-ux-review', 'status', 'latest', '--json'], { cwd: hookImageUxTmp, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
|
|
2648
|
+
if (imageUxStatusResult.code !== 0) throw new Error(`selftest failed: sks image-ux-review status failed: ${imageUxStatusResult.stderr || imageUxStatusResult.stdout}`);
|
|
2649
|
+
const imageUxStatus = JSON.parse(imageUxStatusResult.stdout);
|
|
2650
|
+
if (imageUxStatus.ok || imageUxStatus.generated_review_ledger?.provider?.model !== 'gpt-image-2' || !imageUxStatus.files?.gate?.endsWith(IMAGE_UX_REVIEW_GATE_ARTIFACT)) throw new Error('selftest failed: sks image-ux-review status did not report gpt-image-2 gate blockers');
|
|
2532
2651
|
const hookPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '$Goal 로그인 세션 만료 UX 개선' });
|
|
2533
2652
|
const hookResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookGoalTmp, input: hookPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2534
2653
|
if (hookResult.code !== 0) throw new Error(`selftest failed: $Goal hook exited ${hookResult.code}: ${hookResult.stderr}`);
|
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.51';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import { nowIso, sha256, writeJsonAtomic } from './fsx.mjs';
|
|
4
|
+
import { CODEX_APP_IMAGE_GENERATION_DOC_URL } from './routes.mjs';
|
|
5
|
+
|
|
6
|
+
export const IMAGE_UX_REVIEW_GATE_ARTIFACT = 'image-ux-review-gate.json';
|
|
7
|
+
export const IMAGE_UX_REVIEW_POLICY_ARTIFACT = 'image-ux-review-policy.json';
|
|
8
|
+
export const IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT = 'image-ux-screen-inventory.json';
|
|
9
|
+
export const IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT = 'image-ux-generated-review-ledger.json';
|
|
10
|
+
export const IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT = 'image-ux-issue-ledger.json';
|
|
11
|
+
export const IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT = 'image-ux-iteration-report.json';
|
|
12
|
+
export const IMAGE_UX_REVIEW_API_DOC_URL = 'https://developers.openai.com/api/docs/guides/image-generation';
|
|
13
|
+
|
|
14
|
+
export const IMAGE_UX_REVIEW_REQUIRED_GATE_FIELDS = Object.freeze([
|
|
15
|
+
'policy_created',
|
|
16
|
+
'screen_inventory_created',
|
|
17
|
+
'source_screenshots_captured',
|
|
18
|
+
'imagegen_review_images_generated',
|
|
19
|
+
'generated_review_images_analyzed',
|
|
20
|
+
'issue_ledger_created',
|
|
21
|
+
'p0_p1_zero',
|
|
22
|
+
'bounded_iteration_complete',
|
|
23
|
+
'changed_screens_rechecked_or_not_applicable',
|
|
24
|
+
'honest_mode_complete'
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
function cleanText(value, fallback = '') {
|
|
28
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
29
|
+
return text || fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function contractText(contract = {}) {
|
|
33
|
+
return cleanText(`${contract.prompt || ''} ${JSON.stringify(contract.answers || {})}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function compactId(prefix, text) {
|
|
37
|
+
return `${prefix}-${sha256(cleanText(text, prefix)).slice(0, 10)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function buildImageUxReviewPolicy(contract = {}) {
|
|
41
|
+
return {
|
|
42
|
+
schema_version: 1,
|
|
43
|
+
created_at: nowIso(),
|
|
44
|
+
contract_hash: contract.sealed_hash || null,
|
|
45
|
+
policy: 'image_generation_ui_ux_review_loop',
|
|
46
|
+
score_threshold: 0.88,
|
|
47
|
+
minimum_delta_to_continue: 0.03,
|
|
48
|
+
max_full_surface_passes: 2,
|
|
49
|
+
max_screen_retries: 2,
|
|
50
|
+
stop_conditions: [
|
|
51
|
+
'Every source screenshot has a matching generated annotated review image',
|
|
52
|
+
'The generated review image has been analyzed back into structured issue rows',
|
|
53
|
+
'P0/P1 issues are zero after allowed fixes',
|
|
54
|
+
'overall_score >= 0.88',
|
|
55
|
+
'improvement_delta < 0.03 after at least one repair pass',
|
|
56
|
+
'max_full_surface_passes or max_screen_retries reached',
|
|
57
|
+
'Codex App imagegen/gpt-image-2 evidence is unavailable'
|
|
58
|
+
],
|
|
59
|
+
source_capture: {
|
|
60
|
+
required: true,
|
|
61
|
+
evidence_policy: 'Use Codex Computer Use for live UI/browser capture when available, or user-provided screenshots for static review. Do not treat browser automation screenshots as Codex Computer Use evidence.',
|
|
62
|
+
accepted_sources: ['codex_computer_use_screenshot', 'user_provided_screenshot', 'exported_static_artifact_image']
|
|
63
|
+
},
|
|
64
|
+
image_generation_review: {
|
|
65
|
+
required_for_gate: true,
|
|
66
|
+
model: 'gpt-image-2',
|
|
67
|
+
preferred_surface: 'Codex App built-in image generation via $imagegen',
|
|
68
|
+
codex_app_imagegen_doc: CODEX_APP_IMAGE_GENERATION_DOC_URL,
|
|
69
|
+
api_image_generation_doc: IMAGE_UX_REVIEW_API_DOC_URL,
|
|
70
|
+
output_artifact: IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
71
|
+
anti_substitution_rule: 'A text-only visual review cannot satisfy this route. Missing generated annotated review images block the gate instead of being simulated.',
|
|
72
|
+
reference_image_flow: [
|
|
73
|
+
'Use each source UI screenshot as the reference image input',
|
|
74
|
+
'Ask imagegen/gpt-image-2 to create a new annotated critique image, not just prose',
|
|
75
|
+
'Draw numbered callouts directly on problem regions',
|
|
76
|
+
'Show severity labels P0/P1/P2/P3 on the generated image',
|
|
77
|
+
'Include visual hierarchy, contrast, alignment, density, affordance, and flow markers',
|
|
78
|
+
'Add a small corrected mini-comp or before/after strip when useful'
|
|
79
|
+
],
|
|
80
|
+
review_prompt_template: [
|
|
81
|
+
'Review this UI screenshot as a senior product design lead.',
|
|
82
|
+
'Output a new annotated review image, using the screenshot as reference.',
|
|
83
|
+
'Overlay numbered callouts on concrete UI regions; label each with P0/P1/P2/P3.',
|
|
84
|
+
'Mark eye-flow arrows, hierarchy/contrast/alignment/density problems, and ambiguous affordances.',
|
|
85
|
+
'Include a compact corrected mini-comp or before/after strip for the highest-impact fix.',
|
|
86
|
+
'Do not invent product requirements beyond what is visible or provided in the route context.'
|
|
87
|
+
].join(' ')
|
|
88
|
+
},
|
|
89
|
+
extraction_policy: {
|
|
90
|
+
input_artifact: IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
91
|
+
output_artifact: IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT,
|
|
92
|
+
rule: 'Analyze the generated annotated review image with vision/OCR, then convert each visible callout into an issue row with severity, region, evidence image id, likely cause, and specific fix action.',
|
|
93
|
+
required_issue_fields: ['id', 'severity', 'screen_id', 'callout_id', 'region', 'evidence_image_id', 'title', 'detail', 'fix_action', 'status']
|
|
94
|
+
},
|
|
95
|
+
remediation_policy: {
|
|
96
|
+
code_changes_allowed: 'only_when_user_or_route_contract_requests_fixing',
|
|
97
|
+
priority_order: ['P0', 'P1', 'P2', 'P3'],
|
|
98
|
+
patch_rule: 'Patch P0/P1 first, then cheap local P2. Re-run only changed, failed, or high-risk screens.',
|
|
99
|
+
no_fallback: 'Do not replace the image-generation review with a hand-written fallback review.'
|
|
100
|
+
},
|
|
101
|
+
evidence_artifacts: [
|
|
102
|
+
IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT,
|
|
103
|
+
IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
104
|
+
IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT,
|
|
105
|
+
IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT,
|
|
106
|
+
IMAGE_UX_REVIEW_GATE_ARTIFACT
|
|
107
|
+
],
|
|
108
|
+
notes: [
|
|
109
|
+
'The central mechanism is generated visual critique: gpt-image-2/imagegen must produce a new review image from the source UI screenshot.',
|
|
110
|
+
'The generated review image is then read back into text/JSON issues. This is intentionally different from direct text-only screenshot critique.'
|
|
111
|
+
]
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function buildImageUxScreenInventory(contract = {}) {
|
|
116
|
+
const text = contractText(contract);
|
|
117
|
+
const suppliedImages = [
|
|
118
|
+
...(Array.isArray(contract.answers?.IMAGE_UX_REVIEW_SOURCE_IMAGES) ? contract.answers.IMAGE_UX_REVIEW_SOURCE_IMAGES : []),
|
|
119
|
+
...(Array.isArray(contract.answers?.SOURCE_SCREENSHOTS) ? contract.answers.SOURCE_SCREENSHOTS : [])
|
|
120
|
+
].map((item) => cleanText(item)).filter(Boolean);
|
|
121
|
+
const target = cleanText(contract.answers?.TARGET_URL || contract.answers?.TARGET_SURFACE || contract.prompt, 'UI surface to review');
|
|
122
|
+
return {
|
|
123
|
+
schema_version: 1,
|
|
124
|
+
created_at: nowIso(),
|
|
125
|
+
contract_hash: contract.sealed_hash || null,
|
|
126
|
+
target,
|
|
127
|
+
task_signature: compactId('image-ux-target', text),
|
|
128
|
+
capture_required: suppliedImages.length === 0,
|
|
129
|
+
source_screens: suppliedImages.map((source, index) => ({
|
|
130
|
+
id: `screen-${index + 1}`,
|
|
131
|
+
source,
|
|
132
|
+
source_type: /^https?:\/\//i.test(source) ? 'url_or_remote_image' : 'local_or_named_image',
|
|
133
|
+
status: 'provided_unverified'
|
|
134
|
+
})),
|
|
135
|
+
capture_policy: 'Capture actual UI screens with Codex Computer Use when the target is live. For static images, record the provided image path or attachment id. Each source screen must later map to a generated review image.',
|
|
136
|
+
passed: suppliedImages.length > 0,
|
|
137
|
+
blockers: suppliedImages.length > 0 ? [] : ['source_screenshots_not_captured_yet']
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function buildImageUxGeneratedReviewLedger(contract = {}, inventory = buildImageUxScreenInventory(contract), existing = null) {
|
|
142
|
+
const existingImages = Array.isArray(existing?.generated_review_images) ? existing.generated_review_images : [];
|
|
143
|
+
const sourceScreens = inventory.source_screens || [];
|
|
144
|
+
const missingScreens = sourceScreens.filter((screen) => !existingImages.some((image) => image.source_screen_id === screen.id));
|
|
145
|
+
const blockers = [];
|
|
146
|
+
if (sourceScreens.length === 0) blockers.push('no_source_screenshots_for_imagegen_review');
|
|
147
|
+
if (missingScreens.length > 0) blockers.push('missing_generated_annotated_review_images');
|
|
148
|
+
return {
|
|
149
|
+
schema_version: 1,
|
|
150
|
+
created_at: nowIso(),
|
|
151
|
+
contract_hash: contract.sealed_hash || null,
|
|
152
|
+
provider: {
|
|
153
|
+
model: 'gpt-image-2',
|
|
154
|
+
preferred_surface: 'Codex App $imagegen',
|
|
155
|
+
codex_app_imagegen_doc: CODEX_APP_IMAGE_GENERATION_DOC_URL,
|
|
156
|
+
api_image_generation_doc: IMAGE_UX_REVIEW_API_DOC_URL
|
|
157
|
+
},
|
|
158
|
+
required: true,
|
|
159
|
+
generated_review_images: existingImages,
|
|
160
|
+
planned_reviews: sourceScreens.map((screen) => ({
|
|
161
|
+
id: compactId('image-ux-review', `${screen.id}:${screen.source || screen.id}`),
|
|
162
|
+
source_screen_id: screen.id,
|
|
163
|
+
status: existingImages.some((image) => image.source_screen_id === screen.id) ? 'generated' : 'pending_imagegen',
|
|
164
|
+
required_output: 'annotated_review_image_with_numbered_callouts_and_optional_mini_comp'
|
|
165
|
+
})),
|
|
166
|
+
generated_count: existingImages.length,
|
|
167
|
+
required_count: sourceScreens.length,
|
|
168
|
+
blockers,
|
|
169
|
+
passed: sourceScreens.length > 0 && blockers.length === 0,
|
|
170
|
+
notes: [
|
|
171
|
+
'This ledger records real generated review images. It must not be marked passed from prose-only critique.',
|
|
172
|
+
'Route workers should attach generated image paths, Codex App output ids, or API output paths before passing the gate.'
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function buildImageUxIssueLedger(contract = {}, generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract), existing = null) {
|
|
178
|
+
const issues = Array.isArray(existing?.issues) ? existing.issues : [];
|
|
179
|
+
const missingGeneratedReview = generatedReviewLedger.passed !== true;
|
|
180
|
+
const blockers = missingGeneratedReview ? ['generated_review_images_missing_or_incomplete'] : [];
|
|
181
|
+
const blockingIssues = issues.filter((issue) => ['P0', 'P1'].includes(issue.severity) && issue.status !== 'fixed' && issue.status !== 'accepted_not_applicable');
|
|
182
|
+
if (blockingIssues.length > 0) blockers.push('p0_p1_issues_unresolved');
|
|
183
|
+
return {
|
|
184
|
+
schema_version: 1,
|
|
185
|
+
created_at: nowIso(),
|
|
186
|
+
contract_hash: contract.sealed_hash || null,
|
|
187
|
+
extraction_source: IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
188
|
+
extraction_rule: 'Issues must be extracted from the generated annotated review image callouts, not invented from memory.',
|
|
189
|
+
issues,
|
|
190
|
+
blocking_issue_count: blockingIssues.length,
|
|
191
|
+
p0_p1_zero: blockingIssues.length === 0,
|
|
192
|
+
blockers,
|
|
193
|
+
passed: generatedReviewLedger.passed === true && blockingIssues.length === 0,
|
|
194
|
+
scorecard: {
|
|
195
|
+
visual_review_completion: generatedReviewLedger.passed ? 0.92 : 0.25,
|
|
196
|
+
issue_extraction_integrity: generatedReviewLedger.passed && issues.length > 0 ? 0.9 : 0.4,
|
|
197
|
+
p0_p1_resolution: blockingIssues.length === 0 ? 0.9 : 0.4,
|
|
198
|
+
overall_score: Number((generatedReviewLedger.passed && blockingIssues.length === 0 ? 0.9 : 0.42).toFixed(3))
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function buildImageUxIterationReport(contract = {}, policy = buildImageUxReviewPolicy(contract), generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract), issueLedger = buildImageUxIssueLedger(contract, generatedReviewLedger)) {
|
|
204
|
+
const passed = generatedReviewLedger.passed === true
|
|
205
|
+
&& issueLedger.passed === true
|
|
206
|
+
&& Number(issueLedger.scorecard?.overall_score || 0) >= Number(policy.score_threshold || 0.88);
|
|
207
|
+
return {
|
|
208
|
+
schema_version: 1,
|
|
209
|
+
created_at: nowIso(),
|
|
210
|
+
contract_hash: contract.sealed_hash || null,
|
|
211
|
+
loop_policy: {
|
|
212
|
+
max_full_surface_passes: policy.max_full_surface_passes,
|
|
213
|
+
max_screen_retries: policy.max_screen_retries,
|
|
214
|
+
score_threshold: policy.score_threshold,
|
|
215
|
+
minimum_delta_to_continue: policy.minimum_delta_to_continue
|
|
216
|
+
},
|
|
217
|
+
passes: [
|
|
218
|
+
{
|
|
219
|
+
pass: 1,
|
|
220
|
+
type: generatedReviewLedger.passed ? 'imagegen_visual_review_extraction' : 'waiting_for_imagegen_generated_review_images',
|
|
221
|
+
generated_review_images: generatedReviewLedger.generated_count || 0,
|
|
222
|
+
blocking_issue_count: issueLedger.blocking_issue_count || 0,
|
|
223
|
+
score: issueLedger.scorecard?.overall_score || 0,
|
|
224
|
+
status: passed ? 'passed' : 'blocked'
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
stopped: true,
|
|
228
|
+
stop_reason: passed ? 'score_threshold_met_and_no_p0_p1_issues' : 'imagegen_review_evidence_or_issue_resolution_required',
|
|
229
|
+
passed
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function defaultImageUxReviewGate(contract = {}, parts = {}) {
|
|
234
|
+
const policy = parts.policy || buildImageUxReviewPolicy(contract);
|
|
235
|
+
const inventory = parts.inventory || buildImageUxScreenInventory(contract);
|
|
236
|
+
const generatedReviewLedger = parts.generatedReviewLedger || buildImageUxGeneratedReviewLedger(contract, inventory);
|
|
237
|
+
const issueLedger = parts.issueLedger || buildImageUxIssueLedger(contract, generatedReviewLedger);
|
|
238
|
+
const iterationReport = parts.iterationReport || buildImageUxIterationReport(contract, policy, generatedReviewLedger, issueLedger);
|
|
239
|
+
return {
|
|
240
|
+
schema_version: 1,
|
|
241
|
+
created_at: nowIso(),
|
|
242
|
+
contract_hash: contract.sealed_hash || null,
|
|
243
|
+
passed: false,
|
|
244
|
+
policy_created: true,
|
|
245
|
+
screen_inventory_created: true,
|
|
246
|
+
source_screenshots_captured: inventory.passed === true,
|
|
247
|
+
imagegen_review_images_generated: generatedReviewLedger.passed === true,
|
|
248
|
+
generated_review_images_analyzed: issueLedger.extraction_source === IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT && generatedReviewLedger.passed === true,
|
|
249
|
+
issue_ledger_created: true,
|
|
250
|
+
p0_p1_zero: issueLedger.p0_p1_zero === true && generatedReviewLedger.passed === true,
|
|
251
|
+
bounded_iteration_complete: iterationReport.passed === true,
|
|
252
|
+
changed_screens_rechecked_or_not_applicable: iterationReport.passed === true,
|
|
253
|
+
honest_mode_complete: false,
|
|
254
|
+
required_artifacts: [
|
|
255
|
+
IMAGE_UX_REVIEW_POLICY_ARTIFACT,
|
|
256
|
+
IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT,
|
|
257
|
+
IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
258
|
+
IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT,
|
|
259
|
+
IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT
|
|
260
|
+
],
|
|
261
|
+
blockers: [
|
|
262
|
+
...(inventory.blockers || []),
|
|
263
|
+
...(generatedReviewLedger.blockers || []),
|
|
264
|
+
...(issueLedger.blockers || [])
|
|
265
|
+
],
|
|
266
|
+
notes: [
|
|
267
|
+
'Do not pass this gate from direct text-only screenshot critique.',
|
|
268
|
+
'Pass only after source screenshots have real generated annotated review images and those generated images are extracted into issue rows.'
|
|
269
|
+
]
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export async function writeImageUxReviewRouteArtifacts(dir, contract = {}) {
|
|
274
|
+
const policy = buildImageUxReviewPolicy(contract);
|
|
275
|
+
const inventory = buildImageUxScreenInventory(contract);
|
|
276
|
+
const existingGenerated = await readExistingJson(dir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT);
|
|
277
|
+
const existingIssues = await readExistingJson(dir, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT);
|
|
278
|
+
const generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract, inventory, existingGenerated);
|
|
279
|
+
const issueLedger = buildImageUxIssueLedger(contract, generatedReviewLedger, existingIssues);
|
|
280
|
+
const iterationReport = buildImageUxIterationReport(contract, policy, generatedReviewLedger, issueLedger);
|
|
281
|
+
const gate = defaultImageUxReviewGate(contract, { policy, inventory, generatedReviewLedger, issueLedger, iterationReport });
|
|
282
|
+
await writeJsonAtomic(path.join(dir, IMAGE_UX_REVIEW_POLICY_ARTIFACT), policy);
|
|
283
|
+
await writeJsonAtomic(path.join(dir, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT), inventory);
|
|
284
|
+
await writeJsonAtomic(path.join(dir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT), generatedReviewLedger);
|
|
285
|
+
await writeJsonAtomic(path.join(dir, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT), issueLedger);
|
|
286
|
+
await writeJsonAtomic(path.join(dir, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT), iterationReport);
|
|
287
|
+
await writeJsonAtomic(path.join(dir, IMAGE_UX_REVIEW_GATE_ARTIFACT), gate);
|
|
288
|
+
return { policy, inventory, generated_review_ledger: generatedReviewLedger, issue_ledger: issueLedger, iteration_report: iterationReport, gate };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function readExistingJson(dir, file) {
|
|
292
|
+
try {
|
|
293
|
+
const raw = await fsp.readFile(path.join(dir, file), 'utf8');
|
|
294
|
+
return JSON.parse(raw);
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
package/src/core/init.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { isHarnessSourceProject, writeHarnessGuardPolicy } from './harness-guard
|
|
|
7
7
|
import { repairSksGeneratedArtifacts } from './harness-conflicts.mjs';
|
|
8
8
|
import { installVersionGitHook } from './version-manager.mjs';
|
|
9
9
|
import { MIN_TEAM_REVIEWER_LANES, MIN_TEAM_REVIEW_POLICY_TEXT } from './team-review-policy.mjs';
|
|
10
|
-
import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_COMMAND_ALIASES, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_DESIGN_REFERENCES, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
10
|
+
import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_COMMAND_ALIASES, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_DESIGN_REFERENCES, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, imageUxReviewPipelinePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
11
11
|
import { SKILL_DREAM_POLICY, skillDreamPolicyText } from './skill-forge.mjs';
|
|
12
12
|
|
|
13
13
|
const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
|
|
@@ -96,6 +96,12 @@ function isSksManagedHook(hook) {
|
|
|
96
96
|
|
|
97
97
|
const AGENTS_BLOCK = "\n# Sneakoscope Codex Managed Rules\n\nThis repository uses Sneakoscope Codex.\n\n## Core Rules\n\n- Codex native `/goal` workflows are the persisted continuation surface; Ralph is removed from the user-facing SKS surface.\n- Keep runtime state bounded: raw logs go to files, prompts get tails/summaries, and `sks gc` may prune stale artifacts.\n- Before substantive work, SKS checks npm for a newer package. If newer, ask update-now vs skip-for-this-conversation.\n- Versioning is managed by the SKS pre-commit hook; check `sks versioning status`. Bypass only with `SKS_DISABLE_VERSIONING=1`.\n- Installed harness files are immutable to LLM edits: `.codex/*`, `.agents/skills/`, `.codex/agents/`, `.sneakoscope/*policy*.json`, `AGENTS.md`, and `node_modules/sneakoscope`. The Sneakoscope engine source repo is the only automatic exception.\n- OMX/DCodex conflicts block setup/doctor. Show `sks conflicts prompt`; cleanup requires explicit human approval.\n- Do not stop at a plan when implementation was requested. Finish, verify, or report the hard blocker.\n- Do not create unrequested fallback implementation code. If the requested path is impossible, block with evidence instead of inventing substitute behavior.\n\n## Routes\n\n- General execution/code-changing prompts default to `$Team`: analysis scouts, TriWiki refresh/validate, read-only debate, consensus, concrete runtime task graph/inboxes, fresh executor team, minimum five-lane Team review, integration, Honest Mode.\n- `$Computer-Use` / `$CU` is the maximum-speed Codex Computer Use lane for UI/browser/visual tasks: skip Team debate and upfront TriWiki loops, use Codex Computer Use directly, then refresh/validate TriWiki and run Honest Mode at final closeout.\n- `$Goal` is a fast bridge/overlay for Codex native `/goal` create/pause/resume/clear persistence controls; implementation continues through the selected SKS execution route.\n- TriWiki recall must stay bounded. Use `sks wiki sweep` to record demote, soft-forget, archive, delete, promote-to-skill, and promote-to-rule candidates instead of injecting every old claim.\n- Team missions must keep schema-backed evidence current: `work-order-ledger.json`, `effort-decision.json`, `team-dashboard-state.json`, and route-specific visual/dogfood artifacts where applicable. Team completion requires at least five independent reviewer/QA validation lanes before integration or final, even when a prompt requests fewer reviewers. Use `sks validate-artifacts latest` before claiming those artifacts pass.\n- `$DFix` is only for tiny design/content edits and bypasses the main pipeline, Team, TriWiki/TriFix/reflection recording, and persistent route state; it still uses a one-line DFix-specific Honest check before final. `$PPT` is the restrained, information-first HTML/PDF presentation route and must seal delivery context, audience profile, STP, decision context, and 3+ pain-point/solution/aha mappings before design/render work. It must avoid over-designed visuals, carry detail through hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents, preserve editable source HTML under `source-html/`, record `ppt-parallel-report.json`, and clean PPT-only temporary build files before completion. `$Answer`, `$Help`, and `$Wiki` stay lightweight.\n- For code work, surface route/guard/write scopes first, split independent worker scopes when available, and keep parent-owned integration and verification.\n- Design work reads `design.md` as the only design decision SSOT. If missing, create it through `design-system-builder` from `docs/Design-Sys-Prompt.md`; getdesign.md, getdesign-reference, and curated DESIGN.md examples from https://github.com/VoltAgent/awesome-design-md are source inputs to fuse into that SSOT or route-local style tokens, not parallel design authorities. Image/logo/raster assets use `imagegen`, which must prefer official Codex App built-in image generation via `$imagegen` / `gpt-image-2` before API generation.\n- Research, AutoResearch, performance, token, accuracy, SEO/GEO, or workflow-improvement claims need experiment/eval evidence. Do not claim live model accuracy without a scored dataset.\n- Treat handwritten files above 3000 lines as split-review risks. Run `sks code-structure scan` and prefer extraction before adding substantial logic.\n- Skill dreaming stays lightweight: route use records JSON counters in `.sneakoscope/skills/dream-state.json`, and full skill inventory/recommendation runs only after the configured count/cooldown threshold. Reports are recommendation-only; deleting or merging skills needs explicit user approval.\n\n## Evidence And Context\n\n- Context7 is required for external libraries, APIs, MCPs, package managers, SDKs, and generated docs: resolve-library-id then query-docs.\n- When tech stack, framework, package, runtime, or deployment-platform versions change, use Context7 or official vendor web docs, record current syntax/security/limit guidance as high-priority TriWiki claims, then refresh and validate before coding.\n- TriWiki is the context-tracking SSOT for long-running missions, Team handoffs, and context-pressure recovery. Read `.sneakoscope/wiki/context-pack.json` before each stage, use `attention.use_first` for compact high-trust recall, hydrate `attention.hydrate_first` from source before risky or lower-trust decisions, refresh after findings or artifact changes, and validate before handoffs/final claims.\n- Source priority: current code/tests/config, decision contract, vgraph, beta, GX render/snapshot metadata, LLM Wiki coordinate index, then model knowledge only if allowed.\n- Final response before stop: summarize what was done, what changed for the user/repo, what was verified, and what remains unverified or blocked; then run Honest Mode. Say what passed and what was not verified.\n- `$From-Chat-IMG` uses forensic visual effort, not ordinary Team effort. Completion is blocked until source inventory, visual mapping, work-order coverage, scoped dogfood/QA, and post-fix verification artifacts are present and valid.\n\n## Safety\n\n- Database access is high risk. Use read-only inspection by default; live data mutation is out of scope unless a sealed contract allows local or branch-only migration files.\n- MAD and MAD-SKS widen only explicit scoped permissions; they still do not authorize unrequested fallback implementation code.\n- Task completion requires relevant tests or justification, zero unsupported critical claims, accepted visual/wiki drift, and final evidence.\n\n## Codex App\n\nUse `.codex/SNEAKOSCOPE.md`, generated `.agents/skills`, `.codex/hooks.json`, and SKS dollar commands (`$sks`, `$team`, `$computer-use`, `$cu`, `$ppt`, `$goal`, `$dfix`, `$qa-loop`, etc.) as the app control surface.\n";
|
|
98
98
|
|
|
99
|
+
function agentsBlockText() {
|
|
100
|
+
return AGENTS_BLOCK
|
|
101
|
+
.replace('`$Answer`, `$Help`, and `$Wiki` stay lightweight.', '`$Image-UX-Review` / `$UX-Review` is the imagegen/gpt-image-2 UI/UX review route: source screenshots must become generated annotated review images, those generated images must be extracted into issue ledgers, and text-only critique cannot pass the route gate. `$Answer`, `$Help`, and `$Wiki` stay lightweight.')
|
|
102
|
+
.replace('`$ppt`, `$goal`, `$dfix`, `$qa-loop`', '`$ppt`, `$image-ux-review`, `$ux-review`, `$goal`, `$dfix`, `$qa-loop`');
|
|
103
|
+
}
|
|
104
|
+
|
|
99
105
|
export async function initProject(root, opts = {}) {
|
|
100
106
|
const created = [];
|
|
101
107
|
const installScope = normalizeInstallScope(opts.installScope || 'global');
|
|
@@ -602,7 +608,7 @@ function upsertTomlTable(text, table, block) {
|
|
|
602
608
|
if (localOnly && existingAgents && !hasManagedAgentsBlock) {
|
|
603
609
|
created.push('AGENTS.md skipped (local-only existing file)');
|
|
604
610
|
} else {
|
|
605
|
-
await mergeManagedBlock(agentsMdPath, 'Sneakoscope Codex GX MANAGED BLOCK',
|
|
611
|
+
await mergeManagedBlock(agentsMdPath, 'Sneakoscope Codex GX MANAGED BLOCK', agentsBlockText());
|
|
606
612
|
created.push('AGENTS.md managed block');
|
|
607
613
|
}
|
|
608
614
|
|
|
@@ -732,6 +738,7 @@ function codexAppQuickReference(scope, commandPrefix) {
|
|
|
732
738
|
}
|
|
733
739
|
|
|
734
740
|
export async function installSkills(root) {
|
|
741
|
+
const imageUxReviewSkill = (name) => `---\nname: ${name}\ndescription: $Image-UX-Review/$UX-Review imagegen/gpt-image-2 annotated UI/UX review loop.\n---\n\nUse only for $Image-UX-Review, $UX-Review, $visual-review, or $ui-ux-review UI/UX review requests. ${imageUxReviewPipelinePolicyText()} Core loop: capture or attach source UI screenshots, then invoke Codex App $imagegen with gpt-image-2 to create a new generated annotated review image from each source screenshot, then analyze the generated review image with vision/OCR into image-ux-issue-ledger.json, then apply only requested safe fixes and recheck changed screens. Text-only screenshot critique cannot satisfy this route; missing generated annotated review images must keep image-ux-review-gate.json blocked. Use Codex Computer Use for live UI/browser capture when available; browser automation screenshots are not a substitute for Codex Computer Use evidence. Required artifacts: image-ux-review-policy.json, image-ux-screen-inventory.json, image-ux-generated-review-ledger.json, image-ux-issue-ledger.json, image-ux-iteration-report.json, image-ux-review-gate.json. Finish with reflection and Honest Mode.\n`;
|
|
735
742
|
const skills = {
|
|
736
743
|
'dfix': `---\nname: dfix\ndescription: Ultralight fast design/content fix mode for $DFix or $dfix requests and inferred simple edits such as text color, copy, labels, spacing, or translation.\n---\n\nUse for tiny copy/color/label/spacing/translation edits. List exact micro-edits, inspect only needed files, apply only those edits, and run cheap verification. Bypass broad SKS routing, mission state, TriWiki/TriFix/reflection/state recording, Goal, Research, eval, redesign, and repeated full-route Honest Mode loops. Start the final answer with \`DFix 완료 요약:\` and include one \`DFix 솔직모드:\` line covering verified, not verified, and remaining issues. Read \`design.md\` for UI work when present; use imagegen for image/logo/raster assets.\n`,
|
|
737
744
|
'answer': `---\nname: answer\ndescription: Answer-only research route for ordinary questions that should not start implementation.\n---\n\nUse for explanations, comparisons, status, facts, source-backed research, or docs guidance. Use repo/TriWiki first for project-local facts; hydrate low-trust claims from source. Browse or use Context7 for current external package/API/framework/MCP docs. End with a concise answer summary plus Honest Mode; do not create missions, subagents, or file edits.\n`,
|
|
@@ -751,7 +758,7 @@ export async function installSkills(root) {
|
|
|
751
758
|
'mad-sks': `---\nname: mad-sks\ndescription: Explicit high-risk authorization modifier for $MAD-SKS scoped Supabase MCP DB permission widening.\n---\n\nUse only when the user explicitly invokes $MAD-SKS or top-level sks --mad. It can be combined with another route, such as $MAD-SKS $Team or $DB ... $MAD-SKS; in that case the other command remains the primary workflow and MAD-SKS is only the temporary permission grant. The widened permission applies only while the active mission gate is open, must be deactivated when the task ends, and opens live server work, Supabase MCP database writes, column/schema cleanup, direct execute SQL, migration application when required, and normal targeted DB writes. Keep only catastrophic safeguards: whole database/schema/table removal, truncate, all-row delete/update, reset, dangerous project/branch management, credential exfiltration, persistent security weakening, and unrequested fallback implementation remain blocked. Do not carry MAD-SKS permission into later prompts or routes. The permission profile is centralized in src/core/permission-gates.mjs so skill/hook/MCP-style gates share one decision function.\n`,
|
|
752
759
|
'gx': `---\nname: gx\ndescription: Dollar-command route for $GX or $gx deterministic GX visual context cartridges.\n---\n\nUse when the user invokes $GX/$gx or asks for architecture/context visualization through SKS. Prefer sks gx init, render, validate, drift, and snapshot. vgraph.json remains the source of truth.\n`,
|
|
753
760
|
'help': `---\nname: help\ndescription: Dollar-command route for $Help or $help explaining installed SKS commands and workflows.\n---\n\nUse when the user invokes $Help/$help or asks what commands exist. Prefer concise output from sks commands, sks usage <topic>, sks quickstart, sks aliases, and sks codex-app.\n`,
|
|
754
|
-
'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline for execution prompts; Answer and DFix bypass it.\n---\n\nClassify intent: Answer only for real questions; question-shaped implicit instructions, complaints, and mandatory-policy statements route to Team. DFix handles tiny design/content; code defaults to Team unless safety/research/GX route fits. Infer goal, target, constraints, acceptance, risk, and smallest safe route. Score ambiguity first using goal, constraints, success criteria, and codebase context; ask only the lowest-clarity scope/safety/behavior/acceptance-changing questions within a small question budget, otherwise seal inferred answers. Materialize pipeline-plan.json for the runtime lane, kept/skipped stages, no-fallback invariant, and verification; inspect with sks pipeline plan, adding --proof-field when changed files are known. Code work surfaces route/guard/scopes, materializes team-roster.json from default or explicit counts before implementation, compiles concrete Team runtime graph/inbox artifacts after consensus, and parent owns integration/tests/Context7/Honest Mode. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${skillDreamPolicyText()}\n\n${chatCaptureIntakeText()}\n\nDesign: non-PPT UI/UX reads design.md; if missing use design-system-builder; use imagegen for image/logo/raster, and imagegen must prefer Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) before API generation. For $PPT, ${pptPipelineAllowlistPolicyText()} ${getdesignReferencePolicyText()} TriWiki context-tracking SSOT: .sneakoscope/wiki/context-pack.json; read only the latest coordinate+voxel overlay pack before every route stage, run sks wiki refresh/pack after changes, validate before handoffs/final.\n`,
|
|
761
|
+
'prompt-pipeline': `---\nname: prompt-pipeline\ndescription: Default SKS prompt optimization pipeline for execution prompts; Answer and DFix bypass it.\n---\n\nClassify intent: Answer only for real questions; question-shaped implicit instructions, complaints, and mandatory-policy statements route to Team. DFix handles tiny design/content; code defaults to Team unless safety/research/GX route fits. Infer goal, target, constraints, acceptance, risk, and smallest safe route. Score ambiguity first using goal, constraints, success criteria, and codebase context; ask only the lowest-clarity scope/safety/behavior/acceptance-changing questions within a small question budget, otherwise seal inferred answers. Materialize pipeline-plan.json for the runtime lane, kept/skipped stages, no-fallback invariant, and verification; inspect with sks pipeline plan, adding --proof-field when changed files are known. Code work surfaces route/guard/scopes, materializes team-roster.json from default or explicit counts before implementation, compiles concrete Team runtime graph/inbox artifacts after consensus, and parent owns integration/tests/Context7/Honest Mode. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${skillDreamPolicyText()}\n\n${chatCaptureIntakeText()}\n\nDesign: non-PPT UI/UX reads design.md; if missing use design-system-builder; use imagegen for image/logo/raster, and imagegen must prefer Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) before API generation. For UI/UX review/audit requests that mention image generation, gpt-image-2, callouts, or annotated review images, route to $Image-UX-Review/$UX-Review and require generated annotated review image evidence before issue extraction; do not satisfy that route with text-only critique. For $PPT, ${pptPipelineAllowlistPolicyText()} ${getdesignReferencePolicyText()} TriWiki context-tracking SSOT: .sneakoscope/wiki/context-pack.json; read only the latest coordinate+voxel overlay pack before every route stage, run sks wiki refresh/pack after changes, validate before handoffs/final.\n`,
|
|
755
762
|
'reasoning-router': `---\nname: reasoning-router\ndescription: Temporary SKS reasoning-effort routing for every command and pipeline route.\n---\n\nmedium: simple copy/color/discovery/setup/mechanical edits. high: logic, safety, architecture, DB, orchestration, refactor, multi-file work. xhigh: research, AutoResearch, falsification, benchmarks, SEO/GEO, open-ended discovery, and From-Chat-IMG image work-order analysis. Routing is temporary; return to default after the gate. Inspect with sks reasoning and sks pipeline status.\n`,
|
|
756
763
|
'pipeline-runner': `---\nname: pipeline-runner\ndescription: Execute SKS dollar-command routes as stateful pipelines with mission artifacts, route gates, Context7 evidence, temporary reasoning routing, reflection, and Honest Mode.\n---\n\nEvery $ command is a route. Use current.json, mission artifacts, and pipeline-plan.json as the execution plan: it records the lane, skipped stages, kept stages, verification, and no-unrequested-fallback invariant. Use temporary reasoning, TriWiki before stages, source hydration, Context7 when required, Team cleanup before reflection, reflection for full routes, and completion summary plus Honest Mode before final. Surface guard/scopes, record evidence, refresh/pack/validate TriWiki, and check sks pipeline status/resume/plan. ${speedLanePolicyText()} ${skillDreamPolicyText()}\n`,
|
|
757
764
|
'context7-docs': `---\nname: context7-docs\ndescription: Enforce Context7 MCP documentation evidence for SKS routes that depend on external libraries, frameworks, APIs, MCPs, package managers, DB SDKs, or generated docs.\n---\n\nWhen required, resolve-library-id, then query-docs for the resolved id. Legacy get-library-docs evidence is accepted. Prefer sks context7 tools/resolve/docs/evidence and finish only after both evidence stages exist. Check setup with sks context7 check.\n`,
|
|
@@ -768,6 +775,10 @@ export async function installSkills(root) {
|
|
|
768
775
|
'turbo-context-pack': `---\nname: turbo-context-pack\ndescription: Build ultra-low-token context packet with Q4 bits, Q3 tags, top-K claims, and minimal evidence.\n---\n\nDefault to Q4/Q3 plus TriWiki RGBA anchors and attention.use_first. Add Q2/Q1 only when needed or when attention.hydrate_first says source hydration is required. Keep id, hash, path, and coordinate tuple for hydration.\n`,
|
|
769
776
|
'research-discovery': `---\nname: research-discovery\ndescription: Run SKS Research Mode for frontier-style research, hypotheses, novelty ledgers, falsification, and experiments.\n---\n\nFrame criteria, map assumptions, generate hypotheses, falsify, keep surviving insights, and record novelty/confidence/falsifiers/next experiments. Do not overclaim.\n`,
|
|
770
777
|
'performance-evaluator': `---\nname: performance-evaluator\ndescription: Evaluate SKS performance, token-saving, accuracy-proxy, context-compression, or workflow improvements.\n---\n\nUse sks eval run/compare before claims. Report token_savings_pct, accuracy_delta/proxy, required_recall, support, and meaningful_improvement.\n`,
|
|
778
|
+
'image-ux-review': imageUxReviewSkill('image-ux-review'),
|
|
779
|
+
'ux-review': imageUxReviewSkill('ux-review'),
|
|
780
|
+
'visual-review': imageUxReviewSkill('visual-review'),
|
|
781
|
+
'ui-ux-review': imageUxReviewSkill('ui-ux-review'),
|
|
771
782
|
'imagegen': `---\nname: imagegen\ndescription: Required bridge to Codex App built-in image generation for logos, image assets, raster visuals, and image edits.\n---\n\nUse for generated or edited image assets: logo, product image, illustration, sprite, mockup, texture, cutout, or bitmap. Prefer the official Codex App built-in image generation feature documented at ${CODEX_APP_IMAGE_GENERATION_DOC_URL}: ask naturally or invoke \`$imagegen\`; Codex uses built-in image generation with gpt-image-2 and counts it against Codex usage limits. For larger batches only, use the API path when OPENAI_API_KEY is explicitly available and the user or route contract allows API-priced generation. Do not substitute placeholder SVG/HTML/CSS for requested raster assets; follow design.md when relevant.\n`,
|
|
772
783
|
'getdesign-reference': `---\nname: getdesign-reference\ndescription: Use getdesign.md official design reference as an input to the design.md SSOT for UI/UX, presentation, and HTML/PDF systems.\n---\n\nUse when creating or improving design.md, UI/UX design systems, deck-like HTML artifacts, presentation PDFs, or brand-inspired visual systems. design.md is the only design decision SSOT; reference ${GETDESIGN_REFERENCE.url}, ${GETDESIGN_REFERENCE.docs_url}, and ${AWESOME_DESIGN_MD_REFERENCE.url} only as source inputs to synthesize or update that SSOT or a route-local style-token artifact. Prefer the official Codex skill if available with \`${GETDESIGN_REFERENCE.codex_skill_install}\`. If the skill CLI is unavailable, use this generated skill plus official docs/API/CLI/SDK references and curated DESIGN.md examples as inputs. Do not claim getdesign MCP is configured unless a current official MCP surface is actually installed.\n`,
|
|
773
784
|
'design-system-builder': `---\nname: design-system-builder\ndescription: Create the single design.md SSOT from docs/Design-Sys-Prompt.md when UI/UX work has no design system.\n---\n\nWhen \`design.md\` is missing, read docs/Design-Sys-Prompt.md as the builder prompt, inspect product/UI context, and use getdesign-reference, official getdesign.md docs, and curated DESIGN.md examples from ${AWESOME_DESIGN_MD_REFERENCE.url} only as source inputs. Fuse those inputs into one design.md SSOT with tokens, components, states, imagery, accessibility, and verification rules; do not leave multiple design files or references as competing authorities. Use the plan tool only for real ambiguity plus default font recommendation. Use imagegen for assets.\n`,
|
|
@@ -834,7 +845,7 @@ async function removeStaleGeneratedSkillsFromManifest(root, skillNames) {
|
|
|
834
845
|
}
|
|
835
846
|
|
|
836
847
|
function enrichSkillContent(name, content) {
|
|
837
|
-
if (!['sks', 'answer', 'wiki', 'team', 'qa-loop', 'ppt', 'computer-use', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'reflection', 'prompt-pipeline', 'pipeline-runner', 'context7-docs', 'turbo-context-pack', 'hproof-evidence-bind'].includes(name)) return content;
|
|
848
|
+
if (!['sks', 'answer', 'wiki', 'team', 'qa-loop', 'ppt', 'image-ux-review', 'ux-review', 'visual-review', 'ui-ux-review', 'computer-use', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'reflection', 'prompt-pipeline', 'pipeline-runner', 'context7-docs', 'turbo-context-pack', 'hproof-evidence-bind'].includes(name)) return content;
|
|
838
849
|
const text = String(content || '').trimEnd();
|
|
839
850
|
const activation = pipelineActivationText(name);
|
|
840
851
|
if (text.includes('TriWiki context-tracking SSOT')) {
|
|
@@ -852,7 +863,7 @@ Context tracking:
|
|
|
852
863
|
}
|
|
853
864
|
|
|
854
865
|
function pipelineActivationText(name) {
|
|
855
|
-
const stateful = new Set(['sks', 'team', 'qa-loop', 'ppt', 'computer-use', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'prompt-pipeline', 'pipeline-runner']);
|
|
866
|
+
const stateful = new Set(['sks', 'team', 'qa-loop', 'ppt', 'image-ux-review', 'ux-review', 'visual-review', 'ui-ux-review', 'computer-use', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'prompt-pipeline', 'pipeline-runner']);
|
|
856
867
|
if (!stateful.has(name)) return '';
|
|
857
868
|
return `Codex App pipeline activation:
|
|
858
869
|
- If the SKS UserPromptSubmit hook already injected route context, follow that context.
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -14,9 +14,10 @@ import { MISTAKE_RECALL_ARTIFACT, mistakeRecallGateStatus } from './mistake-reca
|
|
|
14
14
|
import { recordSkillDreamEvent, skillDreamPolicyText, writeSkillForgeReport } from './skill-forge.mjs';
|
|
15
15
|
import { writeResearchPlan } from './research.mjs';
|
|
16
16
|
import { PPT_REQUIRED_GATE_FIELDS } from './ppt.mjs';
|
|
17
|
+
import { IMAGE_UX_REVIEW_GATE_ARTIFACT, IMAGE_UX_REVIEW_POLICY_ARTIFACT, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT, IMAGE_UX_REVIEW_REQUIRED_GATE_FIELDS, writeImageUxReviewRouteArtifacts } from './image-ux-review.mjs';
|
|
17
18
|
import { SPEED_LANE_POLICY } from './proof-field.mjs';
|
|
18
19
|
import { permissionGateSummary } from './permission-gates.mjs';
|
|
19
|
-
import { CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, chatCaptureIntakeText, context7RequirementText, dollarCommand, evidenceMentionsForbiddenBrowserAutomation, getdesignReferencePolicyText, hasFromChatImgSignal, hasMadSksSignal, noUnrequestedFallbackCodePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stripDollarCommand, stripMadSksSignal, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
20
|
+
import { CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, chatCaptureIntakeText, context7RequirementText, dollarCommand, evidenceMentionsForbiddenBrowserAutomation, getdesignReferencePolicyText, hasFromChatImgSignal, hasMadSksSignal, imageUxReviewPipelinePolicyText, noUnrequestedFallbackCodePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stripDollarCommand, stripMadSksSignal, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
20
21
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from './team-dag.mjs';
|
|
21
22
|
import { formatRoleCounts, initTeamLive, parseTeamSpecText } from './team-live.mjs';
|
|
22
23
|
import { evaluateTeamReviewPolicyGate, MIN_TEAM_REVIEWER_LANES, MIN_TEAM_REVIEW_POLICY_TEXT, teamReviewPolicy } from './team-review-policy.mjs';
|
|
@@ -285,6 +286,7 @@ export function promptPipelineContext(prompt, route = routePrompt(prompt)) {
|
|
|
285
286
|
if (route?.id === 'Team') lines.push(`Team route: scouts, TriWiki refresh, debate, consensus, runtime graph compile with concrete task ids and worker inboxes, close planning agents, fresh executors, minimum ${MIN_TEAM_REVIEWER_LANES}-lane review/integration, ${TEAM_SESSION_CLEANUP_ARTIFACT}, reflection, and Honest Mode. ${MIN_TEAM_REVIEW_POLICY_TEXT}`);
|
|
286
287
|
if (route?.id === 'Goal') lines.push('Goal route: write SKS goal bridge artifacts, then use Codex native /goal persistence for create, pause, resume, and clear continuation controls.');
|
|
287
288
|
if (route?.id === 'PPT') lines.push(`PPT route: before design or PDF work, seal delivery context, audience profile including average age/job/industry, STP strategy, decision context, and at least three pain-point to solution mappings. Keep the visual system simple, restrained, and information-first; design detail should come from hierarchy, spacing, alignment, rules, and subtle accents rather than decorative overdesign. ${pptPipelineAllowlistPolicyText()} If generated image assets or slide visual critique are needed, use gpt-image-2/imagegen only when that asset/review need is explicitly sealed in the $PPT contract, preferring Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) and using the OpenAI Image API when OPENAI_API_KEY is available; do not fabricate image files or image-review results when gpt-image-2 is unavailable. Then build source ledger, fact ledger, image asset ledger, storyboard with aha moments, style tokens, editable source HTML under source-html/, PDF artifact, render QA, bounded review ledger/iteration report, PPT-only temporary build file cleanup, and ppt-parallel-report.json so independent strategy/render/file-write phases stay parallel-friendly, then reflection and Honest Mode.`);
|
|
289
|
+
if (route?.id === 'ImageUXReview') lines.push(`Image UX Review route: ${imageUxReviewPipelinePolicyText()} Use ${IMAGE_UX_REVIEW_POLICY_ARTIFACT}, ${IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT}, ${IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT}, ${IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT}, ${IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT}, and ${IMAGE_UX_REVIEW_GATE_ARTIFACT} as the route evidence set. The route may suggest safe fixes only when the user requested fixing; otherwise report findings and blockers.`);
|
|
288
290
|
if (route?.id === 'AutoResearch') lines.push('AutoResearch route: load autoresearch-loop plus seo-geo-optimizer when SEO/GEO, discoverability, README, npm, GitHub stars, ranking, or AI-search visibility is relevant.');
|
|
289
291
|
if (route?.id === 'DB') lines.push('DB route: scan/check database risk first; destructive DB operations remain forbidden.');
|
|
290
292
|
if (route?.id === 'GX') lines.push('GX route: use deterministic vgraph/beta render, validate, drift, and snapshot artifacts.');
|
|
@@ -337,6 +339,7 @@ export async function prepareRoute(root, prompt, state = {}) {
|
|
|
337
339
|
if (route.id === 'ComputerUse') return withSkillDreamContext(await prepareComputerUseFastRoute(route, task), dreamContext);
|
|
338
340
|
if (route.id === 'Wiki') return withSkillDreamContext(await prepareWikiQuickRoute(route, task), dreamContext);
|
|
339
341
|
if (route.id === 'Goal') return withSkillDreamContext(await prepareGoal(root, route, task, routeNeedsContext7(route, prompt)), dreamContext);
|
|
342
|
+
if (route.id === 'ImageUXReview') return withSkillDreamContext(await prepareImageUxReview(root, route, task, routeNeedsContext7(route, prompt)), dreamContext);
|
|
340
343
|
const required = routeNeedsContext7(route, prompt);
|
|
341
344
|
const reasoning = routeReasoning(route, prompt);
|
|
342
345
|
const subagentsRequired = routeRequiresSubagents(route, prompt);
|
|
@@ -429,6 +432,45 @@ async function prepareWikiQuickRoute(route, task) {
|
|
|
429
432
|
};
|
|
430
433
|
}
|
|
431
434
|
|
|
435
|
+
async function prepareImageUxReview(root, route, task, required) {
|
|
436
|
+
const { id, dir, mission } = await createMission(root, { mode: 'image-ux-review', prompt: task });
|
|
437
|
+
const contract = {
|
|
438
|
+
prompt: task,
|
|
439
|
+
answers: {
|
|
440
|
+
TARGET_SURFACE: task,
|
|
441
|
+
IMAGE_UX_REVIEW_SOURCE_IMAGES: []
|
|
442
|
+
},
|
|
443
|
+
sealed_hash: null,
|
|
444
|
+
mission_id: id
|
|
445
|
+
};
|
|
446
|
+
const artifacts = await writeImageUxReviewRouteArtifacts(dir, contract);
|
|
447
|
+
await writeJsonAtomic(path.join(dir, 'route-context.json'), {
|
|
448
|
+
route: route.id,
|
|
449
|
+
command: route.command,
|
|
450
|
+
mode: route.mode,
|
|
451
|
+
task,
|
|
452
|
+
mission_id: mission.id,
|
|
453
|
+
required_skills: route.requiredSkills,
|
|
454
|
+
context7_required: required,
|
|
455
|
+
context_tracking: triwikiContextTracking(),
|
|
456
|
+
stop_gate: route.stopGate,
|
|
457
|
+
artifact_policy: 'imagegen_generated_review_image_required_before_issue_extraction'
|
|
458
|
+
});
|
|
459
|
+
const pipelinePlan = await writePipelinePlan(dir, { missionId: id, route, task, required, ambiguity: { required: false, status: 'direct_route' } });
|
|
460
|
+
await setCurrent(root, routeState(id, route, 'IMAGE_UX_REVIEW_READY', required, {
|
|
461
|
+
prompt: task,
|
|
462
|
+
implementation_allowed: true,
|
|
463
|
+
ambiguity_gate_required: false,
|
|
464
|
+
ambiguity_gate_passed: true,
|
|
465
|
+
stop_gate: route.stopGate,
|
|
466
|
+
image_ux_review_gate_ready: true,
|
|
467
|
+
image_ux_review_policy_ready: true,
|
|
468
|
+
pipeline_plan_ready: validatePipelinePlan(pipelinePlan).ok,
|
|
469
|
+
pipeline_plan_path: PIPELINE_PLAN_ARTIFACT
|
|
470
|
+
}));
|
|
471
|
+
return routeContext(route, id, task, required, `Capture or attach source UI screenshots, run Codex App $imagegen/gpt-image-2 to generate annotated review images, extract those generated images into ${IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT}, then update ${IMAGE_UX_REVIEW_GATE_ARTIFACT}. Initial gate blockers: ${(artifacts.gate.blockers || []).join(', ') || 'none'}.`);
|
|
472
|
+
}
|
|
473
|
+
|
|
432
474
|
export async function activeRouteContext(root, state) {
|
|
433
475
|
if (!state?.route && !state?.mode) return '';
|
|
434
476
|
const id = state.route || state.mode;
|
|
@@ -1369,11 +1411,15 @@ function missingRequiredGateFields(file, state, gate = {}) {
|
|
|
1369
1411
|
return gate[key] !== true;
|
|
1370
1412
|
});
|
|
1371
1413
|
}
|
|
1414
|
+
if (file === IMAGE_UX_REVIEW_GATE_ARTIFACT || mode === 'IMAGE_UX_REVIEW') {
|
|
1415
|
+
return IMAGE_UX_REVIEW_REQUIRED_GATE_FIELDS.filter((key) => gate[key] !== true);
|
|
1416
|
+
}
|
|
1372
1417
|
return [];
|
|
1373
1418
|
}
|
|
1374
1419
|
|
|
1375
1420
|
async function missingRequiredGateArtifacts(root, file, state, gate = {}) {
|
|
1376
1421
|
const mode = String(state?.mode || '').toUpperCase();
|
|
1422
|
+
if (file === IMAGE_UX_REVIEW_GATE_ARTIFACT || mode === 'IMAGE_UX_REVIEW') return missingImageUxReviewArtifacts(root, state, gate);
|
|
1377
1423
|
if (file !== 'team-gate.json' && mode !== 'TEAM') return [];
|
|
1378
1424
|
const missing = [];
|
|
1379
1425
|
if (gate.team_roster_confirmed === true && !(await exists(path.join(missionDir(root, state.mission_id), 'team-roster.json')))) missing.push('team-roster.json');
|
|
@@ -1393,6 +1439,42 @@ async function missingRequiredGateArtifacts(root, file, state, gate = {}) {
|
|
|
1393
1439
|
return missing;
|
|
1394
1440
|
}
|
|
1395
1441
|
|
|
1442
|
+
async function missingImageUxReviewArtifacts(root, state = {}, gate = {}) {
|
|
1443
|
+
const missing = [];
|
|
1444
|
+
const id = state?.mission_id;
|
|
1445
|
+
if (!id) return [`${IMAGE_UX_REVIEW_GATE_ARTIFACT}:mission_id`];
|
|
1446
|
+
const dir = missionDir(root, id);
|
|
1447
|
+
const required = [
|
|
1448
|
+
[IMAGE_UX_REVIEW_POLICY_ARTIFACT, 'policy_created'],
|
|
1449
|
+
[IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT, 'screen_inventory_created'],
|
|
1450
|
+
[IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT, 'imagegen_review_images_generated'],
|
|
1451
|
+
[IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT, 'issue_ledger_created'],
|
|
1452
|
+
[IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT, 'bounded_iteration_complete']
|
|
1453
|
+
];
|
|
1454
|
+
for (const [artifact, field] of required) {
|
|
1455
|
+
if (gate[field] === true && !(await exists(path.join(dir, artifact)))) missing.push(artifact);
|
|
1456
|
+
}
|
|
1457
|
+
const generated = await readJson(path.join(dir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT), null);
|
|
1458
|
+
if (gate.imagegen_review_images_generated === true) {
|
|
1459
|
+
if (!generated) missing.push(IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT);
|
|
1460
|
+
else {
|
|
1461
|
+
if (generated.passed !== true) missing.push(`${IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT}:passed`);
|
|
1462
|
+
if (!Array.isArray(generated.generated_review_images) || generated.generated_review_images.length === 0) missing.push(`${IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT}:generated_review_images`);
|
|
1463
|
+
if (String(generated.provider?.model || '') !== 'gpt-image-2') missing.push(`${IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT}:gpt-image-2`);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
const issues = await readJson(path.join(dir, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT), null);
|
|
1467
|
+
if (gate.generated_review_images_analyzed === true || gate.p0_p1_zero === true) {
|
|
1468
|
+
if (!issues) missing.push(IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT);
|
|
1469
|
+
else {
|
|
1470
|
+
if (issues.passed !== true && gate.p0_p1_zero === true) missing.push(`${IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT}:passed`);
|
|
1471
|
+
if (issues.extraction_source !== IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT) missing.push(`${IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT}:extraction_source`);
|
|
1472
|
+
if (Number(issues.blocking_issue_count || 0) !== 0 && gate.p0_p1_zero === true) missing.push(`${IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT}:blocking_issue_count`);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
return missing;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1396
1478
|
function fromChatImgCoverageRequired(state = {}, gate = {}) {
|
|
1397
1479
|
return state?.from_chat_img_required === true || gate?.from_chat_img_required === true;
|
|
1398
1480
|
}
|
|
@@ -1468,6 +1550,7 @@ function gateFilesForState(state) {
|
|
|
1468
1550
|
if (state.mode === 'GX') return ['gx-gate.json'];
|
|
1469
1551
|
if (state.mode === 'QALOOP') return ['qa-gate.json'];
|
|
1470
1552
|
if (state.mode === 'PPT') return ['ppt-gate.json'];
|
|
1553
|
+
if (state.mode === 'IMAGE_UX_REVIEW') return [IMAGE_UX_REVIEW_GATE_ARTIFACT];
|
|
1471
1554
|
return ['done-gate.json'];
|
|
1472
1555
|
}
|
|
1473
1556
|
|
package/src/core/routes.mjs
CHANGED
|
@@ -7,9 +7,10 @@ export const FROM_CHAT_IMG_CHECKLIST_ARTIFACT = 'from-chat-img-checklist.md';
|
|
|
7
7
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT = 'from-chat-img-temp-triwiki.json';
|
|
8
8
|
export const FROM_CHAT_IMG_QA_LOOP_ARTIFACT = 'from-chat-img-qa-loop.json';
|
|
9
9
|
export const FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS = 5;
|
|
10
|
-
export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|tmux|auto-review|team|qa-loop|ppt|goal|research|db|codex-app|openclaw|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|harness|hproof|gx|wiki|code-structure|proof-field|skill-dream';
|
|
10
|
+
export const USAGE_TOPICS = 'install|setup|bootstrap|root|deps|tmux|auto-review|team|qa-loop|ppt|image-ux-review|goal|research|db|codex-app|openclaw|dfix|design|imagegen|dollar|context7|pipeline|reasoning|guard|conflicts|versioning|eval|harness|hproof|gx|wiki|code-structure|proof-field|skill-dream';
|
|
11
11
|
export const CODEX_COMPUTER_USE_EVIDENCE_SOURCE = 'codex_computer_use';
|
|
12
12
|
export const CODEX_APP_IMAGE_GENERATION_DOC_URL = 'https://developers.openai.com/codex/app/features#image-generation';
|
|
13
|
+
export const OPENAI_IMAGE_GENERATION_DOC_URL = 'https://developers.openai.com/api/docs/guides/image-generation';
|
|
13
14
|
export const CODEX_COMPUTER_USE_ONLY_POLICY = 'Pipeline UI/browser verification and visual inspection must use Codex Computer Use only. Do not use Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or any other browser automation substitute; if Codex Computer Use is unavailable, mark the UI/browser evidence unverified instead of substituting another tool.';
|
|
14
15
|
export const FORBIDDEN_BROWSER_AUTOMATION_RE = /\b(playwright|chrome\s+mcp|browser\s+use|selenium|puppeteer)\b/i;
|
|
15
16
|
|
|
@@ -95,6 +96,10 @@ export function getdesignReferencePolicyText() {
|
|
|
95
96
|
return `Design SSOT policy: ${DESIGN_SYSTEM_SSOT.authority_file} is the single design decision authority. If it is missing, create or update it through ${DESIGN_SYSTEM_SSOT.builder_prompt}; getdesign.md (${GETDESIGN_REFERENCE.url}), its official docs, and curated DESIGN.md examples at ${AWESOME_DESIGN_MD_REFERENCE.url} are source inputs to fuse into that SSOT or into route-local style tokens, not parallel authorities. Prefer the official Codex skill when available (${GETDESIGN_REFERENCE.codex_skill_install}); otherwise use the generated getdesign-reference skill plus official Web/API/CLI/SDK docs and curated DESIGN.md examples as inputs. Do not claim an official getdesign MCP server is configured unless a current official MCP surface is actually available.`;
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
export function imageUxReviewPipelinePolicyText() {
|
|
100
|
+
return `Image UX review pipeline: the core mechanism is not text-only screenshot critique. Capture or receive source UI screenshots, then use Codex App imagegen/$imagegen with gpt-image-2 (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) to create new annotated review images from those screenshots as reference inputs. The generated review image must visibly mark numbered callouts, P0/P1/P2/P3 labels, eye-flow, hierarchy, contrast, alignment, density, affordance problems, and a small corrected mini-comp or before/after strip when useful. Then analyze that generated review image with vision/OCR and convert the visible callouts into image-ux-issue-ledger.json rows. Missing generated review images block image-ux-review-gate.json; never pass this route from a hand-written text-only substitute. For larger API-backed batches, use the official OpenAI image generation/editing path only when OPENAI_API_KEY and the route contract allow API-priced generation (${OPENAI_IMAGE_GENERATION_DOC_URL}).`;
|
|
101
|
+
}
|
|
102
|
+
|
|
98
103
|
export const RECOMMENDED_SKILLS = [
|
|
99
104
|
'reasoning-router',
|
|
100
105
|
'pipeline-runner',
|
|
@@ -107,6 +112,7 @@ export const RECOMMENDED_SKILLS = [
|
|
|
107
112
|
'design-ui-editor',
|
|
108
113
|
'getdesign-reference',
|
|
109
114
|
'imagegen',
|
|
115
|
+
'image-ux-review',
|
|
110
116
|
'computer-use',
|
|
111
117
|
'computer-use-fast',
|
|
112
118
|
'db-safety-guard',
|
|
@@ -301,6 +307,22 @@ export const ROUTES = [
|
|
|
301
307
|
cliEntrypoint: 'Codex App prompt route only: $PPT <topic>',
|
|
302
308
|
examples: ['$PPT 우리 SaaS 소개자료를 HTML 기반 PDF로 만들어줘', '$PPT 투자자용 피치덱 만들어줘']
|
|
303
309
|
},
|
|
310
|
+
{
|
|
311
|
+
id: 'ImageUXReview',
|
|
312
|
+
command: '$Image-UX-Review',
|
|
313
|
+
mode: 'IMAGE_UX_REVIEW',
|
|
314
|
+
route: 'image-generation UI/UX review loop',
|
|
315
|
+
description: 'Review UI/UX through the imagegen/gpt-image-2 visual critique loop: source screenshots become generated annotated review images, those images become issue ledgers, then fixes are rechecked.',
|
|
316
|
+
requiredSkills: ['image-ux-review', 'imagegen', 'computer-use', 'pipeline-runner', REFLECTION_SKILL_NAME, 'honest-mode'],
|
|
317
|
+
dollarAliases: ['$UX-Review'],
|
|
318
|
+
appSkillAliases: ['ux-review', 'visual-review', 'ui-ux-review'],
|
|
319
|
+
lifecycle: ['target_and_capture_inventory', 'source_screenshots', 'gpt_image_2_annotated_review_image', 'generated_image_text_extraction', 'issue_ledger', 'optional_safe_fixes', 'changed_screen_recheck', 'post_route_reflection', 'honest_mode'],
|
|
320
|
+
context7Policy: 'if_external_docs',
|
|
321
|
+
reasoningPolicy: 'high',
|
|
322
|
+
stopGate: 'image-ux-review-gate.json',
|
|
323
|
+
cliEntrypoint: 'Codex App prompt route: $Image-UX-Review <target>; inspect with sks image-ux-review status latest',
|
|
324
|
+
examples: ['$Image-UX-Review localhost 화면을 이미지 생성 리뷰 루프로 검수해줘', '$UX-Review 이 스크린샷을 gpt-image-2 콜아웃 리뷰로 분석하고 고쳐줘']
|
|
325
|
+
},
|
|
304
326
|
{
|
|
305
327
|
id: 'ComputerUse',
|
|
306
328
|
command: '$Computer-Use',
|
|
@@ -466,6 +488,7 @@ export const COMMAND_CATALOG = [
|
|
|
466
488
|
{ name: 'dfix', usage: 'sks dfix', description: 'Explain $DFix ultralight design/content fix mode.' },
|
|
467
489
|
{ name: 'qa-loop', usage: 'sks qa-loop prepare|answer|run|status ...', description: 'Dogfood UI/API as human proxy with safety gates, safe fixes, rechecks, Codex Computer Use-only UI evidence, report.' },
|
|
468
490
|
{ name: 'ppt', usage: 'sks ppt build|status <mission-id|latest> [--json]', description: 'Build or inspect $PPT HTML/PDF artifacts from a sealed presentation decision contract.' },
|
|
491
|
+
{ name: 'image-ux-review', usage: 'sks image-ux-review status <mission-id|latest> [--json]', description: 'Inspect $Image-UX-Review gpt-image-2/imagegen annotated UI/UX review artifacts.' },
|
|
469
492
|
{ name: 'context7', usage: 'sks context7 check|setup|tools|resolve|docs|evidence ...', description: 'Check, configure, and call the local Context7 MCP requirement.' },
|
|
470
493
|
{ name: 'pipeline', usage: 'sks pipeline status|resume|plan|answer ...', description: 'Inspect the active skill-first route, materialized execution plan, ambiguity gates, and completion gates.' },
|
|
471
494
|
{ name: 'guard', usage: 'sks guard check [--json]', description: 'Check SKS harness self-protection lock, fingerprints, and source-repo exception state.' },
|
|
@@ -554,6 +577,15 @@ export function looksLikePresentationArtifactRequest(prompt = '') {
|
|
|
554
577
|
return /만들|작성|생성|제작|디자인|export|pdf|html|create|generate|build|write|make/i.test(text) || /\b(ppt|presentation|deck|slides?)\b/.test(lower);
|
|
555
578
|
}
|
|
556
579
|
|
|
580
|
+
export function looksLikeImageUxReviewRequest(prompt = '') {
|
|
581
|
+
const text = String(prompt || '');
|
|
582
|
+
const reviewCue = /(ui\/?ux|ux|ui|screen|screenshot|visual|interface|화면|스크린|캡처|비주얼|인터페이스|사용성|유아이|유엑스)/i.test(text)
|
|
583
|
+
&& /(review|critique|audit|inspect|analy[sz]e|검수|리뷰|분석|평가|진단)/i.test(text);
|
|
584
|
+
const imagegenCue = /(gpt-image-2|imagegen|\$imagegen|image\s*generation|generated\s*review|annotated\s*review|callout|이미지\s*생성|생성\s*이미지|콜아웃|주석\s*이미지)/i.test(text);
|
|
585
|
+
const commandCue = /\$?(?:image-ux-review|ux-review|visual-review|ui-ux-review)\b/i.test(text);
|
|
586
|
+
return commandCue || (reviewCue && imagegenCue);
|
|
587
|
+
}
|
|
588
|
+
|
|
557
589
|
export function routePrompt(prompt) {
|
|
558
590
|
const command = dollarCommand(prompt);
|
|
559
591
|
const text = String(prompt || '');
|
|
@@ -574,6 +606,7 @@ export function routePrompt(prompt) {
|
|
|
574
606
|
}
|
|
575
607
|
if (hasFromChatImgSignal(text)) return routeById('Team');
|
|
576
608
|
if (looksLikePresentationArtifactRequest(text)) return routeById('PPT');
|
|
609
|
+
if (looksLikeImageUxReviewRequest(text)) return routeById('ImageUXReview');
|
|
577
610
|
if (looksLikeComputerUseFastLane(text)) return routeById('ComputerUse');
|
|
578
611
|
if (looksLikeFastDesignFix(text)) return routeById('DFix');
|
|
579
612
|
if (looksLikeQuestionShapedDirective(text)) return routeById('Team');
|
|
@@ -644,6 +677,7 @@ export function routeRequiresSubagents(route, prompt = '') {
|
|
|
644
677
|
if (route.id === 'SKS') return looksLikeTeamDefaultWork(prompt);
|
|
645
678
|
if (route.id === 'Help' || route.id === 'Answer' || route.id === 'Wiki' || route.id === 'ComputerUse') return false;
|
|
646
679
|
if (route.id === 'PPT') return false;
|
|
680
|
+
if (route.id === 'ImageUXReview') return false;
|
|
647
681
|
if (route.id === 'Research' || route.id === 'AutoResearch') return true;
|
|
648
682
|
if (route.id === 'Goal') return looksLikeExecutionWork(prompt) || looksLikeTeamDefaultWork(stripDollarCommand(prompt));
|
|
649
683
|
if (route.id === 'DB' || route.id === 'GX') return looksLikeExecutionWork(prompt);
|
|
@@ -653,7 +687,7 @@ export function routeRequiresSubagents(route, prompt = '') {
|
|
|
653
687
|
|
|
654
688
|
export function reflectionRequiredForRoute(route) {
|
|
655
689
|
const id = String(route?.id || route?.mode || route?.route || route || '').replace(/^\$/, '');
|
|
656
|
-
return /^(team|qaloop|qa-loop|ppt|research|autoresearch|db|database|madsks|mad-sks|gx)$/i.test(id);
|
|
690
|
+
return /^(team|qaloop|qa-loop|ppt|imageuxreview|image-ux-review|research|autoresearch|db|database|madsks|mad-sks|gx)$/i.test(id);
|
|
657
691
|
}
|
|
658
692
|
|
|
659
693
|
export function looksLikeCodeChangingWork(prompt = '') {
|
|
@@ -696,6 +730,7 @@ export function routeReasoning(route, prompt = '') {
|
|
|
696
730
|
const base = ALLOWED_REASONING_EFFORTS.has(route?.reasoningPolicy) ? route.reasoningPolicy : 'medium';
|
|
697
731
|
if (hasFromChatImgSignal(text)) return reasoning('xhigh', 'from_chat_img_image_work_order_analysis');
|
|
698
732
|
if (route?.id === 'Research' || route?.id === 'AutoResearch') return reasoning('xhigh', 'research_or_experiment_route');
|
|
733
|
+
if (route?.id === 'ImageUXReview') return reasoning('high', 'image_generation_visual_review_route');
|
|
699
734
|
if (/\b(research|autoresearch|hypothesis|falsify|novelty|frontier|benchmark|experiment|SEO|GEO|ranking|연구|실험|가설|검증)\b/i.test(text)) return reasoning('xhigh', 'research_level_prompt');
|
|
700
735
|
if (base === 'xhigh') return reasoning('xhigh', 'route_policy_xhigh');
|
|
701
736
|
if (base === 'high' || /\b(architecture|design|migration|database|security|parallel|orchestrat|refactor|algorithm|logic|tradeoff|검토|설계|마이그레이션|보안|병렬|팀|논리)\b/i.test(text)) return reasoning('high', 'logical_or_safety_work');
|