sdtk-design-kit 0.1.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.
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { parseFlags } = require("../lib/args");
6
+ const { describeDesignPaths, resolveProjectPath } = require("../lib/design-paths");
7
+ const { ValidationError } = require("../lib/errors");
8
+
9
+ const STATUS_FLAG_DEFS = {
10
+ help: { type: "boolean" },
11
+ "project-path": { type: "string" },
12
+ };
13
+
14
+ function cmdStatusHelp() {
15
+ console.log(`SDTK-DESIGN Status
16
+
17
+ Usage:
18
+ sdtk-design status [--project-path <path>]
19
+
20
+ Example:
21
+ sdtk-design status
22
+
23
+ Reports:
24
+ Complete artifacts.
25
+ Missing artifacts.
26
+ Next recommended command.
27
+
28
+ Safety:
29
+ Read-only local filesystem inspection.
30
+ No .sdtk/atlas creation or mutation.
31
+ No SDTK-WIKI output mutation.
32
+ No network call, Pro entitlement, or production app code generation.`);
33
+ return 0;
34
+ }
35
+
36
+ function artifactPlan(paths) {
37
+ return [
38
+ { label: "Design brief", relativePath: "docs/design/DESIGN_BRIEF.md", filePath: paths.designBriefPath, phase: "core" },
39
+ { label: "Screen map", relativePath: "docs/design/SCREEN_MAP.md", filePath: paths.screenMapPath, phase: "core" },
40
+ { label: "Landing wireframe", relativePath: "docs/design/wireframes/LANDING.md", filePath: path.join(paths.wireframesPath, "LANDING.md"), phase: "core" },
41
+ { label: "Onboarding wireframe", relativePath: "docs/design/wireframes/ONBOARDING.md", filePath: path.join(paths.wireframesPath, "ONBOARDING.md"), phase: "core" },
42
+ { label: "Dashboard wireframe", relativePath: "docs/design/wireframes/DASHBOARD.md", filePath: path.join(paths.wireframesPath, "DASHBOARD.md"), phase: "core" },
43
+ { label: "Design system", relativePath: "docs/design/DESIGN_SYSTEM.md", filePath: paths.designSystemPath, phase: "core" },
44
+ { label: "Design handoff", relativePath: "docs/design/DESIGN_HANDOFF.md", filePath: paths.designHandoffPath, phase: "handoff" },
45
+ ];
46
+ }
47
+
48
+ function hasReview(paths) {
49
+ if (!fs.existsSync(paths.reviewsPath) || !fs.statSync(paths.reviewsPath).isDirectory()) {
50
+ return false;
51
+ }
52
+ return fs.readdirSync(paths.reviewsPath).some((name) => /^DESIGN_REVIEW_\d{8}\.md$/.test(name));
53
+ }
54
+
55
+ function inspectDesignStatus(projectPath) {
56
+ const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
57
+ if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
58
+ throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}. No project files were changed.`);
59
+ }
60
+
61
+ const paths = describeDesignPaths(resolvedProjectPath);
62
+ const artifacts = artifactPlan(paths).map((artifact) => ({
63
+ ...artifact,
64
+ exists: fs.existsSync(artifact.filePath),
65
+ }));
66
+ const reviewExists = hasReview(paths);
67
+ const coreMissing = artifacts.filter((artifact) => artifact.phase === "core" && !artifact.exists);
68
+ const handoffMissing = artifacts.find((artifact) => artifact.phase === "handoff" && !artifact.exists);
69
+
70
+ let nextCommand = "SDTK-CODE can consume docs/design/DESIGN_HANDOFF.md";
71
+ if (coreMissing.length > 0) {
72
+ nextCommand = 'sdtk-design start --idea "<idea>"';
73
+ } else if (!reviewExists) {
74
+ nextCommand = "sdtk-design review --artifact docs/design/wireframes/LANDING.md";
75
+ } else if (handoffMissing) {
76
+ nextCommand = "sdtk-design handoff";
77
+ }
78
+
79
+ return {
80
+ projectPath: resolvedProjectPath,
81
+ artifacts,
82
+ reviewExists,
83
+ nextCommand,
84
+ };
85
+ }
86
+
87
+ function cmdStatus(args) {
88
+ const { flags } = parseFlags(args || [], STATUS_FLAG_DEFS);
89
+ if (flags.help) return cmdStatusHelp();
90
+
91
+ const status = inspectDesignStatus(flags["project-path"]);
92
+ const complete = status.artifacts.filter((artifact) => artifact.exists);
93
+ const missing = status.artifacts.filter((artifact) => !artifact.exists);
94
+
95
+ console.log(`SDTK-DESIGN Status: ${status.projectPath}`);
96
+ console.log("");
97
+ console.log("Complete artifacts:");
98
+ if (complete.length === 0 && !status.reviewExists) {
99
+ console.log(" - none");
100
+ } else {
101
+ for (const artifact of complete) {
102
+ console.log(` - ${artifact.relativePath}`);
103
+ }
104
+ if (status.reviewExists) {
105
+ console.log(" - docs/design/reviews/DESIGN_REVIEW_YYYYMMDD.md");
106
+ }
107
+ }
108
+ console.log("");
109
+ console.log("Missing artifacts:");
110
+ if (missing.length === 0 && status.reviewExists) {
111
+ console.log(" - none");
112
+ } else {
113
+ for (const artifact of missing) {
114
+ console.log(` - ${artifact.relativePath}`);
115
+ }
116
+ if (!status.reviewExists) {
117
+ console.log(" - docs/design/reviews/DESIGN_REVIEW_YYYYMMDD.md");
118
+ }
119
+ }
120
+ console.log("");
121
+ console.log(`Next recommended command: ${status.nextCommand}`);
122
+ console.log("No .sdtk/atlas, SDTK-WIKI output, network, or app code was modified.");
123
+ return 0;
124
+ }
125
+
126
+ module.exports = {
127
+ artifactPlan,
128
+ cmdStatus,
129
+ cmdStatusHelp,
130
+ inspectDesignStatus,
131
+ };
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { parseFlags } = require("../lib/args");
6
+ const { describeDesignPaths, resolveProjectPath } = require("../lib/design-paths");
7
+ const { ValidationError } = require("../lib/errors");
8
+
9
+ const SYSTEM_FLAG_DEFS = {
10
+ help: { type: "boolean" },
11
+ "project-path": { type: "string" },
12
+ force: { type: "boolean" },
13
+ };
14
+
15
+ function cmdSystemHelp() {
16
+ console.log(`SDTK-DESIGN System
17
+
18
+ Usage:
19
+ sdtk-design system [--project-path <path>] [--force]
20
+
21
+ Example:
22
+ sdtk-design system
23
+
24
+ Reads:
25
+ docs/design/DESIGN_BRIEF.md
26
+ docs/design/SCREEN_MAP.md
27
+
28
+ Creates:
29
+ docs/design/DESIGN_SYSTEM.md
30
+
31
+ Safety:
32
+ Local files only.
33
+ Existing DESIGN_SYSTEM.md is not overwritten unless --force is explicit.
34
+ No .sdtk/atlas creation or mutation.
35
+ No SDTK-WIKI output mutation.
36
+ No network call, Pro entitlement, or production app code generation.`);
37
+ return 0;
38
+ }
39
+
40
+ function includesAny(text, terms) {
41
+ const value = text.toLowerCase();
42
+ return terms.some((term) => value.includes(term));
43
+ }
44
+
45
+ function systemContent(briefContent, screenMapContent) {
46
+ const source = `${briefContent}\n${screenMapContent}`;
47
+ const isCrm = includesAny(source, ["crm", "lead", "follow-up", "pipeline"]);
48
+ const primaryAction = isCrm ? "Add first lead" : "Start first workflow";
49
+ const toneObject = isCrm ? "lead, follow-up, note, and pipeline" : "record, task, status, and next action";
50
+
51
+ return [
52
+ "# Design System",
53
+ "",
54
+ "## Product Direction",
55
+ "",
56
+ "- Use a calm, work-focused interface that helps a solo founder move from idea to repeated MVP workflow.",
57
+ "- Favor clarity, scannability, and obvious next actions over decorative marketing composition.",
58
+ `- Primary repeated action: ${primaryAction}.`,
59
+ "",
60
+ "## Typography",
61
+ "",
62
+ "- Font stack: system UI, Segoe UI, Roboto, Helvetica, Arial, sans-serif.",
63
+ "- Page title: 28px, 36px line-height, semibold.",
64
+ "- Section heading: 18px, 26px line-height, semibold.",
65
+ "- Body text: 15px, 22px line-height, regular.",
66
+ "- Label text: 13px, 18px line-height, medium.",
67
+ "- Do not scale font size with viewport width; keep letter spacing at 0.",
68
+ "",
69
+ "## Color Tokens",
70
+ "",
71
+ "- `color-bg`: #F7F8FA for the application background.",
72
+ "- `color-surface`: #FFFFFF for panels, forms, and lists.",
73
+ "- `color-text`: #1F2933 for primary text.",
74
+ "- `color-muted`: #667085 for secondary text.",
75
+ "- `color-primary`: #2563EB for the main CTA and selected states.",
76
+ "- `color-success`: #0F8A5F for confirmed progress.",
77
+ "- `color-warning`: #B7791F for attention without blocking.",
78
+ "- `color-danger`: #B42318 for destructive or failed states.",
79
+ "- `color-border`: #D9DEE7 for quiet dividers and input borders.",
80
+ "",
81
+ "## Spacing",
82
+ "",
83
+ "- Base grid: 4px.",
84
+ "- Tight gap: 8px for label-control and icon-text pairs.",
85
+ "- Standard gap: 16px for form fields and list rows.",
86
+ "- Section gap: 24px for screen regions.",
87
+ "- Page padding: 20px mobile, 32px desktop.",
88
+ "- Keep fixed-format controls stable with explicit min-height and predictable grid tracks.",
89
+ "",
90
+ "## Button Style",
91
+ "",
92
+ "- Primary button: filled `color-primary`, white label, 40px minimum height, 8px radius.",
93
+ "- Secondary button: white surface, `color-border` outline, primary text.",
94
+ "- Icon buttons: square 36px control with tooltip when the icon is not self-evident.",
95
+ "- Disabled state: 45% opacity with no hover elevation.",
96
+ "- CTA copy should use verbs tied to the workflow, such as Add, Save, Continue, or Review.",
97
+ "",
98
+ "## Card Style",
99
+ "",
100
+ "- Use cards only for repeated records or genuinely framed tool surfaces.",
101
+ "- Radius: 8px maximum.",
102
+ "- Border: 1px solid `color-border`; avoid heavy shadows.",
103
+ "- Card content order: status, title, next action, supporting metadata.",
104
+ "- Avoid nested cards and decorative gradient backgrounds.",
105
+ "",
106
+ "## Form Style",
107
+ "",
108
+ "- Place labels above inputs with concise helper text only when it reduces uncertainty.",
109
+ "- Required fields should be obvious before submit.",
110
+ "- Inline errors must identify the field and preserve entered values.",
111
+ "- Prefer segmented controls or toggles for short option sets.",
112
+ "- Forms should ask for the minimum needed before the first useful action.",
113
+ "",
114
+ "## Tone",
115
+ "",
116
+ `- Use practical language around ${toneObject}; avoid enterprise jargon.`,
117
+ "- Keep copy short, direct, and action-oriented.",
118
+ "- Empty states should explain what is missing and offer one next action.",
119
+ "- Success copy should confirm progress without celebration-heavy language.",
120
+ "",
121
+ "## Accessibility Baseline",
122
+ "",
123
+ "- Maintain visible keyboard focus for all interactive controls.",
124
+ "- Use 4.5:1 contrast for body text and 3:1 for large text and essential UI boundaries.",
125
+ "- Do not rely on color alone for status; pair status color with text or icon labels.",
126
+ "- All form inputs require labels; all icon-only controls require accessible names.",
127
+ "- Mobile targets should be at least 44px by 44px where practical.",
128
+ "- Error and success states must be announced through text in the layout.",
129
+ "",
130
+ "## Boundaries",
131
+ "",
132
+ "- This design system is guidance for SDTK-DESIGN artifacts, not generated production CSS.",
133
+ "- No Figma, Lovable, v0, or full app-builder replacement behavior is claimed.",
134
+ "- No network call, Pro entitlement, `.sdtk/atlas`, or SDTK-WIKI output is required.",
135
+ "",
136
+ ].join("\n");
137
+ }
138
+
139
+ function runDesignSystem({ projectPath, force = false }) {
140
+ const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
141
+ if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
142
+ throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}. No project files were changed.`);
143
+ }
144
+
145
+ const paths = describeDesignPaths(resolvedProjectPath);
146
+ if (!fs.existsSync(paths.designBriefPath)) {
147
+ throw new ValidationError("Missing docs/design/DESIGN_BRIEF.md. Run sdtk-design brief --idea \"<idea>\" first. No project files were changed.");
148
+ }
149
+ if (!fs.existsSync(paths.screenMapPath)) {
150
+ throw new ValidationError("Missing docs/design/SCREEN_MAP.md. Run sdtk-design screens first. No project files were changed.");
151
+ }
152
+ if (fs.existsSync(paths.designSystemPath) && !force) {
153
+ throw new ValidationError("docs/design/DESIGN_SYSTEM.md already exists. Re-run with --force to replace this managed design system.");
154
+ }
155
+
156
+ const briefContent = fs.readFileSync(paths.designBriefPath, "utf-8");
157
+ const screenMapContent = fs.readFileSync(paths.screenMapPath, "utf-8");
158
+ fs.mkdirSync(path.dirname(paths.designSystemPath), { recursive: true });
159
+ fs.writeFileSync(paths.designSystemPath, systemContent(briefContent, screenMapContent), "utf-8");
160
+
161
+ return {
162
+ projectPath: resolvedProjectPath,
163
+ relativeDesignSystemPath: "docs/design/DESIGN_SYSTEM.md",
164
+ forced: Boolean(force),
165
+ };
166
+ }
167
+
168
+ function cmdSystem(args) {
169
+ const { flags } = parseFlags(args || [], SYSTEM_FLAG_DEFS);
170
+ if (flags.help) return cmdSystemHelp();
171
+
172
+ const result = runDesignSystem({
173
+ projectPath: flags["project-path"],
174
+ force: Boolean(flags.force),
175
+ });
176
+
177
+ console.log(`[design] Wrote ${result.relativeDesignSystemPath}: ${result.projectPath}`);
178
+ console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
179
+ console.log("[design] No .sdtk/atlas, SDTK-WIKI output, source files, network, or app code was modified.");
180
+ console.log("[design] Next: sdtk-design handoff");
181
+ return 0;
182
+ }
183
+
184
+ module.exports = {
185
+ cmdSystem,
186
+ cmdSystemHelp,
187
+ runDesignSystem,
188
+ systemContent,
189
+ };
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { parseFlags } = require("../lib/args");
6
+ const { describeDesignPaths, resolveProjectPath } = require("../lib/design-paths");
7
+ const { ValidationError } = require("../lib/errors");
8
+
9
+ const WIREFRAME_FLAG_DEFS = {
10
+ help: { type: "boolean" },
11
+ screen: { type: "string" },
12
+ "project-path": { type: "string" },
13
+ force: { type: "boolean" },
14
+ };
15
+
16
+ const SCREEN_CONFIGS = {
17
+ landing: {
18
+ id: "LANDING",
19
+ title: "Landing",
20
+ fileName: "LANDING.md",
21
+ intent: "Convert a visitor from product promise to the first setup step.",
22
+ cta: "Add first lead",
23
+ sections: [
24
+ "Header with product name, concise positioning, and one primary CTA.",
25
+ "Hero promise with target user, pain point, MVP promise, and CTA.",
26
+ "Workflow preview showing lead capture, follow-up, notes, and pipeline visibility.",
27
+ "Trust strip with simple benefits and clear boundary against enterprise complexity.",
28
+ ],
29
+ components: ["Header", "Hero copy block", "Primary CTA button", "Workflow preview list", "Benefit bullets"],
30
+ mobile: "Stack hero copy, CTA, and preview vertically. Keep the CTA visible above the first scroll.",
31
+ copy: "Use direct solo-founder language. Lead with the outcome, then reduce fear of enterprise CRM complexity.",
32
+ },
33
+ onboarding: {
34
+ id: "ONBOARDING",
35
+ title: "Onboarding",
36
+ fileName: "ONBOARDING.md",
37
+ intent: "Capture the minimum setup needed before the first repeated workflow action.",
38
+ cta: "Create workspace",
39
+ sections: [
40
+ "Progress header with one short setup step.",
41
+ "Workspace basics form with required label and optional sample data choice.",
42
+ "First lead prompt that lets the user continue directly into lead capture.",
43
+ "Footer actions for continue and skip non-essential setup.",
44
+ ],
45
+ components: ["Progress indicator", "Text input", "Segmented sample-data control", "Primary CTA button", "Inline validation message"],
46
+ mobile: "Use a single-column form with large touch targets and persistent continue action after required fields.",
47
+ copy: "Keep labels plain and reassuring. Explain why each required field matters.",
48
+ },
49
+ dashboard: {
50
+ id: "DASHBOARD",
51
+ title: "Dashboard",
52
+ fileName: "DASHBOARD.md",
53
+ intent: "Help the user scan active work and choose the next follow-up action.",
54
+ cta: "Add first lead",
55
+ sections: [
56
+ "Top summary row with active leads, due follow-ups, and simple revenue pipeline status.",
57
+ "Priority list of leads with status, next action date, and latest note.",
58
+ "Inline quick-add action for a new lead or note.",
59
+ "Empty-state panel that routes back to the first lead CTA.",
60
+ ],
61
+ components: ["Summary counters", "Lead list", "Status pill", "Quick-add button", "Empty-state panel", "Recoverable error alert"],
62
+ mobile: "Collapse summary counters into two columns, then show lead cards with next action first.",
63
+ copy: "Use action-first labels such as Due today, Add note, and Move stage. Avoid enterprise CRM jargon.",
64
+ },
65
+ };
66
+
67
+ function cmdWireframeHelp() {
68
+ console.log(`SDTK-DESIGN Wireframe
69
+
70
+ Usage:
71
+ sdtk-design wireframe --screen landing [--project-path <path>] [--force]
72
+ sdtk-design wireframe --screen all [--project-path <path>] [--force]
73
+
74
+ Examples:
75
+ sdtk-design wireframe --screen landing
76
+ sdtk-design wireframe --screen all
77
+
78
+ Reads:
79
+ docs/design/SCREEN_MAP.md
80
+
81
+ Creates:
82
+ docs/design/wireframes/LANDING.md
83
+ docs/design/wireframes/ONBOARDING.md
84
+ docs/design/wireframes/DASHBOARD.md
85
+
86
+ Safety:
87
+ Local files only.
88
+ Existing wireframe files are not overwritten unless --force is explicit.
89
+ No .sdtk/atlas creation or mutation.
90
+ No SDTK-WIKI output mutation.
91
+ No network call, Pro entitlement, or production app code generation.`);
92
+ return 0;
93
+ }
94
+
95
+ function normalizeScreenName(screen) {
96
+ return String(screen || "").trim().toLowerCase();
97
+ }
98
+
99
+ function screenTargets(screen) {
100
+ const normalized = normalizeScreenName(screen);
101
+ if (!normalized) {
102
+ throw new ValidationError('Missing required --screen "<landing|onboarding|dashboard|all>". No project files were changed.');
103
+ }
104
+ if (normalized === "all") {
105
+ return ["landing", "onboarding", "dashboard"];
106
+ }
107
+ if (!SCREEN_CONFIGS[normalized]) {
108
+ throw new ValidationError(`Unsupported --screen "${screen}". Use landing, onboarding, dashboard, or all. No project files were changed.`);
109
+ }
110
+ return [normalized];
111
+ }
112
+
113
+ function extractScreenMapLine(screenMapContent, screenId, label) {
114
+ const lines = screenMapContent.split(/\r?\n/);
115
+ const start = lines.findIndex((line) => line.trim().startsWith(`### ${screenId} `));
116
+ if (start < 0) return "";
117
+ for (let index = start + 1; index < lines.length; index += 1) {
118
+ const line = lines[index].trim();
119
+ if (line.startsWith("### ")) return "";
120
+ if (line.startsWith(`- ${label}:`)) {
121
+ return line.slice(`- ${label}:`.length).trim();
122
+ }
123
+ }
124
+ return "";
125
+ }
126
+
127
+ function wireframeContent(screenKey, screenMapContent) {
128
+ const config = SCREEN_CONFIGS[screenKey];
129
+ const purpose = extractScreenMapLine(screenMapContent, config.id, "Purpose") || config.intent;
130
+ const dataNeeded = extractScreenMapLine(screenMapContent, config.id, "Data needed") || "Primary workflow data from the screen map.";
131
+ const emptyState = extractScreenMapLine(screenMapContent, config.id, "Empty state") || "Guide the user to the first meaningful action.";
132
+ const successState = extractScreenMapLine(screenMapContent, config.id, "Success state") || "User completes the screen's primary action.";
133
+ const errorState = extractScreenMapLine(screenMapContent, config.id, "Error state") || "Show a recoverable error and preserve user input.";
134
+
135
+ return [
136
+ `# ${config.id} Wireframe`,
137
+ "",
138
+ "## Source Screen",
139
+ "",
140
+ `- Screen: ${config.title}`,
141
+ `- Purpose: ${purpose}`,
142
+ `- Data needed: ${dataNeeded}`,
143
+ "",
144
+ "## Layout Sections",
145
+ "",
146
+ ...config.sections.map((section) => `- ${section}`),
147
+ "",
148
+ "## Components",
149
+ "",
150
+ ...config.components.map((component) => `- ${component}`),
151
+ "",
152
+ "## Primary CTA",
153
+ "",
154
+ `- Label: ${config.cta}`,
155
+ "- Placement: first viewport and repeated near the workflow completion point.",
156
+ "- Behavior: advances to the next MVP step without opening external tools.",
157
+ "",
158
+ "## State Handling",
159
+ "",
160
+ `- Empty: ${emptyState}`,
161
+ `- Success: ${successState}`,
162
+ `- Error: ${errorState}`,
163
+ "",
164
+ "## Mobile Notes",
165
+ "",
166
+ `- ${config.mobile}`,
167
+ "- Avoid side-by-side dependencies; each section must scan as a single column on narrow screens.",
168
+ "",
169
+ "## Copy Direction",
170
+ "",
171
+ `- ${config.copy}`,
172
+ "- Keep copy concrete, short, and tied to the user action on the current screen.",
173
+ "",
174
+ "## Acceptance Criteria",
175
+ "",
176
+ "- The screen has one visually dominant primary CTA.",
177
+ "- Required data, empty, success, and error states are represented in the layout.",
178
+ "- Mobile layout preserves the CTA and next action without horizontal scrolling.",
179
+ "- The wireframe does not specify production app code, network integrations, or entitlement gates.",
180
+ "- No Figma, Lovable, v0, or full app-builder replacement behavior is claimed.",
181
+ "",
182
+ ].join("\n");
183
+ }
184
+
185
+ function runDesignWireframe({ screen, projectPath, force = false }) {
186
+ const targets = screenTargets(screen);
187
+ const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
188
+ if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
189
+ throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}. No project files were changed.`);
190
+ }
191
+
192
+ const paths = describeDesignPaths(resolvedProjectPath);
193
+ if (!fs.existsSync(paths.screenMapPath)) {
194
+ throw new ValidationError("Missing docs/design/SCREEN_MAP.md. Run sdtk-design screens first. No project files were changed.");
195
+ }
196
+
197
+ const outputTargets = targets.map((target) => ({
198
+ screenKey: target,
199
+ filePath: path.join(paths.wireframesPath, SCREEN_CONFIGS[target].fileName),
200
+ relativePath: `docs/design/wireframes/${SCREEN_CONFIGS[target].fileName}`,
201
+ }));
202
+ const existing = outputTargets.filter((target) => fs.existsSync(target.filePath));
203
+ if (existing.length > 0 && !force) {
204
+ const files = existing.map((target) => target.relativePath).join(", ");
205
+ throw new ValidationError(`${files} already exists. Re-run with --force to replace managed wireframe output.`);
206
+ }
207
+
208
+ const screenMapContent = fs.readFileSync(paths.screenMapPath, "utf-8");
209
+ fs.mkdirSync(paths.wireframesPath, { recursive: true });
210
+ for (const target of outputTargets) {
211
+ fs.writeFileSync(target.filePath, wireframeContent(target.screenKey, screenMapContent), "utf-8");
212
+ }
213
+
214
+ return {
215
+ projectPath: resolvedProjectPath,
216
+ written: outputTargets.map((target) => target.relativePath),
217
+ forced: Boolean(force),
218
+ };
219
+ }
220
+
221
+ function cmdWireframe(args) {
222
+ const { flags } = parseFlags(args || [], WIREFRAME_FLAG_DEFS);
223
+ if (flags.help) return cmdWireframeHelp();
224
+
225
+ const result = runDesignWireframe({
226
+ screen: flags.screen,
227
+ projectPath: flags["project-path"],
228
+ force: Boolean(flags.force),
229
+ });
230
+
231
+ console.log(`[design] Wrote wireframes: ${result.written.join(", ")}`);
232
+ console.log(`[design] Project: ${result.projectPath}`);
233
+ console.log(`[design] Overwrite: ${result.forced ? "enabled by --force" : "not needed"}`);
234
+ console.log("[design] No .sdtk/atlas, SDTK-WIKI output, source files, network, or app code was modified.");
235
+ console.log("[design] Next: sdtk-design system");
236
+ return 0;
237
+ }
238
+
239
+ module.exports = {
240
+ SCREEN_CONFIGS,
241
+ cmdWireframe,
242
+ cmdWireframeHelp,
243
+ runDesignWireframe,
244
+ wireframeContent,
245
+ };
package/src/index.js ADDED
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+
3
+ const { cmdBrief } = require("./commands/brief");
4
+ const { cmdHandoff } = require("./commands/handoff");
5
+ const { cmdHelp } = require("./commands/help");
6
+ const { cmdInit } = require("./commands/init");
7
+ const { cmdReview } = require("./commands/review");
8
+ const { cmdScreens } = require("./commands/screens");
9
+ const { cmdStart } = require("./commands/start");
10
+ const { cmdStatus } = require("./commands/status");
11
+ const { cmdSystem } = require("./commands/system");
12
+ const { cmdWireframe } = require("./commands/wireframe");
13
+ const { ValidationError } = require("./lib/errors");
14
+
15
+ const DEFERRED_COMMANDS = new Map([]);
16
+
17
+ function getVersion() {
18
+ const pkg = require("../package.json");
19
+ return pkg.version;
20
+ }
21
+
22
+ function parseCommand(argv) {
23
+ if (!argv || argv.length === 0) {
24
+ return { command: "help", args: [] };
25
+ }
26
+
27
+ const [first, ...rest] = argv;
28
+ if (first === "-h" || first === "--help") {
29
+ return { command: "help", args: [] };
30
+ }
31
+ if (first === "-v" || first === "--version") {
32
+ return { command: "version", args: [] };
33
+ }
34
+
35
+ return { command: first, args: rest };
36
+ }
37
+
38
+ async function run(argv) {
39
+ const { command, args } = parseCommand(argv);
40
+
41
+ if (command === "help") {
42
+ return cmdHelp();
43
+ }
44
+ if (command === "version") {
45
+ console.log(`sdtk-design-kit ${getVersion()}`);
46
+ return 0;
47
+ }
48
+ if (command === "init") {
49
+ return cmdInit(args);
50
+ }
51
+ if (command === "brief") {
52
+ return cmdBrief(args);
53
+ }
54
+ if (command === "screens") {
55
+ return cmdScreens(args);
56
+ }
57
+ if (command === "wireframe") {
58
+ return cmdWireframe(args);
59
+ }
60
+ if (command === "system") {
61
+ return cmdSystem(args);
62
+ }
63
+ if (command === "handoff") {
64
+ return cmdHandoff(args);
65
+ }
66
+ if (command === "review") {
67
+ return cmdReview(args);
68
+ }
69
+ if (command === "start") {
70
+ return cmdStart(args);
71
+ }
72
+ if (command === "status") {
73
+ return cmdStatus(args);
74
+ }
75
+ if (DEFERRED_COMMANDS.has(command)) {
76
+ throw new ValidationError(
77
+ `Command "${command}" is planned for ${DEFERRED_COMMANDS.get(command)} and is not implemented yet. Run "sdtk-design --help" for the current Foundation Beta surface.`
78
+ );
79
+ }
80
+
81
+ throw new ValidationError(`Unknown command: "${command}". Run "sdtk-design --help" for available commands.`);
82
+ }
83
+
84
+ module.exports = {
85
+ DEFERRED_COMMANDS,
86
+ parseCommand,
87
+ run,
88
+ };