sdtk-design-kit 0.1.2 → 0.2.0
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/package.json +1 -1
- package/src/commands/handoff.js +357 -203
- package/src/commands/help.js +2 -2
- package/src/commands/prototype.js +276 -17
- package/src/commands/review.js +230 -0
- package/src/commands/start.js +23 -1
- package/src/lib/component-contract.js +142 -0
- package/src/lib/design-input-contract.js +271 -11
- package/src/lib/design-paths.js +24 -0
- package/src/lib/prototype-density.js +147 -0
- package/src/lib/prototype-renderer.js +325 -0
- package/src/lib/screen-briefs.js +340 -0
package/package.json
CHANGED
package/src/commands/handoff.js
CHANGED
|
@@ -1,248 +1,402 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
5
|
const { parseFlags } = require("../lib/args");
|
|
6
6
|
const { describeDesignPaths, resolveProjectPath } = require("../lib/design-paths");
|
|
7
|
-
const { inferDomainProfile } = require("../lib/domain-profile");
|
|
8
|
-
const { ValidationError } = require("../lib/errors");
|
|
9
|
-
|
|
10
|
-
const HANDOFF_FLAG_DEFS = {
|
|
11
|
-
help: { type: "boolean" },
|
|
12
|
-
"project-path": { type: "string" },
|
|
13
|
-
force: { type: "boolean" },
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const REQUIRED_WIREFRAMES = ["LANDING.md", "ONBOARDING.md", "DASHBOARD.md"];
|
|
17
|
-
|
|
18
|
-
function cmdHandoffHelp() {
|
|
19
|
-
console.log(`SDTK-DESIGN Handoff
|
|
20
|
-
|
|
21
|
-
Usage:
|
|
22
|
-
sdtk-design handoff [--project-path <path>] [--force]
|
|
7
|
+
const { inferDomainProfile } = require("../lib/domain-profile");
|
|
8
|
+
const { ValidationError } = require("../lib/errors");
|
|
9
|
+
|
|
10
|
+
const HANDOFF_FLAG_DEFS = {
|
|
11
|
+
help: { type: "boolean" },
|
|
12
|
+
"project-path": { type: "string" },
|
|
13
|
+
force: { type: "boolean" },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const REQUIRED_WIREFRAMES = ["LANDING.md", "ONBOARDING.md", "DASHBOARD.md"];
|
|
17
|
+
|
|
18
|
+
function cmdHandoffHelp() {
|
|
19
|
+
console.log(`SDTK-DESIGN Handoff
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
sdtk-design handoff [--project-path <path>] [--force]
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
sdtk-design handoff
|
|
26
|
+
|
|
27
|
+
Reads:
|
|
28
|
+
docs/design/DESIGN_BRIEF.md
|
|
29
|
+
docs/design/SCREEN_MAP.md
|
|
30
|
+
docs/design/wireframes/LANDING.md
|
|
31
|
+
docs/design/wireframes/ONBOARDING.md
|
|
32
|
+
docs/design/wireframes/DASHBOARD.md
|
|
33
|
+
docs/design/DESIGN_SYSTEM.md
|
|
34
|
+
|
|
35
|
+
Creates:
|
|
36
|
+
docs/design/DESIGN_HANDOFF.md
|
|
37
|
+
|
|
38
|
+
Safety:
|
|
39
|
+
Local files only.
|
|
40
|
+
Existing DESIGN_HANDOFF.md is not overwritten unless --force is explicit.
|
|
41
|
+
No .sdtk/atlas creation or mutation.
|
|
42
|
+
No SDTK-WIKI output mutation.
|
|
43
|
+
No network call, Pro entitlement, or production app code generation.`);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function requiredArtifacts(paths) {
|
|
48
|
+
return [
|
|
49
|
+
{ label: "Design brief", relativePath: "docs/design/DESIGN_BRIEF.md", filePath: paths.designBriefPath },
|
|
50
|
+
{ label: "Screen map", relativePath: "docs/design/SCREEN_MAP.md", filePath: paths.screenMapPath },
|
|
51
|
+
{ label: "Design system", relativePath: "docs/design/DESIGN_SYSTEM.md", filePath: paths.designSystemPath },
|
|
52
|
+
...REQUIRED_WIREFRAMES.map((fileName) => ({
|
|
53
|
+
label: `${fileName.replace(".md", "")} wireframe`,
|
|
54
|
+
relativePath: `docs/design/wireframes/${fileName}`,
|
|
55
|
+
filePath: path.join(paths.wireframesPath, fileName),
|
|
56
|
+
})),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
23
59
|
|
|
24
|
-
|
|
25
|
-
|
|
60
|
+
function readJsonSafe(filePath) {
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
63
|
+
} catch (_err) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
26
67
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
docs/design/DESIGN_SYSTEM.md
|
|
68
|
+
function readInputContractState(paths) {
|
|
69
|
+
if (!fs.existsSync(paths.designStartInputStatePath) || !fs.statSync(paths.designStartInputStatePath).isFile()) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return readJsonSafe(paths.designStartInputStatePath);
|
|
73
|
+
}
|
|
34
74
|
|
|
35
|
-
|
|
36
|
-
|
|
75
|
+
function listScreenBriefArtifacts(paths) {
|
|
76
|
+
if (!fs.existsSync(paths.screensPath) || !fs.statSync(paths.screensPath).isDirectory()) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
return fs
|
|
80
|
+
.readdirSync(paths.screensPath)
|
|
81
|
+
.filter((name) => name.endsWith("_DESIGN_BRIEF.md"))
|
|
82
|
+
.sort()
|
|
83
|
+
.map((name) => `docs/design/screens/${name}`);
|
|
84
|
+
}
|
|
37
85
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
86
|
+
function listPrototypeArtifacts(paths) {
|
|
87
|
+
const refs = [];
|
|
88
|
+
if (fs.existsSync(paths.prototypeIndexPath)) {
|
|
89
|
+
refs.push("docs/design/prototype/index.html");
|
|
90
|
+
}
|
|
91
|
+
if (fs.existsSync(paths.prototypeScreensPath) && fs.statSync(paths.prototypeScreensPath).isDirectory()) {
|
|
92
|
+
refs.push(
|
|
93
|
+
...fs
|
|
94
|
+
.readdirSync(paths.prototypeScreensPath)
|
|
95
|
+
.filter((name) => name.toLowerCase().endsWith(".html"))
|
|
96
|
+
.sort()
|
|
97
|
+
.map((name) => `docs/design/prototype/screens/${name}`)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return refs;
|
|
45
101
|
}
|
|
46
102
|
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
103
|
+
function requiredComponentsForRole(role) {
|
|
104
|
+
const map = {
|
|
105
|
+
home: ["hero section", "category or featured component", "primary CTA"],
|
|
106
|
+
category: ["filter sidebar", "product card grid"],
|
|
107
|
+
"product-detail": ["spec table or gallery area", "quantity or add-to-cart control"],
|
|
108
|
+
search: ["search toolbar", "result list and no-result state"],
|
|
109
|
+
cart: ["cart line table", "summary or checkout CTA"],
|
|
110
|
+
checkout: ["checkout stepper", "form plus summary"],
|
|
111
|
+
"order-history": ["order list or table"],
|
|
112
|
+
"order-detail": ["status timeline", "detail summary card"],
|
|
113
|
+
"account-info": ["account sidebar", "view or edit form"],
|
|
114
|
+
"mode-b-configurator": ["wizard shell", "preview panel", "BOM table", "exclude/recalculate", "add-to-cart CTA"],
|
|
115
|
+
};
|
|
116
|
+
return map[role] || ["screen-specific component set from design brief"];
|
|
58
117
|
}
|
|
59
118
|
|
|
60
|
-
function
|
|
119
|
+
function normalizeScreenRole(screen) {
|
|
120
|
+
const token = `${screen.screenId || ""} ${screen.title || ""}`.toLowerCase();
|
|
121
|
+
if (token.includes("home")) return "home";
|
|
122
|
+
if (token.includes("category")) return "category";
|
|
123
|
+
if (token.includes("product-detail") || token.includes("product detail") || token.includes("pdp")) return "product-detail";
|
|
124
|
+
if (token.includes("search")) return "search";
|
|
125
|
+
if (token.includes("cart")) return "cart";
|
|
126
|
+
if (token.includes("checkout")) return "checkout";
|
|
127
|
+
if (token.includes("order-history") || token.includes("order history")) return "order-history";
|
|
128
|
+
if (token.includes("order-detail") || token.includes("order detail")) return "order-detail";
|
|
129
|
+
if (token.includes("account")) return "account-info";
|
|
130
|
+
if (token.includes("configurator") || token.includes("mode-b")) return "mode-b-configurator";
|
|
131
|
+
return screen.screenId || "screen";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function handoffContent({ hasPrototype = false, prototypeRefs = [], profile = inferDomainProfile() } = {}) {
|
|
61
135
|
const prototypeReference = hasPrototype
|
|
62
136
|
? "`docs/design/prototype/index.html` is available as the static visual preview reference."
|
|
63
137
|
: "Run `sdtk-design prototype` to create `docs/design/prototype/index.html` before implementation if visual preview evidence is needed.";
|
|
64
|
-
const setupKey = profile.storageKeys[0];
|
|
65
|
-
const itemKey = profile.storageKeys[1];
|
|
138
|
+
const setupKey = profile.storageKeys[0];
|
|
139
|
+
const itemKey = profile.storageKeys[1];
|
|
140
|
+
|
|
141
|
+
return [
|
|
142
|
+
"# Design Handoff",
|
|
143
|
+
"",
|
|
144
|
+
"## Readiness Verdict",
|
|
145
|
+
"",
|
|
146
|
+
"- Verdict: READY_FOR_SDTK_CODE",
|
|
147
|
+
"- Rationale: Required Phase A design artifacts exist and provide enough screen, component, state, and design-system context for implementation planning.",
|
|
148
|
+
"",
|
|
149
|
+
"## Source Artifacts",
|
|
150
|
+
"",
|
|
151
|
+
"- `docs/design/DESIGN_BRIEF.md`",
|
|
152
|
+
"- `docs/design/SCREEN_MAP.md`",
|
|
153
|
+
"- `docs/design/wireframes/LANDING.md`",
|
|
154
|
+
"- `docs/design/wireframes/ONBOARDING.md`",
|
|
155
|
+
"- `docs/design/wireframes/DASHBOARD.md`",
|
|
156
|
+
"- `docs/design/DESIGN_SYSTEM.md`",
|
|
157
|
+
hasPrototype ? "- `docs/design/prototype/index.html`" : "- `docs/design/prototype/index.html` (optional, not present at handoff time)",
|
|
158
|
+
"",
|
|
159
|
+
"## Screen-To-Component Mapping",
|
|
160
|
+
"",
|
|
161
|
+
"| Screen | Design artifact | Component direction |",
|
|
162
|
+
"|---|---|---|",
|
|
163
|
+
"| Landing | `docs/design/wireframes/LANDING.md` | Header, hero copy, primary CTA, workflow preview, benefit bullets |",
|
|
164
|
+
"| Onboarding | `docs/design/wireframes/ONBOARDING.md` | Progress indicator, setup form, segmented sample-data control, inline validation, continue CTA |",
|
|
165
|
+
`| Dashboard | \`docs/design/wireframes/DASHBOARD.md\` | Summary counters, ${profile.collectionSurface}, status treatment, quick-add action, empty-state panel, recoverable error alert |`,
|
|
166
|
+
"",
|
|
167
|
+
"## Screen-To-User-Story Mapping",
|
|
168
|
+
"",
|
|
169
|
+
"| Screen | User story | Acceptance signal |",
|
|
170
|
+
"|---|---|---|",
|
|
171
|
+
`| Landing | As ${profile.targetUser}, I want to understand the ${profile.productName} promise so I can decide whether to start. | Primary CTA is visible and routes to onboarding. |`,
|
|
172
|
+
`| Onboarding | As a new user, I want a short setup path so I can create my first ${profile.itemSingular} without heavy configuration. | Required fields are clear, validation is inline, and entered values are preserved on error. |`,
|
|
173
|
+
`| Dashboard | As ${profile.targetUser}, I want to see active ${profile.itemPlural} and next actions so I can choose what to do next. | ${profile.itemPlural.charAt(0).toUpperCase() + profile.itemPlural.slice(1)}, next actions, context, and status are scannable with empty and error states. |`,
|
|
174
|
+
"",
|
|
175
|
+
"## Design Decisions",
|
|
176
|
+
"",
|
|
177
|
+
"- Keep the MVP to three core screens: Landing, Onboarding, and Dashboard.",
|
|
178
|
+
"- Optimize for repeated action and scan speed over decorative presentation.",
|
|
179
|
+
"- Use a restrained operational design system with stable controls, clear form states, and accessible status language.",
|
|
180
|
+
"- Preserve `docs/design` as the human-facing design output boundary.",
|
|
181
|
+
"",
|
|
182
|
+
"## Implementation Notes For SDTK-CODE",
|
|
183
|
+
"",
|
|
184
|
+
"- Treat the markdown artifacts as planning input, not generated production code.",
|
|
185
|
+
"- Implement screens in this order: Landing, Onboarding, Dashboard.",
|
|
186
|
+
"- Keep user data capture minimal until the first useful workflow is complete.",
|
|
187
|
+
"- Preserve empty, success, and error states from the screen map and wireframes.",
|
|
188
|
+
"- Apply typography, color, spacing, button, card, form, tone, and accessibility rules from `DESIGN_SYSTEM.md`.",
|
|
189
|
+
"",
|
|
190
|
+
"## Suggested Data Model",
|
|
191
|
+
"",
|
|
192
|
+
"Workspace fields:",
|
|
193
|
+
"",
|
|
194
|
+
"- `id`: local identifier.",
|
|
195
|
+
`- \`name\`: ${profile.setupLabel.toLowerCase()} name.`,
|
|
196
|
+
"- `createdAt`: ISO timestamp.",
|
|
197
|
+
"",
|
|
198
|
+
`${profile.modelName} fields:`,
|
|
199
|
+
"",
|
|
200
|
+
"- `id`: local identifier.",
|
|
201
|
+
`- \`name\`: ${profile.itemSingular} name or label.`,
|
|
202
|
+
"- `status`: workflow status.",
|
|
203
|
+
"- `nextAction`: next action or due context.",
|
|
204
|
+
"- `note`: latest context note or summary.",
|
|
205
|
+
"- `createdAt`: ISO timestamp.",
|
|
206
|
+
"- `updatedAt`: ISO timestamp.",
|
|
207
|
+
"",
|
|
208
|
+
"## Domain Vocabulary / Status Taxonomy",
|
|
209
|
+
"",
|
|
210
|
+
`- ${profile.itemLabel}: the primary record the MVP helps the user create and track.`,
|
|
211
|
+
"- Next action: the next planned step needed to keep the workflow moving.",
|
|
212
|
+
"- Note: short context needed before the next action.",
|
|
213
|
+
`- ${profile.collectionSurface.charAt(0).toUpperCase() + profile.collectionSurface.slice(1)}: the grouped view for active ${profile.itemPlural}.`,
|
|
214
|
+
`- Status taxonomy: ${profile.statusTaxonomy.join(", ")}.`,
|
|
215
|
+
"",
|
|
216
|
+
"## Interaction Flow",
|
|
217
|
+
"",
|
|
218
|
+
"1. Landing -> user chooses the primary CTA.",
|
|
219
|
+
`2. Onboarding -> user enters ${profile.setupLabel.toLowerCase()} name and continues.`,
|
|
220
|
+
`3. Dashboard -> user sees empty state, ${profile.itemSingular} form, and status counters.`,
|
|
221
|
+
`4. ${profile.itemAction} -> validate required fields, append ${profile.itemSingular}, update list/counts, and show the saved note/next action.`,
|
|
222
|
+
`5. Refresh -> restore ${profile.setupLabel.toLowerCase()} and ${profile.itemPlural} from local persistence before rendering dashboard state.`,
|
|
223
|
+
"",
|
|
224
|
+
"## Persistence Guidance",
|
|
225
|
+
"",
|
|
226
|
+
"- For a frontend-only MVP, use browser `localStorage` or equivalent local persistence.",
|
|
227
|
+
`- Suggested keys: \`${setupKey}\` and \`${itemKey}\`.`,
|
|
228
|
+
"- Store arrays/objects as JSON and tolerate parse failures by falling back to empty state.",
|
|
229
|
+
"- Do not introduce backend, API, auth, database, or network persistence unless a later SDTK-CODE task explicitly adds it.",
|
|
230
|
+
"",
|
|
231
|
+
"## Visual Implementation Contract",
|
|
232
|
+
"",
|
|
233
|
+
"- Implement the selected visual preset from `DESIGN_SYSTEM.md` before adding extra decoration.",
|
|
234
|
+
`- Preserve landing hero hierarchy, onboarding form clarity, dashboard metric density, ${profile.collectionSurface}, status treatment, and empty state.`,
|
|
235
|
+
"- Keep responsive behavior explicit: grids collapse at mobile width and controls remain at least 44px tall.",
|
|
236
|
+
"- Pair every status color with text; do not rely on color alone.",
|
|
237
|
+
"- Avoid shipping a gray wireframe look when the prototype/design system provides finished tokens.",
|
|
238
|
+
"",
|
|
239
|
+
"## Prototype Reference",
|
|
240
|
+
"",
|
|
241
|
+
`- ${prototypeReference}`,
|
|
242
|
+
...(prototypeRefs.length > 0 ? prototypeRefs.map((ref) => `- \`${ref}\``) : []),
|
|
243
|
+
"- Treat the prototype as visual direction only, not production application code.",
|
|
244
|
+
"",
|
|
245
|
+
"## SDTK-CODE Build Notes",
|
|
246
|
+
"",
|
|
247
|
+
"- A static frontend implementation may use `app/index.html`, `app/styles.css`, and `app/app.js` when no framework is required.",
|
|
248
|
+
"- Keep runtime dependencies optional; this handoff does not require a framework, server, or package install.",
|
|
249
|
+
`- Add verification for local rendering, ${profile.itemAction.toLowerCase()} behavior, persistence after refresh, and mobile-width layout.`,
|
|
250
|
+
"- SDTK-CODE owns implementation planning, code changes, tests, and ship readiness.",
|
|
251
|
+
"",
|
|
252
|
+
"## Acceptance Criteria",
|
|
253
|
+
"",
|
|
254
|
+
"- Landing renders the product promise, workflow preview, and a single dominant primary CTA.",
|
|
255
|
+
"- Onboarding captures only required setup data and keeps validation inline.",
|
|
256
|
+
`- Dashboard shows active ${profile.itemPlural}, next action, status, empty state, and recoverable error state.`,
|
|
257
|
+
"- Components follow the design system tokens and accessibility baseline.",
|
|
258
|
+
"- No production app code, network integration, Pro entitlement, `.sdtk/atlas`, or SDTK-WIKI mutation is implied by this handoff.",
|
|
259
|
+
"",
|
|
260
|
+
"## Non-Goals",
|
|
261
|
+
"",
|
|
262
|
+
"- Figma, Lovable, v0, or full app-builder replacement behavior.",
|
|
263
|
+
`- ${profile.nonGoals}`,
|
|
264
|
+
"- Package publish or deployment.",
|
|
265
|
+
"",
|
|
266
|
+
].join("\n");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function highFidelityHandoffAppendix({ paths, statePayload, hasPrototype }) {
|
|
270
|
+
const briefRefs = listScreenBriefArtifacts(paths);
|
|
271
|
+
const prototypeRefs = listPrototypeArtifacts(paths);
|
|
272
|
+
const hasComponentLibrary = fs.existsSync(paths.componentPatternLibraryPath);
|
|
273
|
+
const hasDesignTokens = fs.existsSync(paths.designTokensPath);
|
|
274
|
+
const prototypeRef = hasPrototype
|
|
275
|
+
? "docs/design/prototype/index.html"
|
|
276
|
+
: "docs/design/prototype/index.html (generate with sdtk-design prototype)";
|
|
277
|
+
const screens = statePayload && statePayload.screenModel && Array.isArray(statePayload.screenModel.screens) ? statePayload.screenModel.screens : [];
|
|
278
|
+
const rows = screens.map((screen) => {
|
|
279
|
+
const role = normalizeScreenRole(screen);
|
|
280
|
+
const reqs = requiredComponentsForRole(role).join("; ");
|
|
281
|
+
const brief = briefRefs.find((ref) => ref.toLowerCase().includes(`${String(screen.screenId || "").toLowerCase()}_design_brief.md`)) || "missing brief ref";
|
|
282
|
+
return `| ${screen.title} | ${screen.route || "(unspecified route)"} | ${brief} | ${reqs} |`;
|
|
283
|
+
});
|
|
66
284
|
|
|
67
285
|
return [
|
|
68
|
-
"
|
|
69
|
-
"",
|
|
70
|
-
"## Readiness Verdict",
|
|
71
|
-
"",
|
|
72
|
-
"- Verdict: READY_FOR_SDTK_CODE",
|
|
73
|
-
"- Rationale: Required Phase A design artifacts exist and provide enough screen, component, state, and design-system context for implementation planning.",
|
|
74
|
-
"",
|
|
75
|
-
"## Source Artifacts",
|
|
76
|
-
"",
|
|
77
|
-
"- `docs/design/DESIGN_BRIEF.md`",
|
|
78
|
-
"- `docs/design/SCREEN_MAP.md`",
|
|
79
|
-
"- `docs/design/wireframes/LANDING.md`",
|
|
80
|
-
"- `docs/design/wireframes/ONBOARDING.md`",
|
|
81
|
-
"- `docs/design/wireframes/DASHBOARD.md`",
|
|
82
|
-
"- `docs/design/DESIGN_SYSTEM.md`",
|
|
83
|
-
hasPrototype ? "- `docs/design/prototype/index.html`" : "- `docs/design/prototype/index.html` (optional, not present at handoff time)",
|
|
84
|
-
"",
|
|
85
|
-
"## Screen-To-Component Mapping",
|
|
286
|
+
"## High-Fidelity UI Contract",
|
|
86
287
|
"",
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
288
|
+
`- Input contract state: ${statePayload && statePayload.analysisStatus ? statePayload.analysisStatus : "not available"}`,
|
|
289
|
+
`- Component pattern library: ${hasComponentLibrary ? "`docs/design/COMPONENT_PATTERN_LIBRARY.md`" : "missing"}`,
|
|
290
|
+
`- Design tokens: ${hasDesignTokens ? "`docs/design/DESIGN_TOKENS.json`" : "missing"}`,
|
|
291
|
+
`- Prototype reference: \`${prototypeRef}\``,
|
|
292
|
+
`- Screen brief count: ${briefRefs.length}`,
|
|
92
293
|
"",
|
|
93
|
-
"
|
|
294
|
+
"### Prototype Pages",
|
|
94
295
|
"",
|
|
95
|
-
|
|
96
|
-
"|---|---|---|",
|
|
97
|
-
`| Landing | As ${profile.targetUser}, I want to understand the ${profile.productName} promise so I can decide whether to start. | Primary CTA is visible and routes to onboarding. |`,
|
|
98
|
-
`| Onboarding | As a new user, I want a short setup path so I can create my first ${profile.itemSingular} without heavy configuration. | Required fields are clear, validation is inline, and entered values are preserved on error. |`,
|
|
99
|
-
`| Dashboard | As ${profile.targetUser}, I want to see active ${profile.itemPlural} and next actions so I can choose what to do next. | ${profile.itemPlural.charAt(0).toUpperCase() + profile.itemPlural.slice(1)}, next actions, context, and status are scannable with empty and error states. |`,
|
|
296
|
+
...(prototypeRefs.length > 0 ? prototypeRefs.map((ref) => `- \`${ref}\``) : ["- none"]),
|
|
100
297
|
"",
|
|
101
|
-
"
|
|
298
|
+
"### Screen Brief Links",
|
|
102
299
|
"",
|
|
103
|
-
|
|
104
|
-
"- Optimize for repeated action and scan speed over decorative presentation.",
|
|
105
|
-
"- Use a restrained operational design system with stable controls, clear form states, and accessible status language.",
|
|
106
|
-
"- Preserve `docs/design` as the human-facing design output boundary.",
|
|
300
|
+
...(briefRefs.length > 0 ? briefRefs.map((ref) => `- \`${ref}\``) : ["- none"]),
|
|
107
301
|
"",
|
|
108
|
-
"
|
|
302
|
+
"### Required UI Components Per Screen",
|
|
109
303
|
"",
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
"- Preserve empty, success, and error states from the screen map and wireframes.",
|
|
114
|
-
"- Apply typography, color, spacing, button, card, form, tone, and accessibility rules from `DESIGN_SYSTEM.md`.",
|
|
304
|
+
"| Screen | Route | Brief | Required components |",
|
|
305
|
+
"|---|---|---|---|",
|
|
306
|
+
...(rows.length > 0 ? rows : ["| none | none | none | none |"]),
|
|
115
307
|
"",
|
|
116
|
-
"
|
|
308
|
+
"### No-Debug-UI Acceptance Rules",
|
|
117
309
|
"",
|
|
118
|
-
"
|
|
310
|
+
"- Customer-facing UI must not expose raw JSON dumps, `<pre>` debug panels, stack traces, or schema payloads.",
|
|
311
|
+
"- Prototype and implementation should keep structured components as the default surface for state and errors.",
|
|
312
|
+
"- Debug data can exist only behind developer-only diagnostics not visible in customer-facing routes.",
|
|
119
313
|
"",
|
|
120
|
-
"-
|
|
121
|
-
`- \`name\`: ${profile.setupLabel.toLowerCase()} name.`,
|
|
122
|
-
"- `createdAt`: ISO timestamp.",
|
|
314
|
+
"### Route-Level Visual Acceptance Checks",
|
|
123
315
|
"",
|
|
124
|
-
|
|
316
|
+
"- Every screen route must map to a corresponding screen brief and required component set.",
|
|
317
|
+
"- Navigation links between covered screens must remain visible and deterministic.",
|
|
318
|
+
"- Required states (empty, success, error, and screen-specific states) must be rendered without raw debug fallback.",
|
|
125
319
|
"",
|
|
126
|
-
"-
|
|
127
|
-
`- \`name\`: ${profile.itemSingular} name or label.`,
|
|
128
|
-
"- `status`: workflow status.",
|
|
129
|
-
"- `nextAction`: next action or due context.",
|
|
130
|
-
"- `note`: latest context note or summary.",
|
|
131
|
-
"- `createdAt`: ISO timestamp.",
|
|
132
|
-
"- `updatedAt`: ISO timestamp.",
|
|
320
|
+
"### SDTK-CODE Preservation Notes",
|
|
133
321
|
"",
|
|
134
|
-
"
|
|
135
|
-
"",
|
|
136
|
-
|
|
137
|
-
"- Next action: the next planned step needed to keep the workflow moving.",
|
|
138
|
-
"- Note: short context needed before the next action.",
|
|
139
|
-
`- ${profile.collectionSurface.charAt(0).toUpperCase() + profile.collectionSurface.slice(1)}: the grouped view for active ${profile.itemPlural}.`,
|
|
140
|
-
`- Status taxonomy: ${profile.statusTaxonomy.join(", ")}.`,
|
|
141
|
-
"",
|
|
142
|
-
"## Interaction Flow",
|
|
143
|
-
"",
|
|
144
|
-
"1. Landing -> user chooses the primary CTA.",
|
|
145
|
-
`2. Onboarding -> user enters ${profile.setupLabel.toLowerCase()} name and continues.`,
|
|
146
|
-
`3. Dashboard -> user sees empty state, ${profile.itemSingular} form, and status counters.`,
|
|
147
|
-
`4. ${profile.itemAction} -> validate required fields, append ${profile.itemSingular}, update list/counts, and show the saved note/next action.`,
|
|
148
|
-
`5. Refresh -> restore ${profile.setupLabel.toLowerCase()} and ${profile.itemPlural} from local persistence before rendering dashboard state.`,
|
|
149
|
-
"",
|
|
150
|
-
"## Persistence Guidance",
|
|
151
|
-
"",
|
|
152
|
-
"- For a frontend-only MVP, use browser `localStorage` or equivalent local persistence.",
|
|
153
|
-
`- Suggested keys: \`${setupKey}\` and \`${itemKey}\`.`,
|
|
154
|
-
"- Store arrays/objects as JSON and tolerate parse failures by falling back to empty state.",
|
|
155
|
-
"- Do not introduce backend, API, auth, database, or network persistence unless a later SDTK-CODE task explicitly adds it.",
|
|
156
|
-
"",
|
|
157
|
-
"## Visual Implementation Contract",
|
|
158
|
-
"",
|
|
159
|
-
"- Implement the selected visual preset from `DESIGN_SYSTEM.md` before adding extra decoration.",
|
|
160
|
-
`- Preserve landing hero hierarchy, onboarding form clarity, dashboard metric density, ${profile.collectionSurface}, status treatment, and empty state.`,
|
|
161
|
-
"- Keep responsive behavior explicit: grids collapse at mobile width and controls remain at least 44px tall.",
|
|
162
|
-
"- Pair every status color with text; do not rely on color alone.",
|
|
163
|
-
"- Avoid shipping a gray wireframe look when the prototype/design system provides finished tokens.",
|
|
164
|
-
"",
|
|
165
|
-
"## Prototype Reference",
|
|
166
|
-
"",
|
|
167
|
-
`- ${prototypeReference}`,
|
|
168
|
-
"- Treat the prototype as visual direction only, not production application code.",
|
|
169
|
-
"",
|
|
170
|
-
"## SDTK-CODE Build Notes",
|
|
171
|
-
"",
|
|
172
|
-
"- A static frontend implementation may use `app/index.html`, `app/styles.css`, and `app/app.js` when no framework is required.",
|
|
173
|
-
"- Keep runtime dependencies optional; this handoff does not require a framework, server, or package install.",
|
|
174
|
-
`- Add verification for local rendering, ${profile.itemAction.toLowerCase()} behavior, persistence after refresh, and mobile-width layout.`,
|
|
175
|
-
"- SDTK-CODE owns implementation planning, code changes, tests, and ship readiness.",
|
|
176
|
-
"",
|
|
177
|
-
"## Acceptance Criteria",
|
|
178
|
-
"",
|
|
179
|
-
"- Landing renders the product promise, workflow preview, and a single dominant primary CTA.",
|
|
180
|
-
"- Onboarding captures only required setup data and keeps validation inline.",
|
|
181
|
-
`- Dashboard shows active ${profile.itemPlural}, next action, status, empty state, and recoverable error state.`,
|
|
182
|
-
"- Components follow the design system tokens and accessibility baseline.",
|
|
183
|
-
"- No production app code, network integration, Pro entitlement, `.sdtk/atlas`, or SDTK-WIKI mutation is implied by this handoff.",
|
|
184
|
-
"",
|
|
185
|
-
"## Non-Goals",
|
|
186
|
-
"",
|
|
187
|
-
"- Figma, Lovable, v0, or full app-builder replacement behavior.",
|
|
188
|
-
`- ${profile.nonGoals}`,
|
|
189
|
-
"- Package publish or deployment.",
|
|
322
|
+
"- Preserve token contract (`DESIGN_TOKENS.json`) and component pattern references during implementation.",
|
|
323
|
+
"- Do not replace required UI components with generic placeholder panels when high-fidelity contract artifacts exist.",
|
|
324
|
+
"- Keep no-debug-UI rules as QA-checked acceptance gates for customer-facing screens.",
|
|
190
325
|
"",
|
|
191
326
|
].join("\n");
|
|
192
327
|
}
|
|
193
|
-
|
|
328
|
+
|
|
194
329
|
function runDesignHandoff({ projectPath, force = false }) {
|
|
195
|
-
const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
|
|
196
|
-
if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
|
|
197
|
-
throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}. No project files were changed.`);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const paths = describeDesignPaths(resolvedProjectPath);
|
|
201
|
-
const missing = requiredArtifacts(paths).filter((artifact) => !fs.existsSync(artifact.filePath));
|
|
202
|
-
if (missing.length > 0) {
|
|
203
|
-
const missingList = missing.map((artifact) => artifact.relativePath).join(", ");
|
|
204
|
-
throw new ValidationError(`Missing required design artifacts: ${missingList}. Run the previous SDTK-DESIGN commands first. No project files were changed.`);
|
|
205
|
-
}
|
|
206
|
-
if (fs.existsSync(paths.designHandoffPath) && !force) {
|
|
207
|
-
throw new ValidationError("docs/design/DESIGN_HANDOFF.md already exists. Re-run with --force to replace this managed handoff.");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const briefContent = fs.readFileSync(paths.designBriefPath, "utf-8");
|
|
211
|
-
const screenMapContent = fs.readFileSync(paths.screenMapPath, "utf-8");
|
|
212
|
-
const designSystemContent = fs.readFileSync(paths.designSystemPath, "utf-8");
|
|
213
|
-
const wireframeContents = REQUIRED_WIREFRAMES.map((fileName) => fs.readFileSync(path.join(paths.wireframesPath, fileName), "utf-8"));
|
|
330
|
+
const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
|
|
331
|
+
if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
|
|
332
|
+
throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}. No project files were changed.`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const paths = describeDesignPaths(resolvedProjectPath);
|
|
336
|
+
const missing = requiredArtifacts(paths).filter((artifact) => !fs.existsSync(artifact.filePath));
|
|
337
|
+
if (missing.length > 0) {
|
|
338
|
+
const missingList = missing.map((artifact) => artifact.relativePath).join(", ");
|
|
339
|
+
throw new ValidationError(`Missing required design artifacts: ${missingList}. Run the previous SDTK-DESIGN commands first. No project files were changed.`);
|
|
340
|
+
}
|
|
341
|
+
if (fs.existsSync(paths.designHandoffPath) && !force) {
|
|
342
|
+
throw new ValidationError("docs/design/DESIGN_HANDOFF.md already exists. Re-run with --force to replace this managed handoff.");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const briefContent = fs.readFileSync(paths.designBriefPath, "utf-8");
|
|
346
|
+
const screenMapContent = fs.readFileSync(paths.screenMapPath, "utf-8");
|
|
347
|
+
const designSystemContent = fs.readFileSync(paths.designSystemPath, "utf-8");
|
|
348
|
+
const wireframeContents = REQUIRED_WIREFRAMES.map((fileName) => fs.readFileSync(path.join(paths.wireframesPath, fileName), "utf-8"));
|
|
214
349
|
const profile = inferDomainProfile({ briefContent, screenMapContent, designSystemContent, wireframeContents });
|
|
350
|
+
const statePayload = readInputContractState(paths);
|
|
351
|
+
const highFidelityReady =
|
|
352
|
+
statePayload &&
|
|
353
|
+
statePayload.mode === "from-spec" &&
|
|
354
|
+
statePayload.analysisStatus === "INPUT_CONTRACT_READY" &&
|
|
355
|
+
fs.existsSync(paths.componentPatternLibraryPath) &&
|
|
356
|
+
fs.existsSync(paths.designTokensPath) &&
|
|
357
|
+
listScreenBriefArtifacts(paths).length > 0;
|
|
215
358
|
|
|
216
359
|
fs.mkdirSync(path.dirname(paths.designHandoffPath), { recursive: true });
|
|
217
|
-
|
|
360
|
+
const prototypeRefs = listPrototypeArtifacts(paths);
|
|
361
|
+
const base = handoffContent({ hasPrototype: fs.existsSync(paths.prototypeIndexPath), prototypeRefs, profile });
|
|
362
|
+
const appendix = highFidelityReady
|
|
363
|
+
? highFidelityHandoffAppendix({
|
|
364
|
+
paths,
|
|
365
|
+
statePayload,
|
|
366
|
+
hasPrototype: fs.existsSync(paths.prototypeIndexPath),
|
|
367
|
+
})
|
|
368
|
+
: "## High-Fidelity UI Contract\n\n- Not activated for this project state. Simple MVP handoff remains valid.\n";
|
|
369
|
+
fs.writeFileSync(paths.designHandoffPath, `${base}\n${appendix}\n`, "utf-8");
|
|
218
370
|
|
|
219
371
|
return {
|
|
220
372
|
projectPath: resolvedProjectPath,
|
|
221
373
|
relativeDesignHandoffPath: "docs/design/DESIGN_HANDOFF.md",
|
|
374
|
+
highFidelityReady,
|
|
222
375
|
forced: Boolean(force),
|
|
223
376
|
};
|
|
224
377
|
}
|
|
225
|
-
|
|
226
|
-
function cmdHandoff(args) {
|
|
227
|
-
const { flags } = parseFlags(args || [], HANDOFF_FLAG_DEFS);
|
|
228
|
-
if (flags.help) return cmdHandoffHelp();
|
|
229
|
-
|
|
230
|
-
const result = runDesignHandoff({
|
|
231
|
-
projectPath: flags["project-path"],
|
|
232
|
-
force: Boolean(flags.force),
|
|
233
|
-
});
|
|
234
|
-
|
|
378
|
+
|
|
379
|
+
function cmdHandoff(args) {
|
|
380
|
+
const { flags } = parseFlags(args || [], HANDOFF_FLAG_DEFS);
|
|
381
|
+
if (flags.help) return cmdHandoffHelp();
|
|
382
|
+
|
|
383
|
+
const result = runDesignHandoff({
|
|
384
|
+
projectPath: flags["project-path"],
|
|
385
|
+
force: Boolean(flags.force),
|
|
386
|
+
});
|
|
387
|
+
|
|
235
388
|
console.log(`[design] Wrote ${result.relativeDesignHandoffPath}: ${result.projectPath}`);
|
|
236
389
|
console.log(`[design] Verdict: READY_FOR_SDTK_CODE`);
|
|
390
|
+
console.log(`[design] High-fidelity contract: ${result.highFidelityReady ? "enabled" : "not enabled"}`);
|
|
237
391
|
console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
|
|
238
|
-
console.log("[design] No .sdtk/atlas, SDTK-WIKI output, source files, network, or app code was modified.");
|
|
239
|
-
return 0;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
module.exports = {
|
|
243
|
-
cmdHandoff,
|
|
244
|
-
cmdHandoffHelp,
|
|
245
|
-
handoffContent,
|
|
246
|
-
requiredArtifacts,
|
|
247
|
-
runDesignHandoff,
|
|
248
|
-
};
|
|
392
|
+
console.log("[design] No .sdtk/atlas, SDTK-WIKI output, source files, network, or app code was modified.");
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
module.exports = {
|
|
397
|
+
cmdHandoff,
|
|
398
|
+
cmdHandoffHelp,
|
|
399
|
+
handoffContent,
|
|
400
|
+
requiredArtifacts,
|
|
401
|
+
runDesignHandoff,
|
|
402
|
+
};
|
package/src/commands/help.js
CHANGED
|
@@ -8,7 +8,7 @@ Usage:
|
|
|
8
8
|
sdtk-design --version
|
|
9
9
|
sdtk-design init
|
|
10
10
|
sdtk-design start --idea "<idea>" --style premium-dashboard
|
|
11
|
-
sdtk-design start --from-spec . --reference-dir ./docs/
|
|
11
|
+
sdtk-design start --from-spec . --reference-dir ./docs/design/reference-export --profile b2b-commerce
|
|
12
12
|
sdtk-design brief --idea "<idea>"
|
|
13
13
|
sdtk-design screens
|
|
14
14
|
sdtk-design wireframe --screen landing
|
|
@@ -20,7 +20,7 @@ Usage:
|
|
|
20
20
|
|
|
21
21
|
Examples:
|
|
22
22
|
sdtk-design start --idea "I want to build a lightweight CRM for solo consultants to track leads." --style premium-dashboard
|
|
23
|
-
sdtk-design start --from-spec . --reference-dir ./docs/
|
|
23
|
+
sdtk-design start --from-spec . --reference-dir ./docs/design/reference-export --profile b2b-commerce
|
|
24
24
|
sdtk-design prototype --style premium-dashboard
|
|
25
25
|
sdtk-design review --artifact docs/design/prototype/index.html
|
|
26
26
|
sdtk-design handoff
|