sneakoscope 0.7.50 → 0.7.52
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 +7 -6
- package/package.json +1 -1
- package/src/cli/main.mjs +134 -12
- package/src/core/codex-app.mjs +65 -9
- package/src/core/fsx.mjs +1 -1
- package/src/core/image-ux-review.mjs +300 -0
- package/src/core/init.mjs +24 -12
- package/src/core/pipeline.mjs +87 -4
- package/src/core/questions.mjs +1 -1
- package/src/core/routes.mjs +57 -8
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,14 +43,15 @@ 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. |
|
|
50
50
|
| Skill dreaming | Records cheap generated-skill usage counters in JSON and only periodically scans `.agents/skills` for keep, merge, prune, and improvement candidates. Reports are recommendation-only and never delete skills automatically. |
|
|
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
|
-
| 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
|
|
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 must use real `$imagegen`/`gpt-image-2` output 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 missing generated review images or 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. |
|
|
@@ -64,7 +65,7 @@ sks selftest --mock
|
|
|
64
65
|
- Node.js `>=20.11`
|
|
65
66
|
- npm
|
|
66
67
|
- Codex CLI for terminal workflows
|
|
67
|
-
- Codex App for app-facing workflows, with Codex Computer Use required for UI/browser evidence
|
|
68
|
+
- Codex App for app-facing workflows, with Codex Computer Use required for UI/browser evidence and `$imagegen`/`gpt-image-2` required for generated raster assets or generated image-review evidence
|
|
68
69
|
- tmux for the CLI-first runtime
|
|
69
70
|
- Context7 MCP for current-docs-gated routes
|
|
70
71
|
|
|
@@ -550,7 +551,7 @@ sks codex-app check
|
|
|
550
551
|
codex mcp list
|
|
551
552
|
```
|
|
552
553
|
|
|
553
|
-
Codex App workflows need the app installed. QA and visual-evidence workflows require first-party Codex Computer Use; Browser Use may support non-UI browser context, but it is not valid UI/browser verification evidence.
|
|
554
|
+
Codex App workflows need the app installed. QA and UI/browser visual-evidence workflows require first-party Codex Computer Use; Browser Use may support non-UI browser context, but it is not valid UI/browser verification evidence. Generated raster assets and image-review evidence require real Codex App `$imagegen`/`gpt-image-2` output, or the route must stay blocked/unverified.
|
|
554
555
|
|
|
555
556
|
### Setup is blocked by another harness
|
|
556
557
|
|
|
@@ -585,7 +586,7 @@ npm run sizecheck
|
|
|
585
586
|
npm run release:check
|
|
586
587
|
```
|
|
587
588
|
|
|
588
|
-
Package pipeline UI/browser verification and visual inspection evidence must come from Codex Computer Use only. Do not use Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or other browser automation as substitutes for that evidence.
|
|
589
|
+
Package pipeline UI/browser verification and visual inspection evidence must come from Codex Computer Use only. Do not use Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or other browser automation as substitutes for that evidence. Package image-generation evidence must come from real `$imagegen`/`gpt-image-2` output when generated raster assets or generated image-review evidence are required.
|
|
589
590
|
|
|
590
591
|
Dry-run publish:
|
|
591
592
|
|
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.52",
|
|
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,9 +41,18 @@ 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
|
-
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';
|
|
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, CODEX_IMAGEGEN_REQUIRED_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';
|
|
47
56
|
import { PIPELINE_PLAN_ARTIFACT, buildPipelinePlan, context7Evidence, evaluateStop, projectGateStatus, recordContext7Evidence, recordSubagentEvidence, validatePipelinePlan, writePipelinePlan } from '../core/pipeline.mjs';
|
|
48
57
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
|
|
49
58
|
import { appendTeamEvent, initTeamLive, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane } from '../core/team-live.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 || '';
|
|
@@ -1294,6 +1382,7 @@ function printDepsStatus(status) {
|
|
|
1294
1382
|
console.log(`SKS bin: ${status.sneakoscope.ok ? 'ok' : 'missing'} ${status.sneakoscope.bin || ''}`.trimEnd());
|
|
1295
1383
|
console.log(`Codex CLI: ${status.codex_cli.ok ? 'ok' : 'missing'} ${status.codex_cli.version || status.codex_cli.bin || ''}`.trimEnd());
|
|
1296
1384
|
console.log(`Codex App: ${status.codex_app.app.installed ? 'ok' : 'missing'}`);
|
|
1385
|
+
console.log(`Image Gen: ${status.codex_app.features?.image_generation ? 'ok' : 'missing'}`);
|
|
1297
1386
|
console.log(`Context7: ${status.context7.ok ? 'ok' : 'missing'}`);
|
|
1298
1387
|
console.log(`Browser Use: ${status.browser_use.ok ? 'ok' : 'missing'}`);
|
|
1299
1388
|
console.log(`Computer Use:${status.computer_use.ok ? ' ok' : ' missing'}`);
|
|
@@ -1475,7 +1564,8 @@ function usage(args = []) {
|
|
|
1475
1564
|
openclaw: ['OpenClaw', '', ' sks openclaw install', ' sks openclaw path', ' sks openclaw print SKILL.md', '', 'Installs an OpenClaw skill package under ~/.openclaw/skills/sneakoscope-codex so OpenClaw agents can attach skills: [sneakoscope-codex] with the shell tool and call local SKS commands from a project root.'],
|
|
1476
1565
|
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
1566
|
'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
|
-
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-
|
|
1567
|
+
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 $imagegen/gpt-image-2 output blocks instead of being simulated.'],
|
|
1568
|
+
'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
1569
|
goal: ['Goal', '', ' sks goal create "task"', ' sks goal status latest', ' sks goal pause latest', ' sks goal resume latest', ' sks goal clear latest'],
|
|
1480
1570
|
'codex-app': ['Codex App', '', ' sks bootstrap', ' sks codex-app check', ' sks codex-app remote-control --status', ' sks dollar-commands', ' cat .codex/SNEAKOSCOPE.md'],
|
|
1481
1571
|
dollar: ['Dollar Commands', '', formatDollarCommandsCompact(' '), '', 'Terminal: sks dollar-commands [--json]'],
|
|
@@ -1621,7 +1711,7 @@ async function setup(args) {
|
|
|
1621
1711
|
else console.log('Git: .gitignore ignores SKS generated files');
|
|
1622
1712
|
console.log(`Codex App: .codex/config.toml, .codex/hooks.json, .agents/skills, .codex/agents, .codex/SNEAKOSCOPE.md`);
|
|
1623
1713
|
console.log(`Global $: ${globalSkills.status === 'installed' ? 'ok' : globalSkills.status} ${globalSkills.root || ''}`.trimEnd());
|
|
1624
|
-
console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'}`);
|
|
1714
|
+
console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'} Image Gen=${appRuntime.features?.image_generation ? 'ok' : 'missing'}`);
|
|
1625
1715
|
console.log(`Prompt: intent-first routing, $Answer fact-check route, $DFix ultralight design/content route, $PPT HTML/PDF presentation route, Context7 gate`);
|
|
1626
1716
|
console.log(`Skills: .agents/skills`);
|
|
1627
1717
|
console.log(`Next: sks context7 check; sks selftest --mock; sks commands; sks dollar-commands`);
|
|
@@ -1630,7 +1720,7 @@ async function setup(args) {
|
|
|
1630
1720
|
if (!cliTools.tmux.ok) console.log(`\ntmux ${tmuxStatusKind(cliTools.tmux)}. Install: ${cliTools.tmux.install_hint}`);
|
|
1631
1721
|
if (!install.ok && install.scope === 'global') console.log('\nGlobal command missing. Run: npm i -g sneakoscope');
|
|
1632
1722
|
if (!install.ok && install.scope === 'project') console.log('\nProject package missing. Run: npm i -D sneakoscope');
|
|
1633
|
-
if (!appRuntime.ok) console.log('\nCodex App
|
|
1723
|
+
if (!appRuntime.ok) console.log('\nCodex App, first-party Codex Computer Use, and $imagegen/gpt-image-2 are required for SKS visual evidence; Browser Use is not a UI verification substitute. Run: sks codex-app check');
|
|
1634
1724
|
}
|
|
1635
1725
|
|
|
1636
1726
|
function formatCodexCliToolStatus(status = {}) {
|
|
@@ -1747,7 +1837,7 @@ async function doctor(args) {
|
|
|
1747
1837
|
console.log(`Rust acc.: ${rust.available ? rust.version : 'optional-missing'}`);
|
|
1748
1838
|
console.log(`State: ${result.sneakoscope.ok ? 'ok' : 'missing .sneakoscope'}`);
|
|
1749
1839
|
console.log(`Context7: ${result.context7.ok ? 'ok' : 'missing MCP config'} project=${result.context7.project.ok ? 'ok' : 'missing'} global=${result.context7.global.ok ? 'ok' : 'missing'}`);
|
|
1750
|
-
console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'}`);
|
|
1840
|
+
console.log(`App tools: ${appRuntime.ok ? 'ok' : 'needs setup'} Codex App=${appRuntime.app.installed ? 'ok' : 'missing'} Browser Use=${appRuntime.mcp.has_browser_use ? 'ok' : 'missing'} Computer Use=${appRuntime.mcp.has_computer_use ? 'ok' : 'missing'} Image Gen=${appRuntime.features?.image_generation ? 'ok' : 'missing'}`);
|
|
1751
1841
|
console.log(`tmux: ${tmuxStatusKind(result.runtime.tmux)} ${result.runtime.tmux.version || result.runtime.tmux.error || ''}`.trimEnd());
|
|
1752
1842
|
console.log(`Guard: ${result.harness_guard.ok ? 'ok' : 'blocked'}${result.harness_guard.source_exception ? ' source-exception' : ''}`);
|
|
1753
1843
|
console.log(`Version: ${result.versioning.ok ? 'ok' : 'missing'}${result.versioning.enabled ? ` ${result.versioning.package_version || ''}` : ` ${result.versioning.reason || 'disabled'}`}`);
|
|
@@ -2435,18 +2525,20 @@ async function selftest() {
|
|
|
2435
2525
|
if (!teamSkillText.includes('Codex App pipeline activation:') || !teamSkillText.includes('sks pipeline status') || !teamSkillText.includes('mission/pipeline artifacts')) throw new Error('selftest failed: Team skill missing pipeline activation fallback');
|
|
2436
2526
|
if (!promptPipelineText.includes('before every route stage') || !promptPipelineText.includes('sks wiki refresh')) throw new Error('selftest failed: prompt pipeline missing per-stage TriWiki policy');
|
|
2437
2527
|
if (!promptPipelineText.includes('single design decision authority') || !promptPipelineText.includes('imagegen') || !promptPipelineText.includes('getdesign-reference') || !promptPipelineText.includes(AWESOME_DESIGN_MD_REFERENCE.url) || !promptPipelineText.includes('not parallel authorities')) throw new Error('selftest failed: prompt pipeline missing design SSOT/source-input routing');
|
|
2438
|
-
if (!promptPipelineText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL)) throw new Error('selftest failed: prompt pipeline missing Codex App image generation policy');
|
|
2528
|
+
if (!promptPipelineText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL) || !promptPipelineText.includes(CODEX_IMAGEGEN_REQUIRED_POLICY)) throw new Error('selftest failed: prompt pipeline missing Codex App image generation policy');
|
|
2439
2529
|
if (!promptPipelineText.includes('From-Chat-IMG') || !promptPipelineText.includes('Do not assume ordinary image prompts are chat captures')) throw new Error('selftest failed: prompt pipeline missing explicit From-Chat-IMG gating');
|
|
2440
2530
|
const fromChatImgSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'from-chat-img', 'SKILL.md'));
|
|
2441
2531
|
if (!fromChatImgSkillText.includes('normal Team pipeline') || !fromChatImgSkillText.includes('Codex Computer Use visual inspection') || !fromChatImgSkillText.includes(CODEX_COMPUTER_USE_ONLY_POLICY) || !fromChatImgSkillText.includes(FROM_CHAT_IMG_CHECKLIST_ARTIFACT) || !fromChatImgSkillText.includes(FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT) || !fromChatImgSkillText.includes(FROM_CHAT_IMG_QA_LOOP_ARTIFACT)) throw new Error('selftest failed: from-chat-img skill missing Team/Computer Use-only inspection checklist guidance');
|
|
2442
2532
|
if (fromChatImgSkillText.includes('Computer Use/browser visual inspection')) throw new Error('selftest failed: from-chat-img skill still allows browser visual inspection wording');
|
|
2443
2533
|
const fromChatImgSkillMeta = await safeReadText(path.join(tmp, '.agents', 'skills', 'from-chat-img', 'agents', 'openai.yaml'));
|
|
2444
2534
|
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']) {
|
|
2535
|
+
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
2536
|
if (!(await exists(path.join(tmp, '.agents', 'skills', supportSkill, 'SKILL.md')))) throw new Error(`selftest failed: ${supportSkill} skill not installed`);
|
|
2447
2537
|
}
|
|
2448
2538
|
const imagegenSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'imagegen', 'SKILL.md'));
|
|
2449
|
-
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
|
+
if (!imagegenSkillText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL) || !imagegenSkillText.includes('$imagegen') || !imagegenSkillText.includes('gpt-image-2') || !imagegenSkillText.includes('OPENAI_API_KEY') || !imagegenSkillText.includes(CODEX_IMAGEGEN_REQUIRED_POLICY)) throw new Error('selftest failed: imagegen skill missing official Codex App image generation priority');
|
|
2540
|
+
const imageUxReviewSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'image-ux-review', 'SKILL.md'));
|
|
2541
|
+
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) || !imageUxReviewSkillText.includes(CODEX_IMAGEGEN_REQUIRED_POLICY)) throw new Error('selftest failed: image-ux-review skill missing gpt-image-2 generated-image review gate guidance');
|
|
2450
2542
|
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
2543
|
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
2544
|
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,14 +2554,24 @@ async function selftest() {
|
|
|
2462
2554
|
if (new Set(DOLLAR_COMMANDS.map((c) => c.command)).size !== DOLLAR_COMMANDS.length) throw new Error('selftest failed: duplicate dollar commands');
|
|
2463
2555
|
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
2556
|
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');
|
|
2557
|
+
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
2558
|
if (!DOLLAR_COMMANDS.some((entry) => entry.command === '$From-Chat-IMG')) throw new Error('selftest failed: $From-Chat-IMG missing from dollar command list');
|
|
2559
|
+
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
2560
|
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
2561
|
if (routePrompt('$agent-team run specialists')) throw new Error('selftest failed: deprecated $agent-team route still resolved');
|
|
2468
2562
|
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');
|
|
2563
|
+
if (routePrompt('[$qa-loop](/tmp/qa-loop/SKILL.md) localhost UI 검증, Codex Computer Use만 사용')?.id !== 'QALoop') throw new Error('selftest failed: markdown-linked $QA-LOOP was hijacked by heuristic routing');
|
|
2564
|
+
if (routePrompt('[$research](/tmp/research/SKILL.md) Codex Computer Use 도구 노출 문제를 QA루프 관점에서 연구')?.id !== 'Research') throw new Error('selftest failed: markdown-linked $Research was not treated as explicit route');
|
|
2469
2565
|
if (routePrompt('$WikiRefresh 갱신')) throw new Error('selftest failed: deprecated $WikiRefresh route still resolved');
|
|
2470
2566
|
if (routePrompt('$MAD-SKS Supabase MCP main 작업')?.id !== 'MadSKS') throw new Error('selftest failed: $MAD-SKS route did not resolve');
|
|
2471
2567
|
if (routePrompt('$MAD-SKS $Team Supabase MCP main 작업')?.id !== 'Team') throw new Error('selftest failed: $MAD-SKS did not compose with $Team');
|
|
2472
2568
|
if (routePrompt('$DB Supabase 점검 $MAD-SKS')?.id !== 'DB') throw new Error('selftest failed: trailing $MAD-SKS changed primary route');
|
|
2569
|
+
const imageUxRoute = routePrompt('$Image-UX-Review localhost 화면 검수');
|
|
2570
|
+
if (imageUxRoute?.id !== 'ImageUXReview') throw new Error('selftest failed: $Image-UX-Review did not route to ImageUXReview');
|
|
2571
|
+
if (routePrompt('$UX-Review 스크린샷 gpt-image-2 콜아웃 리뷰')?.id !== 'ImageUXReview') throw new Error('selftest failed: $UX-Review did not route to ImageUXReview');
|
|
2572
|
+
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');
|
|
2573
|
+
if (routeRequiresSubagents(imageUxRoute, '$Image-UX-Review localhost 화면 검수')) throw new Error('selftest failed: ImageUXReview route should not require subagents');
|
|
2574
|
+
if (!reflectionRequiredForRoute(imageUxRoute)) throw new Error('selftest failed: ImageUXReview route should require reflection');
|
|
2473
2575
|
const madStandaloneTmp = tmpdir();
|
|
2474
2576
|
await initProject(madStandaloneTmp, {});
|
|
2475
2577
|
const madStandalonePayload = JSON.stringify({ cwd: madStandaloneTmp, prompt: '$MAD-SKS main 권한 열어줘' });
|
|
@@ -2498,7 +2600,8 @@ async function selftest() {
|
|
|
2498
2600
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$Team')) throw new Error('selftest failed: dollar-commands missing Team default routing guidance');
|
|
2499
2601
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$From-Chat-IMG')) throw new Error('selftest failed: dollar-commands missing From-Chat-IMG guidance');
|
|
2500
2602
|
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$MAD-SKS')) throw new Error('selftest failed: dollar-commands missing MAD-SKS scoped override guidance');
|
|
2501
|
-
if (!
|
|
2603
|
+
if (!DOLLAR_DEFAULT_PIPELINE_TEXT.includes('$Image-UX-Review')) throw new Error('selftest failed: dollar-commands missing Image UX Review guidance');
|
|
2604
|
+
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
2605
|
const openClawTmp = tmpdir();
|
|
2503
2606
|
const openClawResult = await installOpenClawSkill({ targetDir: path.join(openClawTmp, 'skills', OPENCLAW_SKILL_NAME) });
|
|
2504
2607
|
if (!openClawResult.ok) throw new Error(`selftest failed: OpenClaw skill install blocked: ${openClawResult.reason}`);
|
|
@@ -2529,6 +2632,25 @@ async function selftest() {
|
|
|
2529
2632
|
const hookGoalTmp = tmpdir();
|
|
2530
2633
|
await initProject(hookGoalTmp, {});
|
|
2531
2634
|
const hookBin = path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
2635
|
+
const hookImageUxTmp = tmpdir();
|
|
2636
|
+
await initProject(hookImageUxTmp, {});
|
|
2637
|
+
const hookImageUxPayload = JSON.stringify({ cwd: hookImageUxTmp, prompt: '$Image-UX-Review localhost 화면을 gpt-image-2 콜아웃 리뷰로 검수해줘' });
|
|
2638
|
+
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 });
|
|
2639
|
+
if (hookImageUxResult.code !== 0) throw new Error(`selftest failed: $Image-UX-Review hook exited ${hookImageUxResult.code}: ${hookImageUxResult.stderr}`);
|
|
2640
|
+
const hookImageUxJson = JSON.parse(hookImageUxResult.stdout);
|
|
2641
|
+
const imageUxContext = hookImageUxJson.hookSpecificOutput?.additionalContext || '';
|
|
2642
|
+
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');
|
|
2643
|
+
const hookImageUxState = await readJson(stateFile(hookImageUxTmp), {});
|
|
2644
|
+
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');
|
|
2645
|
+
const imageUxMissionDir = missionDir(hookImageUxTmp, hookImageUxState.mission_id);
|
|
2646
|
+
const imageUxGate = await readJson(path.join(imageUxMissionDir, IMAGE_UX_REVIEW_GATE_ARTIFACT));
|
|
2647
|
+
const imageUxGeneratedLedger = await readJson(path.join(imageUxMissionDir, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT));
|
|
2648
|
+
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');
|
|
2649
|
+
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');
|
|
2650
|
+
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 });
|
|
2651
|
+
if (imageUxStatusResult.code !== 0) throw new Error(`selftest failed: sks image-ux-review status failed: ${imageUxStatusResult.stderr || imageUxStatusResult.stdout}`);
|
|
2652
|
+
const imageUxStatus = JSON.parse(imageUxStatusResult.stdout);
|
|
2653
|
+
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
2654
|
const hookPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '$Goal 로그인 세션 만료 UX 개선' });
|
|
2533
2655
|
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
2656
|
if (hookResult.code !== 0) throw new Error(`selftest failed: $Goal hook exited ${hookResult.code}: ${hookResult.stderr}`);
|
|
@@ -3250,7 +3372,7 @@ async function selftest() {
|
|
|
3250
3372
|
}
|
|
3251
3373
|
const pptSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'ppt', 'SKILL.md'));
|
|
3252
3374
|
if (!pptSkillText.includes('STP') || !pptSkillText.includes('target audience profile') || !pptSkillText.includes('decision context') || !pptSkillText.includes('3+ pain-point to solution mappings')) throw new Error('selftest failed: generated PPT skill missing STP/audience/pain-point guidance');
|
|
3253
|
-
if (!pptSkillText.includes('simple, restrained, and information-first') || !pptSkillText.includes('over-designed decoration') || !pptSkillText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL) || !pptSkillText.includes(AWESOME_DESIGN_MD_REFERENCE.url) || !pptSkillText.includes('only design decision SSOT') || !pptSkillText.includes('instead of treating references as parallel authorities')) throw new Error('selftest failed: generated PPT skill missing restrained design/imagegen/fused-SSOT guidance');
|
|
3375
|
+
if (!pptSkillText.includes('simple, restrained, and information-first') || !pptSkillText.includes('over-designed decoration') || !pptSkillText.includes(CODEX_APP_IMAGE_GENERATION_DOC_URL) || !pptSkillText.includes(CODEX_IMAGEGEN_REQUIRED_POLICY) || !pptSkillText.includes(AWESOME_DESIGN_MD_REFERENCE.url) || !pptSkillText.includes('only design decision SSOT') || !pptSkillText.includes('instead of treating references as parallel authorities')) throw new Error('selftest failed: generated PPT skill missing restrained design/imagegen/fused-SSOT guidance');
|
|
3254
3376
|
if (!pptSkillText.includes('PPT pipeline allowlist') || !pptSkillText.includes('ignore installed skills and MCPs') || !pptSkillText.includes('prevent AI-like generic presentation design') || !pptSkillText.includes('Do not use generic design skills such as design-artifact-expert')) throw new Error('selftest failed: generated PPT skill missing pipeline allowlist enforcement');
|
|
3255
3377
|
if (!pptSkillText.includes('source-html/') || !pptSkillText.includes('temporary build files') || !pptSkillText.includes('ppt-parallel-report.json')) throw new Error('selftest failed: generated PPT skill missing source preservation/temp cleanup/parallel guidance');
|
|
3256
3378
|
if (!pptSkillText.includes('ppt-fact-ledger.json') || !pptSkillText.includes('ppt-image-asset-ledger.json') || !pptSkillText.includes('OpenAI Image API') || !pptSkillText.includes('ppt-review-ledger.json') || !pptSkillText.includes('ppt-iteration-report.json') || !pptSkillText.includes('never simulate missing gpt-image-2 output')) throw new Error('selftest failed: generated PPT skill missing fact/image/review loop anti-fake guidance');
|
package/src/core/codex-app.mjs
CHANGED
|
@@ -78,18 +78,38 @@ export async function codexMcpList(opts = {}) {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
export async function codexFeatureList(opts = {}) {
|
|
82
|
+
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
83
|
+
if (!codex.bin) return { ok: false, checked: false, stdout: '', stderr: 'Codex CLI missing.' };
|
|
84
|
+
const out = await runProcess(codex.bin, ['features', 'list'], {
|
|
85
|
+
timeoutMs: opts.timeoutMs || 10000,
|
|
86
|
+
maxOutputBytes: 64 * 1024
|
|
87
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
88
|
+
return {
|
|
89
|
+
ok: out.code === 0,
|
|
90
|
+
checked: true,
|
|
91
|
+
stdout: out.stdout || '',
|
|
92
|
+
stderr: out.stderr || ''
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
export async function codexAppIntegrationStatus(opts = {}) {
|
|
82
97
|
const appPath = await findCodexApp(opts);
|
|
83
98
|
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
84
99
|
const mcpList = await codexMcpList({ ...opts, codex });
|
|
100
|
+
const featureList = await codexFeatureList({ ...opts, codex });
|
|
85
101
|
const remoteControl = codexRemoteControlStatusFromInfo(codex);
|
|
86
102
|
const mcpText = `${mcpList.stdout}\n${mcpList.stderr}`;
|
|
103
|
+
const featureText = `${featureList.stdout}\n${featureList.stderr}`;
|
|
87
104
|
const browserUsePath = await findPluginCache('browser-use', opts);
|
|
88
105
|
const computerUsePath = await findPluginCache('computer-use', opts);
|
|
89
|
-
const
|
|
90
|
-
const
|
|
106
|
+
const computerUseMcpListed = /computer[-_ ]?use/i.test(mcpText);
|
|
107
|
+
const browserUseMcpListed = /browser[-_ ]?use/i.test(mcpText);
|
|
108
|
+
const imageGenerationReady = codexFeatureEnabled(featureText, 'image_generation');
|
|
109
|
+
const computerUseReady = computerUseMcpListed || Boolean(computerUsePath);
|
|
110
|
+
const browserUseReady = browserUseMcpListed || Boolean(browserUsePath);
|
|
91
111
|
const appInstalled = Boolean(appPath);
|
|
92
|
-
const ready = appInstalled && Boolean(codex.bin) && mcpList.ok && computerUseReady && browserUseReady;
|
|
112
|
+
const ready = appInstalled && Boolean(codex.bin) && mcpList.ok && featureList.ok && imageGenerationReady && computerUseReady && browserUseReady;
|
|
93
113
|
return {
|
|
94
114
|
ok: ready,
|
|
95
115
|
app: {
|
|
@@ -108,14 +128,24 @@ export async function codexAppIntegrationStatus(opts = {}) {
|
|
|
108
128
|
ok: mcpList.ok,
|
|
109
129
|
has_computer_use: computerUseReady,
|
|
110
130
|
has_browser_use: browserUseReady,
|
|
131
|
+
computer_use_source: computerUseMcpListed ? 'mcp_list' : computerUsePath ? 'plugin_cache' : 'missing',
|
|
132
|
+
browser_use_source: browserUseMcpListed ? 'mcp_list' : browserUsePath ? 'plugin_cache' : 'missing',
|
|
111
133
|
stdout: mcpList.stdout,
|
|
112
134
|
stderr: mcpList.stderr
|
|
113
135
|
},
|
|
136
|
+
features: {
|
|
137
|
+
checked: featureList.checked,
|
|
138
|
+
ok: featureList.ok,
|
|
139
|
+
image_generation: imageGenerationReady,
|
|
140
|
+
image_generation_source: imageGenerationReady ? 'codex_features_list' : 'missing',
|
|
141
|
+
stdout: featureList.stdout,
|
|
142
|
+
stderr: featureList.stderr
|
|
143
|
+
},
|
|
114
144
|
plugins: {
|
|
115
145
|
computer_use_cache: computerUsePath,
|
|
116
146
|
browser_use_cache: browserUsePath
|
|
117
147
|
},
|
|
118
|
-
guidance: codexAppGuidance({ appInstalled, codex, mcpList, computerUseReady, browserUseReady, remoteControl })
|
|
148
|
+
guidance: codexAppGuidance({ appInstalled, codex, mcpList, featureList, imageGenerationReady, computerUseReady, browserUseReady, computerUseMcpListed, browserUseMcpListed, remoteControl })
|
|
119
149
|
};
|
|
120
150
|
}
|
|
121
151
|
|
|
@@ -170,10 +200,10 @@ export function formatCodexRemoteControlStatus(status) {
|
|
|
170
200
|
return lines.filter(Boolean).join('\n');
|
|
171
201
|
}
|
|
172
202
|
|
|
173
|
-
export function codexAppGuidance({ appInstalled, codex, mcpList, computerUseReady, browserUseReady, remoteControl }) {
|
|
203
|
+
export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, imageGenerationReady, computerUseReady, browserUseReady, computerUseMcpListed, browserUseMcpListed, remoteControl }) {
|
|
174
204
|
const lines = [];
|
|
175
205
|
if (!appInstalled) {
|
|
176
|
-
lines.push('Install and open Codex App for first-party MCP/plugin tools. SKS tmux launch can still run with Codex CLI alone, but Codex Computer Use evidence will be unavailable until Codex App is ready.');
|
|
206
|
+
lines.push('Install and open Codex App for first-party MCP/plugin tools. SKS tmux launch can still run with Codex CLI alone, but Codex Computer Use and imagegen/gpt-image-2 evidence will be unavailable until Codex App is ready.');
|
|
177
207
|
lines.push(`Docs: ${CODEX_APP_DOCS_URL}`);
|
|
178
208
|
}
|
|
179
209
|
if (!codex?.bin) lines.push('Install Codex CLI too: npm i -g @openai/codex, or set SKS_CODEX_BIN.');
|
|
@@ -187,12 +217,27 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, computerUseRead
|
|
|
187
217
|
lines.push(`Codex MCP/config check failed: ${summarizeCodexMcpError(mcpList.stderr || mcpList.stdout)}`);
|
|
188
218
|
lines.push('Verify with: codex mcp list');
|
|
189
219
|
}
|
|
220
|
+
if (featureList?.checked && !featureList.ok) {
|
|
221
|
+
lines.push(`Codex feature check failed: ${summarizeCodexMcpError(featureList.stderr || featureList.stdout)}`);
|
|
222
|
+
lines.push('Verify with: codex features list');
|
|
223
|
+
}
|
|
190
224
|
if (appInstalled && (!computerUseReady || !browserUseReady)) {
|
|
191
225
|
lines.push('Open Codex App settings and enable recommended MCP/plugin tools. Codex CLI 0.130.0+ remote-control/app-server sessions can pick up config changes live; restart older CLI/TUI sessions.');
|
|
192
226
|
lines.push('Required for SKS QA-LOOP UI/browser evidence: Codex Computer Use only. Browser Use can support non-UI browser context, but it does not satisfy UI-level E2E verification.');
|
|
193
227
|
lines.push('Verify with: codex mcp list');
|
|
194
228
|
}
|
|
195
|
-
if (
|
|
229
|
+
if (imageGenerationReady) {
|
|
230
|
+
lines.push('Image generation is enabled; required raster assets and generated image-review evidence must invoke $imagegen/gpt-image-2 and record real output.');
|
|
231
|
+
} else if (appInstalled || codex?.bin) {
|
|
232
|
+
lines.push('Codex image_generation was not visible from `codex features list`. Required imagegen/gpt-image-2 evidence must stay blocked or unverified until $imagegen is available in Codex App.');
|
|
233
|
+
}
|
|
234
|
+
if (computerUseReady && !computerUseMcpListed) {
|
|
235
|
+
lines.push('Computer Use plugin files are installed, but this check cannot prove the current thread exposes the live Computer Use tools. Start a new Codex App thread and invoke @Computer or @AppName; if the tool is still absent from the model tool list, mark UI/browser evidence unverified.');
|
|
236
|
+
}
|
|
237
|
+
if (browserUseReady && !browserUseMcpListed) {
|
|
238
|
+
lines.push('Browser Use plugin files are installed, but `codex mcp list` does not list a browser-use MCP server. Treat Browser Use as plugin-scoped, not as SKS UI verification evidence.');
|
|
239
|
+
}
|
|
240
|
+
if (!lines.length) lines.push('Codex App, Codex CLI, Computer Use, Browser Use, and image generation checks look ready. UI-level E2E still requires Codex Computer Use evidence; generated image evidence still requires $imagegen/gpt-image-2 output.');
|
|
196
241
|
return lines;
|
|
197
242
|
}
|
|
198
243
|
|
|
@@ -203,13 +248,15 @@ export function formatCodexAppStatus(status, { includeRaw = false } = {}) {
|
|
|
203
248
|
`Codex App: ${status.app.installed ? 'ok' : 'missing'}${status.app.path ? ` ${status.app.path}` : ''}`,
|
|
204
249
|
`Codex CLI: ${status.codex_cli.ok ? 'ok' : 'missing'}${status.codex_cli.version ? ` ${status.codex_cli.version}` : ''}`,
|
|
205
250
|
`Remote Ctrl: ${status.remote_control?.ok ? 'ok' : 'missing'}${status.remote_control?.codex_cli?.version_number ? ` min ${status.remote_control.min_version}` : ''}`,
|
|
206
|
-
`Computer Use:${status.mcp.has_computer_use ? ' ok' : ' missing'}`,
|
|
207
|
-
`Browser Use: ${status.mcp.has_browser_use ? 'ok' : 'missing'}`,
|
|
251
|
+
`Computer Use:${status.mcp.has_computer_use ? status.mcp.computer_use_source === 'plugin_cache' ? ' installed (verify @Computer in thread)' : ' ok' : ' missing'}`,
|
|
252
|
+
`Browser Use: ${status.mcp.has_browser_use ? status.mcp.browser_use_source === 'plugin_cache' ? 'installed (plugin scoped)' : 'ok' : 'missing'}`,
|
|
253
|
+
`Image Gen: ${status.features?.image_generation ? 'ok ($imagegen/gpt-image-2)' : status.features?.checked ? 'missing' : 'not checked'}`,
|
|
208
254
|
`Ready: ${status.ok ? 'yes' : 'no'}`,
|
|
209
255
|
'',
|
|
210
256
|
...status.guidance.map((line) => `- ${line}`)
|
|
211
257
|
];
|
|
212
258
|
if (includeRaw && status.mcp.stdout) lines.push('', status.mcp.stdout.trim());
|
|
259
|
+
if (includeRaw && status.features?.stdout) lines.push('', status.features.stdout.trim());
|
|
213
260
|
return lines.join('\n');
|
|
214
261
|
}
|
|
215
262
|
|
|
@@ -225,6 +272,15 @@ function summarizeCodexMcpError(text) {
|
|
|
225
272
|
return variantLine || errorLine || cleanLines[0] || 'codex mcp list failed';
|
|
226
273
|
}
|
|
227
274
|
|
|
275
|
+
function codexFeatureEnabled(text, featureName) {
|
|
276
|
+
const name = escapeRegExp(featureName);
|
|
277
|
+
return new RegExp(`(?:^|\\n)\\s*${name}\\s+\\S+\\s+true\\b`, 'i').test(String(text || ''));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function escapeRegExp(value) {
|
|
281
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
282
|
+
}
|
|
283
|
+
|
|
228
284
|
function remoteControlGuidance(status = {}) {
|
|
229
285
|
if (!status.codex_cli?.ok) return 'Codex remote-control requires Codex CLI 0.130.0+. Install with: npm i -g @openai/codex@latest';
|
|
230
286
|
if (status.reason === 'codex_cli_version_unknown') return 'Codex remote-control requires Codex CLI 0.130.0+, but the installed CLI version could not be parsed. Check: codex --version';
|
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.52';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|