sneakoscope 0.7.18 → 0.7.24
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 +23 -5
- package/package.json +1 -1
- package/src/cli/main.mjs +44 -58
- package/src/core/fsx.mjs +1 -1
- package/src/core/init.mjs +16 -7
- package/src/core/pipeline.mjs +5 -3
- package/src/core/ppt.mjs +14 -2
- package/src/core/questions.mjs +240 -37
- package/src/core/routes.mjs +27 -1
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, 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, 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
|
|
|
@@ -46,16 +46,16 @@ sks selftest --mock
|
|
|
46
46
|
| CLI runtime | `sks tmux open` and `sks --mad` explicitly launch Codex CLI with tmux; bare `sks` only prints help/readiness surfaces. |
|
|
47
47
|
| 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. |
|
|
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
|
-
| Team orchestration | Runs substantial work through 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. |
|
|
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,
|
|
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
54
|
| 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
55
|
| 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
56
|
| TriWiki voxels | Maintains `.sneakoscope/wiki/context-pack.json` as the context SSOT with coordinate anchors, voxel metadata, `attention.use_first`, and `attention.hydrate_first`. |
|
|
57
57
|
| Context7 | Requires current docs for external packages, APIs, MCPs, SDKs, and framework/runtime behavior when correctness depends on current guidance. |
|
|
58
|
-
|
|
|
58
|
+
| Design SSOT | Treats `design.md` as the only design decision source of truth. `docs/Design-Sys-Prompt.md` is the builder prompt; getdesign.md, official getdesign docs, and curated DESIGN.md examples from `VoltAgent/awesome-design-md` are source inputs that must be fused into `design.md` or route-local style tokens instead of becoming parallel authorities. |
|
|
59
59
|
| DB safety | Treats SQL, migrations, Supabase, RLS, and destructive operations as high risk. |
|
|
60
60
|
| Release hygiene | Checks versioning, changelog, package contents, tarball size, syntax, selftests, and dry-run publishing. |
|
|
61
61
|
|
|
@@ -97,7 +97,7 @@ sks bootstrap
|
|
|
97
97
|
|
|
98
98
|
Project setup writes shared `.gitignore` entries for generated SKS files: `.sneakoscope/`, `.codex/`, `.agents/`, and managed `AGENTS.md`. Use `sks setup --local-only` when you want those excludes kept only in `.git/info/exclude`.
|
|
99
99
|
|
|
100
|
-
During npm postinstall, SKS also installs generated Codex App skills and tries the official getdesign Codex skill command, `skills add MohtashamMurshid/getdesign`, when the `skills` CLI is available. If that CLI is missing, setup still installs the generated `getdesign-reference` skill
|
|
100
|
+
During npm postinstall, SKS also installs generated Codex App skills and tries the official getdesign Codex skill command, `skills add MohtashamMurshid/getdesign`, when the `skills` CLI is available. If that CLI is missing, setup still installs the generated `getdesign-reference` skill. Design work still flows through one authority: `design.md`. When `design.md` is missing, `docs/Design-Sys-Prompt.md` is the builder prompt and getdesign plus curated DESIGN.md examples such as [VoltAgent/awesome-design-md](https://github.com/VoltAgent/awesome-design-md) are inputs to fuse into that SSOT or into route-local `$PPT` style tokens.
|
|
101
101
|
|
|
102
102
|
### One-Shot Install
|
|
103
103
|
|
|
@@ -236,12 +236,28 @@ sks code-structure scan --json
|
|
|
236
236
|
|
|
237
237
|
`sks proof-field scan` is SKS's lightweight outcome rubric: it maps the goal to proof cones, records unrelated work that can be skipped with evidence, reports a simplicity score, and names escalation triggers for when the route must return to the full Team/Honest proof path. The rubric embeds Hyperplan-style adversarial pressure as compact lenses instead of a new command: challenge framing, subtract surface, demand evidence, test integration risk, and consider one simpler alternative. When `execution_lane.lane` is `proof_field_fast_lane`, SKS can keep the parent-owned minimal patch plus listed verification and skip Team debate, fresh executor teams, broad route rework, and unrelated checks. Database, security, visual-forensic, unknown, broad, failed, or unsupported-claim signals fail closed to the normal Team/Honest path. Use `sks pipeline plan --proof-field` after changed files are known to bind that Proof Field decision to the mission plan.
|
|
238
238
|
|
|
239
|
+
### Ambiguity Questions
|
|
240
|
+
|
|
241
|
+
SKS no longer starts from a fixed checklist such as `GOAL_PRECISE` and `ACCEPTANCE_CRITERIA`. The clarification gate first scores goal clarity, constraint clarity, success-criteria clarity, and codebase-context clarity, then asks only the lowest-clarity item that can change execution. Predictable UI defaults, DB safety defaults, test scope, fallback policy, and ordinary implementation acceptance criteria are inferred and sealed automatically.
|
|
242
|
+
|
|
243
|
+
The design borrows two useful ideas from external planning systems without copying their route weight: Ouroboros-style ambiguity thresholds decide whether the prompt is clear enough to proceed, while Prometheus/Hyperplan-style adversarial lenses challenge framing, remove unnecessary surface, demand evidence, test integration risk, and consider a simpler alternative before Team work starts.
|
|
244
|
+
|
|
239
245
|
`sks skill-dream` keeps generated skill complexity bounded without doing a heavy evaluation on every prompt. Route use writes compact counters to `.sneakoscope/skills/dream-state.json`; after the configured count/cooldown threshold, or when you run `sks skill-dream run`, SKS scans `.agents/skills` and writes `.sneakoscope/reports/skill-dream-latest.json` with keep, merge, prune, and improvement candidates. The report is intentionally advisory: deleting or merging skills requires explicit approval.
|
|
240
246
|
|
|
241
247
|
`sks goal` and `$Goal` only prepare/control the native `/goal` persistence bridge. They do not replace Team, QA, DB, or other implementation routes; use the selected execution route for the actual work and verification. Context7 is only needed for Goal when external API/library documentation becomes relevant.
|
|
242
248
|
|
|
243
249
|
Use `$Computer-Use` or `$CU` inside Codex App when the task specifically needs Codex Computer Use speed for UI/browser/visual work. This lane intentionally skips Team debate, QA-LOOP clarification, subagents, and upfront TriWiki refresh. It still requires Codex Computer Use as the evidence source, and it defers TriWiki refresh/validate plus Honest Mode to the final closeout.
|
|
244
250
|
|
|
251
|
+
### Create A Presentation
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
$PPT create a customer proposal deck as HTML/PDF
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`$PPT` seals presentation-specific context before artifact work: delivery format, target audience, STP strategy, decision context, and at least three pain-point/solution/aha mappings. The route writes source and render evidence such as `ppt-audience-strategy.json`, `ppt-source-ledger.json`, `ppt-storyboard.json`, `ppt-style-tokens.json`, `ppt-render-report.json`, and `ppt-parallel-report.json`.
|
|
258
|
+
|
|
259
|
+
Design references do not compete with each other. `design.md` is the design decision SSOT; if it is missing, SKS uses `docs/Design-Sys-Prompt.md` to build or project the system. getdesign.md, official getdesign docs, and curated DESIGN.md examples from `VoltAgent/awesome-design-md` are source inputs that get fused into `design.md` or route-local `$PPT` style tokens. `$PPT` ignores installed design skills and MCP servers that are not in the route allowlist; generic design skills such as `design-artifact-expert`, `design-ui-editor`, and `design-system-builder` are not automatically used just because they are installed. This is an anti-AI-like-design guard: `$PPT` must ground visual choices in audience, source material, getdesign reference, and the design SSOT instead of freeform cards, gradients, and vague SaaS styling.
|
|
260
|
+
|
|
245
261
|
## Codex App Usage
|
|
246
262
|
|
|
247
263
|
Sneakoscope has two surfaces:
|
|
@@ -263,6 +279,7 @@ Then open Codex App and use prompt commands directly in the chat. Examples:
|
|
|
263
279
|
$Team implement the checkout fix and verify it
|
|
264
280
|
$DFix change this label and spacing only
|
|
265
281
|
$QA-LOOP dogfood localhost:3000 and fix safe issues
|
|
282
|
+
$PPT create an investor deck as HTML/PDF
|
|
266
283
|
$Goal persist this migration workflow with native /goal continuation
|
|
267
284
|
$Wiki refresh and validate the context pack
|
|
268
285
|
$DB inspect this migration for destructive risk
|
|
@@ -296,6 +313,7 @@ Use these inside Codex App or another agent prompt. They are prompt commands, no
|
|
|
296
313
|
| `$Answer` | You want an answer only and no implementation should start. |
|
|
297
314
|
| `$SKS` | You need setup, status, usage, or workflow help. |
|
|
298
315
|
| `$QA-LOOP` | You want UI/API dogfooding, safe fixes, and rechecks. |
|
|
316
|
+
| `$PPT` | You want a restrained HTML/PDF presentation with sealed delivery context, audience profile, STP strategy, decision context, and 3+ pain-point/solution/aha mappings. |
|
|
299
317
|
| `$Computer-Use` / `$CU` | You want the fastest Codex Computer Use lane for UI/browser/visual inspection or small safe fixes. |
|
|
300
318
|
| `$Goal` | You want a fast SKS bridge overlay for Codex native persisted `/goal` continuation. |
|
|
301
319
|
| `$Research` | You need frontier-style research with hypotheses and falsification. |
|
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.24",
|
|
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
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
} from '../core/ppt.mjs';
|
|
39
39
|
import { contextCapsule } from '../core/triwiki-attention.mjs';
|
|
40
40
|
import { rgbaKey, rgbaToWikiCoord, validateWikiCoordinateIndex } from '../core/wiki-coordinate.mjs';
|
|
41
|
-
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, RECOMMENDED_SKILLS, ROUTES, USAGE_TOPICS, context7ConfigToml, hasContext7ConfigText, hasFromChatImgSignal, looksLikeAnswerOnlyRequest, noUnrequestedFallbackCodePolicyText, reflectionRequiredForRoute, reasoningInstruction, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stackCurrentDocsPolicy, triwikiContextTracking } from '../core/routes.mjs';
|
|
41
|
+
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';
|
|
42
42
|
import { PIPELINE_PLAN_ARTIFACT, buildPipelinePlan, context7Evidence, evaluateStop, projectGateStatus, recordContext7Evidence, recordSubagentEvidence, validatePipelinePlan, writePipelinePlan } from '../core/pipeline.mjs';
|
|
43
43
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
|
|
44
44
|
import { appendTeamEvent, initTeamLive, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane } from '../core/team-live.mjs';
|
|
@@ -83,54 +83,17 @@ export async function main(args) {
|
|
|
83
83
|
if (!cmd) return help();
|
|
84
84
|
if (cmd === '--help' || cmd === '-h') return help();
|
|
85
85
|
if (cmd === '--version' || cmd === '-v' || cmd === 'version') return version();
|
|
86
|
-
if (cmd === 'postinstall') return postinstall({ bootstrap });
|
|
87
|
-
if (cmd === 'wizard' || cmd === 'ui') return wizard(tail);
|
|
88
86
|
if (cmd === 'tmux') return !sub || String(sub).startsWith('--') ? tmuxCommand('check', tail) : tmuxCommand(sub, rest);
|
|
89
87
|
if (cmd === 'auto-review' || cmd === 'autoreview') return autoReviewCommand(sub, rest);
|
|
90
|
-
if (cmd === 'update-check') return updateCheck(tail);
|
|
91
|
-
if (cmd === 'help') return help(tail);
|
|
92
|
-
if (cmd === 'commands') return commands(tail);
|
|
93
|
-
if (cmd === 'usage') return usage(tail);
|
|
94
|
-
if (cmd === 'root') return rootCommand(tail);
|
|
95
|
-
if (cmd === 'quickstart') return quickstartCommand();
|
|
96
|
-
if (cmd === 'codex-app') return codexAppHelp(tail);
|
|
97
|
-
if (cmd === 'bootstrap') return bootstrap(tail);
|
|
98
|
-
if (cmd === 'deps') return deps(sub, rest);
|
|
99
88
|
if (cmd === 'dollar-commands' || cmd === 'dollars' || cmd === '$') return dollarCommands(tail);
|
|
100
89
|
if (String(cmd).toLowerCase() === 'dfix') return dfixHelp();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (cmd
|
|
108
|
-
if (cmd === 'reasoning') return reasoningCommand(tail);
|
|
109
|
-
if (cmd === 'aliases') return aliases();
|
|
110
|
-
if (cmd === 'setup') return setup(tail);
|
|
111
|
-
if (cmd === 'fix-path') return fixPath(tail);
|
|
112
|
-
if (cmd === 'doctor') return doctor(tail);
|
|
113
|
-
if (cmd === 'init') return init(tail);
|
|
114
|
-
if (cmd === 'selftest') return selftest(tail);
|
|
115
|
-
if (cmd === 'goal') return goalCommand(sub, rest);
|
|
116
|
-
if (cmd === 'research') return researchCommand(sub, rest);
|
|
117
|
-
if (cmd === 'hook') return emitHook(sub);
|
|
118
|
-
if (cmd === 'profile') return profileCommand(sub, rest);
|
|
119
|
-
if (cmd === 'hproof') return hproofCommand(sub, rest);
|
|
120
|
-
if (cmd === 'validate-artifacts') return validateArtifactsCommand(tail);
|
|
121
|
-
if (cmd === 'perf') return perfCommand(sub, rest);
|
|
122
|
-
if (cmd === 'proof-field') return proofFieldCommand(sub, rest);
|
|
123
|
-
if (cmd === 'skill-dream') return skillDreamCommand(sub, rest);
|
|
124
|
-
if (cmd === 'code-structure') return codeStructureCommand(sub, rest);
|
|
125
|
-
if (cmd === 'memory') return memoryCommand(sub, rest);
|
|
126
|
-
if (cmd === 'gx') return gxCommand(sub, rest);
|
|
127
|
-
if (cmd === 'team') return team(tail);
|
|
128
|
-
if (cmd === 'db') return dbCommand(sub, rest);
|
|
129
|
-
if (cmd === 'eval') return evalCommand(sub, rest);
|
|
130
|
-
if (cmd === 'harness') return harnessCommand(sub, rest);
|
|
131
|
-
if (cmd === 'wiki') return wikiCommand(sub, rest);
|
|
132
|
-
if (cmd === 'gc') return gcCommand(tail);
|
|
133
|
-
if (cmd === 'stats') return statsCommand(tail);
|
|
90
|
+
const handlers = {
|
|
91
|
+
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), bootstrap: () => bootstrap(tail), deps: () => deps(sub, rest),
|
|
92
|
+
'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),
|
|
93
|
+
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),
|
|
94
|
+
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)
|
|
95
|
+
};
|
|
96
|
+
if (handlers[cmd]) return handlers[cmd]();
|
|
134
97
|
console.error(`Unknown command: ${cmd}`);
|
|
135
98
|
process.exitCode = 1;
|
|
136
99
|
}
|
|
@@ -1231,7 +1194,7 @@ function usage(args = []) {
|
|
|
1231
1194
|
tmux: ['tmux', '', ' sks tmux open', ' sks tmux check', ' sks tmux status --once', ' sks deps install tmux', '', 'tmux launch is explicit. Running bare `sks` prints help and never opens tmux by itself.'],
|
|
1232
1195
|
team: ['Team', '', ' sks team "task" executor:5 reviewer:2 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.'],
|
|
1233
1196
|
'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'],
|
|
1234
|
-
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, and render QA. 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
|
|
1197
|
+
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, and render QA. 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 and Context7 are conditional only when the sealed PPT contract needs raster assets or current external docs.'],
|
|
1235
1198
|
goal: ['Goal', '', ' sks goal create "task"', ' sks goal status latest', ' sks goal pause latest', ' sks goal resume latest', ' sks goal clear latest'],
|
|
1236
1199
|
'codex-app': ['Codex App', '', ' sks bootstrap', ' sks codex-app check', ' sks dollar-commands', ' cat .codex/SNEAKOSCOPE.md'],
|
|
1237
1200
|
dollar: ['Dollar Commands', '', formatDollarCommandsCompact(' '), '', 'Terminal: sks dollar-commands [--json]'],
|
|
@@ -2095,7 +2058,7 @@ async function selftest() {
|
|
|
2095
2058
|
const hookGoalTmp = tmpdir();
|
|
2096
2059
|
await initProject(hookGoalTmp, {});
|
|
2097
2060
|
const hookBin = path.join(packageRoot(), 'bin', 'sks.mjs');
|
|
2098
|
-
const hookPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '$Goal 로그인 세션 만료 UX 개선
|
|
2061
|
+
const hookPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '$Goal 로그인 세션 만료 UX 개선' });
|
|
2099
2062
|
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 });
|
|
2100
2063
|
if (hookResult.code !== 0) throw new Error(`selftest failed: $Goal hook exited ${hookResult.code}: ${hookResult.stderr}`);
|
|
2101
2064
|
const hookJson = JSON.parse(hookResult.stdout);
|
|
@@ -2109,7 +2072,7 @@ async function selftest() {
|
|
|
2109
2072
|
if (!(await exists(path.join(missionDir(hookGoalTmp, hookState.mission_id), GOAL_WORKFLOW_ARTIFACT)))) throw new Error('selftest failed: $Goal hook did not write goal workflow artifact');
|
|
2110
2073
|
const hookGoalDelegationTmp = tmpdir();
|
|
2111
2074
|
await initProject(hookGoalDelegationTmp, {});
|
|
2112
|
-
const hookGoalDelegationPayload = JSON.stringify({ cwd: hookGoalDelegationTmp, prompt: '$Goal
|
|
2075
|
+
const hookGoalDelegationPayload = JSON.stringify({ cwd: hookGoalDelegationTmp, prompt: '$Goal $Team 발표자료 만들어줘' });
|
|
2113
2076
|
const hookGoalDelegationResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookGoalDelegationTmp, input: hookGoalDelegationPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2114
2077
|
if (hookGoalDelegationResult.code !== 0) throw new Error(`selftest failed: $Goal implementation delegation hook exited ${hookGoalDelegationResult.code}: ${hookGoalDelegationResult.stderr}`);
|
|
2115
2078
|
const hookGoalDelegationJson = JSON.parse(hookGoalDelegationResult.stdout);
|
|
@@ -2121,7 +2084,7 @@ async function selftest() {
|
|
|
2121
2084
|
if (hookGoalDelegationState.mode !== 'TEAM' || hookGoalDelegationState.phase !== 'TEAM_CLARIFICATION_AWAITING_ANSWERS' || hookGoalDelegationState.implementation_allowed !== false) throw new Error('selftest failed: $Goal implementation delegation did not leave Team gate current');
|
|
2122
2085
|
if (!(await exists(path.join(missionDir(hookGoalDelegationTmp, hookGoalDelegationBridgeMatch[1]), GOAL_WORKFLOW_ARTIFACT)))) throw new Error('selftest failed: $Goal implementation delegation did not write bridge workflow artifact');
|
|
2123
2086
|
const activeGoalMissionId = hookState.mission_id;
|
|
2124
|
-
const hookGoalOverlayPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '
|
|
2087
|
+
const hookGoalOverlayPayload = JSON.stringify({ cwd: hookGoalTmp, prompt: '$Team 발표자료 만들어줘' });
|
|
2125
2088
|
const hookGoalOverlayResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookGoalTmp, input: hookGoalOverlayPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2126
2089
|
if (hookGoalOverlayResult.code !== 0) throw new Error(`selftest failed: active Goal overlay hook exited ${hookGoalOverlayResult.code}: ${hookGoalOverlayResult.stderr}`);
|
|
2127
2090
|
const hookGoalOverlayJson = JSON.parse(hookGoalOverlayResult.stdout);
|
|
@@ -2134,11 +2097,12 @@ async function selftest() {
|
|
|
2134
2097
|
if (!(await exists(path.join(missionDir(hookGoalTmp, hookGoalOverlayState.mission_id), 'required-answers.schema.json')))) throw new Error('selftest failed: active Goal overlay Team mission did not write ambiguity schema');
|
|
2135
2098
|
const hookUpdateCurrentTmp = tmpdir();
|
|
2136
2099
|
await initProject(hookUpdateCurrentTmp, {});
|
|
2100
|
+
const hookUpdateCurrentEnv = { SKS_DISABLE_UPDATE_CHECK: '0', SKS_NPM_VIEW_SNEAKOSCOPE_VERSION: '9.9.9', SKS_INSTALLED_SKS_VERSION: '9.9.9' };
|
|
2137
2101
|
const hookUpdateCurrentPayload = JSON.stringify({ cwd: hookUpdateCurrentTmp, prompt: '상태 확인해줘' });
|
|
2138
2102
|
const hookUpdateCurrentResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], {
|
|
2139
2103
|
cwd: hookUpdateCurrentTmp,
|
|
2140
2104
|
input: hookUpdateCurrentPayload,
|
|
2141
|
-
env:
|
|
2105
|
+
env: hookUpdateCurrentEnv,
|
|
2142
2106
|
timeoutMs: 15000,
|
|
2143
2107
|
maxOutputBytes: 256 * 1024
|
|
2144
2108
|
});
|
|
@@ -2160,7 +2124,7 @@ async function selftest() {
|
|
|
2160
2124
|
const hookUpdatePendingResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], {
|
|
2161
2125
|
cwd: hookUpdatePendingTmp,
|
|
2162
2126
|
input: hookUpdatePendingPayload,
|
|
2163
|
-
env:
|
|
2127
|
+
env: hookUpdateCurrentEnv,
|
|
2164
2128
|
timeoutMs: 15000,
|
|
2165
2129
|
maxOutputBytes: 256 * 1024
|
|
2166
2130
|
});
|
|
@@ -2181,7 +2145,7 @@ async function selftest() {
|
|
|
2181
2145
|
const hookUpdateSkippedResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], {
|
|
2182
2146
|
cwd: hookUpdateSkippedTmp,
|
|
2183
2147
|
input: hookUpdateSkippedPayload,
|
|
2184
|
-
env:
|
|
2148
|
+
env: hookUpdateCurrentEnv,
|
|
2185
2149
|
timeoutMs: 15000,
|
|
2186
2150
|
maxOutputBytes: 256 * 1024
|
|
2187
2151
|
});
|
|
@@ -2197,7 +2161,7 @@ async function selftest() {
|
|
|
2197
2161
|
const hookUpdateOldResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], {
|
|
2198
2162
|
cwd: hookUpdateOldTmp,
|
|
2199
2163
|
input: hookUpdateOldPayload,
|
|
2200
|
-
env: {
|
|
2164
|
+
env: { ...hookUpdateCurrentEnv, SKS_INSTALLED_SKS_VERSION: '0.0.0' },
|
|
2201
2165
|
timeoutMs: 15000,
|
|
2202
2166
|
maxOutputBytes: 256 * 1024
|
|
2203
2167
|
});
|
|
@@ -2219,16 +2183,29 @@ async function selftest() {
|
|
|
2219
2183
|
if (hookKoreanSksContext.includes('SKS answer-only pipeline active')) throw new Error('selftest failed: Korean implementation prompt still used answer-only pipeline');
|
|
2220
2184
|
const hookKoreanSksState = await readJson(stateFile(hookKoreanSksTmp), {});
|
|
2221
2185
|
if (hookKoreanSksState.phase !== 'TEAM_CLARIFICATION_CONTRACT_SEALED' || hookKoreanSksState.implementation_allowed !== true || !hookKoreanSksState.ambiguity_gate_passed) throw new Error('selftest failed: Korean Team auto-seal');
|
|
2186
|
+
const hookPaymentTeamTmp = tmpdir();
|
|
2187
|
+
await initProject(hookPaymentTeamTmp, {});
|
|
2188
|
+
const hookPaymentTeamPayload = JSON.stringify({ cwd: hookPaymentTeamTmp, prompt: '$Team 결제 재시도 정책과 로그인 세션 만료 버그 수정 executor:2 reviewer:1 user:1' });
|
|
2189
|
+
const hookPaymentTeamResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookPaymentTeamTmp, input: hookPaymentTeamPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2190
|
+
if (hookPaymentTeamResult.code !== 0) throw new Error(`selftest failed: payment/auth Team hook exited ${hookPaymentTeamResult.code}: ${hookPaymentTeamResult.stderr}`);
|
|
2191
|
+
const hookPaymentTeamJson = JSON.parse(hookPaymentTeamResult.stdout);
|
|
2192
|
+
const hookPaymentTeamContext = hookPaymentTeamJson.hookSpecificOutput?.additionalContext || '';
|
|
2193
|
+
if (!hookPaymentTeamContext.includes('Ambiguity gate auto-sealed')) throw new Error('selftest failed: predictable payment/auth Team prompt did not auto-seal');
|
|
2194
|
+
if (hookPaymentTeamContext.includes('PAYMENT_RETRY_POLICY') || hookPaymentTeamContext.includes('AUTH_PROTOCOL_CHANGE_ALLOWED')) throw new Error('selftest failed: predictable payment/auth policy defaults were asked instead of inferred');
|
|
2195
|
+
const hookPaymentTeamState = await readJson(stateFile(hookPaymentTeamTmp), {});
|
|
2196
|
+
if (hookPaymentTeamState.phase !== 'TEAM_CLARIFICATION_CONTRACT_SEALED' || hookPaymentTeamState.implementation_allowed !== true || !hookPaymentTeamState.ambiguity_gate_passed) throw new Error('selftest failed: predictable payment/auth Team state was not executable after auto-seal');
|
|
2197
|
+
const hookPaymentTeamSchema = await readJson(path.join(missionDir(hookPaymentTeamTmp, hookPaymentTeamState.mission_id), 'required-answers.schema.json'));
|
|
2198
|
+
if (hookPaymentTeamSchema.slots.length !== 0 || hookPaymentTeamSchema.inferred_answers?.PAYMENT_RETRY_POLICY === undefined || hookPaymentTeamSchema.inferred_answers?.AUTH_SESSION_EXPIRED_BEHAVIOR === undefined) throw new Error('selftest failed: predictable payment/auth defaults were not recorded as inferred answers');
|
|
2222
2199
|
const hookTeamTmp = tmpdir();
|
|
2223
2200
|
await initProject(hookTeamTmp, {});
|
|
2224
|
-
const hookTeamPayload = JSON.stringify({ cwd: hookTeamTmp, prompt: '$Team
|
|
2201
|
+
const hookTeamPayload = JSON.stringify({ cwd: hookTeamTmp, prompt: '$Team 발표자료 만들어줘 executor:2 reviewer:1 user:1' });
|
|
2225
2202
|
const hookTeamResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookTeamTmp, input: hookTeamPayload, env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2226
2203
|
if (hookTeamResult.code !== 0) throw new Error(`selftest failed: $Team hook exited ${hookTeamResult.code}: ${hookTeamResult.stderr}`);
|
|
2227
2204
|
const hookTeamJson = JSON.parse(hookTeamResult.stdout);
|
|
2228
2205
|
if (!hookTeamJson.hookSpecificOutput?.additionalContext?.includes('MANDATORY ambiguity-removal gate activated')) throw new Error('selftest failed: $Team hook did not force ambiguity gate before Team execution');
|
|
2229
2206
|
if (!hookTeamJson.hookSpecificOutput?.additionalContext?.includes('VISIBLE RESPONSE CONTRACT') || !String(hookTeamJson.systemMessage || '').includes('clarification questions')) throw new Error('selftest failed: $Team ambiguity gate did not force visible question response');
|
|
2230
2207
|
if (hookTeamJson.hookSpecificOutput?.additionalContext?.includes('GOAL_PRECISE: 이번 작업의 최종 목표')) throw new Error('selftest failed: static Team goal');
|
|
2231
|
-
if (!hookTeamJson.hookSpecificOutput?.additionalContext?.includes('
|
|
2208
|
+
if (!hookTeamJson.hookSpecificOutput?.additionalContext?.includes('PRESENTATION_DELIVERY_CONTEXT')) throw new Error('selftest failed: missing Team presentation question');
|
|
2232
2209
|
if (!hookTeamJson.hookSpecificOutput?.additionalContext?.includes('Codex plan-tool interaction')) throw new Error('selftest failed: $Team ambiguity gate did not inject plan-tool guidance');
|
|
2233
2210
|
const hookTeamState = await readJson(stateFile(hookTeamTmp), {});
|
|
2234
2211
|
if (hookTeamState.phase !== 'TEAM_CLARIFICATION_AWAITING_ANSWERS' || hookTeamState.implementation_allowed !== false) throw new Error('selftest failed: $Team hook did not lock execution behind ambiguity gate');
|
|
@@ -2240,13 +2217,13 @@ async function selftest() {
|
|
|
2240
2217
|
const hookTeamPendingState = await readJson(stateFile(hookTeamTmp), {});
|
|
2241
2218
|
const hookTeamPendingContext = hookTeamPendingJson.hookSpecificOutput?.additionalContext || '';
|
|
2242
2219
|
if (hookTeamPendingState.mission_id !== hookTeamState.mission_id) throw new Error('selftest failed: pending clarification allowed a new route mission to replace the visible question sheet');
|
|
2243
|
-
if (!hookTeamPendingContext.includes('Required questions still pending') || !hookTeamPendingContext.includes('VISIBLE RESPONSE CONTRACT') || !hookTeamPendingContext.includes('
|
|
2220
|
+
if (!hookTeamPendingContext.includes('Required questions still pending') || !hookTeamPendingContext.includes('VISIBLE RESPONSE CONTRACT') || !hookTeamPendingContext.includes('PRESENTATION_DELIVERY_CONTEXT')) throw new Error('selftest failed: pending clarification did not re-expose the question sheet');
|
|
2244
2221
|
if (hookTeamPendingContext.includes('MANDATORY ambiguity-removal gate activated')) throw new Error('selftest failed: pending clarification prepared a new ambiguity gate instead of reusing the active one');
|
|
2245
2222
|
const hookTeamStopResult = await runProcess(process.execPath, [hookBin, 'hook', 'stop'], { cwd: hookTeamTmp, input: JSON.stringify({ cwd: hookTeamTmp, last_assistant_message: 'I need three decisions before implementation, but I will not paste the Required questions block.' }), env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
|
|
2246
2223
|
if (hookTeamStopResult.code !== 0) throw new Error(`selftest failed: Team stop hook exited ${hookTeamStopResult.code}: ${hookTeamStopResult.stderr}`);
|
|
2247
2224
|
const hookTeamStopJson = JSON.parse(hookTeamStopResult.stdout);
|
|
2248
2225
|
if (hookTeamStopJson.decision !== 'block' || !String(hookTeamStopJson.reason || '').includes('mandatory ambiguity-removal')) throw new Error('selftest failed: Stop hook did not block missing Team ambiguity answers');
|
|
2249
|
-
if (!String(hookTeamStopJson.reason || '').includes('Required questions') || !String(hookTeamStopJson.reason || '').includes('
|
|
2226
|
+
if (!String(hookTeamStopJson.reason || '').includes('Required questions') || !String(hookTeamStopJson.reason || '').includes('PRESENTATION_DELIVERY_CONTEXT')) throw new Error('selftest failed: missing Team stop presentation question');
|
|
2250
2227
|
if (String(hookTeamStopJson.reason || '').includes('GOAL_PRECISE: 이번 작업의 최종 목표')) throw new Error('selftest failed: static Team stop goal');
|
|
2251
2228
|
if (!String(hookTeamStopJson.reason || '').includes('sks pipeline answer')) throw new Error('selftest failed: Stop hook did not provide pipeline answer command');
|
|
2252
2229
|
if (!String(hookTeamStopJson.reason || '').includes('Codex plan-tool interaction')) throw new Error('selftest failed: Stop hook did not reprint plan-tool guidance');
|
|
@@ -2778,10 +2755,17 @@ async function selftest() {
|
|
|
2778
2755
|
const buttonUxSchema = buildQuestionSchema('$Team 버튼 UX 수정');
|
|
2779
2756
|
const buttonUxSlotIds = buttonUxSchema.slots.map((s) => s.id);
|
|
2780
2757
|
if (buttonUxSlotIds.includes('UI_STATE_BEHAVIOR') || buttonUxSlotIds.includes('VISUAL_REGRESSION_REQUIRED')) throw new Error('selftest failed: predictable UI defaults should be inferred, not asked');
|
|
2758
|
+
if (buttonUxSlotIds.length) throw new Error(`selftest failed: clear small UI work should auto-seal, got ${buttonUxSlotIds.join(',')}`);
|
|
2781
2759
|
if (buttonUxSchema.inferred_answers.UI_STATE_BEHAVIOR !== 'infer_from_task_context_and_existing_design_system; preserve existing loading/error/empty/retry behavior unless explicitly requested; add only standard states required by the touched surface') throw new Error('selftest failed: UI state default inference missing');
|
|
2782
2760
|
if (buttonUxSchema.inferred_answers.VISUAL_REGRESSION_REQUIRED !== 'yes_if_available') throw new Error('selftest failed: visual regression default inference missing');
|
|
2761
|
+
const vagueSchema = buildQuestionSchema('뭔가 개선해줘');
|
|
2762
|
+
const vagueSlotIds = vagueSchema.slots.map((s) => s.id);
|
|
2763
|
+
if (!vagueSlotIds.includes('INTENT_TARGET') || vagueSlotIds.includes('GOAL_PRECISE') || vagueSlotIds.includes('ACCEPTANCE_CRITERIA')) throw new Error(`selftest failed: vague work should ask dynamic intent questions only, got ${vagueSlotIds.join(',')}`);
|
|
2764
|
+
if (vagueSchema.ambiguity_assessment?.method !== 'weighted_clarity_interview' || !vagueSchema.ambiguity_assessment?.adversarial_lenses?.includes('challenge_framing')) throw new Error('selftest failed: ambiguity schema missing weighted clarity / planning lenses');
|
|
2783
2765
|
const pptRoute = routePrompt('$PPT 투자자용 피치덱 만들어줘');
|
|
2784
2766
|
if (pptRoute?.id !== 'PPT') throw new Error('selftest failed: $PPT did not route to presentation pipeline');
|
|
2767
|
+
if (JSON.stringify(pptRoute.requiredSkills) !== JSON.stringify(PPT_PIPELINE_SKILL_ALLOWLIST)) throw new Error(`selftest failed: PPT route required skills are not allowlisted: ${pptRoute.requiredSkills.join(',')}`);
|
|
2768
|
+
if (pptRoute.requiredSkills.includes('design-artifact-expert') || pptRoute.requiredSkills.includes('design-ui-editor') || pptRoute.requiredSkills.includes('design-system-builder')) throw new Error('selftest failed: PPT route still requires generic design skills');
|
|
2785
2769
|
const pptSchema = buildQuestionSchema('$PPT 투자자용 피치덱 만들어줘');
|
|
2786
2770
|
const pptSlotIds = pptSchema.slots.map((s) => s.id);
|
|
2787
2771
|
for (const id of ['PRESENTATION_DELIVERY_CONTEXT', 'PRESENTATION_AUDIENCE_PROFILE', 'PRESENTATION_STP_STRATEGY', 'PRESENTATION_PAINPOINT_SOLUTION_MAP', 'PRESENTATION_DECISION_CONTEXT']) {
|
|
@@ -2790,6 +2774,7 @@ async function selftest() {
|
|
|
2790
2774
|
const pptSkillText = await safeReadText(path.join(tmp, '.agents', 'skills', 'ppt', 'SKILL.md'));
|
|
2791
2775
|
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');
|
|
2792
2776
|
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');
|
|
2777
|
+
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');
|
|
2793
2778
|
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');
|
|
2794
2779
|
if (routeRequiresSubagents(pptRoute, '$PPT 투자자용 피치덱 만들어줘')) throw new Error('selftest failed: PPT route should not require subagents by default');
|
|
2795
2780
|
if (!reflectionRequiredForRoute(pptRoute)) throw new Error('selftest failed: PPT route should require reflection');
|
|
@@ -2819,6 +2804,7 @@ async function selftest() {
|
|
|
2819
2804
|
if (!pptHtml.includes('<html') || pptHtml.includes('gradient')) throw new Error('selftest failed: PPT HTML artifact missing or over-designed');
|
|
2820
2805
|
const pptStyleTokens = await readJson(path.join(pptMission.dir, 'ppt-style-tokens.json'));
|
|
2821
2806
|
if (pptStyleTokens.design_policy?.design_ssot?.authority !== DESIGN_SYSTEM_SSOT.authority_file || !pptStyleTokens.design_policy?.source_inputs?.some((entry) => entry.url === AWESOME_DESIGN_MD_REFERENCE.url && entry.role === 'source_input_for_ssot') || !pptStyleTokens.design_policy?.anti_generic_ai_style) throw new Error('selftest failed: PPT style tokens missing fused design SSOT/source-input anti-generic policy');
|
|
2807
|
+
if (JSON.stringify(pptStyleTokens.design_policy?.pipeline_allowlist?.required_skills || []) !== JSON.stringify(PPT_PIPELINE_SKILL_ALLOWLIST) || !pptStyleTokens.design_policy?.pipeline_allowlist?.ignore_installed_out_of_pipeline_skills || !(pptStyleTokens.design_policy?.pipeline_allowlist?.ignored_design_skills_even_if_installed || []).includes('design-artifact-expert') || !/AI-like/.test(pptStyleTokens.design_policy?.pipeline_allowlist?.anti_ai_design_goal || '')) throw new Error('selftest failed: PPT style tokens missing skill/MCP allowlist enforcement');
|
|
2822
2808
|
const audienceScript = pptHtml.match(/id="ppt-audience-strategy">([^<]+)<\/script>/);
|
|
2823
2809
|
if (!audienceScript) throw new Error('selftest failed: PPT HTML missing audience strategy script data');
|
|
2824
2810
|
JSON.parse(audienceScript[1]);
|
|
@@ -2842,7 +2828,7 @@ async function selftest() {
|
|
|
2842
2828
|
const dbQuestionGateSchema = buildQuestionSchema('DB_SCHEMA_CHANGE_ALLOWED DATABASE_TARGET_ENVIRONMENT DATABASE_WRITE_MODE SUPABASE_MCP_POLICY DB_READ_ONLY_QUERY_LIMIT 이런 질문은 사용자에게 묻지 말고 알아서 판단해줘');
|
|
2843
2829
|
const dbQuestionGateSlotIds = dbQuestionGateSchema.slots.map((s) => s.id);
|
|
2844
2830
|
if (dbQuestionGateSlotIds.length) throw new Error(`selftest failed: predictable DB safety prompt should auto-seal, got ${dbQuestionGateSlotIds.join(',')}`);
|
|
2845
|
-
const { id, dir, mission } = await createMission(tmp, { mode: 'goal', prompt: '
|
|
2831
|
+
const { id, dir, mission } = await createMission(tmp, { mode: 'goal', prompt: '발표자료 만들어줘' });
|
|
2846
2832
|
const schema = buildQuestionSchema(mission.prompt);
|
|
2847
2833
|
await writeQuestions(dir, schema);
|
|
2848
2834
|
if (validateAnswers(schema, {}).ok) throw new Error('selftest failed: empty answers valid');
|
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.24';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
package/src/core/init.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { DEFAULT_DB_SAFETY_POLICY } from './db-safety.mjs';
|
|
|
6
6
|
import { isHarnessSourceProject, writeHarnessGuardPolicy } from './harness-guard.mjs';
|
|
7
7
|
import { repairSksGeneratedArtifacts } from './harness-conflicts.mjs';
|
|
8
8
|
import { installVersionGitHook } from './version-manager.mjs';
|
|
9
|
-
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, RECOMMENDED_DESIGN_REFERENCES, RECOMMENDED_MCP_SERVERS, RECOMMENDED_SKILLS, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, outcomeRubricPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
9
|
+
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
10
|
import { SKILL_DREAM_POLICY, skillDreamPolicyText } from './skill-forge.mjs';
|
|
11
11
|
|
|
12
12
|
const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
|
|
@@ -142,7 +142,10 @@ export async function initProject(root, opts = {}) {
|
|
|
142
142
|
default_enabled: true,
|
|
143
143
|
dollar_commands: DOLLAR_COMMANDS.map((c) => c.command),
|
|
144
144
|
dollar_skill_names: DOLLAR_SKILL_NAMES,
|
|
145
|
-
fast_design_command: '$DFix'
|
|
145
|
+
fast_design_command: '$DFix',
|
|
146
|
+
ppt_skill_allowlist: PPT_PIPELINE_SKILL_ALLOWLIST,
|
|
147
|
+
ppt_conditional_skill_allowlist: PPT_CONDITIONAL_SKILL_ALLOWLIST,
|
|
148
|
+
ppt_mcp_allowlist: PPT_PIPELINE_MCP_ALLOWLIST
|
|
146
149
|
},
|
|
147
150
|
recommended_skills: RECOMMENDED_SKILLS,
|
|
148
151
|
recommended_mcp_servers: RECOMMENDED_MCP_SERVERS,
|
|
@@ -242,7 +245,10 @@ export async function initProject(root, opts = {}) {
|
|
|
242
245
|
route_without_command: true,
|
|
243
246
|
dollar_commands: DOLLAR_COMMANDS.map((c) => c.command),
|
|
244
247
|
dollar_skill_names: DOLLAR_SKILL_NAMES,
|
|
245
|
-
fast_design_command: '$DFix'
|
|
248
|
+
fast_design_command: '$DFix',
|
|
249
|
+
ppt_skill_allowlist: PPT_PIPELINE_SKILL_ALLOWLIST,
|
|
250
|
+
ppt_conditional_skill_allowlist: PPT_CONDITIONAL_SKILL_ALLOWLIST,
|
|
251
|
+
ppt_mcp_allowlist: PPT_PIPELINE_MCP_ALLOWLIST
|
|
246
252
|
},
|
|
247
253
|
context7: {
|
|
248
254
|
...(policy.context7 || {}),
|
|
@@ -372,7 +378,10 @@ export async function initProject(root, opts = {}) {
|
|
|
372
378
|
route_without_command: true,
|
|
373
379
|
dollar_commands: DOLLAR_COMMANDS.map((c) => c.command),
|
|
374
380
|
dollar_skill_names: DOLLAR_SKILL_NAMES,
|
|
375
|
-
fast_design_command: '$DFix'
|
|
381
|
+
fast_design_command: '$DFix',
|
|
382
|
+
ppt_skill_allowlist: PPT_PIPELINE_SKILL_ALLOWLIST,
|
|
383
|
+
ppt_conditional_skill_allowlist: PPT_CONDITIONAL_SKILL_ALLOWLIST,
|
|
384
|
+
ppt_mcp_allowlist: PPT_PIPELINE_MCP_ALLOWLIST
|
|
376
385
|
},
|
|
377
386
|
context7: {
|
|
378
387
|
required_for_external_docs: true,
|
|
@@ -547,10 +556,10 @@ export async function installSkills(root) {
|
|
|
547
556
|
'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`,
|
|
548
557
|
'sks': `---\nname: sks\ndescription: General Sneakoscope Codex command route for $SKS or $sks usage, setup, status, and workflow help.\n---\n\nUse local SKS commands: bootstrap, deps, commands, quickstart, codex-app, context7, guard, conflicts, reasoning, wiki, pipeline status, pipeline plan, skill-dream. Promote code-changing work to Team unless Answer/DFix/Help/Wiki/safety route fits. Surface route/guard/scope, use TriWiki, do not edit installed harness files outside this engine repo, and require human-approved conflict cleanup. ${skillDreamPolicyText()}\n`,
|
|
549
558
|
'wiki': `---\nname: wiki\ndescription: Dollar-command route for $Wiki TriWiki refresh, pack, validate, and prune commands.\n---\n\nUse for $Wiki or Korean wiki-refresh requests. Refresh/update/갱신: run sks wiki refresh, then validate .sneakoscope/wiki/context-pack.json. Pack: run sks wiki pack, then validate. Prune/clean/정리: use sks wiki refresh --prune, or sks wiki prune --dry-run for inspection. Report claims, anchors, trust, attention.use_first/hydrate_first, validation, and blockers. Do not start ambiguity-gated implementation, subagents, or unrelated work.\n`,
|
|
550
|
-
'team': `---\nname: team\ndescription: SKS Team orchestration for $Team/code work; $From-Chat-IMG is the explicit chat-image alias.\n---\n\nUse for $Team/code work. Ambiguity gate first. Read pipeline-plan.json or run sks pipeline plan to see the runtime lane, kept/skipped stages, and verification before implementation. Write team-roster.json; team-gate.json needs team_roster_confirmed=true. executor:N means N scouts, N debate voices, then fresh N executors. After consensus, compile team-graph.json, team-runtime-tasks.json, team-decomposition-report.json, and team-inbox/ so worker handoff uses concrete runtime task ids with role/path/domain/lane hints. Refresh/validate TriWiki before debate, implementation, review, and final; consume attention.use_first and hydrate attention.hydrate_first before risky decisions. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${skillDreamPolicyText()} Log events and use sks team message for bounded inter-agent communication in transcript/lane panes. Color-coded tmux lanes distinguish overview/scout/planning/execution/review/safety sessions. End with cleanup-tmux or a cleanup event so follow panes show cleanup and stop; pass team-session-cleanup.json, then reflection and Honest Mode. Parent integrates/verifies.\n\n${chatCaptureIntakeText()}\n`,
|
|
559
|
+
'team': `---\nname: team\ndescription: SKS Team orchestration for $Team/code work; $From-Chat-IMG is the explicit chat-image alias.\n---\n\nUse for $Team/code work. Ambiguity gate first, but score goal, constraints, success criteria, and codebase context before asking; ask only the lowest-clarity scope/safety/behavior/acceptance question(s), otherwise auto-seal inferred answers. Read pipeline-plan.json or run sks pipeline plan to see the runtime lane, kept/skipped stages, and verification before implementation. Write team-roster.json; team-gate.json needs team_roster_confirmed=true. executor:N means N scouts, N debate voices, then fresh N executors. After consensus, compile team-graph.json, team-runtime-tasks.json, team-decomposition-report.json, and team-inbox/ so worker handoff uses concrete runtime task ids with role/path/domain/lane hints. Refresh/validate TriWiki before debate, implementation, review, and final; consume attention.use_first and hydrate attention.hydrate_first before risky decisions. ${outcomeRubricPolicyText()} ${speedLanePolicyText()} ${skillDreamPolicyText()} Log events and use sks team message for bounded inter-agent communication in transcript/lane panes. Color-coded tmux lanes distinguish overview/scout/planning/execution/review/safety sessions. End with cleanup-tmux or a cleanup event so follow panes show cleanup and stop; pass team-session-cleanup.json, then reflection and Honest Mode. Parent integrates/verifies.\n\n${chatCaptureIntakeText()}\n`,
|
|
551
560
|
'from-chat-img': `---\nname: from-chat-img\ndescription: Explicit $From-Chat-IMG Team alias for chat screenshot plus attachment analysis.\n---\n\nUse only for From-Chat-IMG/$From-Chat-IMG. It enters the normal Team pipeline. Treat uploads as chat screenshot plus originals. Use Codex Computer Use visual inspection when available, list requirements first, match regions to attachments with confidence, write ${FROM_CHAT_IMG_COVERAGE_ARTIFACT}, ${FROM_CHAT_IMG_CHECKLIST_ARTIFACT}, ${FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT}, and ${FROM_CHAT_IMG_QA_LOOP_ARTIFACT}, then continue Team gates, review, reflection, and Honest Mode. ${CODEX_COMPUTER_USE_ONLY_POLICY} The ledger must account for every visible customer request, screenshot image region, and separate attachment; ${FROM_CHAT_IMG_CHECKLIST_ARTIFACT} must have a checked item for each request, image-region/attachment match, work item, scoped QA-LOOP, and verification step; ${FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT} stores temporary TriWiki-backed session context with expires_after_sessions=${FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS}. ${FROM_CHAT_IMG_QA_LOOP_ARTIFACT} must prove QA-LOOP ran over the exact customer-request work-order range after implementation, with every work item covered, post-fix verification complete, and zero unresolved findings. team-gate.json cannot pass From-Chat-IMG completion until unresolved_items is empty, every checklist box is checked, and scoped_qa_loop_completed=true.\n`,
|
|
552
561
|
'qa-loop': `---\nname: qa-loop\ndescription: $QA-LOOP dogfoods UI/API as human proxy with safety gates, Codex Computer Use-only UI evidence, safe fixes, rechecks, and a QA report.\n---\n\nUse only $QA-LOOP. Ask scope, target, mutation, login. Credentials are runtime-only; never save secrets. UI-level E2E needs Codex Computer Use evidence or must be marked unverified; Chrome MCP, Browser Use, Playwright, Selenium, Puppeteer, and other browser automation do not satisfy UI/browser verification. Deployed targets are read-only; destructive removal is forbidden. After answer/run, dogfood real flows, apply safe contract-allowed code/test/docs fixes, recheck, and do not pass qa-gate.json with unresolved findings or without post_fix_verification_complete. Finish qa-ledger, date/version report, gate, completion summary, and Honest Mode.\n`,
|
|
553
|
-
'ppt': `---\nname: ppt\ndescription: $PPT information-first HTML/PDF presentation pipeline with STP, audience, pain-point, format, research, design-system, and verification questions.\n---\n\nUse only when the user invokes $PPT or asks to create a presentation, deck, slides, pitch deck, proposal deck, HTML presentation, or PDF presentation artifact. Before artifact work, seal presentation-specific ambiguity answers: delivery context, target audience profile including role/average age/job/industry/topic familiarity/decision power, STP strategy, decision context and objections, and 3+ pain-point to solution mappings with expected aha moments. Presentation design must be simple, restrained, and information-first: avoid over-designed decoration, ornamental gradients, nested cards, and effects that compete with the message. Design detail should be embedded through typography hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents. Use design.md as the only design decision SSOT. If design.md is missing, use docs/Design-Sys-Prompt.md plus getdesign-reference and curated DESIGN.md examples from ${AWESOME_DESIGN_MD_REFERENCE.url} only as source inputs, then fuse them into route-local PPT style tokens with a recorded design_ssot instead of treating references as parallel authorities. If generated image assets are needed, use imagegen and prefer Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) before API generation. Use web or Context7 evidence when external facts/libraries are
|
|
562
|
+
'ppt': `---\nname: ppt\ndescription: $PPT information-first HTML/PDF presentation pipeline with STP, audience, pain-point, format, research, design-system, and verification questions.\n---\n\nUse only when the user invokes $PPT or asks to create a presentation, deck, slides, pitch deck, proposal deck, HTML presentation, or PDF presentation artifact. Before artifact work, seal presentation-specific ambiguity answers: delivery context, target audience profile including role/average age/job/industry/topic familiarity/decision power, STP strategy, decision context and objections, and 3+ pain-point to solution mappings with expected aha moments. Presentation design must be simple, restrained, and information-first: avoid over-designed decoration, ornamental gradients, nested cards, and effects that compete with the message. Design detail should be embedded through typography hierarchy, spacing, alignment, thin rules, source clarity, and subtle accents. ${pptPipelineAllowlistPolicyText()} Use design.md as the only design decision SSOT. If design.md is missing, use docs/Design-Sys-Prompt.md plus getdesign-reference and curated DESIGN.md examples from ${AWESOME_DESIGN_MD_REFERENCE.url} only as source inputs, then fuse them into route-local PPT style tokens with a recorded design_ssot instead of treating references as parallel authorities. If generated image assets are needed, use imagegen only when that asset need is explicitly sealed in the $PPT contract and prefer Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}) before API generation. Use web or Context7 evidence only when external facts/libraries/current docs are required by the PPT contract, then create the PDF plus editable source HTML under source-html/, keep independent strategy/render/file-write phases parallel where inputs allow, record ppt-parallel-report.json, and verify readability, overlap, format fit, source coverage, export state, and temporary build files cleanup. Finish with reflection and Honest Mode; do not skip STP/audience questions for presentation artifacts.\n`,
|
|
554
563
|
'computer-use': `---\nname: computer-use\ndescription: Maximum-speed $Computer-Use/$CU lane for Codex Computer Use UI/browser/visual tasks.\n---\n\nUse only when the user invokes $Computer-Use/$CU or asks for a Computer Use-specific fast lane. Skip Team debate, QA-LOOP clarification, upfront TriWiki refresh, Context7, subagents, and reflection unless explicitly requested. Infer the smallest target, use Codex Computer Use directly, and never substitute Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or other browser automation for UI/browser evidence. If Computer Use is unavailable, mark UI/browser evidence unverified and stop with the blocker. At the end only, refresh or pack TriWiki, validate it, then provide a concise completion summary plus Honest Mode.\n`,
|
|
555
564
|
'computer-use-fast': `---\nname: computer-use-fast\ndescription: Alias for the maximum-speed $Computer-Use/$CU Codex Computer Use lane.\n---\n\nUse the same rules as computer-use: skip Team debate, QA-LOOP clarification, upfront TriWiki refresh, Context7, subagents, and reflection unless explicitly requested. Use Codex Computer Use directly; never substitute Playwright, Chrome MCP, Browser Use, Selenium, Puppeteer, or other browser automation for UI/browser evidence. At the end only, refresh/pack TriWiki, validate it, then provide a concise completion summary plus Honest Mode.\n`,
|
|
556
565
|
'cu': `---\nname: cu\ndescription: Short alias for the maximum-speed $Computer-Use Codex Computer Use lane.\n---\n\nUse the same rules as computer-use. This is a speed lane for focused UI/browser/visual tasks that require Codex Computer Use evidence, with TriWiki refresh/validate and Honest Mode deferred to final closeout.\n`,
|
|
@@ -561,7 +570,7 @@ export async function installSkills(root) {
|
|
|
561
570
|
'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. 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 DB permission applies only while the active mission gate is open, must be deactivated when the task ends, and opens Supabase MCP column/schema cleanup, direct execute SQL, and normal DB write permissions. Keep only catastrophic database-wipe safeguards: whole database/table removal, all-row delete/update, reset, and dangerous project/branch management remain blocked. Do not carry MAD-SKS permission into later prompts or routes.\n`,
|
|
562
571
|
'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`,
|
|
563
572
|
'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`,
|
|
564
|
-
'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.
|
|
573
|
+
'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`,
|
|
565
574
|
'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`,
|
|
566
575
|
'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`,
|
|
567
576
|
'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`,
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { recordSkillDreamEvent, skillDreamPolicyText, writeSkillForgeReport } fr
|
|
|
14
14
|
import { writeResearchPlan } from './research.mjs';
|
|
15
15
|
import { PPT_REQUIRED_GATE_FIELDS } from './ppt.mjs';
|
|
16
16
|
import { SPEED_LANE_POLICY } from './proof-field.mjs';
|
|
17
|
-
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, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stripDollarCommand, stripMadSksSignal, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from './routes.mjs';
|
|
17
|
+
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';
|
|
18
18
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from './team-dag.mjs';
|
|
19
19
|
import { formatRoleCounts, initTeamLive, parseTeamSpecText } from './team-live.mjs';
|
|
20
20
|
|
|
@@ -266,7 +266,9 @@ export function promptPipelineContext(prompt, route = routePrompt(prompt)) {
|
|
|
266
266
|
outcomeRubricPolicyText(),
|
|
267
267
|
speedLanePolicyText(),
|
|
268
268
|
skillDreamPolicyText(),
|
|
269
|
-
|
|
269
|
+
route?.id === 'PPT'
|
|
270
|
+
? `${pptPipelineAllowlistPolicyText()} ${getdesignReferencePolicyText()}`
|
|
271
|
+
: `Design routing: UI/UX reads design.md first; if missing, use design-system-builder from docs/Design-Sys-Prompt.md with plan-tool clarification and a default font recommendation. Existing designs use design-ui-editor plus design-artifact-expert. Image/logo/raster assets use imagegen, which must prefer Codex App built-in image generation documented at ${CODEX_APP_IMAGE_GENERATION_DOC_URL}. ${getdesignReferencePolicyText()}`,
|
|
270
272
|
triwikiContextTrackingText(),
|
|
271
273
|
triwikiStagePolicyText(),
|
|
272
274
|
stackCurrentDocsPolicyText(),
|
|
@@ -278,7 +280,7 @@ export function promptPipelineContext(prompt, route = routePrompt(prompt)) {
|
|
|
278
280
|
if (reflectionRequiredForRoute(route)) lines.push(reflectionInstructionText());
|
|
279
281
|
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, review/integration, ${TEAM_SESSION_CLEANUP_ARTIFACT}, reflection, and Honest Mode.`);
|
|
280
282
|
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.');
|
|
281
|
-
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. If generated image assets are needed,
|
|
283
|
+
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 are needed, use imagegen only when that asset need is explicitly sealed in the $PPT contract, preferring Codex App built-in image generation (${CODEX_APP_IMAGE_GENERATION_DOC_URL}). Then build source ledger, storyboard with aha moments, style tokens, editable source HTML under source-html/, PDF artifact, render QA, 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.`);
|
|
282
284
|
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.');
|
|
283
285
|
if (route?.id === 'DB') lines.push('DB route: scan/check database risk first; destructive DB operations remain forbidden.');
|
|
284
286
|
if (route?.id === 'GX') lines.push('GX route: use deterministic vgraph/beta render, validate, drift, and snapshot artifacts.');
|
package/src/core/ppt.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fsp from 'node:fs/promises';
|
|
3
3
|
import { nowIso, readJson, writeJsonAtomic, writeTextAtomic } from './fsx.mjs';
|
|
4
|
-
import { AWESOME_DESIGN_MD_REFERENCE, DESIGN_SYSTEM_SSOT, GETDESIGN_REFERENCE } from './routes.mjs';
|
|
4
|
+
import { AWESOME_DESIGN_MD_REFERENCE, DESIGN_SYSTEM_SSOT, GETDESIGN_REFERENCE, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST } from './routes.mjs';
|
|
5
5
|
|
|
6
6
|
export const PPT_AUDIENCE_STRATEGY_ARTIFACT = 'ppt-audience-strategy.json';
|
|
7
7
|
export const PPT_GATE_ARTIFACT = 'ppt-gate.json';
|
|
@@ -315,6 +315,15 @@ export function buildPptStyleTokens(contract = {}) {
|
|
|
315
315
|
design_policy: {
|
|
316
316
|
priority: 'information_first',
|
|
317
317
|
visual_style: 'simple_restrained_detailed',
|
|
318
|
+
pipeline_allowlist: {
|
|
319
|
+
required_skills: [...PPT_PIPELINE_SKILL_ALLOWLIST],
|
|
320
|
+
conditional_skills: [...PPT_CONDITIONAL_SKILL_ALLOWLIST],
|
|
321
|
+
allowed_mcp_servers: [...PPT_PIPELINE_MCP_ALLOWLIST],
|
|
322
|
+
ignore_installed_out_of_pipeline_skills: true,
|
|
323
|
+
ignored_design_skills_even_if_installed: ['design-artifact-expert', 'design-ui-editor', 'design-system-builder'],
|
|
324
|
+
anti_ai_design_goal: 'prevent AI-like generic presentation design by forcing decisions through audience, sources, getdesign reference, and the design SSOT instead of freeform decorative design skills',
|
|
325
|
+
rule: 'PPT design and render work must use only the route allowlist. Installed skills or MCP servers outside this allowlist are ignored unless the sealed PPT contract explicitly activates a conditional entry.'
|
|
326
|
+
},
|
|
318
327
|
design_ssot: {
|
|
319
328
|
authority: DESIGN_SYSTEM_SSOT.authority_file,
|
|
320
329
|
builder_prompt: DESIGN_SYSTEM_SSOT.builder_prompt,
|
|
@@ -335,7 +344,7 @@ export function buildPptStyleTokens(contract = {}) {
|
|
|
335
344
|
],
|
|
336
345
|
avoid: ['over-designed decoration', 'ornamental gradients', 'nested cards', 'low-contrast gray body text', 'excessive motion or effects'],
|
|
337
346
|
detail_strategy: ['precise spacing', 'clear hierarchy', 'thin rules', 'disciplined alignment', 'subtle accent color only when it clarifies meaning'],
|
|
338
|
-
anti_generic_ai_style: 'select a concrete DESIGN.md visual system before
|
|
347
|
+
anti_generic_ai_style: 'prevent AI-like design: select a concrete DESIGN.md or route-local visual system before styling; do not default to generic cards, gradients, vague SaaS visuals, oversized decoration, or unsupported image-like flourishes',
|
|
339
348
|
image_policy: 'use images only when they improve comprehension; prefer Codex App built-in image generation via https://developers.openai.com/codex/app/features#image-generation when generated assets are needed'
|
|
340
349
|
}
|
|
341
350
|
};
|
|
@@ -486,6 +495,9 @@ export function buildPptRenderReport({ contract = {}, audience, sourceLedger, st
|
|
|
486
495
|
{ id: 'restrained_detail', passed: styleTokens.design_policy?.visual_style === 'simple_restrained_detailed' },
|
|
487
496
|
{ id: 'design_ssot_declared', passed: styleTokens.design_policy?.design_ssot?.authority === DESIGN_SYSTEM_SSOT.authority_file },
|
|
488
497
|
{ id: 'curated_design_md_input_fused', passed: (styleTokens.design_policy?.source_inputs || []).some((entry) => entry.url === AWESOME_DESIGN_MD_REFERENCE.url && entry.role === 'source_input_for_ssot') },
|
|
498
|
+
{ id: 'ppt_skill_allowlist_enforced', passed: JSON.stringify(styleTokens.design_policy?.pipeline_allowlist?.required_skills || []) === JSON.stringify([...PPT_PIPELINE_SKILL_ALLOWLIST]) },
|
|
499
|
+
{ id: 'out_of_pipeline_design_skills_ignored', passed: styleTokens.design_policy?.pipeline_allowlist?.ignore_installed_out_of_pipeline_skills === true && (styleTokens.design_policy?.pipeline_allowlist?.ignored_design_skills_even_if_installed || []).includes('design-artifact-expert') },
|
|
500
|
+
{ id: 'ppt_mcp_allowlist_scoped', passed: (styleTokens.design_policy?.pipeline_allowlist?.allowed_mcp_servers || []).every((entry) => entry.mcp === 'context7' && /external_documentation/.test(entry.condition || '')) },
|
|
489
501
|
{ id: 'no_decorative_overdesign', passed: !String(html).includes('gradient') }
|
|
490
502
|
],
|
|
491
503
|
broken_links: [],
|
package/src/core/questions.mjs
CHANGED
|
@@ -60,6 +60,134 @@ function hasAnswer(value) {
|
|
|
60
60
|
return true;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
const AMBIGUITY_READY_THRESHOLD = 0.2;
|
|
64
|
+
const CLARITY_FLOORS = {
|
|
65
|
+
goal: 0.75,
|
|
66
|
+
constraints: 0.65,
|
|
67
|
+
success: 0.7,
|
|
68
|
+
context: 0.6
|
|
69
|
+
};
|
|
70
|
+
const CLARITY_WEIGHTS = {
|
|
71
|
+
goal: 0.35,
|
|
72
|
+
constraints: 0.25,
|
|
73
|
+
success: 0.25,
|
|
74
|
+
context: 0.15
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function clamp01(n) {
|
|
78
|
+
if (!Number.isFinite(n)) return 0;
|
|
79
|
+
return Math.max(0, Math.min(1, n));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function hasAny(re, text) {
|
|
83
|
+
return re.test(text);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function scoreComponent(name, clarity, weight, justification) {
|
|
87
|
+
return {
|
|
88
|
+
name,
|
|
89
|
+
clarity_score: Number(clamp01(clarity).toFixed(2)),
|
|
90
|
+
weight,
|
|
91
|
+
ambiguity_contribution: Number(((1 - clamp01(clarity)) * weight).toFixed(3)),
|
|
92
|
+
justification
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function summarizeAnswer(value) {
|
|
97
|
+
if (Array.isArray(value)) return value.map((v) => String(v || '').trim()).filter(Boolean).join('; ');
|
|
98
|
+
return String(value || '').trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function promptedGoalFromAnswers(explicitAnswers = {}) {
|
|
102
|
+
const target = summarizeAnswer(explicitAnswers.INTENT_TARGET);
|
|
103
|
+
const outcome = summarizeAnswer(explicitAnswers.REQUIRED_OUTCOME || explicitAnswers.SUCCESS_CRITERIA_OR_ACCEPTANCE);
|
|
104
|
+
if (target && outcome) return `${target}: ${outcome}`;
|
|
105
|
+
return target || outcome || '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function promptHasExplicitAcceptance(lower) {
|
|
109
|
+
return /완료\s*기준|성공\s*기준|acceptance|criteria|definition of done|검증|테스트|pass|green|확인|완성도|완전히|처음부터|바로\s*보이|노출|표시|end[- ]?to[- ]?end/.test(lower);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function promptHasTarget(text, lower) {
|
|
113
|
+
return /[`'"][^`'"]+[`'"]/.test(text)
|
|
114
|
+
|| /(?:^|\s)(?:src|bin|scripts|docs|README|CHANGELOG|package\.json|\.sneakoscope|\.agents|\.codex|[A-Za-z0-9_.-]+\/)[^\s,)]*/.test(text)
|
|
115
|
+
|| /\$[A-Za-z0-9_-]+/.test(text)
|
|
116
|
+
|| /(모호성|질문|파이프라인|게이트|라우트|화면|버튼|모달|디자인|레이아웃|컴포넌트|프론트|리드미|코덱스|결제|로그인|인증|세션|codex|route|pipeline|ambiguity|clarification|question|decision[- ]?contract|hyperplan|prometheus|ouroboros|openagent|payment|billing|auth|session|팀|team|qa|ppt|db|ui|ux|설치|버전|readme|changelog)/.test(lower);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function promptHasAction(lower) {
|
|
120
|
+
return /(구현|수정|개선|고쳐|만들|추가|삭제|정리|리팩터|바꿔|교체|재설계|처음부터|알려|보이게|보여|노출|표시|rebuild|rewrite|implement|fix|improve|add|remove|refactor|change|replace|redesign|reverse engineer)/.test(lower);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function promptIsUnderspecified(lower) {
|
|
124
|
+
const trimmed = lower.trim();
|
|
125
|
+
return trimmed.length < 12
|
|
126
|
+
|| /^(이거|저거|그거|뭔가|문제|고쳐줘|수정해줘|개선해줘|해줘|fix this|improve this|do it)\s*[.!?。]*$/.test(trimmed)
|
|
127
|
+
|| /^(이거|저거|그거)\s+(고쳐|수정|개선|해줘)/.test(trimmed);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function promptHasRisk(lower) {
|
|
131
|
+
return /(운영|production|prod|live|배포|publish|release|결제|payment|billing|auth|인증|보안|security|db|database|supabase|postgres|sql|schema|migration|마이그레이션|삭제|delete|drop|truncate|reset|권한|permission|credential|secret)/.test(lower);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function promptHasContextTarget(text, lower) {
|
|
135
|
+
return promptHasTarget(text, lower)
|
|
136
|
+
|| /https?:\/\/\S+/.test(text)
|
|
137
|
+
|| /(프로젝트|repo|repository|codebase|코드베이스|현재 코드|current code|기존|existing|local|로컬)/.test(lower);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function buildAmbiguityAssessment(prompt, explicitAnswers = {}) {
|
|
141
|
+
const text = String(prompt || '');
|
|
142
|
+
const lower = text.toLowerCase();
|
|
143
|
+
const target = promptHasTarget(text, lower) || hasAnswer(explicitAnswers.INTENT_TARGET) || hasAnswer(explicitAnswers.GOAL_PRECISE);
|
|
144
|
+
const action = promptHasAction(lower) || hasAnswer(explicitAnswers.REQUIRED_OUTCOME) || hasAnswer(explicitAnswers.GOAL_PRECISE);
|
|
145
|
+
const underspecified = promptIsUnderspecified(lower);
|
|
146
|
+
const acceptance = promptHasExplicitAcceptance(lower) || hasAnswer(explicitAnswers.ACCEPTANCE_CRITERIA) || hasAnswer(explicitAnswers.SUCCESS_CRITERIA_OR_ACCEPTANCE);
|
|
147
|
+
const risk = promptHasRisk(lower);
|
|
148
|
+
const contextTarget = promptHasContextTarget(text, lower) || hasAnswer(explicitAnswers.CODEBASE_CONTEXT_TARGET);
|
|
149
|
+
const predictableSafetyDefault = /(재시도|retry|세션\s*만료|session\s*expired|session\s*expiry|token\s*expired)/.test(lower);
|
|
150
|
+
const hasPolicy = hasAnswer(explicitAnswers.RISK_BOUNDARY) || hasAnswer(explicitAnswers.RISK_AND_BOUNDARY) || predictableSafetyDefault || /(하지\s*마|금지|no\s+|never|묻지|보존|preserve|safe|안전|검증|approval|승인|알아서|판단|추론|infer|default|기본)/.test(lower);
|
|
151
|
+
const hasMultipleChoiceRisk = /(\bor\b|또는|아니면|선택|둘 중|여러|multiple|대안)/.test(lower) && !/(알아서|판단|infer|추론|default|기본)/.test(lower);
|
|
152
|
+
|
|
153
|
+
const goalClarity = underspecified ? (target || action ? 0.45 : 0.2) : (target && action ? 0.9 : target || action ? 0.62 : 0.25);
|
|
154
|
+
const constraintClarity = risk ? (hasPolicy ? 0.78 : 0.64) : 0.82;
|
|
155
|
+
const successClarity = acceptance ? 0.86 : (target && action && !risk ? 0.73 : target && action ? 0.66 : 0.38);
|
|
156
|
+
const contextClarity = contextTarget ? 0.82 : (underspecified ? 0.35 : 0.62);
|
|
157
|
+
const components = {
|
|
158
|
+
goal: scoreComponent('goal_clarity', goalClarity, CLARITY_WEIGHTS.goal, target && action ? 'target and action are present' : 'target or action is missing'),
|
|
159
|
+
constraints: scoreComponent('constraint_clarity', constraintClarity, CLARITY_WEIGHTS.constraints, risk ? (hasPolicy ? 'risk cues include a policy boundary' : 'risk cues need a boundary') : 'no high-risk cue detected'),
|
|
160
|
+
success: scoreComponent('success_criteria_clarity', successClarity, CLARITY_WEIGHTS.success, acceptance ? 'success or verification language is explicit' : 'success criteria can be inferred only if goal/risk are clear enough'),
|
|
161
|
+
context: scoreComponent('context_clarity', contextClarity, CLARITY_WEIGHTS.context, contextTarget ? 'target context is named or discoverable' : 'target context is not discoverable from prompt')
|
|
162
|
+
};
|
|
163
|
+
const overall = Object.values(components).reduce((sum, item) => sum + item.ambiguity_contribution, 0);
|
|
164
|
+
const floorFailures = [];
|
|
165
|
+
if (components.goal.clarity_score < CLARITY_FLOORS.goal) floorFailures.push('goal_clarity');
|
|
166
|
+
if (components.constraints.clarity_score < CLARITY_FLOORS.constraints) floorFailures.push('constraint_clarity');
|
|
167
|
+
if (components.success.clarity_score < CLARITY_FLOORS.success) floorFailures.push('success_criteria_clarity');
|
|
168
|
+
if (components.context.clarity_score < CLARITY_FLOORS.context) floorFailures.push('context_clarity');
|
|
169
|
+
const unresolved = [];
|
|
170
|
+
if (components.goal.clarity_score < CLARITY_FLOORS.goal) unresolved.push('intent_target_or_required_outcome');
|
|
171
|
+
if (components.success.clarity_score < CLARITY_FLOORS.success && (!target || !action || risk)) unresolved.push('success_criteria_or_acceptance');
|
|
172
|
+
if (components.constraints.clarity_score < CLARITY_FLOORS.constraints || hasMultipleChoiceRisk) unresolved.push('risk_boundary_or_choice');
|
|
173
|
+
if (components.context.clarity_score < CLARITY_FLOORS.context) unresolved.push('codebase_context_target');
|
|
174
|
+
const uniqueUnresolved = [...new Set(unresolved)];
|
|
175
|
+
return {
|
|
176
|
+
schema_version: 1,
|
|
177
|
+
method: 'weighted_clarity_interview',
|
|
178
|
+
inspired_by: ['ouroboros_ambiguity_threshold', 'prometheus_interview_plan_first', 'hyperplan_adversarial_lenses'],
|
|
179
|
+
threshold: AMBIGUITY_READY_THRESHOLD,
|
|
180
|
+
overall_score: Number(overall.toFixed(3)),
|
|
181
|
+
ready_for_contract: overall <= AMBIGUITY_READY_THRESHOLD && floorFailures.length === 0,
|
|
182
|
+
component_floors_passed: floorFailures.length === 0,
|
|
183
|
+
floor_failures: floorFailures,
|
|
184
|
+
components,
|
|
185
|
+
unresolved_dimensions: uniqueUnresolved,
|
|
186
|
+
question_budget: risk ? 3 : 2,
|
|
187
|
+
adversarial_lenses: ['challenge_framing', 'subtract_unneeded_surface', 'demand_evidence', 'test_integration_risk', 'consider_simpler_alternative']
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
63
191
|
function addInferred(out, notes, id, value, note) {
|
|
64
192
|
if (!hasAnswer(value) && !(Array.isArray(value) && value.length === 0)) return;
|
|
65
193
|
out[id] = value;
|
|
@@ -78,6 +206,7 @@ function looksLikePresentationArtifactPrompt(lower) {
|
|
|
78
206
|
export function inferAnswersForPrompt(prompt, explicitAnswers = {}) {
|
|
79
207
|
const text = `${prompt || ''}\n${explicitAnswers.GOAL_PRECISE || ''}`;
|
|
80
208
|
const lower = text.toLowerCase();
|
|
209
|
+
const ambiguity = buildAmbiguityAssessment(prompt, explicitAnswers);
|
|
81
210
|
const inferred = {};
|
|
82
211
|
const notes = {};
|
|
83
212
|
const normalizedPrompt = String(prompt || '')
|
|
@@ -97,6 +226,8 @@ export function inferAnswersForPrompt(prompt, explicitAnswers = {}) {
|
|
|
97
226
|
const dbLocalWork = /\blocal\b|localhost|local_dev|dev\s*db|로컬|개발\s*db/.test(lower);
|
|
98
227
|
const dbPreviewWork = /preview|staging|branch|preview_branch|스테이징|프리뷰|브랜치/.test(lower);
|
|
99
228
|
const dbApplyMigrationWork = /(apply|run|execute|적용|실행).*(migration|migrate|마이그레이션)|((migration|migrate|마이그레이션).*(apply|run|execute|적용|실행))/.test(lower);
|
|
229
|
+
const paymentWork = /결제|payment|billing|invoice|checkout|order/.test(lower);
|
|
230
|
+
const authWork = /로그인|auth|session|token|인증/.test(lower);
|
|
100
231
|
const prioritySignalWork = /화|짜증|답답|;;|!!|강력|기억|우선|자주|반복|카운팅|count|frequency|frequent|priority|weight/.test(lower);
|
|
101
232
|
const cliSurfaceWork = /\b(cli|command|route|usage|help|sks)\b|명령|커맨드|사용법/.test(lower);
|
|
102
233
|
const chatCaptureWork = hasFromChatImgSignal(text)
|
|
@@ -120,26 +251,30 @@ export function inferAnswersForPrompt(prompt, explicitAnswers = {}) {
|
|
|
120
251
|
presentation: ['audience profile and STP strategy are explicit before artifact creation', 'target pain points map to proposed solution moments', 'decision context and likely objections are sealed before storyboarding', 'presentation format, device, and delivery context are fixed before design work'],
|
|
121
252
|
install: ['bootstrap/deps initialize readiness', 'missing runtime deps show repair actions', 'readiness output is concrete']
|
|
122
253
|
};
|
|
123
|
-
|
|
254
|
+
const explicitPromptedGoal = promptedGoalFromAnswers(explicitAnswers);
|
|
255
|
+
const canInferCoreGoal = explicitPromptedGoal || !ambiguity.unresolved_dimensions.includes('intent_target_or_required_outcome');
|
|
256
|
+
if (!hasAnswer(explicitAnswers.GOAL_PRECISE) && canInferCoreGoal) {
|
|
124
257
|
addInferred(
|
|
125
258
|
inferred,
|
|
126
259
|
notes,
|
|
127
260
|
'GOAL_PRECISE',
|
|
128
|
-
presentationWork ? goals.presentation : (kind ? goals[kind] : (normalizedPrompt ? `사용자 요청을 현재 코드 기준으로 구현한다: ${normalizedPrompt}` : '사용자 요청을 현재 코드 기준으로 구현한다')),
|
|
129
|
-
presentationWork ? 'presentation' : (kind || 'prompt-derived-goal')
|
|
261
|
+
explicitPromptedGoal || (presentationWork ? goals.presentation : (kind ? goals[kind] : (normalizedPrompt ? `사용자 요청을 현재 코드 기준으로 구현한다: ${normalizedPrompt}` : '사용자 요청을 현재 코드 기준으로 구현한다'))),
|
|
262
|
+
explicitPromptedGoal ? 'user-answered-dynamic-intent' : (presentationWork ? 'presentation' : (kind || 'prompt-derived-goal'))
|
|
130
263
|
);
|
|
131
264
|
}
|
|
132
|
-
|
|
265
|
+
const explicitAcceptance = explicitAnswers.SUCCESS_CRITERIA_OR_ACCEPTANCE;
|
|
266
|
+
const canInferAcceptance = hasAnswer(explicitAcceptance) || Boolean(kind || presentationWork || paymentWork || authWork) || !ambiguity.unresolved_dimensions.includes('success_criteria_or_acceptance');
|
|
267
|
+
if (!hasAnswer(explicitAnswers.ACCEPTANCE_CRITERIA) && canInferAcceptance) {
|
|
133
268
|
addInferred(
|
|
134
269
|
inferred,
|
|
135
270
|
notes,
|
|
136
271
|
'ACCEPTANCE_CRITERIA',
|
|
137
|
-
presentationWork ? criteria.presentation : (kind ? criteria[kind] : [
|
|
272
|
+
hasAnswer(explicitAcceptance) ? explicitAcceptance : (presentationWork ? criteria.presentation : (kind ? criteria[kind] : [
|
|
138
273
|
'requested behavior is implemented in the relevant code path',
|
|
139
274
|
'relevant tests/checks pass or any unavailable check is explicitly justified',
|
|
140
275
|
'final response states what was changed, verified, and left unverified'
|
|
141
|
-
]),
|
|
142
|
-
presentationWork ? 'presentation' : (kind || 'default-implementation-criteria')
|
|
276
|
+
])),
|
|
277
|
+
hasAnswer(explicitAcceptance) ? 'user-answered-dynamic-acceptance' : (presentationWork ? 'presentation' : (kind || 'default-implementation-criteria'))
|
|
143
278
|
);
|
|
144
279
|
}
|
|
145
280
|
|
|
@@ -155,11 +290,12 @@ export function inferAnswersForPrompt(prompt, explicitAnswers = {}) {
|
|
|
155
290
|
}
|
|
156
291
|
if (!hasAnswer(explicitAnswers.RISK_BOUNDARY)) {
|
|
157
292
|
addInferred(inferred, notes, 'RISK_BOUNDARY', [
|
|
293
|
+
...(hasAnswer(explicitAnswers.RISK_AND_BOUNDARY) ? [summarizeAnswer(explicitAnswers.RISK_AND_BOUNDARY)] : []),
|
|
158
294
|
'no npm publish unless explicitly requested',
|
|
159
295
|
'do not revert unrelated changes',
|
|
160
296
|
'no destructive commands or live data writes',
|
|
161
297
|
'no unrequested fallback implementation code'
|
|
162
|
-
], 'safety');
|
|
298
|
+
], hasAnswer(explicitAnswers.RISK_AND_BOUNDARY) ? 'user-answered-dynamic-risk-boundary' : 'safety');
|
|
163
299
|
}
|
|
164
300
|
if (uiuxWork) {
|
|
165
301
|
if (!hasAnswer(explicitAnswers.UI_STATE_BEHAVIOR)) {
|
|
@@ -204,6 +340,46 @@ export function inferAnswersForPrompt(prompt, explicitAnswers = {}) {
|
|
|
204
340
|
if (!hasAnswer(explicitAnswers.DB_MIGRATION_APPLY_ALLOWED)) addInferred(inferred, notes, 'DB_MIGRATION_APPLY_ALLOWED', migrationApplyAllowed, 'migration-apply-safe-default');
|
|
205
341
|
if (!hasAnswer(explicitAnswers.DB_READ_ONLY_QUERY_LIMIT)) addInferred(inferred, notes, 'DB_READ_ONLY_QUERY_LIMIT', '1000', 'read-only-query-limit-default');
|
|
206
342
|
}
|
|
343
|
+
if (paymentWork) {
|
|
344
|
+
if (!hasAnswer(explicitAnswers.PAYMENT_SUCCESS_INVARIANT)) {
|
|
345
|
+
addInferred(
|
|
346
|
+
inferred,
|
|
347
|
+
notes,
|
|
348
|
+
'PAYMENT_SUCCESS_INVARIANT',
|
|
349
|
+
'이미 성공 처리된 결제는 중복 승인, 중복 배송, 중복 포인트 지급, 중복 영수증 발행이 발생하면 안 됩니다. 성공 상태, 결제 금액, 주문 연결은 보존하고 후속 재시도는 멱등 처리합니다.',
|
|
350
|
+
'payment-safe-default'
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (!hasAnswer(explicitAnswers.PAYMENT_RETRY_POLICY)) {
|
|
354
|
+
addInferred(
|
|
355
|
+
inferred,
|
|
356
|
+
notes,
|
|
357
|
+
'PAYMENT_RETRY_POLICY',
|
|
358
|
+
'일시적 실패만 최대 3회 재시도하고 backoff는 1초, 3초, 10초로 증가시킵니다. 최종 실패 시 failed 상태로 확정하고 재시도 가능한 오류를 보여주며, 이미 성공한 결제는 재시도하지 않습니다.',
|
|
359
|
+
'payment-safe-default'
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (authWork) {
|
|
364
|
+
if (!hasAnswer(explicitAnswers.AUTH_SESSION_EXPIRED_BEHAVIOR)) {
|
|
365
|
+
addInferred(
|
|
366
|
+
inferred,
|
|
367
|
+
notes,
|
|
368
|
+
'AUTH_SESSION_EXPIRED_BEHAVIOR',
|
|
369
|
+
'세션/토큰 만료 시 API는 401을 반환하고 UI는 로그인 화면으로 이동하되, 가능하면 진행 중이던 작업 맥락과 return path를 보존합니다.',
|
|
370
|
+
'auth-safe-default'
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
if (!hasAnswer(explicitAnswers.AUTH_PROTOCOL_CHANGE_ALLOWED)) {
|
|
374
|
+
addInferred(
|
|
375
|
+
inferred,
|
|
376
|
+
notes,
|
|
377
|
+
'AUTH_PROTOCOL_CHANGE_ALLOWED',
|
|
378
|
+
'yes_if_needed',
|
|
379
|
+
'auth-safe-default'
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
207
383
|
return { answers: inferred, notes };
|
|
208
384
|
}
|
|
209
385
|
|
|
@@ -215,36 +391,47 @@ export function buildQuestionSchema(prompt) {
|
|
|
215
391
|
if (/\b(ui|modal|screen|button|visual|design|layout|component|prototype|frontend)\b|화면|버튼|모달|디자인|레이아웃|컴포넌트|프론트|시각|발표자료|디자인\s*시스템/.test(lower)) domainHints.push('uiux');
|
|
216
392
|
if (looksLikePresentationArtifactPrompt(lower)) domainHints.push('presentation');
|
|
217
393
|
if (/db|database|schema|migration|테이블|마이그레이션|supabase|postgres|sql/.test(lower)) domainHints.push('db');
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
{ id: 'PUBLIC_API_CHANGE_ALLOWED', question: 'public API 또는 외부 계약 변경을 허용하나요?', required: true, type: 'enum', options: ['no', 'yes_if_needed', 'yes'] },
|
|
223
|
-
{ id: 'DB_SCHEMA_CHANGE_ALLOWED', question: 'DB schema 또는 migration 변경을 허용하나요?', required: true, type: 'enum', options: ['no', 'yes_if_needed', 'yes_with_migration'] },
|
|
224
|
-
{ id: 'DEPENDENCY_CHANGE_ALLOWED', question: '새 dependency 추가를 허용하나요?', required: true, type: 'enum', options: ['no', 'yes_if_already_approved', 'yes'] },
|
|
225
|
-
{ id: 'TEST_SCOPE', question: '완료 전 실행 또는 정당화해야 할 테스트 범위를 지정해주세요.', required: true, type: 'array_or_string', examples: ['unit', 'integration', 'e2e', 'lint', 'typecheck'] },
|
|
226
|
-
{ id: 'MID_RUN_UNKNOWN_POLICY', question: '실행 중 새 모호성이 생기면 사용자에게 묻지 않고 어떤 해결 순서로 판단할까요? 이 항목은 대체 구현 또는 fallback 코드를 새로 만드는 허가가 아닙니다.', required: true, type: 'array', options: ['preserve_existing_behavior', 'smallest_reversible_change', 'defer_optional_scope', 'block_only_if_no_safe_path'] },
|
|
227
|
-
{ id: 'RISK_BOUNDARY', question: '보안, 결제, 데이터 손상, 권한, 인증 등 절대 넘으면 안 되는 위험 경계를 적어주세요.', required: true, type: 'array_or_string' },
|
|
228
|
-
|
|
229
|
-
{ id: 'DATABASE_TARGET_ENVIRONMENT', question: 'DB 관련 작업의 대상 환경을 지정해주세요. production write는 Sneakoscope Codex가 허용하지 않습니다.', required: true, type: 'enum', options: ['no_database', 'local_dev', 'preview_branch', 'supabase_branch', 'production_read_only'] },
|
|
230
|
-
{ id: 'DATABASE_WRITE_MODE', question: 'DB 쓰기 정책을 선택해주세요. Supabase/Postgres MCP live write는 기본 차단됩니다.', required: true, type: 'enum', options: ['read_only_only', 'migration_files_only', 'non_destructive_writes_to_local_or_branch_only'] },
|
|
231
|
-
{ id: 'SUPABASE_MCP_POLICY', question: 'Supabase MCP를 사용한다면 어떤 안전 정책을 적용할까요?', required: true, type: 'enum', options: ['not_used', 'read_only_project_scoped_only', 'branch_only_no_live_writes'] },
|
|
232
|
-
{ id: 'DESTRUCTIVE_DB_OPERATIONS_ALLOWED', question: 'DROP/TRUNCATE/DB reset/mass DELETE/branch reset/project delete 같은 파괴적 DB 작업을 허용하나요? Sneakoscope Codex는 never만 허용합니다.', required: true, type: 'enum', options: ['never'] },
|
|
233
|
-
{ id: 'DB_BACKUP_OR_BRANCH_REQUIRED', question: 'DB 쓰기가 필요한 경우 local/preview branch 또는 백업이 있어야만 진행하도록 할까요?', required: true, type: 'enum', options: ['yes_for_any_write'] },
|
|
234
|
-
{ id: 'DB_MAX_BLAST_RADIUS', question: 'DML이 꼭 필요한 경우 허용 가능한 최대 영향 범위를 적어주세요. 기본 권장값은 no_live_dml입니다.', required: true, type: 'string' }
|
|
235
|
-
];
|
|
236
|
-
if (domainHints.includes('payment')) {
|
|
394
|
+
const ambiguity = buildAmbiguityAssessment(prompt);
|
|
395
|
+
const slots = [];
|
|
396
|
+
const presentationSpecific = domainHints.includes('presentation');
|
|
397
|
+
if (!presentationSpecific && ambiguity.unresolved_dimensions.includes('intent_target_or_required_outcome')) {
|
|
237
398
|
slots.push(
|
|
238
|
-
{ id: '
|
|
239
|
-
{ id: 'PAYMENT_RETRY_POLICY', question: '재시도 횟수, backoff, 실패 최종 상태 정책을 지정해주세요.', required: true, type: 'string' }
|
|
399
|
+
{ id: 'INTENT_TARGET', question: '실제로 바꿀 대상과 원하는 결과를 한 문장으로만 적어주세요. 파일/화면/기능명이 있으면 같이 적어주세요.', required: true, type: 'string' }
|
|
240
400
|
);
|
|
241
401
|
}
|
|
242
|
-
if (
|
|
402
|
+
if (!presentationSpecific && ambiguity.unresolved_dimensions.includes('success_criteria_or_acceptance')) {
|
|
403
|
+
slots.push(
|
|
404
|
+
{ id: 'SUCCESS_CRITERIA_OR_ACCEPTANCE', question: '완료라고 판단할 수 있는 관찰 가능한 기준을 1~3개만 적어주세요. 모르면 “현재 코드 기준으로 판단”이라고 적어도 됩니다.', required: true, type: 'array_or_string' }
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
if (ambiguity.unresolved_dimensions.includes('risk_boundary_or_choice')) {
|
|
408
|
+
slots.push(
|
|
409
|
+
{ id: 'RISK_AND_BOUNDARY', question: '여러 선택지가 있거나 위험한 변경이 있다면 반드시 지켜야 할 경계만 적어주세요. 없으면 “기존 동작 보존, 파괴적 작업 금지”라고 답해주세요.', required: true, type: 'string' }
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
if (ambiguity.unresolved_dimensions.includes('codebase_context_target')) {
|
|
243
413
|
slots.push(
|
|
244
|
-
{ id: '
|
|
245
|
-
{ id: 'AUTH_PROTOCOL_CHANGE_ALLOWED', question: '인증 프로토콜 변경을 허용하나요?', required: true, type: 'enum', options: ['no', 'yes_if_needed', 'yes'] }
|
|
414
|
+
{ id: 'CODEBASE_CONTEXT_TARGET', question: '이 요청이 가리키는 repo/브랜치/화면/파일/최근 오류 맥락을 알려주세요.', required: true, type: 'string' }
|
|
246
415
|
);
|
|
247
416
|
}
|
|
417
|
+
if (domainHints.includes('payment')) {
|
|
418
|
+
const inferred = inferAnswersForPrompt(prompt);
|
|
419
|
+
if (!hasAnswer(inferred.answers.PAYMENT_SUCCESS_INVARIANT)) {
|
|
420
|
+
slots.push({ id: 'PAYMENT_SUCCESS_INVARIANT', question: '이미 성공 처리된 결제에 대해서는 어떤 invariant를 보존해야 하나요?', required: true, type: 'string' });
|
|
421
|
+
}
|
|
422
|
+
if (!hasAnswer(inferred.answers.PAYMENT_RETRY_POLICY)) {
|
|
423
|
+
slots.push({ id: 'PAYMENT_RETRY_POLICY', question: '재시도 횟수, backoff, 실패 최종 상태 정책을 지정해주세요.', required: true, type: 'string' });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (domainHints.includes('auth')) {
|
|
427
|
+
const inferred = inferAnswersForPrompt(prompt);
|
|
428
|
+
if (!hasAnswer(inferred.answers.AUTH_SESSION_EXPIRED_BEHAVIOR)) {
|
|
429
|
+
slots.push({ id: 'AUTH_SESSION_EXPIRED_BEHAVIOR', question: '세션/토큰 만료 시 사용자가 보게 될 UX 또는 API 동작을 지정해주세요.', required: true, type: 'string' });
|
|
430
|
+
}
|
|
431
|
+
if (!hasAnswer(inferred.answers.AUTH_PROTOCOL_CHANGE_ALLOWED)) {
|
|
432
|
+
slots.push({ id: 'AUTH_PROTOCOL_CHANGE_ALLOWED', question: '인증 프로토콜 변경을 허용하나요?', required: true, type: 'enum', options: ['no', 'yes_if_needed', 'yes'] });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
248
435
|
if (domainHints.includes('uiux')) {
|
|
249
436
|
slots.push(
|
|
250
437
|
{ id: 'UI_STATE_BEHAVIOR', question: '로딩, 에러, 빈 상태, 재시도 등 UI 상태별 기대 동작을 지정해주세요.', required: true, type: 'string' },
|
|
@@ -266,15 +453,22 @@ export function buildQuestionSchema(prompt) {
|
|
|
266
453
|
{ id: 'DB_READ_ONLY_QUERY_LIMIT', question: 'MCP/SQL read-only 조회 시 기본 LIMIT를 몇으로 둘까요?', required: true, type: 'string' }
|
|
267
454
|
);
|
|
268
455
|
}
|
|
269
|
-
const skippedByDefault = new RegExp('^(D' + 'B_|D' + 'ATABASE_|D' + 'ESTRUCTIVE_D' + 'B_|SUPA' + 'BASE_)');
|
|
270
456
|
const inferred = inferAnswersForPrompt(prompt);
|
|
271
|
-
const inferredSlots = new Set(
|
|
272
|
-
const askedSlots = slots
|
|
457
|
+
const inferredSlots = new Set(Object.keys(inferred.answers));
|
|
458
|
+
const askedSlots = slots
|
|
459
|
+
.filter((s) => {
|
|
460
|
+
if (inferredSlots.has(s.id)) return false;
|
|
461
|
+
if (s.id === 'INTENT_TARGET' && hasAnswer(inferred.answers.GOAL_PRECISE)) return false;
|
|
462
|
+
if (s.id === 'SUCCESS_CRITERIA_OR_ACCEPTANCE' && hasAnswer(inferred.answers.ACCEPTANCE_CRITERIA)) return false;
|
|
463
|
+
return true;
|
|
464
|
+
})
|
|
465
|
+
.slice(0, domainHints.includes('presentation') ? slots.length : ambiguity.question_budget);
|
|
273
466
|
return {
|
|
274
|
-
schema_version:
|
|
275
|
-
description: '
|
|
467
|
+
schema_version: 2,
|
|
468
|
+
description: 'SKS scores goal, constraints, success criteria, and codebase context first, then asks only the lowest-clarity questions that can change execution. The rest is inferred from the prompt, TriWiki/current-code defaults, and conservative SKS safety policy. After the contract is sealed, SKS resolves with the decision ladder instead of asking mid-run questions.',
|
|
276
469
|
prompt,
|
|
277
470
|
domain_hints: domainHints,
|
|
471
|
+
ambiguity_assessment: ambiguity,
|
|
278
472
|
inferred_answers: inferred.answers,
|
|
279
473
|
inference_notes: inferred.notes,
|
|
280
474
|
slots: askedSlots
|
|
@@ -297,6 +491,15 @@ export function questionsMarkdown(schema) {
|
|
|
297
491
|
lines.push('사용자 의도가 실제로 모호한 항목만 묻고, 나머지는 TriWiki/current-code 기본값으로 추론합니다.');
|
|
298
492
|
}
|
|
299
493
|
if (schema.description) lines.push(schema.description);
|
|
494
|
+
if (schema.ambiguity_assessment) {
|
|
495
|
+
lines.push('');
|
|
496
|
+
lines.push('## Ambiguity Assessment');
|
|
497
|
+
lines.push('');
|
|
498
|
+
lines.push(`- method: ${schema.ambiguity_assessment.method}`);
|
|
499
|
+
lines.push(`- score: ${schema.ambiguity_assessment.overall_score} (ready threshold <= ${schema.ambiguity_assessment.threshold})`);
|
|
500
|
+
lines.push(`- unresolved dimensions: ${(schema.ambiguity_assessment.unresolved_dimensions || []).join(', ') || 'none'}`);
|
|
501
|
+
lines.push(`- question budget: ${schema.ambiguity_assessment.question_budget}`);
|
|
502
|
+
}
|
|
300
503
|
if (schema.inferred_answers && Object.keys(schema.inferred_answers).length) {
|
|
301
504
|
lines.push('');
|
|
302
505
|
lines.push('## Inferred Answers');
|
package/src/core/routes.mjs
CHANGED
|
@@ -65,6 +65,32 @@ export const AWESOME_DESIGN_MD_REFERENCE = {
|
|
|
65
65
|
|
|
66
66
|
export const RECOMMENDED_DESIGN_REFERENCES = [GETDESIGN_REFERENCE, AWESOME_DESIGN_MD_REFERENCE];
|
|
67
67
|
|
|
68
|
+
export const PPT_PIPELINE_SKILL_ALLOWLIST = Object.freeze([
|
|
69
|
+
'ppt',
|
|
70
|
+
'getdesign-reference',
|
|
71
|
+
'prompt-pipeline',
|
|
72
|
+
REFLECTION_SKILL_NAME,
|
|
73
|
+
'honest-mode'
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
export const PPT_CONDITIONAL_SKILL_ALLOWLIST = Object.freeze([
|
|
77
|
+
{
|
|
78
|
+
skill: 'imagegen',
|
|
79
|
+
condition: 'only_when_the_sealed_ppt_contract_explicitly_requires_generated_raster_assets'
|
|
80
|
+
}
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
export const PPT_PIPELINE_MCP_ALLOWLIST = Object.freeze([
|
|
84
|
+
{
|
|
85
|
+
mcp: 'context7',
|
|
86
|
+
condition: 'only_when_current_external_documentation_is_required_for_sources_or_package_api_usage'
|
|
87
|
+
}
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
export function pptPipelineAllowlistPolicyText() {
|
|
91
|
+
return `PPT pipeline allowlist: during $PPT design/render work, ignore installed skills and MCPs that are not explicitly part of the $PPT pipeline. The purpose is to prevent AI-like generic presentation design: decorative gradients, nested cards, vague SaaS visuals, and style choices not grounded in the audience, source material, getdesign reference, or the project design SSOT. Required skills are ${PPT_PIPELINE_SKILL_ALLOWLIST.join(', ')}. Do not use generic design skills such as design-artifact-expert, design-ui-editor, or design-system-builder for $PPT just because they are installed. $PPT design must use getdesign-reference plus the built-in PPT design implementation pipeline: ${DESIGN_SYSTEM_SSOT.authority_file} when present, ${DESIGN_SYSTEM_SSOT.builder_prompt} as the builder prompt when missing, and route-local ppt-style-tokens.json as the fused design projection. Conditional skills/MCPs are allowed only when their condition is sealed in the contract: ${PPT_CONDITIONAL_SKILL_ALLOWLIST.map((entry) => `${entry.skill}=${entry.condition}`).join('; ')}; ${PPT_PIPELINE_MCP_ALLOWLIST.map((entry) => `${entry.mcp}=${entry.condition}`).join('; ')}.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
export function getdesignReferencePolicyText() {
|
|
69
95
|
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.`;
|
|
70
96
|
}
|
|
@@ -267,7 +293,7 @@ export const ROUTES = [
|
|
|
267
293
|
mode: 'PPT',
|
|
268
294
|
route: 'HTML/PDF presentation pipeline',
|
|
269
295
|
description: 'Create restrained, information-first HTML/PDF presentation artifacts after delivery context, audience profile, STP, decision context, pain-point, research, design-system, and verification questions are sealed.',
|
|
270
|
-
requiredSkills: [
|
|
296
|
+
requiredSkills: [...PPT_PIPELINE_SKILL_ALLOWLIST],
|
|
271
297
|
lifecycle: ['stp_audience_questions', 'audience_strategy_artifact', 'contract_sealed', 'source_ledger', 'storyboard_aha_moments', 'design_system', 'html_artifact', 'pdf_export', 'render_qa', 'post_route_reflection', 'honest_mode'],
|
|
272
298
|
context7Policy: 'if_external_docs',
|
|
273
299
|
reasoningPolicy: 'high',
|