sdtk-design-kit 0.2.1 → 0.3.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.
@@ -1,60 +1,60 @@
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]
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
-
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
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
- ];
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
58
  }
59
59
 
60
60
  function readJsonSafe(filePath) {
@@ -130,140 +130,140 @@ function normalizeScreenRole(screen) {
130
130
  if (token.includes("configurator") || token.includes("mode-b")) return "mode-b-configurator";
131
131
  return screen.screenId || "screen";
132
132
  }
133
-
133
+
134
134
  function handoffContent({ hasPrototype = false, prototypeRefs = [], profile = inferDomainProfile() } = {}) {
135
135
  const prototypeReference = hasPrototype
136
136
  ? "`docs/design/prototype/index.html` is available as the static visual preview reference."
137
137
  : "Run `sdtk-design prototype` to create `docs/design/prototype/index.html` before implementation if visual preview evidence is needed.";
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
- "",
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
239
  "## Prototype Reference",
240
240
  "",
241
241
  `- ${prototypeReference}`,
242
242
  ...(prototypeRefs.length > 0 ? prototypeRefs.map((ref) => `- \`${ref}\``) : []),
243
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");
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
267
  }
268
268
 
269
269
  function highFidelityHandoffAppendix({ paths, statePayload, hasPrototype }) {
@@ -325,27 +325,27 @@ function highFidelityHandoffAppendix({ paths, statePayload, hasPrototype }) {
325
325
  "",
326
326
  ].join("\n");
327
327
  }
328
-
328
+
329
329
  function runDesignHandoff({ projectPath, force = false }) {
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"));
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"));
349
349
  const profile = inferDomainProfile({ briefContent, screenMapContent, designSystemContent, wireframeContents });
350
350
  const statePayload = readInputContractState(paths);
351
351
  const highFidelityReady =
@@ -375,28 +375,28 @@ function runDesignHandoff({ projectPath, force = false }) {
375
375
  forced: Boolean(force),
376
376
  };
377
377
  }
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
-
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
+
388
388
  console.log(`[design] Wrote ${result.relativeDesignHandoffPath}: ${result.projectPath}`);
389
389
  console.log(`[design] Verdict: READY_FOR_SDTK_CODE`);
390
390
  console.log(`[design] High-fidelity contract: ${result.highFidelityReady ? "enabled" : "not enabled"}`);
391
391
  console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
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
- };
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
+ };
@@ -13,18 +13,21 @@ Usage:
13
13
  sdtk-design screens
14
14
  sdtk-design wireframe --screen landing
15
15
  sdtk-design system --style minimal-saas
16
- sdtk-design prototype --style premium-dashboard
16
+ sdtk-design prototype
17
17
  sdtk-design review --artifact docs/design/prototype/index.html
18
18
  sdtk-design handoff
19
19
  sdtk-design status
20
+ sdtk-design update --check-only
20
21
 
21
22
  Examples:
22
23
  sdtk-design start --idea "I want to build a lightweight CRM for solo consultants to track leads." --style premium-dashboard
23
24
  sdtk-design start --from-spec . --reference-dir ./docs/design/reference-export --profile b2b-commerce
24
- sdtk-design prototype --style premium-dashboard
25
+ sdtk-design prototype
25
26
  sdtk-design review --artifact docs/design/prototype/index.html
26
27
  sdtk-design handoff
27
28
  sdtk-design status
29
+ sdtk-design update --check-only
30
+ npm install -g sdtk-design-kit@<version>
28
31
 
29
32
  Visual style presets:
30
33
  minimal-saas, premium-dashboard, bold-founder, warm-editorial
@@ -53,11 +56,12 @@ Command status:
53
56
  screens Available.
54
57
  wireframe Available.
55
58
  system Available.
56
- prototype Available static HTML/CSS preview.
59
+ prototype Available SPEC-driven prototype manifest launcher.
57
60
  handoff Available.
58
61
  review Available for local markdown and static HTML artifacts.
59
62
  start Available beginner facade.
60
63
  status Available.
64
+ update Available package-only updater; no project/design files are mutated in R1.
61
65
 
62
66
  Deferred:
63
67
  URL/browser/screenshot/vision review is not implemented in Foundation Beta.
@@ -17,6 +17,13 @@ const INIT_FLAG_DEFS = {
17
17
  "no-state": { type: "boolean" },
18
18
  };
19
19
 
20
+ const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
21
+ const DESIGN_PROTOTYPE_SKILL_SOURCE = path.join(PACKAGE_ROOT, "skills", "design-prototype");
22
+ const DESIGN_PROTOTYPE_SKILL_TARGETS = [
23
+ path.join(".claude", "skills", "design-prototype"),
24
+ path.join(".agents", "skills", "design-prototype"),
25
+ ];
26
+
20
27
  function toPosix(value) {
21
28
  return String(value || "").replace(/\\/g, "/");
22
29
  }
@@ -113,6 +120,51 @@ function writeManagedFile(filePath, content, projectPath, force, result) {
113
120
  result.created.push(toPosix(path.relative(projectPath, filePath)));
114
121
  }
115
122
 
123
+ function listSkillFiles(sourceDir) {
124
+ const results = [];
125
+ const stack = [sourceDir];
126
+ while (stack.length > 0) {
127
+ const current = stack.pop();
128
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
129
+ const absolutePath = path.join(current, entry.name);
130
+ if (entry.isDirectory()) {
131
+ stack.push(absolutePath);
132
+ continue;
133
+ }
134
+ if (entry.isFile()) {
135
+ results.push(absolutePath);
136
+ }
137
+ }
138
+ }
139
+ return results.sort();
140
+ }
141
+
142
+ function installDesignPrototypeSkill(projectPath, force, result) {
143
+ if (!fs.existsSync(DESIGN_PROTOTYPE_SKILL_SOURCE) || !fs.statSync(DESIGN_PROTOTYPE_SKILL_SOURCE).isDirectory()) {
144
+ throw new ValidationError(`Bundled design-prototype skill is missing: ${DESIGN_PROTOTYPE_SKILL_SOURCE}. No project files were changed.`);
145
+ }
146
+
147
+ for (const relativeTargetDir of DESIGN_PROTOTYPE_SKILL_TARGETS) {
148
+ const targetDir = path.join(projectPath, relativeTargetDir);
149
+ assertProjectLocalPath(targetDir, projectPath, "skill directory");
150
+ fs.mkdirSync(targetDir, { recursive: true });
151
+ result.created.push(toPosix(relativeTargetDir) + "/");
152
+
153
+ for (const sourceFile of listSkillFiles(DESIGN_PROTOTYPE_SKILL_SOURCE)) {
154
+ const relativeFile = path.relative(DESIGN_PROTOTYPE_SKILL_SOURCE, sourceFile);
155
+ const targetFile = path.join(targetDir, relativeFile);
156
+ assertProjectLocalPath(targetFile, projectPath, "skill file");
157
+ if (fs.existsSync(targetFile) && !force) {
158
+ result.skipped.push(toPosix(path.relative(projectPath, targetFile)));
159
+ continue;
160
+ }
161
+ fs.mkdirSync(path.dirname(targetFile), { recursive: true });
162
+ fs.copyFileSync(sourceFile, targetFile);
163
+ result.created.push(toPosix(path.relative(projectPath, targetFile)));
164
+ }
165
+ }
166
+ }
167
+
116
168
  function runDesignInit({ projectPath, force = false, state = true }) {
117
169
  const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
118
170
  if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
@@ -131,6 +183,7 @@ function runDesignInit({ projectPath, force = false, state = true }) {
131
183
  ensureDirectory(paths.wireframesPath, resolvedProjectPath, result);
132
184
  ensureDirectory(paths.reviewsPath, resolvedProjectPath, result);
133
185
  writeManagedFile(paths.designReadmePath, designReadmeContent(), resolvedProjectPath, force, result);
186
+ installDesignPrototypeSkill(resolvedProjectPath, force, result);
134
187
 
135
188
  if (state) {
136
189
  ensureDirectory(paths.designStatePath, resolvedProjectPath, result);