supipowers 1.5.3 → 2.0.1
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 +14 -8
- package/bin/install.mjs +20 -5
- package/bin/install.ts +95 -0
- package/package.json +8 -4
- package/skills/context-mode/SKILL.md +17 -10
- package/skills/harness/SKILL.md +94 -0
- package/skills/ui-design/SKILL.md +63 -0
- package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
- package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
- package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
- package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
- package/skills/ultraplan-discover/SKILL.md +96 -0
- package/skills/ultraplan-intake/SKILL.md +89 -0
- package/skills/ultraplan-research/SKILL.md +129 -0
- package/skills/ultraplan-review/SKILL.md +86 -0
- package/skills/ultraplan-review-scope/SKILL.md +111 -0
- package/skills/ultraplan-review-structure/SKILL.md +120 -0
- package/skills/ultraplan-review-tdd/SKILL.md +142 -0
- package/skills/ultraplan-scout/SKILL.md +110 -0
- package/skills/ultraplan-synthesize/SKILL.md +124 -0
- package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
- package/src/ai/schema-text.ts +129 -0
- package/src/ai/structured-output.ts +274 -0
- package/src/ai/template.ts +27 -0
- package/src/bootstrap.ts +63 -28
- package/src/commands/agents.ts +131 -42
- package/src/commands/ai-review.ts +251 -30
- package/src/commands/clear.ts +434 -0
- package/src/commands/commit.ts +1 -0
- package/src/commands/config.ts +242 -44
- package/src/commands/context.ts +55 -28
- package/src/commands/doctor.ts +234 -6
- package/src/commands/fix-pr.ts +306 -131
- package/src/commands/generate.ts +111 -21
- package/src/commands/memory.ts +192 -0
- package/src/commands/model-picker.ts +28 -21
- package/src/commands/model.ts +18 -8
- package/src/commands/optimize-context.ts +408 -29
- package/src/commands/plan.ts +2 -0
- package/src/commands/qa.ts +312 -137
- package/src/commands/release.ts +259 -76
- package/src/commands/review.ts +293 -59
- package/src/commands/status.ts +200 -13
- package/src/commands/supi.ts +3 -35
- package/src/commands/ui-design.ts +394 -0
- package/src/commands/ultraplan.ts +1518 -0
- package/src/commands/update.ts +86 -0
- package/src/config/defaults.ts +62 -0
- package/src/config/loader.ts +448 -60
- package/src/config/schema.ts +108 -2
- package/src/context/optimizer.ts +25 -33
- package/src/context/rule-renderer.ts +223 -0
- package/src/context/savings.ts +258 -0
- package/src/context/startup-check.ts +380 -0
- package/src/context/startup-optimizer.ts +355 -0
- package/src/context/tokenignore.ts +146 -0
- package/src/context-mode/cache-handle.ts +49 -0
- package/src/context-mode/cache-preview.ts +71 -0
- package/src/context-mode/cache-store.ts +738 -0
- package/src/context-mode/compressor.ts +131 -26
- package/src/context-mode/dedup.ts +108 -0
- package/src/context-mode/detector.ts +35 -4
- package/src/context-mode/event-extractor.ts +14 -12
- package/src/context-mode/event-store.ts +91 -36
- package/src/context-mode/hooks.ts +798 -56
- package/src/context-mode/knowledge/store.ts +255 -11
- package/src/context-mode/memory-store.ts +325 -0
- package/src/context-mode/metrics-recorder.ts +158 -0
- package/src/context-mode/metrics-store.ts +765 -0
- package/src/context-mode/model.ts +24 -0
- package/src/context-mode/processor-keys.ts +29 -0
- package/src/context-mode/processors/build.ts +66 -0
- package/src/context-mode/processors/docker.ts +57 -0
- package/src/context-mode/processors/git.ts +111 -0
- package/src/context-mode/processors/json.ts +112 -0
- package/src/context-mode/processors/k8s.ts +67 -0
- package/src/context-mode/processors/lint.ts +67 -0
- package/src/context-mode/processors/log.ts +86 -0
- package/src/context-mode/processors/registry.ts +116 -0
- package/src/context-mode/processors/test-runner.ts +102 -0
- package/src/context-mode/processors/types.ts +20 -0
- package/src/context-mode/repomap.ts +400 -0
- package/src/context-mode/routing.ts +97 -24
- package/src/context-mode/sandbox/runners.ts +5 -1
- package/src/context-mode/snapshot-builder.ts +106 -11
- package/src/context-mode/source-hash.ts +173 -0
- package/src/context-mode/tool-name.ts +11 -0
- package/src/context-mode/tools.ts +654 -22
- package/src/context-mode/web/fetcher.ts +31 -12
- package/src/debug/logger.ts +2 -1
- package/src/deps/registry.ts +1 -1
- package/src/discipline/failure-summarizer.ts +170 -0
- package/src/discipline/failure-taxonomy.ts +131 -0
- package/src/discipline/workflow-invariants.ts +125 -0
- package/src/discovery/index.ts +31 -0
- package/src/discovery/lsp.ts +87 -0
- package/src/discovery/rank.ts +144 -0
- package/src/discovery/sources.ts +89 -0
- package/src/discovery/workflow.ts +87 -0
- package/src/docs/contracts.ts +39 -0
- package/src/docs/drift.ts +117 -87
- package/src/fix-pr/assessment.ts +200 -0
- package/src/fix-pr/contracts.ts +47 -0
- package/src/fix-pr/fetch-comments.ts +80 -0
- package/src/fix-pr/prompt-builder.ts +58 -40
- package/src/fix-pr/scripts/exec.ts +34 -0
- package/src/fix-pr/scripts/trigger-review.ts +106 -0
- package/src/fix-pr/scripts/wait-and-check.ts +108 -0
- package/src/fix-pr/types.ts +4 -0
- package/src/git/branch-finish.ts +5 -0
- package/src/git/commit-contract.ts +83 -0
- package/src/git/commit.ts +121 -184
- package/src/git/status.ts +62 -8
- package/src/harness/anti_slop/architecture-parser.ts +210 -0
- package/src/harness/anti_slop/backend-factory.ts +30 -0
- package/src/harness/anti_slop/backend.ts +140 -0
- package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
- package/src/harness/anti_slop/fallow-adapter.ts +305 -0
- package/src/harness/anti_slop/installer.ts +227 -0
- package/src/harness/anti_slop/queue.ts +216 -0
- package/src/harness/anti_slop/recommend.ts +84 -0
- package/src/harness/anti_slop/score.ts +180 -0
- package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
- package/src/harness/artifacts/agents-md.ts +88 -0
- package/src/harness/artifacts/checks-wiring.ts +57 -0
- package/src/harness/artifacts/docs-tree.ts +79 -0
- package/src/harness/artifacts/lint-configs.ts +136 -0
- package/src/harness/artifacts/review-agents.ts +67 -0
- package/src/harness/bare-entry.ts +108 -0
- package/src/harness/command.ts +1010 -0
- package/src/harness/default-agents/design.md +23 -0
- package/src/harness/default-agents/discover.md +18 -0
- package/src/harness/default-agents/implement.md +24 -0
- package/src/harness/default-agents/plan.md +19 -0
- package/src/harness/default-agents/research.md +21 -0
- package/src/harness/default-agents/validate.md +22 -0
- package/src/harness/gc/reporter.ts +28 -0
- package/src/harness/gc/runner.ts +136 -0
- package/src/harness/hooks/layer-context-inject.ts +155 -0
- package/src/harness/hooks/post-session-sweep.ts +130 -0
- package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
- package/src/harness/hooks/register.ts +118 -0
- package/src/harness/model.ts +117 -0
- package/src/harness/pipeline.ts +348 -0
- package/src/harness/project-paths.ts +235 -0
- package/src/harness/stage-runner.ts +107 -0
- package/src/harness/stages/design.ts +386 -0
- package/src/harness/stages/discover.ts +454 -0
- package/src/harness/stages/implement.ts +162 -0
- package/src/harness/stages/plan.ts +335 -0
- package/src/harness/stages/research.ts +263 -0
- package/src/harness/stages/validate.ts +684 -0
- package/src/harness/storage.ts +467 -0
- package/src/harness/tools.ts +426 -0
- package/src/lsp/bridge.ts +56 -95
- package/src/lsp/capabilities.ts +108 -0
- package/src/lsp/contracts.ts +35 -0
- package/src/lsp/detector.ts +8 -12
- package/src/markdown-frontmatter.ts +68 -0
- package/src/mempalace/bridge.ts +135 -0
- package/src/mempalace/config.ts +75 -0
- package/src/mempalace/format.ts +163 -0
- package/src/mempalace/hooks.ts +370 -0
- package/src/mempalace/installer-helper.ts +194 -0
- package/src/mempalace/python/mempalace_bridge.py +440 -0
- package/src/mempalace/runtime.ts +565 -0
- package/src/mempalace/schema.ts +268 -0
- package/src/mempalace/session-summary.ts +198 -0
- package/src/mempalace/tool.ts +186 -0
- package/src/mempalace/uv.ts +256 -0
- package/src/migrate/runner.ts +354 -0
- package/src/planning/approval-flow.ts +206 -9
- package/src/planning/plan-writer-prompt.ts +4 -3
- package/src/planning/planning-ask-tool.ts +39 -0
- package/src/planning/render-markdown.ts +74 -0
- package/src/planning/spec.ts +42 -0
- package/src/planning/system-prompt.ts +11 -8
- package/src/planning/validate.ts +84 -0
- package/src/platform/omp.ts +15 -2
- package/src/platform/system-prompt.ts +37 -0
- package/src/platform/test-utils.ts +3 -0
- package/src/platform/types.ts +6 -1
- package/src/qa/config.ts +12 -6
- package/src/qa/detect-app-type.ts +13 -6
- package/src/qa/matrix.ts +12 -6
- package/src/qa/prompt-builder.ts +28 -30
- package/src/qa/scripts/dev-server-utils.ts +72 -0
- package/src/qa/scripts/run-e2e-tests.ts +226 -0
- package/src/qa/scripts/start-dev-server.ts +138 -0
- package/src/qa/scripts/stop-dev-server.ts +77 -0
- package/src/qa/session.ts +13 -7
- package/src/quality/ai-setup.ts +27 -25
- package/src/quality/contracts.ts +34 -0
- package/src/quality/gates/ai-review.ts +20 -58
- package/src/quality/gates/command.ts +249 -46
- package/src/quality/review-gates.ts +18 -2
- package/src/quality/runner.ts +63 -22
- package/src/quality/schemas.ts +37 -2
- package/src/quality/setup.ts +96 -16
- package/src/release/changelog.ts +1 -1
- package/src/release/channels/custom.ts +13 -3
- package/src/release/channels/types.ts +5 -0
- package/src/release/contracts.ts +90 -0
- package/src/release/executor.ts +122 -45
- package/src/release/prompt.ts +18 -2
- package/src/release/targets.ts +86 -0
- package/src/release/version.ts +96 -71
- package/src/review/agent-loader.ts +221 -109
- package/src/review/fixer.ts +10 -6
- package/src/review/multi-agent-runner.ts +114 -13
- package/src/review/output.ts +12 -139
- package/src/review/runner.ts +12 -6
- package/src/review/scope.ts +144 -24
- package/src/review/types.ts +1 -20
- package/src/review/validator.ts +12 -6
- package/src/storage/fix-pr-sessions.ts +21 -14
- package/src/storage/plans.ts +14 -5
- package/src/storage/qa-sessions.ts +25 -19
- package/src/storage/reliability-metrics.ts +180 -0
- package/src/storage/reports.ts +8 -7
- package/src/storage/review-sessions.ts +55 -20
- package/src/tool-catalog/active-tool-controller.ts +164 -0
- package/src/tool-catalog/active-tool-planner.ts +212 -0
- package/src/tool-catalog/tool-groups.ts +102 -0
- package/src/types.ts +1399 -5
- package/src/ui-design/backend-adapter.ts +78 -0
- package/src/ui-design/backends/local-html.ts +82 -0
- package/src/ui-design/backends/pencil-mcp.ts +111 -0
- package/src/ui-design/components-scanner.ts +124 -0
- package/src/ui-design/config.ts +55 -0
- package/src/ui-design/pen-scanner.ts +95 -0
- package/src/ui-design/pen-selector.ts +72 -0
- package/src/ui-design/prompt-builder.ts +73 -0
- package/src/ui-design/scanner.ts +136 -0
- package/src/ui-design/session.ts +974 -0
- package/src/ui-design/system-prompt.ts +312 -0
- package/src/ui-design/tokens-scanner.ts +181 -0
- package/src/ui-design/types.ts +96 -0
- package/src/ultraplan/agent-catalog.ts +522 -0
- package/src/ultraplan/authoring/agent-catalog.ts +310 -0
- package/src/ultraplan/authoring/authoring-tools.ts +552 -0
- package/src/ultraplan/authoring/command-handlers.ts +339 -0
- package/src/ultraplan/authoring/markdown.ts +510 -0
- package/src/ultraplan/authoring/model.ts +162 -0
- package/src/ultraplan/authoring/pipeline.ts +319 -0
- package/src/ultraplan/authoring/stage-runner.ts +141 -0
- package/src/ultraplan/authoring/stages/approve.ts +249 -0
- package/src/ultraplan/authoring/stages/discover.ts +289 -0
- package/src/ultraplan/authoring/stages/intake.ts +203 -0
- package/src/ultraplan/authoring/stages/research.ts +399 -0
- package/src/ultraplan/authoring/stages/review.ts +333 -0
- package/src/ultraplan/authoring/stages/scout.ts +188 -0
- package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
- package/src/ultraplan/authoring/storage.ts +594 -0
- package/src/ultraplan/authoring/synth-gate.ts +165 -0
- package/src/ultraplan/authoring-draft.ts +653 -0
- package/src/ultraplan/authoring-persist.ts +180 -0
- package/src/ultraplan/authoring-tool.ts +608 -0
- package/src/ultraplan/authoring-wizard.ts +587 -0
- package/src/ultraplan/batch/merge.ts +98 -0
- package/src/ultraplan/batch/planner.ts +150 -0
- package/src/ultraplan/batch/presenter.ts +97 -0
- package/src/ultraplan/batch/storage.ts +420 -0
- package/src/ultraplan/batch/supervisor.ts +317 -0
- package/src/ultraplan/batch/worker.ts +26 -0
- package/src/ultraplan/batch/worktree.ts +110 -0
- package/src/ultraplan/contracts.ts +1593 -0
- package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
- package/src/ultraplan/default-agents/authoring/intake.md +12 -0
- package/src/ultraplan/default-agents/authoring/planner.md +12 -0
- package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
- package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/scout.md +12 -0
- package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
- package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-executor.md +10 -0
- package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-tester.md +10 -0
- package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-executor.md +10 -0
- package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-tester.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
- package/src/ultraplan/execution/contract.ts +71 -0
- package/src/ultraplan/execution/policy.ts +217 -0
- package/src/ultraplan/execution/runtime-tools.ts +107 -0
- package/src/ultraplan/execution/session-runner.ts +281 -0
- package/src/ultraplan/next-router.ts +85 -0
- package/src/ultraplan/presenter.ts +359 -0
- package/src/ultraplan/project-paths.ts +342 -0
- package/src/ultraplan/runtime/active-execution.ts +72 -0
- package/src/ultraplan/runtime/apply-mutation.ts +416 -0
- package/src/ultraplan/runtime/blockers.ts +243 -0
- package/src/ultraplan/runtime/hook-bridge.ts +486 -0
- package/src/ultraplan/runtime/launch-context.ts +207 -0
- package/src/ultraplan/runtime/migration.ts +524 -0
- package/src/ultraplan/runtime/normalize.ts +281 -0
- package/src/ultraplan/runtime/proof.ts +260 -0
- package/src/ultraplan/runtime/reducer.ts +416 -0
- package/src/ultraplan/runtime/repair.ts +251 -0
- package/src/ultraplan/runtime/tracker-storage.ts +368 -0
- package/src/ultraplan/session-selection.ts +291 -0
- package/src/ultraplan/storage.ts +374 -0
- package/src/utils/editor.ts +38 -0
- package/src/utils/executable.ts +80 -0
- package/src/utils/paths.ts +1 -20
- package/src/utils/shell.ts +31 -0
- package/src/visual/companion.ts +2 -1
- package/src/visual/scripts/frame-template.html +60 -0
- package/src/visual/scripts/index.js +59 -13
- package/src/visual/scripts/package.json +3 -0
- package/src/visual/start-server.ts +2 -1
- package/src/workspace/git-scope.ts +64 -0
- package/src/workspace/locks.ts +23 -0
- package/src/workspace/package-manager.ts +117 -0
- package/src/workspace/path-mapping.ts +75 -0
- package/src/workspace/project-slug.ts +92 -0
- package/src/workspace/repo-root.ts +137 -0
- package/src/workspace/selector.ts +115 -0
- package/src/workspace/state-paths.ts +118 -0
- package/src/workspace/targets.ts +313 -0
- package/src/fix-pr/scripts/diff-comments.sh +0 -33
- package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
- package/src/fix-pr/scripts/trigger-review.sh +0 -36
- package/src/fix-pr/scripts/wait-and-check.sh +0 -37
- package/src/qa/scripts/detect-app-type.sh +0 -68
- package/src/qa/scripts/discover-routes.sh +0 -143
- package/src/qa/scripts/run-e2e-tests.sh +0 -131
- package/src/qa/scripts/start-dev-server.sh +0 -46
- package/src/qa/scripts/stop-dev-server.sh +0 -36
- package/src/review/prompts/fix-output-schema.md +0 -18
- package/src/review/prompts/review-output-schema.md +0 -38
- package/src/review/template.ts +0 -15
- /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import type { Platform } from "../platform/types.js";
|
|
2
|
+
import { systemPromptText } from "../platform/system-prompt.js";
|
|
3
|
+
import type { UiDesignBackendId, UiDesignScope } from "./types.js";
|
|
4
|
+
import { getActiveUiDesignSession, isUiDesignActive } from "./session.js";
|
|
5
|
+
|
|
6
|
+
export interface UiDesignSystemPromptOptions {
|
|
7
|
+
skillContent?: string;
|
|
8
|
+
subAgentTemplates?: { name: string; content: string }[];
|
|
9
|
+
dotDirDisplay: string;
|
|
10
|
+
topic?: string;
|
|
11
|
+
scope?: UiDesignScope;
|
|
12
|
+
contextScanSummary: string;
|
|
13
|
+
sessionDir: string;
|
|
14
|
+
companionUrl: string;
|
|
15
|
+
backend: UiDesignBackendId;
|
|
16
|
+
/** Absolute path to the .pen file — required when backend is pencil-mcp. */
|
|
17
|
+
penFilePath?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const NOW_MARKER = "═══════════Now═══════════";
|
|
21
|
+
const SKILLS_HEADER = "# Skills\n";
|
|
22
|
+
const TOOLS_HEADER = "# Tools\n";
|
|
23
|
+
|
|
24
|
+
const STRIP_TAGS = [
|
|
25
|
+
"default-follow-through",
|
|
26
|
+
"behavior",
|
|
27
|
+
"code-integrity",
|
|
28
|
+
"stakes",
|
|
29
|
+
"tool-persistence",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
function escapeRegex(value: string): string {
|
|
33
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function stripTagSection(prompt: string, tag: string): string {
|
|
37
|
+
return prompt.replace(new RegExp(`\\n*<${tag}>[\\s\\S]*?<\\/${tag}>\\n*`, "g"), "\n\n");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stripBetween(prompt: string, start: string, end: string): string {
|
|
41
|
+
return prompt.replace(new RegExp(`${escapeRegex(start)}[\\s\\S]*?(?=${escapeRegex(end)})`), "");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function insertBeforeMarker(prompt: string, marker: string, section: string): string {
|
|
45
|
+
const markerIndex = prompt.indexOf(marker);
|
|
46
|
+
if (markerIndex === -1) {
|
|
47
|
+
return `${prompt.trimEnd()}\n\n${section}`.trim();
|
|
48
|
+
}
|
|
49
|
+
const before = prompt.slice(0, markerIndex).trimEnd();
|
|
50
|
+
const after = prompt.slice(markerIndex);
|
|
51
|
+
return `${before}\n\n${section}\n\n${after}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function replaceNowCriticalBlock(prompt: string, replacement: string): string {
|
|
55
|
+
const markerIndex = prompt.indexOf(NOW_MARKER);
|
|
56
|
+
if (markerIndex === -1) {
|
|
57
|
+
return `${prompt.trimEnd()}\n\n${replacement}`.trim();
|
|
58
|
+
}
|
|
59
|
+
const before = prompt.slice(0, markerIndex + NOW_MARKER.length);
|
|
60
|
+
const after = prompt.slice(markerIndex + NOW_MARKER.length);
|
|
61
|
+
if (!after.includes("<critical>")) {
|
|
62
|
+
return `${before}\n\n${replacement}${after}`;
|
|
63
|
+
}
|
|
64
|
+
return before + after.replace(/<critical>[\s\S]*?<\/critical>/, replacement);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizePrompt(prompt: string): string {
|
|
68
|
+
return prompt.replace(/\n{3,}/g, "\n\n").trim();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildTopicLine(topic?: string): string[] {
|
|
72
|
+
if (!topic) return [];
|
|
73
|
+
return ["", `Initial design request: ${topic}`];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildAdditionalGuidelines(skillContent?: string): string[] {
|
|
77
|
+
if (!skillContent) return [];
|
|
78
|
+
return ["", "## Skill Content", "", skillContent.trim()];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildSubAgentTemplates(templates?: { name: string; content: string }[]): string[] {
|
|
82
|
+
if (!templates || templates.length === 0) return [];
|
|
83
|
+
const out: string[] = ["", "## Sub-agent prompt templates", ""];
|
|
84
|
+
for (const { name, content } of templates) {
|
|
85
|
+
out.push(`### \`${name}\``, "", "```text", content.trim(), "```", "");
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function buildLocalHtmlPhaseTable(): string[] {
|
|
91
|
+
return [
|
|
92
|
+
"## Director state machine (9 model-owned phases)",
|
|
93
|
+
"",
|
|
94
|
+
"| # | Phase | Precondition | Output | Manifest status |",
|
|
95
|
+
"|---|---|---|---|---|",
|
|
96
|
+
"| 1 | Phase 1 — Scope selection | manifest.json with status=in-progress | planning_ask result → update manifest.scope | in-progress |",
|
|
97
|
+
"| 2 | Phase 2 — Context review | manifest.scope populated | `<session>/context.md` | in-progress |",
|
|
98
|
+
"| 3 | Phase 3 — Decomposition | `<session>/context.md` exists | `<session>/screen-decomposition.html` + `<session>/decomposition.json` | in-progress |",
|
|
99
|
+
"| 4 | Phase 4 — Parallel components | `<session>/decomposition.json` exists | `<session>/components/<name>.html` + `<name>.tokens.json` | in-progress |",
|
|
100
|
+
"| 5 | Phase 5 — Section assembly | all components present | `<session>/sections/<name>.html` | in-progress |",
|
|
101
|
+
"| 6 | Phase 6 — Page composition | all sections present | `<session>/page.html` | critiquing |",
|
|
102
|
+
"| 7 | Phase 7 — Design-critic pass | `<session>/page.html` exists | `<session>/critique.md` | awaiting-review |",
|
|
103
|
+
"| 8 | Phase 8 — Fix loop (≤2) | `<session>/critique.md` exists | fixes in-place, critic re-run | awaiting-review |",
|
|
104
|
+
"| 9 | Phase 9 — User review gate | fix loop terminated | `<session>/screen-review.html`; planning_ask → approve/request-changes/discard | complete or discarded |",
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildPencilMcpPhaseTable(penPath: string): string[] {
|
|
109
|
+
return [
|
|
110
|
+
"## Director state machine (9 model-owned phases)",
|
|
111
|
+
"",
|
|
112
|
+
"| # | Phase | Precondition | Output | Manifest status |",
|
|
113
|
+
"|---|---|---|---|---|",
|
|
114
|
+
"| 1 | Phase 1 — Scope selection | manifest.json with status=in-progress | planning_ask result → update manifest.scope | in-progress |",
|
|
115
|
+
`| 2 | Phase 2 — Context review | manifest.scope populated | \`<session>/context.md\`; \`mcp__pencil_open_document\` on \`${penPath}\`; \`mcp__pencil_get_editor_state\` + \`mcp__pencil_batch_get\` to inventory existing content | in-progress |`,
|
|
116
|
+
"| 3 | Phase 3 — Decomposition | `<session>/context.md` exists | `<session>/decomposition.json` (kebab-case unique names); `mcp__pencil_batch_design` creates a top-level `Decomposition` frame with one child frame per section | in-progress |",
|
|
117
|
+
"| 4 | Phase 4 — Parallel components | `<session>/decomposition.json` exists | one reusable node per component under a `Components` frame; update `<session>/node-manifest.json.componentNodeIds` | in-progress |",
|
|
118
|
+
"| 5 | Phase 5 — Section assembly | components present | section frames composed via `mcp__pencil_batch_design`; update `<session>/node-manifest.json.sectionNodeIds` | in-progress |",
|
|
119
|
+
"| 6 | Phase 6 — Page composition | sections present | page frame assembled; set `<session>/node-manifest.json.pageNodeId` | critiquing |",
|
|
120
|
+
"| 7 | Phase 7 — Design-critic pass | `pageNodeId` set | `<session>/critique.md` with `## Fixable` + `## Advisory` | awaiting-review |",
|
|
121
|
+
"| 8 | Phase 8 — Fix loop (≤2) | `<session>/critique.md` exists | `mcp__pencil_batch_design` edits in place (filePath pinned); critic rerun | awaiting-review |",
|
|
122
|
+
"| 9 | Phase 9 — User review gate | fix loop terminated | `mcp__pencil_export_nodes` writes `<session>/screen-review.png`; planning_ask → approve/request-changes/discard | complete or discarded |",
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function buildHardGate(options: UiDesignSystemPromptOptions): string[] {
|
|
127
|
+
const base = [
|
|
128
|
+
"## HARD-GATE",
|
|
129
|
+
"",
|
|
130
|
+
`- All file writes MUST happen inside \`${options.sessionDir}\`. Writing anywhere else is forbidden.`,
|
|
131
|
+
"- You **MUST NOT** generate production code (`.ts`, `.tsx`, `.vue`, `.svelte`, `.py`) into the user's codebase.",
|
|
132
|
+
"- You **MUST NOT** call `exit_plan_mode` or `ExitPlanMode` — completion is driven by the agent_end approval hook.",
|
|
133
|
+
"- You **MUST NOT** use the `ask` tool. Use `planning_ask` for every user question.",
|
|
134
|
+
"- You **MUST NOT** skip a phase. Each phase's precondition file MUST exist on disk before you advance.",
|
|
135
|
+
"- You **MUST NOT** declare completion without updating `manifest.json`.",
|
|
136
|
+
];
|
|
137
|
+
if (options.backend === "pencil-mcp") {
|
|
138
|
+
const penPath = options.penFilePath && options.penFilePath.length > 0
|
|
139
|
+
? options.penFilePath
|
|
140
|
+
: null;
|
|
141
|
+
if (penPath) {
|
|
142
|
+
base.push(
|
|
143
|
+
`- Pencil MCP calls that accept \`filePath\` MUST pass \`filePath: '${penPath}'\`. \`mcp__pencil_open_document\` is the only exception: call it with \`filePathOrTemplate: '${penPath}'\`, never any other path.`,
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
base.push(
|
|
147
|
+
"- Pencil MCP calls that accept `filePath` MUST pass the absolute path recorded in `manifest.json` under `penFilePath`. `mcp__pencil_open_document` is the only exception: read the manifest first, then call it with that path as `filePathOrTemplate`, never any other path.",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
base.push(
|
|
151
|
+
"- You **MUST NOT** call `mcp__pencil_set_variables` or `mcp__pencil_replace_all_matching_properties` unless the user explicitly asks for design-system-wide changes.",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return base;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function buildDirectorSection(options: UiDesignSystemPromptOptions): string {
|
|
158
|
+
const isPencil = options.backend === "pencil-mcp";
|
|
159
|
+
// When the pencil backend is active but the caller failed to pin a path,
|
|
160
|
+
// keep the prompt self-consistent (pencil phase table + pencil tool routing
|
|
161
|
+
// + pencil HARD-GATE) and instruct the director to recover the path from
|
|
162
|
+
// `manifest.json` before any `mcp__pencil_*` write. Mixing an HTML phase
|
|
163
|
+
// table into a pencil session is never correct.
|
|
164
|
+
const penPath = options.penFilePath && options.penFilePath.length > 0
|
|
165
|
+
? options.penFilePath
|
|
166
|
+
: isPencil
|
|
167
|
+
? "<read manifest.json penFilePath>"
|
|
168
|
+
: "";
|
|
169
|
+
const phaseTable = isPencil
|
|
170
|
+
? buildPencilMcpPhaseTable(penPath)
|
|
171
|
+
: buildLocalHtmlPhaseTable();
|
|
172
|
+
const pencilLines = isPencil ? [`.pen file: ${penPath}`] : [];
|
|
173
|
+
const parallelismRules = isPencil
|
|
174
|
+
? [
|
|
175
|
+
"## Parallelism rules",
|
|
176
|
+
"",
|
|
177
|
+
"- Phase 4: parallel via a single `task` call with one sub-task per component. Each sub-agent inserts its reusable node via `mcp__pencil_batch_design` and records the returned node id under `<session>/components/<name>.node`.",
|
|
178
|
+
"- Phase 5: serial. Later sections may reference earlier section node ids.",
|
|
179
|
+
"- Phase 7: single sub-agent.",
|
|
180
|
+
]
|
|
181
|
+
: [
|
|
182
|
+
"## Parallelism rules",
|
|
183
|
+
"",
|
|
184
|
+
"- Phase 4: parallel via a single `task` call with one sub-task per component.",
|
|
185
|
+
"- Phase 5: serial. Later sections may reference earlier sections.",
|
|
186
|
+
"- Phase 7: single sub-agent.",
|
|
187
|
+
];
|
|
188
|
+
const collisionSection = isPencil
|
|
189
|
+
? [
|
|
190
|
+
"## Filename collision prevention (Phase 3)",
|
|
191
|
+
"",
|
|
192
|
+
"Before writing `decomposition.json`, kebab-case every component name and assert `new Set(names).size === names.length`. On collision, disambiguate and re-check. Do **NOT** invoke `task` until the check passes. Component names become reusable node names inside the `.pen` file, so duplicates collide there too.",
|
|
193
|
+
]
|
|
194
|
+
: [
|
|
195
|
+
"## Filename collision prevention (Phase 3)",
|
|
196
|
+
"",
|
|
197
|
+
"Before writing `decomposition.json`, kebab-case every component name and assert `new Set(names).size === names.length`. On collision, disambiguate and re-check. Do **NOT** invoke `task` until the check passes.",
|
|
198
|
+
];
|
|
199
|
+
const toolRouting = isPencil
|
|
200
|
+
? [
|
|
201
|
+
"## Tool routing",
|
|
202
|
+
"",
|
|
203
|
+
"- `planning_ask` — every user question",
|
|
204
|
+
"- `task` — all sub-agents (Phases 4, 5, 7); never `createAgentSession` directly",
|
|
205
|
+
"- `read` / `write` / `edit` — session files only (context.md, decomposition.json, node-manifest.json, critique.md)",
|
|
206
|
+
"- `mcp__pencil_*` — every read/write to the `.pen` file goes through Pencil MCP; pass `filePath` pinned to the HARD-GATE path when the tool schema accepts it, and pass `filePathOrTemplate` for `mcp__pencil_open_document`",
|
|
207
|
+
]
|
|
208
|
+
: [
|
|
209
|
+
"## Tool routing",
|
|
210
|
+
"",
|
|
211
|
+
"- `planning_ask` — every user question",
|
|
212
|
+
"- `task` — all sub-agents (Phases 4, 5, 7); never `createAgentSession` directly",
|
|
213
|
+
"- `read` / `write` / `edit` — session files only",
|
|
214
|
+
];
|
|
215
|
+
return [
|
|
216
|
+
"═══Design Director═══",
|
|
217
|
+
"",
|
|
218
|
+
"<ui-design-mode>",
|
|
219
|
+
"You are the Design Director for `/supi:ui-design`.",
|
|
220
|
+
"Your output is a validated design artifact under the session directory.",
|
|
221
|
+
"You do NOT write production code. You do NOT exit early. You follow the 9 Design Director phases in order.",
|
|
222
|
+
...buildTopicLine(options.topic),
|
|
223
|
+
`Session directory: ${options.sessionDir}`,
|
|
224
|
+
`Backend: ${options.backend}`,
|
|
225
|
+
`Companion URL: ${options.companionUrl}`,
|
|
226
|
+
...pencilLines,
|
|
227
|
+
"</ui-design-mode>",
|
|
228
|
+
"",
|
|
229
|
+
...buildHardGate(options),
|
|
230
|
+
"",
|
|
231
|
+
"## Context Scan Summary",
|
|
232
|
+
"",
|
|
233
|
+
options.contextScanSummary.trim(),
|
|
234
|
+
"",
|
|
235
|
+
...phaseTable,
|
|
236
|
+
"",
|
|
237
|
+
...parallelismRules,
|
|
238
|
+
"",
|
|
239
|
+
...collisionSection,
|
|
240
|
+
"",
|
|
241
|
+
...toolRouting,
|
|
242
|
+
...buildAdditionalGuidelines(options.skillContent),
|
|
243
|
+
...buildSubAgentTemplates(options.subAgentTemplates),
|
|
244
|
+
].join("\n");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildCriticalBlock(options: UiDesignSystemPromptOptions): string {
|
|
248
|
+
return [
|
|
249
|
+
"<critical>",
|
|
250
|
+
"You are in `/supi:ui-design` Design Director mode.",
|
|
251
|
+
"You **MUST NOT** implement production code or write outside the session directory; edit tools, including `apply_patch`, are allowed only for artifacts under the session directory.",
|
|
252
|
+
"You **MUST** follow the 9 Design Director phases sequentially.",
|
|
253
|
+
`All artifacts MUST be written under \`${options.sessionDir}\`.`,
|
|
254
|
+
"When you need to ask the user a question with options, use the `planning_ask` tool — never `ask`.",
|
|
255
|
+
"",
|
|
256
|
+
"## Completion",
|
|
257
|
+
"",
|
|
258
|
+
"Completion is driven by `manifest.json`. Set `status: \"complete\"` + `approvedAt` only after the user approves via Phase 9's review gate.",
|
|
259
|
+
"You **MUST NOT** call `exit_plan_mode`, `ExitPlanMode`, or write to `local://PLAN.md`.",
|
|
260
|
+
"After updating the manifest to a terminal state, stop and yield your turn — the approval UI handles teardown.",
|
|
261
|
+
"</critical>",
|
|
262
|
+
].join("\n");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function buildUiDesignSystemPrompt(
|
|
266
|
+
basePrompt: string,
|
|
267
|
+
options: UiDesignSystemPromptOptions,
|
|
268
|
+
): string {
|
|
269
|
+
let prompt = basePrompt;
|
|
270
|
+
|
|
271
|
+
for (const tag of STRIP_TAGS) {
|
|
272
|
+
prompt = stripTagSection(prompt, tag);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
prompt = stripBetween(prompt, SKILLS_HEADER, TOOLS_HEADER);
|
|
276
|
+
|
|
277
|
+
const directorSection = buildDirectorSection(options);
|
|
278
|
+
prompt = insertBeforeMarker(prompt, NOW_MARKER, directorSection);
|
|
279
|
+
prompt = replaceNowCriticalBlock(prompt, buildCriticalBlock(options));
|
|
280
|
+
|
|
281
|
+
return normalizePrompt(prompt);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
let activePromptOptions: UiDesignSystemPromptOptions | null = null;
|
|
285
|
+
|
|
286
|
+
export function setUiDesignPromptOptions(options: UiDesignSystemPromptOptions | null): void {
|
|
287
|
+
activePromptOptions = options;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function registerUiDesignSystemPromptHook(platform: Platform): void {
|
|
291
|
+
platform.on("before_agent_start", (event: any, _ctx: any) => {
|
|
292
|
+
if (!isUiDesignActive()) return;
|
|
293
|
+
const basePrompt = systemPromptText(event?.systemPrompt);
|
|
294
|
+
if (!basePrompt || !activePromptOptions) {
|
|
295
|
+
// No session prompt options captured — fall back to active session state
|
|
296
|
+
const session = getActiveUiDesignSession();
|
|
297
|
+
if (!session || !basePrompt) return;
|
|
298
|
+
const fallback: UiDesignSystemPromptOptions = {
|
|
299
|
+
dotDirDisplay: platform.paths.dotDirDisplay,
|
|
300
|
+
sessionDir: session.dir,
|
|
301
|
+
companionUrl: session.companionUrl,
|
|
302
|
+
backend: session.backend,
|
|
303
|
+
contextScanSummary: "Context scan summary unavailable. Read `context.md` if it exists.",
|
|
304
|
+
topic: session.topic,
|
|
305
|
+
scope: session.scope,
|
|
306
|
+
...(session.penFilePath ? { penFilePath: session.penFilePath } : {}),
|
|
307
|
+
};
|
|
308
|
+
return { systemPrompt: [buildUiDesignSystemPrompt(basePrompt, fallback)] };
|
|
309
|
+
}
|
|
310
|
+
return { systemPrompt: [buildUiDesignSystemPrompt(basePrompt, activePromptOptions)] };
|
|
311
|
+
});
|
|
312
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { DesignTokens } from "./types.js";
|
|
4
|
+
|
|
5
|
+
const TAILWIND_CONFIG_NAMES = [
|
|
6
|
+
"tailwind.config.js",
|
|
7
|
+
"tailwind.config.cjs",
|
|
8
|
+
"tailwind.config.mjs",
|
|
9
|
+
"tailwind.config.ts",
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const MAX_RAW_BYTES = 4 * 1024;
|
|
13
|
+
|
|
14
|
+
function truncate(value: string): string {
|
|
15
|
+
return value.length > MAX_RAW_BYTES ? value.slice(0, MAX_RAW_BYTES) : value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function findTailwindConfig(repoRoot: string): string | null {
|
|
19
|
+
for (const name of TAILWIND_CONFIG_NAMES) {
|
|
20
|
+
const p = path.join(repoRoot, name);
|
|
21
|
+
if (fs.existsSync(p)) return p;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isInside(configPath: string, repoRoot: string): boolean {
|
|
27
|
+
try {
|
|
28
|
+
const realConfig = fs.realpathSync(configPath);
|
|
29
|
+
const realRoot = fs.realpathSync(repoRoot);
|
|
30
|
+
const rel = path.relative(realRoot, realConfig);
|
|
31
|
+
return !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function scanTailwindRegex(source: string): {
|
|
38
|
+
colors: Record<string, string>;
|
|
39
|
+
fonts: Record<string, string[]>;
|
|
40
|
+
} | null {
|
|
41
|
+
const colors: Record<string, string> = {};
|
|
42
|
+
const colorsMatch = source.match(/colors\s*:\s*\{([^}]*)\}/);
|
|
43
|
+
if (colorsMatch) {
|
|
44
|
+
const entryRe = /([\w-]+)\s*:\s*["']([^"']+)["']/g;
|
|
45
|
+
let m: RegExpExecArray | null;
|
|
46
|
+
while ((m = entryRe.exec(colorsMatch[1]!)) !== null) {
|
|
47
|
+
colors[m[1]!] = m[2]!;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const fonts: Record<string, string[]> = {};
|
|
52
|
+
const fontsMatch = source.match(/fontFamily\s*:\s*\{([^}]*)\}/);
|
|
53
|
+
if (fontsMatch) {
|
|
54
|
+
const entryRe = /([\w-]+)\s*:\s*\[([^\]]*)\]/g;
|
|
55
|
+
let m: RegExpExecArray | null;
|
|
56
|
+
while ((m = entryRe.exec(fontsMatch[1]!)) !== null) {
|
|
57
|
+
const inner = m[2]!.match(/["']([^"']+)["']/g) ?? [];
|
|
58
|
+
fonts[m[1]!] = inner.map((s) => s.slice(1, -1));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Object.keys(colors).length === 0 && Object.keys(fonts).length === 0) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return { colors, fonts };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function scanCssVariables(repoRoot: string): {
|
|
69
|
+
source: string;
|
|
70
|
+
colors: Record<string, string>;
|
|
71
|
+
fonts: Record<string, string[]>;
|
|
72
|
+
raw: string;
|
|
73
|
+
} | null {
|
|
74
|
+
let entries: { file: string; content: string }[];
|
|
75
|
+
try {
|
|
76
|
+
entries = fs
|
|
77
|
+
.readdirSync(repoRoot, { withFileTypes: true })
|
|
78
|
+
.filter((e) => e.isFile() && /\.(css|scss)$/.test(e.name))
|
|
79
|
+
.map((e) => ({
|
|
80
|
+
file: path.join(repoRoot, e.name),
|
|
81
|
+
content: fs.readFileSync(path.join(repoRoot, e.name), "utf-8"),
|
|
82
|
+
}));
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const colors: Record<string, string> = {};
|
|
88
|
+
const fonts: Record<string, string[]> = {};
|
|
89
|
+
const rawParts: string[] = [];
|
|
90
|
+
|
|
91
|
+
for (const { content } of entries) {
|
|
92
|
+
const rootBlocks = content.match(/:root\s*\{[^}]*\}/g);
|
|
93
|
+
if (!rootBlocks) continue;
|
|
94
|
+
for (const block of rootBlocks) {
|
|
95
|
+
rawParts.push(block);
|
|
96
|
+
const varRe = /--([\w-]+)\s*:\s*([^;]+);/g;
|
|
97
|
+
let m: RegExpExecArray | null;
|
|
98
|
+
while ((m = varRe.exec(block)) !== null) {
|
|
99
|
+
const key = m[1]!.trim();
|
|
100
|
+
const value = m[2]!.trim();
|
|
101
|
+
if (/^#|^rgb|^hsl|^oklch/i.test(value) || /^color-/.test(key)) {
|
|
102
|
+
colors[key] = value;
|
|
103
|
+
} else if (/^font-/.test(key)) {
|
|
104
|
+
fonts[key] = [value];
|
|
105
|
+
} else {
|
|
106
|
+
colors[key] = value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (Object.keys(colors).length === 0 && Object.keys(fonts).length === 0) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { source: "css-vars", colors, fonts, raw: rawParts.join("\n\n") };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Inspect the repo for design tokens.
|
|
121
|
+
*
|
|
122
|
+
* Detection order:
|
|
123
|
+
* 1. Tailwind config via static regex extraction.
|
|
124
|
+
* 2. Top-level CSS/SCSS :root { --… } blocks.
|
|
125
|
+
* 3. `missing` when nothing is found.
|
|
126
|
+
*
|
|
127
|
+
* Always returns a DesignTokens; never throws.
|
|
128
|
+
*/
|
|
129
|
+
export async function scanDesignTokens(repoRoot: string): Promise<DesignTokens> {
|
|
130
|
+
const configPath = findTailwindConfig(repoRoot);
|
|
131
|
+
if (configPath) {
|
|
132
|
+
if (!isInside(configPath, repoRoot)) {
|
|
133
|
+
return { status: "error", reason: "tailwind config resolves outside repo root" };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let rawSource = "";
|
|
137
|
+
try {
|
|
138
|
+
rawSource = fs.readFileSync(configPath, "utf-8");
|
|
139
|
+
} catch (err) {
|
|
140
|
+
return {
|
|
141
|
+
status: "error",
|
|
142
|
+
reason: `failed to read tailwind config: ${(err as Error).message}`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const reg = scanTailwindRegex(rawSource);
|
|
148
|
+
if (reg) {
|
|
149
|
+
return {
|
|
150
|
+
status: "ok",
|
|
151
|
+
source: "tailwind",
|
|
152
|
+
colors: reg.colors,
|
|
153
|
+
fonts: reg.fonts,
|
|
154
|
+
raw: truncate(rawSource),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
return {
|
|
159
|
+
status: "error",
|
|
160
|
+
reason: `tailwind regex scan failed: ${(err as Error).message}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const css = scanCssVariables(repoRoot);
|
|
167
|
+
if (css) {
|
|
168
|
+
return {
|
|
169
|
+
status: "ok",
|
|
170
|
+
source: "css-vars",
|
|
171
|
+
colors: css.colors,
|
|
172
|
+
fonts: css.fonts,
|
|
173
|
+
raw: truncate(css.raw),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
return { status: "error", reason: `css scan failed: ${(err as Error).message}` };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { status: "missing" };
|
|
181
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ResolvedModel } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared types for `/supi:ui-design`.
|
|
5
|
+
*
|
|
6
|
+
* Canonical definitions for the Design Director pipeline. Every runtime module,
|
|
7
|
+
* test, and the director prompt MUST reference these types — no shadow definitions.
|
|
8
|
+
* See `.omp/supipowers/specs/2026-04-18-ui-design-design.md` for the contract.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type UiDesignScope = "page" | "flow" | "component";
|
|
12
|
+
|
|
13
|
+
export type UiDesignBackendId = "local-html" | "pencil-mcp";
|
|
14
|
+
|
|
15
|
+
export type ScanFieldStatus = "ok" | "missing" | "error";
|
|
16
|
+
|
|
17
|
+
export type ManifestStatus =
|
|
18
|
+
| "in-progress"
|
|
19
|
+
| "critiquing"
|
|
20
|
+
| "awaiting-review"
|
|
21
|
+
| "complete"
|
|
22
|
+
| "discarded";
|
|
23
|
+
|
|
24
|
+
/** Output of the deterministic design-token scanner. */
|
|
25
|
+
export type DesignTokens =
|
|
26
|
+
| { status: "missing" }
|
|
27
|
+
| { status: "error"; reason: string }
|
|
28
|
+
| {
|
|
29
|
+
status: "ok";
|
|
30
|
+
source: "tailwind" | "css-vars";
|
|
31
|
+
colors: Record<string, string>;
|
|
32
|
+
fonts: Record<string, string[]>;
|
|
33
|
+
raw: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** One discovered component in the user's codebase. */
|
|
37
|
+
export interface ExistingComponent {
|
|
38
|
+
name: string;
|
|
39
|
+
/** Path relative to repo root. */
|
|
40
|
+
path: string;
|
|
41
|
+
framework: "react" | "vue" | "svelte" | "unknown";
|
|
42
|
+
exports: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Full output of `scanDesignContext`. Never throws; fields degrade independently. */
|
|
46
|
+
export interface ContextScan {
|
|
47
|
+
scannedAt: string;
|
|
48
|
+
tokens: DesignTokens;
|
|
49
|
+
components:
|
|
50
|
+
| { status: "missing"; items: [] }
|
|
51
|
+
| { status: "error"; items: []; reason: string }
|
|
52
|
+
| { status: "ok"; items: ExistingComponent[] };
|
|
53
|
+
designMd:
|
|
54
|
+
| { status: "missing" }
|
|
55
|
+
| { status: "error"; reason: string }
|
|
56
|
+
| { status: "ok"; path: string; bytes: number };
|
|
57
|
+
packageInfo:
|
|
58
|
+
| { status: "missing" }
|
|
59
|
+
| { status: "error"; reason: string }
|
|
60
|
+
| {
|
|
61
|
+
status: "ok";
|
|
62
|
+
framework: "react" | "vue" | "svelte" | "next" | "nuxt" | "unknown";
|
|
63
|
+
uiLibraries: string[];
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Minimal handle passed between modules describing a ui-design session. */
|
|
68
|
+
export interface UiDesignSession {
|
|
69
|
+
id: string;
|
|
70
|
+
dir: string;
|
|
71
|
+
scope?: UiDesignScope;
|
|
72
|
+
topic?: string;
|
|
73
|
+
backend: UiDesignBackendId;
|
|
74
|
+
companionUrl: string;
|
|
75
|
+
/** Absolute path to the .pen file. Populated only for `pencil-mcp` sessions. */
|
|
76
|
+
penFilePath?: string;
|
|
77
|
+
resolvedModel?: ResolvedModel;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Canonical manifest written by the director at every phase transition. */
|
|
81
|
+
export interface Manifest {
|
|
82
|
+
id: string;
|
|
83
|
+
scope?: UiDesignScope;
|
|
84
|
+
topic?: string;
|
|
85
|
+
backend: UiDesignBackendId;
|
|
86
|
+
status: ManifestStatus;
|
|
87
|
+
acknowledged: boolean;
|
|
88
|
+
createdAt: string;
|
|
89
|
+
approvedAt?: string;
|
|
90
|
+
components: string[];
|
|
91
|
+
sections: string[];
|
|
92
|
+
page: string;
|
|
93
|
+
critique?: { fixableCount: number; advisoryCount: number; fixIterations: number };
|
|
94
|
+
/** Absolute path to the .pen file. Populated only for `pencil-mcp` sessions. */
|
|
95
|
+
penFilePath?: string;
|
|
96
|
+
}
|