stitch-forge 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.
Files changed (145) hide show
  1. package/.claude/skills/forge-build/SKILL.md +79 -0
  2. package/.claude/skills/forge-design/SKILL.md +64 -0
  3. package/.claude/skills/forge-discover/SKILL.md +139 -0
  4. package/.claude/skills/forge-generate/SKILL.md +77 -0
  5. package/.claude/skills/forge-preview/SKILL.md +26 -0
  6. package/.claude/skills/forge-research/SKILL.md +42 -0
  7. package/.claude/skills/forge-sync/SKILL.md +45 -0
  8. package/DESIGN.md +113 -0
  9. package/LICENSE +21 -0
  10. package/README.es.md +242 -0
  11. package/README.md +242 -0
  12. package/dist/adapters/astro.d.ts +8 -0
  13. package/dist/adapters/astro.js +24 -0
  14. package/dist/adapters/astro.js.map +1 -0
  15. package/dist/adapters/index.d.ts +3 -0
  16. package/dist/adapters/index.js +9 -0
  17. package/dist/adapters/index.js.map +1 -0
  18. package/dist/adapters/nextjs.d.ts +7 -0
  19. package/dist/adapters/nextjs.js +136 -0
  20. package/dist/adapters/nextjs.js.map +1 -0
  21. package/dist/adapters/static.d.ts +7 -0
  22. package/dist/adapters/static.js +43 -0
  23. package/dist/adapters/static.js.map +1 -0
  24. package/dist/adapters/types.d.ts +22 -0
  25. package/dist/adapters/types.js +6 -0
  26. package/dist/adapters/types.js.map +1 -0
  27. package/dist/commands/build.d.ts +7 -0
  28. package/dist/commands/build.js +98 -0
  29. package/dist/commands/build.js.map +1 -0
  30. package/dist/commands/design.d.ts +3 -0
  31. package/dist/commands/design.js +39 -0
  32. package/dist/commands/design.js.map +1 -0
  33. package/dist/commands/discover.d.ts +9 -0
  34. package/dist/commands/discover.js +91 -0
  35. package/dist/commands/discover.js.map +1 -0
  36. package/dist/commands/generate.d.ts +7 -0
  37. package/dist/commands/generate.js +105 -0
  38. package/dist/commands/generate.js.map +1 -0
  39. package/dist/commands/init.d.ts +1 -0
  40. package/dist/commands/init.js +99 -0
  41. package/dist/commands/init.js.map +1 -0
  42. package/dist/commands/preview.d.ts +5 -0
  43. package/dist/commands/preview.js +41 -0
  44. package/dist/commands/preview.js.map +1 -0
  45. package/dist/commands/research.d.ts +1 -0
  46. package/dist/commands/research.js +38 -0
  47. package/dist/commands/research.js.map +1 -0
  48. package/dist/commands/sync.d.ts +1 -0
  49. package/dist/commands/sync.js +53 -0
  50. package/dist/commands/sync.js.map +1 -0
  51. package/dist/commands/workflow.d.ts +1 -0
  52. package/dist/commands/workflow.js +38 -0
  53. package/dist/commands/workflow.js.map +1 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +113 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/mcp/auth.d.ts +15 -0
  58. package/dist/mcp/auth.js +56 -0
  59. package/dist/mcp/auth.js.map +1 -0
  60. package/dist/mcp/client.d.ts +65 -0
  61. package/dist/mcp/client.js +302 -0
  62. package/dist/mcp/client.js.map +1 -0
  63. package/dist/mcp/tools.d.ts +26 -0
  64. package/dist/mcp/tools.js +46 -0
  65. package/dist/mcp/tools.js.map +1 -0
  66. package/dist/research/business-researcher.d.ts +41 -0
  67. package/dist/research/business-researcher.js +888 -0
  68. package/dist/research/business-researcher.js.map +1 -0
  69. package/dist/research/crawler.d.ts +11 -0
  70. package/dist/research/crawler.js +56 -0
  71. package/dist/research/crawler.js.map +1 -0
  72. package/dist/research/design-synthesizer.d.ts +46 -0
  73. package/dist/research/design-synthesizer.js +628 -0
  74. package/dist/research/design-synthesizer.js.map +1 -0
  75. package/dist/research/differ.d.ts +19 -0
  76. package/dist/research/differ.js +58 -0
  77. package/dist/research/differ.js.map +1 -0
  78. package/dist/research/known-state.json +68 -0
  79. package/dist/research/research-cache.d.ts +6 -0
  80. package/dist/research/research-cache.js +62 -0
  81. package/dist/research/research-cache.js.map +1 -0
  82. package/dist/research/types.d.ts +98 -0
  83. package/dist/research/types.js +6 -0
  84. package/dist/research/types.js.map +1 -0
  85. package/dist/research/updater.d.ts +5 -0
  86. package/dist/research/updater.js +43 -0
  87. package/dist/research/updater.js.map +1 -0
  88. package/dist/templates/design-md.d.ts +52 -0
  89. package/dist/templates/design-md.js +315 -0
  90. package/dist/templates/design-md.js.map +1 -0
  91. package/dist/templates/prompts.d.ts +31 -0
  92. package/dist/templates/prompts.js +39 -0
  93. package/dist/templates/prompts.js.map +1 -0
  94. package/dist/templates/workflows.d.ts +9 -0
  95. package/dist/templates/workflows.js +21 -0
  96. package/dist/templates/workflows.js.map +1 -0
  97. package/dist/tui/App.d.ts +1 -0
  98. package/dist/tui/App.js +87 -0
  99. package/dist/tui/App.js.map +1 -0
  100. package/dist/tui/Dashboard.d.ts +5 -0
  101. package/dist/tui/Dashboard.js +23 -0
  102. package/dist/tui/Dashboard.js.map +1 -0
  103. package/dist/tui/DesignEditor.d.ts +6 -0
  104. package/dist/tui/DesignEditor.js +76 -0
  105. package/dist/tui/DesignEditor.js.map +1 -0
  106. package/dist/tui/PromptBuilder.d.ts +5 -0
  107. package/dist/tui/PromptBuilder.js +102 -0
  108. package/dist/tui/PromptBuilder.js.map +1 -0
  109. package/dist/tui/components/QuotaMeter.d.ts +8 -0
  110. package/dist/tui/components/QuotaMeter.js +10 -0
  111. package/dist/tui/components/QuotaMeter.js.map +1 -0
  112. package/dist/tui/components/ScreenCard.d.ts +7 -0
  113. package/dist/tui/components/ScreenCard.js +6 -0
  114. package/dist/tui/components/ScreenCard.js.map +1 -0
  115. package/dist/tui/components/Spinner.d.ts +5 -0
  116. package/dist/tui/components/Spinner.js +7 -0
  117. package/dist/tui/components/Spinner.js.map +1 -0
  118. package/dist/tui/components/StatusBar.d.ts +7 -0
  119. package/dist/tui/components/StatusBar.js +6 -0
  120. package/dist/tui/components/StatusBar.js.map +1 -0
  121. package/dist/utils/config.d.ts +26 -0
  122. package/dist/utils/config.js +66 -0
  123. package/dist/utils/config.js.map +1 -0
  124. package/dist/utils/design-validator.d.ts +44 -0
  125. package/dist/utils/design-validator.js +396 -0
  126. package/dist/utils/design-validator.js.map +1 -0
  127. package/dist/utils/logger.d.ts +8 -0
  128. package/dist/utils/logger.js +10 -0
  129. package/dist/utils/logger.js.map +1 -0
  130. package/dist/utils/output-validator.d.ts +18 -0
  131. package/dist/utils/output-validator.js +194 -0
  132. package/dist/utils/output-validator.js.map +1 -0
  133. package/dist/utils/preview.d.ts +4 -0
  134. package/dist/utils/preview.js +49 -0
  135. package/dist/utils/preview.js.map +1 -0
  136. package/dist/utils/prompt-enhancer.d.ts +21 -0
  137. package/dist/utils/prompt-enhancer.js +104 -0
  138. package/dist/utils/prompt-enhancer.js.map +1 -0
  139. package/dist/utils/quota.d.ts +18 -0
  140. package/dist/utils/quota.js +49 -0
  141. package/dist/utils/quota.js.map +1 -0
  142. package/dist/utils/validators.d.ts +125 -0
  143. package/dist/utils/validators.js +110 -0
  144. package/dist/utils/validators.js.map +1 -0
  145. package/package.json +77 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * Design Quality Validator for DESIGN.md files.
3
+ *
4
+ * Scores a DESIGN.md across four dimensions — specificity, differentiation,
5
+ * completeness, and actionability — and surfaces concrete issues.
6
+ */
7
+ // ─── Hex distance utility ──────────────────────────────────────────
8
+ function parseHex(hex) {
9
+ const clean = hex.replace('#', '');
10
+ const r = parseInt(clean.substring(0, 2), 16);
11
+ const g = parseInt(clean.substring(2, 4), 16);
12
+ const b = parseInt(clean.substring(4, 6), 16);
13
+ return [r, g, b];
14
+ }
15
+ /**
16
+ * Euclidean distance between two hex colors in RGB space.
17
+ * Threshold for "too similar": distance < 50.
18
+ */
19
+ export function hexDistance(a, b) {
20
+ const [r1, g1, b1] = parseHex(a);
21
+ const [r2, g2, b2] = parseHex(b);
22
+ return Math.sqrt((r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2);
23
+ }
24
+ // ─── Section extraction helpers ────────────────────────────────────
25
+ function extractSection(markdown, sectionNumber) {
26
+ const pattern = new RegExp(`## ${sectionNumber}\\..*?\n([\\s\\S]*?)(?=## \\d+\\.|$)`);
27
+ const match = markdown.match(pattern);
28
+ return match ? match[1].trim() : '';
29
+ }
30
+ function extractDosSection(markdown) {
31
+ const match = markdown.match(/### Do\b\n([\s\S]*?)(?=### Don|$)/);
32
+ return match ? match[1].trim() : '';
33
+ }
34
+ function extractDontsSection(markdown) {
35
+ const match = markdown.match(/### Don'?t\b\n([\s\S]*?)(?=##|$)/);
36
+ return match ? match[1].trim() : '';
37
+ }
38
+ function extractColorHexes(markdown) {
39
+ const section = extractSection(markdown, 2);
40
+ const hexPattern = /#[0-9A-Fa-f]{6}/g;
41
+ return [...(section.match(hexPattern) || [])];
42
+ }
43
+ function countColorTableRows(markdown) {
44
+ const section = extractSection(markdown, 2);
45
+ const lines = section.split('\n');
46
+ let count = 0;
47
+ for (const line of lines) {
48
+ // Count table rows with hex values, skip header and separator
49
+ if (line.includes('|') && /#[0-9A-Fa-f]{6}/.test(line)) {
50
+ count++;
51
+ }
52
+ }
53
+ return count;
54
+ }
55
+ function countListItems(text) {
56
+ const items = text.split('\n').filter((l) => /^- /.test(l.trim()));
57
+ return items.length;
58
+ }
59
+ // ─── Sub-scorers ───────────────────────────────────────────────────
60
+ /**
61
+ * Scores how specific and concrete the DESIGN.md is (0-25).
62
+ * Penalizes placeholders, generic adjectives, missing hex values, etc.
63
+ */
64
+ export function scoreSpecificity(markdown) {
65
+ let score = 25;
66
+ const issues = [];
67
+ // -5 for each <!-- --> placeholder
68
+ const placeholders = (markdown.match(/<!--[\s\S]*?-->/g) || []).length;
69
+ score -= placeholders * 5;
70
+ // -3 for generic words used in isolation (not as part of specific rules)
71
+ const genericWords = ['modern', 'clean', 'professional', 'nice', 'beautiful'];
72
+ for (const word of genericWords) {
73
+ // Match word in isolation (not within a quoted string or compound rule)
74
+ const regex = new RegExp(`(?<![\\w"'])\\b${word}\\b(?![\\w"'])`, 'gi');
75
+ const matches = markdown.match(regex) || [];
76
+ // Only penalize if the word appears standalone-ish (not in a longer rule)
77
+ for (const _match of matches) {
78
+ // Check context: find the line containing this match
79
+ const lines = markdown.split('\n');
80
+ for (const line of lines) {
81
+ if (regex.test(line)) {
82
+ // If line is short (< 40 chars) and mostly just the generic word, penalize
83
+ const trimmed = line.replace(/^[-#|*\s]+/, '').trim();
84
+ if (trimmed.length < 40 || trimmed.toLowerCase() === word) {
85
+ score -= 3;
86
+ break;
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ // -2 if no hex color values found in color table
93
+ const hexes = extractColorHexes(markdown);
94
+ if (hexes.length === 0) {
95
+ score -= 2;
96
+ }
97
+ // -2 if typography section has no quoted font names
98
+ const typoSection = extractSection(markdown, 3);
99
+ if (!/"[^"]+"/.test(typoSection)) {
100
+ score -= 2;
101
+ }
102
+ // -2 for each section with no content, -1 for sections under 20 characters
103
+ for (let i = 1; i <= 8; i++) {
104
+ const section = extractSection(markdown, i);
105
+ if (section.length === 0) {
106
+ score -= 2;
107
+ }
108
+ else if (section.length < 20) {
109
+ score -= 1;
110
+ }
111
+ }
112
+ return Math.max(0, score);
113
+ }
114
+ /**
115
+ * Scores how differentiated the design is from competitors (0-25).
116
+ * Without competitor data, caps at 15 (cannot assess differentiation).
117
+ */
118
+ export function scoreDifferentiation(markdown, competitors) {
119
+ const hasCompetitors = competitors && competitors.length > 0;
120
+ let score = hasCompetitors ? 25 : 15;
121
+ if (!hasCompetitors)
122
+ return score;
123
+ // Extract primary color from markdown (try section 2 first, then full markdown)
124
+ const colorSection = extractSection(markdown, 2);
125
+ const searchText = colorSection || markdown;
126
+ const primaryLine = searchText
127
+ .split('\n')
128
+ .find((l) => /primary/i.test(l) && /#[0-9A-Fa-f]{6}/.test(l));
129
+ const primaryHexMatch = primaryLine?.match(/#[0-9A-Fa-f]{6}/);
130
+ const primaryHex = primaryHexMatch ? primaryHexMatch[0] : null;
131
+ if (primaryHex) {
132
+ for (const comp of competitors) {
133
+ const compDominant = comp.palette.dominantHex;
134
+ if (!compDominant)
135
+ continue;
136
+ // -8 for exact match
137
+ if (primaryHex.toLowerCase() === compDominant.toLowerCase()) {
138
+ score -= 8;
139
+ break;
140
+ }
141
+ // -4 if within distance 30
142
+ const dist = hexDistance(primaryHex, compDominant);
143
+ if (dist < 30) {
144
+ score -= 4;
145
+ break;
146
+ }
147
+ // Also check within distance 50 for a lesser penalty
148
+ if (dist < 50) {
149
+ score -= 2;
150
+ break;
151
+ }
152
+ }
153
+ }
154
+ // -5 if heading font matches any competitor's heading font
155
+ const typoSection = extractSection(markdown, 3) || markdown;
156
+ for (const comp of competitors) {
157
+ if (comp.typography.headingFont) {
158
+ if (typoSection.includes(comp.typography.headingFont)) {
159
+ score -= 5;
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ // -3 if "Don't" section doesn't mention any competitor differentiation rule
165
+ const dontsSection = extractDontsSection(markdown) || markdown;
166
+ const hasCompetitorMention = competitors.some((c) => dontsSection.toLowerCase().includes(c.name.toLowerCase()) ||
167
+ (c.typography.headingFont &&
168
+ dontsSection.includes(c.typography.headingFont)));
169
+ if (!hasCompetitorMention) {
170
+ score -= 3;
171
+ }
172
+ return Math.max(0, score);
173
+ }
174
+ /**
175
+ * Scores structural completeness of the DESIGN.md (0-25).
176
+ * Checks for all 8 sections, color count, component patterns, do/don't counts.
177
+ */
178
+ export function scoreCompleteness(markdown) {
179
+ let score = 25;
180
+ // Check for all 8 section headers
181
+ for (let i = 1; i <= 8; i++) {
182
+ const pattern = new RegExp(`## ${i}\\.`);
183
+ if (!pattern.test(markdown)) {
184
+ score -= 4;
185
+ }
186
+ }
187
+ // Check color table has >= 5 rows with hex values
188
+ const colorRows = countColorTableRows(markdown);
189
+ if (colorRows < 5) {
190
+ score -= 3;
191
+ }
192
+ // Check >= 3 component patterns (subsections under section 5)
193
+ const compSection = extractSection(markdown, 5);
194
+ const componentHeaders = (compSection.match(/### /g) || []).length;
195
+ if (componentHeaders < 3) {
196
+ score -= 3;
197
+ }
198
+ // Check Do's has >= 3 items
199
+ const dosSection = extractDosSection(markdown);
200
+ const dosCount = countListItems(dosSection);
201
+ if (dosCount < 3) {
202
+ score -= 3;
203
+ }
204
+ // Check Don'ts has >= 3 items
205
+ const dontsSection = extractDontsSection(markdown);
206
+ const dontsCount = countListItems(dontsSection);
207
+ if (dontsCount < 3) {
208
+ score -= 3;
209
+ }
210
+ return Math.max(0, score);
211
+ }
212
+ /**
213
+ * Scores how actionable and implementation-ready the rules are (0-25).
214
+ * Penalizes vague language, color words without hex, non-numeric sizes.
215
+ */
216
+ export function scoreActionability(markdown) {
217
+ let score = 25;
218
+ const dosSection = extractDosSection(markdown);
219
+ const dontsSection = extractDontsSection(markdown);
220
+ const rulesText = `${dosSection}\n${dontsSection}`;
221
+ // -3 for each "should" or "consider" in Do's/Don'ts
222
+ const vagueMatches = rulesText.match(/\b(should|consider)\b/gi) || [];
223
+ score -= vagueMatches.length * 3;
224
+ // -2 for each rule that is too short to be actionable (< 15 chars)
225
+ const ruleLines = rulesText.split('\n').filter((l) => /^- /.test(l.trim()));
226
+ for (const rule of ruleLines) {
227
+ const content = rule.replace(/^-\s*/, '').trim();
228
+ if (content.length > 0 && content.length < 15) {
229
+ score -= 2;
230
+ }
231
+ }
232
+ // -2 for each color described with words instead of hex
233
+ const colorSection = extractSection(markdown, 2);
234
+ const colorWordPatterns = [
235
+ /\b(trustworthy|calming|vibrant|warm|cool)\s+(blue|red|green|yellow|orange|purple|pink|teal)\b/gi,
236
+ ];
237
+ for (const pattern of colorWordPatterns) {
238
+ const matches = colorSection.match(pattern) || [];
239
+ score -= matches.length * 2;
240
+ }
241
+ // -2 for each font size not in px/rem in typography section
242
+ const typoSection = extractSection(markdown, 3);
243
+ const sizeEntries = typoSection.match(/\|\s*\w+\s*\|\s*([^|]+)\s*\|/g) || [];
244
+ for (const entry of sizeEntries) {
245
+ // Skip header row
246
+ if (/Size/i.test(entry))
247
+ continue;
248
+ if (!/\d+(\.\d+)?(px|rem)/.test(entry)) {
249
+ score -= 2;
250
+ }
251
+ }
252
+ // -2 if spacing section has no numeric values
253
+ const spacingSection = extractSection(markdown, 4);
254
+ if (!/\d+/.test(spacingSection)) {
255
+ score -= 2;
256
+ }
257
+ // -1 for each "e.g." or "for example" in rules
258
+ const exampleMatches = rulesText.match(/\b(e\.g\.|for example)\b/gi) || [];
259
+ score -= exampleMatches.length;
260
+ return Math.max(0, score);
261
+ }
262
+ // ─── Cultural alignment ────────────────────────────────────────────
263
+ /**
264
+ * Checks for cultural alignment issues in the DESIGN.md.
265
+ */
266
+ export function checkCulturalAlignment(markdown, locale, audience) {
267
+ const issues = [];
268
+ const context = `${locale || ''} ${audience || ''}`.toLowerCase();
269
+ if (/\bes\b|mexic|latino|latina|español/.test(context)) {
270
+ // Check for Spanish copy guidance
271
+ const dosSection = extractDosSection(markdown);
272
+ const dontsSection = extractDontsSection(markdown);
273
+ const allRules = `${dosSection}\n${dontsSection}`.toLowerCase();
274
+ if (!allRules.includes('spanish') &&
275
+ !allRules.includes('español') &&
276
+ !allRules.includes('spanish-language')) {
277
+ issues.push({
278
+ section: "Do's and Don'ts",
279
+ severity: 'warning',
280
+ message: 'No Spanish copy guidance found for Spanish-locale audience — add rules for natural Spanish copy',
281
+ });
282
+ }
283
+ // Check imagery for American/European references
284
+ const imagerySection = extractSection(markdown, 7).toLowerCase();
285
+ if (imagerySection.includes('american') ||
286
+ imagerySection.includes('european')) {
287
+ issues.push({
288
+ section: 'Imagery Guidelines',
289
+ severity: 'info',
290
+ message: 'Imagery section references "American" or "European" stock — consider local imagery for this audience',
291
+ });
292
+ }
293
+ }
294
+ return issues;
295
+ }
296
+ // ─── Orchestrator ──────────────────────────────────────────────────
297
+ /**
298
+ * Orchestrates all sub-scorers and returns a complete quality score.
299
+ */
300
+ export function scoreDesignMd(markdown, research) {
301
+ const specificity = scoreSpecificity(markdown);
302
+ const differentiation = scoreDifferentiation(markdown, research?.competitors);
303
+ const completeness = scoreCompleteness(markdown);
304
+ const actionability = scoreActionability(markdown);
305
+ const total = specificity + differentiation + completeness + actionability;
306
+ const issues = [];
307
+ // Collect issues from specificity
308
+ const placeholders = (markdown.match(/<!--[\s\S]*?-->/g) || []).length;
309
+ if (placeholders > 0) {
310
+ issues.push({
311
+ section: 'General',
312
+ severity: 'error',
313
+ message: `Contains ${placeholders} placeholder comment(s) — replace with real content`,
314
+ });
315
+ }
316
+ // Check for vague language in rules
317
+ const dosSection = extractDosSection(markdown);
318
+ const dontsSection = extractDontsSection(markdown);
319
+ const rulesText = `${dosSection}\n${dontsSection}`;
320
+ if (/\b(should|consider)\b/i.test(rulesText)) {
321
+ issues.push({
322
+ section: "Do's and Don'ts",
323
+ severity: 'warning',
324
+ message: 'Contains "should" or "consider" — make rules definitive',
325
+ });
326
+ }
327
+ // Check for missing sections
328
+ for (let i = 1; i <= 8; i++) {
329
+ const section = extractSection(markdown, i);
330
+ if (section.length < 20) {
331
+ const sectionNames = {
332
+ 1: 'Visual Theme & Atmosphere',
333
+ 2: 'Color Palette & Roles',
334
+ 3: 'Typography',
335
+ 4: 'Spacing & Layout',
336
+ 5: 'Component Patterns',
337
+ 6: 'Iconography',
338
+ 7: 'Imagery Guidelines',
339
+ 8: "Do's and Don'ts",
340
+ };
341
+ issues.push({
342
+ section: sectionNames[i] || `Section ${i}`,
343
+ severity: 'warning',
344
+ message: `Section has very little content (${section.length} chars)`,
345
+ });
346
+ }
347
+ }
348
+ // Check competitor differentiation status
349
+ if (!research?.competitors || research.competitors.length === 0) {
350
+ issues.push({
351
+ section: 'Differentiation',
352
+ severity: 'info',
353
+ message: 'No competitor data available for differentiation scoring',
354
+ });
355
+ }
356
+ // Cultural alignment issues
357
+ if (research) {
358
+ const culturalIssues = checkCulturalAlignment(markdown, research.brief.locale, research.brief.targetAudience);
359
+ issues.push(...culturalIssues);
360
+ }
361
+ return {
362
+ specificity,
363
+ differentiation,
364
+ completeness,
365
+ actionability,
366
+ total,
367
+ issues,
368
+ };
369
+ }
370
+ // ─── Report formatter ──────────────────────────────────────────────
371
+ /**
372
+ * Formats a DesignQualityScore into a human-readable report.
373
+ */
374
+ export function formatDesignQualityReport(score) {
375
+ const lines = [];
376
+ lines.push(`Design Quality: ${score.total}/100`);
377
+ lines.push('');
378
+ lines.push(` Specificity: ${String(score.specificity).padStart(2)}/25`);
379
+ lines.push(` Differentiation: ${String(score.differentiation).padStart(2)}/25`);
380
+ lines.push(` Completeness: ${String(score.completeness).padStart(2)}/25`);
381
+ lines.push(` Actionability: ${String(score.actionability).padStart(2)}/25`);
382
+ if (score.issues.length > 0) {
383
+ lines.push('');
384
+ lines.push('Issues:');
385
+ for (const issue of score.issues) {
386
+ const icon = issue.severity === 'error'
387
+ ? '[!]'
388
+ : issue.severity === 'warning'
389
+ ? '[!]'
390
+ : '[i]';
391
+ lines.push(` ${icon} Section "${issue.section}": ${issue.message}`);
392
+ }
393
+ }
394
+ return lines.join('\n');
395
+ }
396
+ //# sourceMappingURL=design-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-validator.js","sourceRoot":"","sources":["../../src/utils/design-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,sEAAsE;AAEtE,SAAS,QAAQ,CAAC,GAAW;IAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,sEAAsE;AAEtE,SAAS,cAAc,CAAC,QAAgB,EAAE,aAAqB;IAC7D,MAAM,OAAO,GAAG,IAAI,MAAM,CACxB,MAAM,aAAa,sCAAsC,CAC1D,CAAC;IACF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB;IACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAClE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACjE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB;IACzC,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,kBAAkB,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,8DAA8D;QAC9D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,sEAAsE;AAEtE;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,mCAAmC;IACnC,MAAM,YAAY,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACvE,KAAK,IAAI,YAAY,GAAG,CAAC,CAAC;IAE1B,yEAAyE;IACzE,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9E,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,wEAAwE;QACxE,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,kBAAkB,IAAI,gBAAgB,EAAE,IAAI,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5C,0EAA0E;QAC1E,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,qDAAqD;YACrD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,2EAA2E;oBAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;oBACtD,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;wBAC1D,KAAK,IAAI,CAAC,CAAC;wBACX,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,MAAM,KAAK,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,oDAAoD;IACpD,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACjC,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,2EAA2E;IAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC/B,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,WAAkC;IAElC,MAAM,cAAc,GAAG,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7D,IAAI,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAErC,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAElC,gFAAgF;IAChF,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,YAAY,IAAI,QAAQ,CAAC;IAC5C,MAAM,WAAW,GAAG,UAAU;SAC3B,KAAK,CAAC,IAAI,CAAC;SACX,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,MAAM,eAAe,GAAG,WAAW,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/D,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;YAC9C,IAAI,CAAC,YAAY;gBAAE,SAAS;YAE5B,qBAAqB;YACrB,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5D,KAAK,IAAI,CAAC,CAAC;gBACX,MAAM;YACR,CAAC;YAED,2BAA2B;YAC3B,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;YACnD,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC;gBACd,KAAK,IAAI,CAAC,CAAC;gBACX,MAAM;YACR,CAAC;YAED,qDAAqD;YACrD,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC;gBACd,KAAK,IAAI,CAAC,CAAC;gBACX,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC5D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;YAChC,IAAI,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBACtD,KAAK,IAAI,CAAC,CAAC;gBACX,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;IAC/D,MAAM,oBAAoB,GAAG,WAAW,CAAC,IAAI,CAC3C,CAAC,CAAC,EAAE,EAAE,CACJ,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACzD,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW;YACvB,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CACrD,CAAC;IACF,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,KAAK,GAAG,EAAE,CAAC;IAEf,kCAAkC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,MAAM,SAAS,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,8DAA8D;IAC9D,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAChD,MAAM,gBAAgB,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACnE,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,4BAA4B;IAC5B,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,8BAA8B;IAC9B,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IAChD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,IAAI,KAAK,GAAG,EAAE,CAAC;IAEf,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,GAAG,UAAU,KAAK,YAAY,EAAE,CAAC;IAEnD,oDAAoD;IACpD,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,yBAAyB,CAAC,IAAI,EAAE,CAAC;IACtE,KAAK,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IAEjC,mEAAmE;IACnE,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5E,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC9C,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACjD,MAAM,iBAAiB,GAAG;QACxB,iGAAiG;KAClG,CAAC;IACF,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAClD,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,4DAA4D;IAC5D,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CACnC,+BAA+B,CAChC,IAAI,EAAE,CAAC;IACR,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,kBAAkB;QAClB,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,8CAA8C;IAC9C,MAAM,cAAc,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,+CAA+C;IAC/C,MAAM,cAAc,GAClB,SAAS,CAAC,KAAK,CAAC,4BAA4B,CAAC,IAAI,EAAE,CAAC;IACtD,KAAK,IAAI,cAAc,CAAC,MAAM,CAAC;IAE/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,sEAAsE;AAEtE;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgB,EAChB,MAAe,EACf,QAAiB;IAEjB,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,GAAG,MAAM,IAAI,EAAE,IAAI,QAAQ,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC;IAElE,IAAI,oCAAoC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,kCAAkC;QAClC,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,GAAG,UAAU,KAAK,YAAY,EAAE,CAAC,WAAW,EAAE,CAAC;QAEhE,IACE,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC7B,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC7B,CAAC,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EACtC,CAAC;YACD,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,iBAAiB;gBAC1B,QAAQ,EAAE,SAAS;gBACnB,OAAO,EACL,iGAAiG;aACpG,CAAC,CAAC;QACL,CAAC;QAED,iDAAiD;QACjD,MAAM,cAAc,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACjE,IACE,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC;YACnC,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,EACnC,CAAC;YACD,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,oBAAoB;gBAC7B,QAAQ,EAAE,MAAM;gBAChB,OAAO,EACL,sGAAsG;aACzG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sEAAsE;AAEtE;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,QAAiC;IAEjC,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,eAAe,GAAG,oBAAoB,CAC1C,QAAQ,EACR,QAAQ,EAAE,WAAW,CACtB,CAAC;IACF,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,WAAW,GAAG,eAAe,GAAG,YAAY,GAAG,aAAa,CAAC;IAE3E,MAAM,MAAM,GAAyB,EAAE,CAAC;IAExC,kCAAkC;IAClC,MAAM,YAAY,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACvE,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,YAAY,YAAY,qDAAqD;SACvF,CAAC,CAAC;IACL,CAAC;IAED,oCAAoC;IACpC,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,GAAG,UAAU,KAAK,YAAY,EAAE,CAAC;IACnD,IAAI,wBAAwB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,iBAAiB;YAC1B,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,yDAAyD;SACnE,CAAC,CAAC;IACL,CAAC;IAED,6BAA6B;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACxB,MAAM,YAAY,GAA2B;gBAC3C,CAAC,EAAE,2BAA2B;gBAC9B,CAAC,EAAE,uBAAuB;gBAC1B,CAAC,EAAE,YAAY;gBACf,CAAC,EAAE,kBAAkB;gBACrB,CAAC,EAAE,oBAAoB;gBACvB,CAAC,EAAE,aAAa;gBAChB,CAAC,EAAE,oBAAoB;gBACvB,CAAC,EAAE,iBAAiB;aACrB,CAAC;YACF,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,EAAE;gBAC1C,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,oCAAoC,OAAO,CAAC,MAAM,SAAS;aACrE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC,QAAQ,EAAE,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,iBAAiB;YAC1B,QAAQ,EAAE,MAAM;YAChB,OAAO,EACL,0DAA0D;SAC7D,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,cAAc,GAAG,sBAAsB,CAC3C,QAAQ,EACR,QAAQ,CAAC,KAAK,CAAC,MAAM,EACrB,QAAQ,CAAC,KAAK,CAAC,cAAc,CAC9B,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;IACjC,CAAC;IAED,OAAO;QACL,WAAW;QACX,eAAe;QACf,YAAY;QACZ,aAAa;QACb,KAAK;QACL,MAAM;KACP,CAAC;AACJ,CAAC;AAED,sEAAsE;AAEtE;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAyB;IAEzB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,mBAAmB,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9E,KAAK,CAAC,IAAI,CACR,uBAAuB,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CACtE,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/E,KAAK,CAAC,IAAI,CACR,uBAAuB,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CACpE,CAAC;IAEF,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,GACR,KAAK,CAAC,QAAQ,KAAK,OAAO;gBACxB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS;oBAC5B,CAAC,CAAC,KAAK;oBACP,CAAC,CAAC,KAAK,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,aAAa,KAAK,CAAC,OAAO,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const log: {
2
+ info: (msg: string) => void;
3
+ success: (msg: string) => void;
4
+ warn: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ step: (n: number, total: number, msg: string) => void;
7
+ quota: (model: string, used: number, limit: number) => void;
8
+ };
@@ -0,0 +1,10 @@
1
+ import chalk from 'chalk';
2
+ export const log = {
3
+ info: (msg) => console.error(chalk.blue('ℹ'), msg),
4
+ success: (msg) => console.error(chalk.green('✓'), msg),
5
+ warn: (msg) => console.error(chalk.yellow('⚠'), msg),
6
+ error: (msg) => console.error(chalk.red('✗'), msg),
7
+ step: (n, total, msg) => console.error(chalk.dim(`[${n}/${total}]`), msg),
8
+ quota: (model, used, limit) => console.error(chalk.dim(`${model}: ${used}/${limit} used`)),
9
+ };
10
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAC1D,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAC9D,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAC5D,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAC1D,IAAI,EAAE,CAAC,CAAS,EAAE,KAAa,EAAE,GAAW,EAAE,EAAE,CAC9C,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC;IAClD,KAAK,EAAE,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa,EAAE,EAAE,CACpD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC;CAC9D,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface ValidationIssue {
2
+ type: 'error' | 'warning' | 'info';
3
+ category: 'color' | 'typography' | 'accessibility' | 'slop' | 'structure';
4
+ message: string;
5
+ }
6
+ export interface OutputValidationResult {
7
+ score: number;
8
+ issues: ValidationIssue[];
9
+ passed: boolean;
10
+ }
11
+ /**
12
+ * Validate generated HTML against design system and quality rules.
13
+ */
14
+ export declare function validateOutput(html: string): OutputValidationResult;
15
+ /**
16
+ * Format validation results for display.
17
+ */
18
+ export declare function formatValidationReport(result: OutputValidationResult): string;
@@ -0,0 +1,194 @@
1
+ import { load } from 'cheerio';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ /**
4
+ * Validate generated HTML against design system and quality rules.
5
+ */
6
+ export function validateOutput(html) {
7
+ const issues = [];
8
+ const $ = load(html);
9
+ // 0. Check for empty or minimal HTML
10
+ const bodyText = $('body').text().trim();
11
+ if (!bodyText && $('body *').length === 0) {
12
+ issues.push({
13
+ type: 'error',
14
+ category: 'structure',
15
+ message: 'Generated HTML is empty — no content in body.',
16
+ });
17
+ }
18
+ // 1. Check for AI slop font patterns (both CSS and Tailwind classes)
19
+ const allStyles = $('style').text() + ($('[style]').map((_i, el) => $(el).attr('style')).get().join(' '));
20
+ const allClasses = $('[class]').map((_i, el) => $(el).attr('class')).get().join(' ');
21
+ if (/font-family[^;]*\bInter\b/i.test(allStyles) && !isIntentionalFont('Inter')) {
22
+ issues.push({
23
+ type: 'warning',
24
+ category: 'slop',
25
+ message: 'Detected "Inter" font — common AI default. Consider a more distinctive typeface.',
26
+ });
27
+ }
28
+ if (/font-family[^;]*\bPoppins\b/i.test(allStyles) && !isIntentionalFont('Poppins')) {
29
+ issues.push({
30
+ type: 'warning',
31
+ category: 'slop',
32
+ message: 'Detected "Poppins" font — common AI default. Consider a more distinctive typeface.',
33
+ });
34
+ }
35
+ // 1b. Check Tailwind font classes (font-sans resolves to Inter/system-ui)
36
+ if (/\bfont-sans\b/.test(allClasses) && !isIntentionalFont('Inter')) {
37
+ issues.push({
38
+ type: 'info',
39
+ category: 'slop',
40
+ message: 'Tailwind "font-sans" class detected — resolves to system sans-serif. Consider specifying a custom font.',
41
+ });
42
+ }
43
+ // 2. Check for purple-to-blue gradients
44
+ if (/gradient[^;]*(purple|#[89a-f][0-9a-f]{5})[^;]*(blue|#[0-5][0-9a-f]{5})/i.test(allStyles) ||
45
+ /gradient[^;]*(blue|#[0-5][0-9a-f]{5})[^;]*(purple|#[89a-f][0-9a-f]{5})/i.test(allStyles)) {
46
+ issues.push({
47
+ type: 'warning',
48
+ category: 'slop',
49
+ message: 'Detected purple-to-blue gradient — extremely common AI pattern.',
50
+ });
51
+ }
52
+ // 3. Check heading hierarchy
53
+ const headings = $('h1, h2, h3, h4, h5, h6').map((_i, el) => parseInt(el.tagName[1])).get();
54
+ if (headings.length > 0) {
55
+ for (let i = 1; i < headings.length; i++) {
56
+ if (headings[i] > headings[i - 1] + 1) {
57
+ issues.push({
58
+ type: 'warning',
59
+ category: 'structure',
60
+ message: `Heading hierarchy skip: h${headings[i - 1]} → h${headings[i]}. Should not skip levels.`,
61
+ });
62
+ break;
63
+ }
64
+ }
65
+ }
66
+ // 4. Check for missing alt attributes
67
+ const imgsWithoutAlt = $('img:not([alt])').length;
68
+ if (imgsWithoutAlt > 0) {
69
+ issues.push({
70
+ type: 'error',
71
+ category: 'accessibility',
72
+ message: `${imgsWithoutAlt} image(s) missing alt attribute.`,
73
+ });
74
+ }
75
+ // 5. Check for DESIGN.md color adherence
76
+ const designColors = extractDesignColors();
77
+ if (designColors.length > 0) {
78
+ const usedColors = extractUsedColors(allStyles);
79
+ const unmatchedColors = usedColors.filter(c => !designColors.some(dc => dc.toLowerCase() === c.toLowerCase()) &&
80
+ !isNeutralColor(c));
81
+ if (unmatchedColors.length > 3) {
82
+ issues.push({
83
+ type: 'warning',
84
+ category: 'color',
85
+ message: `${unmatchedColors.length} colors used that aren't in DESIGN.md palette. Output may deviate from design system.`,
86
+ });
87
+ }
88
+ }
89
+ // 6. Check for three-column icon grid as second section (classic AI slop)
90
+ const sections = $('section, [class*="section"], main > div').toArray();
91
+ if (sections.length >= 2) {
92
+ const secondSection = $(sections[1]);
93
+ const icons = secondSection.find('svg, [class*="icon"], i[class]');
94
+ const columns = secondSection.find('[class*="col"], [class*="grid"]');
95
+ if (icons.length === 3 && columns.length > 0) {
96
+ issues.push({
97
+ type: 'info',
98
+ category: 'slop',
99
+ message: 'Second section appears to be a three-column icon grid — very common AI layout pattern.',
100
+ });
101
+ }
102
+ }
103
+ // 7. Check business model alignment (if DESIGN.md has business context)
104
+ if (existsSync('DESIGN.md')) {
105
+ const designContent = readFileSync('DESIGN.md', 'utf-8').toLowerCase();
106
+ // If NOT e-commerce, check for cart/checkout in generated HTML
107
+ if (designContent.includes('not an e-commerce') || designContent.includes('not e-commerce') || designContent.includes('no online purchasing')) {
108
+ const htmlLower = html.toLowerCase();
109
+ if (/\b(add.?to.?cart|shopping.?cart|checkout|buy.?now|carrito)\b/i.test(htmlLower)) {
110
+ issues.push({
111
+ type: 'error',
112
+ category: 'structure',
113
+ message: 'Generated HTML contains e-commerce elements (cart/checkout) but DESIGN.md specifies this is NOT an e-commerce site.',
114
+ });
115
+ }
116
+ }
117
+ // If store locator is key, check for location-related elements
118
+ if (designContent.includes('store locator') || designContent.includes('find nearest store')) {
119
+ const hasLocationElements = /\b(location|store.?finder|find.?store|sucursal|ubicaci|postal|zip.?code|mapa|map)\b/i.test(html.toLowerCase());
120
+ if (!hasLocationElements) {
121
+ issues.push({
122
+ type: 'info',
123
+ category: 'structure',
124
+ message: 'DESIGN.md specifies store locator as key feature but no location/finder elements detected in output.',
125
+ });
126
+ }
127
+ }
128
+ }
129
+ // Calculate score
130
+ const errorCount = issues.filter(i => i.type === 'error').length;
131
+ const warningCount = issues.filter(i => i.type === 'warning').length;
132
+ const infoCount = issues.filter(i => i.type === 'info').length;
133
+ const score = Math.max(0, 100 - (errorCount * 15) - (warningCount * 8) - (infoCount * 3));
134
+ return {
135
+ score,
136
+ issues,
137
+ passed: score >= 60,
138
+ };
139
+ }
140
+ function isIntentionalFont(fontName) {
141
+ if (!existsSync('DESIGN.md'))
142
+ return false;
143
+ const content = readFileSync('DESIGN.md', 'utf-8');
144
+ return content.includes(fontName);
145
+ }
146
+ function extractDesignColors() {
147
+ if (!existsSync('DESIGN.md'))
148
+ return [];
149
+ const content = readFileSync('DESIGN.md', 'utf-8');
150
+ const hexMatches = content.match(/#[0-9A-Fa-f]{6}/g) || [];
151
+ return [...new Set(hexMatches)];
152
+ }
153
+ function extractUsedColors(styles) {
154
+ const hexMatches = styles.match(/#[0-9A-Fa-f]{6}/g) || [];
155
+ return [...new Set(hexMatches)];
156
+ }
157
+ function isNeutralColor(hex) {
158
+ const r = parseInt(hex.slice(1, 3), 16);
159
+ const g = parseInt(hex.slice(3, 5), 16);
160
+ const b = parseInt(hex.slice(5, 7), 16);
161
+ const maxDiff = Math.max(Math.abs(r - g), Math.abs(g - b), Math.abs(r - b));
162
+ return maxDiff < 20; // Nearly grayscale
163
+ }
164
+ /**
165
+ * Format validation results for display.
166
+ */
167
+ export function formatValidationReport(result) {
168
+ const lines = [];
169
+ lines.push(`Quality Score: ${result.score}/100 (${result.passed ? 'PASS' : 'NEEDS REVIEW'})`);
170
+ lines.push('');
171
+ if (result.issues.length === 0) {
172
+ lines.push('No issues detected.');
173
+ return lines.join('\n');
174
+ }
175
+ const errors = result.issues.filter(i => i.type === 'error');
176
+ const warnings = result.issues.filter(i => i.type === 'warning');
177
+ const infos = result.issues.filter(i => i.type === 'info');
178
+ if (errors.length > 0) {
179
+ lines.push('Errors:');
180
+ errors.forEach(e => lines.push(` [x] ${e.message}`));
181
+ lines.push('');
182
+ }
183
+ if (warnings.length > 0) {
184
+ lines.push('Warnings:');
185
+ warnings.forEach(w => lines.push(` [!] ${w.message}`));
186
+ lines.push('');
187
+ }
188
+ if (infos.length > 0) {
189
+ lines.push('Info:');
190
+ infos.forEach(i => lines.push(` [i] ${i.message}`));
191
+ }
192
+ return lines.join('\n');
193
+ }
194
+ //# sourceMappingURL=output-validator.js.map