real-prototypes-skill 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.
Files changed (60) hide show
  1. package/.claude/skills/agent-browser-skill/SKILL.md +252 -0
  2. package/.claude/skills/real-prototypes-skill/.gitignore +188 -0
  3. package/.claude/skills/real-prototypes-skill/ACCESSIBILITY.md +668 -0
  4. package/.claude/skills/real-prototypes-skill/INSTALL.md +259 -0
  5. package/.claude/skills/real-prototypes-skill/LICENSE +21 -0
  6. package/.claude/skills/real-prototypes-skill/PUBLISH.md +310 -0
  7. package/.claude/skills/real-prototypes-skill/QUICKSTART.md +240 -0
  8. package/.claude/skills/real-prototypes-skill/README.md +442 -0
  9. package/.claude/skills/real-prototypes-skill/SKILL.md +375 -0
  10. package/.claude/skills/real-prototypes-skill/capture/capture-engine.js +1153 -0
  11. package/.claude/skills/real-prototypes-skill/capture/config.schema.json +170 -0
  12. package/.claude/skills/real-prototypes-skill/cli.js +596 -0
  13. package/.claude/skills/real-prototypes-skill/docs/TROUBLESHOOTING.md +278 -0
  14. package/.claude/skills/real-prototypes-skill/docs/schemas/capture-config.md +167 -0
  15. package/.claude/skills/real-prototypes-skill/docs/schemas/design-tokens.md +183 -0
  16. package/.claude/skills/real-prototypes-skill/docs/schemas/manifest.md +169 -0
  17. package/.claude/skills/real-prototypes-skill/examples/CLAUDE.md.example +73 -0
  18. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/CLAUDE.md +136 -0
  19. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/FEATURES.md +222 -0
  20. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/README.md +82 -0
  21. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/design-tokens.json +87 -0
  22. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/homepage-viewport.png +0 -0
  23. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/prototype-chatbot-final.png +0 -0
  24. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/prototype-fullpage-v2.png +0 -0
  25. package/.claude/skills/real-prototypes-skill/references/accessibility-fixes.md +298 -0
  26. package/.claude/skills/real-prototypes-skill/references/accessibility-report.json +253 -0
  27. package/.claude/skills/real-prototypes-skill/scripts/CAPTURE-ENHANCEMENTS.md +344 -0
  28. package/.claude/skills/real-prototypes-skill/scripts/IMPLEMENTATION-SUMMARY.md +517 -0
  29. package/.claude/skills/real-prototypes-skill/scripts/QUICK-START.md +229 -0
  30. package/.claude/skills/real-prototypes-skill/scripts/QUICKSTART-layout-analysis.md +148 -0
  31. package/.claude/skills/real-prototypes-skill/scripts/README-analyze-layout.md +407 -0
  32. package/.claude/skills/real-prototypes-skill/scripts/analyze-layout.js +880 -0
  33. package/.claude/skills/real-prototypes-skill/scripts/capture-platform.js +203 -0
  34. package/.claude/skills/real-prototypes-skill/scripts/comprehensive-capture.js +597 -0
  35. package/.claude/skills/real-prototypes-skill/scripts/create-manifest.js +338 -0
  36. package/.claude/skills/real-prototypes-skill/scripts/enterprise-pipeline.js +428 -0
  37. package/.claude/skills/real-prototypes-skill/scripts/extract-tokens.js +468 -0
  38. package/.claude/skills/real-prototypes-skill/scripts/full-site-capture.js +738 -0
  39. package/.claude/skills/real-prototypes-skill/scripts/generate-tailwind-config.js +296 -0
  40. package/.claude/skills/real-prototypes-skill/scripts/integrate-accessibility.sh +161 -0
  41. package/.claude/skills/real-prototypes-skill/scripts/manifest-schema.json +302 -0
  42. package/.claude/skills/real-prototypes-skill/scripts/setup-prototype.sh +167 -0
  43. package/.claude/skills/real-prototypes-skill/scripts/test-analyze-layout.js +338 -0
  44. package/.claude/skills/real-prototypes-skill/scripts/test-validation.js +307 -0
  45. package/.claude/skills/real-prototypes-skill/scripts/validate-accessibility.js +598 -0
  46. package/.claude/skills/real-prototypes-skill/scripts/validate-manifest.js +499 -0
  47. package/.claude/skills/real-prototypes-skill/scripts/validate-output.js +361 -0
  48. package/.claude/skills/real-prototypes-skill/scripts/validate-prerequisites.js +319 -0
  49. package/.claude/skills/real-prototypes-skill/scripts/verify-layout-analysis.sh +77 -0
  50. package/.claude/skills/real-prototypes-skill/templates/dashboard-widget.tsx.template +91 -0
  51. package/.claude/skills/real-prototypes-skill/templates/data-table.tsx.template +193 -0
  52. package/.claude/skills/real-prototypes-skill/templates/form-section.tsx.template +250 -0
  53. package/.claude/skills/real-prototypes-skill/templates/modal-dialog.tsx.template +239 -0
  54. package/.claude/skills/real-prototypes-skill/templates/nav-item.tsx.template +265 -0
  55. package/.claude/skills/real-prototypes-skill/validation/validation-engine.js +559 -0
  56. package/.env.example +74 -0
  57. package/LICENSE +21 -0
  58. package/README.md +444 -0
  59. package/bin/cli.js +319 -0
  60. package/package.json +59 -0
@@ -0,0 +1,559 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Validation Engine
5
+ *
6
+ * Enterprise-grade validation for platform capture and prototype generation.
7
+ * Runs as gates before and after key operations.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ class ValidationEngine {
14
+ constructor(options = {}) {
15
+ this.referenceDir = options.referenceDir || './references';
16
+ this.prototypeDir = options.prototypeDir || './prototype';
17
+ this.errors = [];
18
+ this.warnings = [];
19
+ }
20
+
21
+ log(message, type = 'info') {
22
+ const colors = {
23
+ info: '\x1b[36m',
24
+ success: '\x1b[32m',
25
+ warning: '\x1b[33m',
26
+ error: '\x1b[31m',
27
+ reset: '\x1b[0m'
28
+ };
29
+ const prefix = { info: 'ℹ', success: '✓', warning: '⚠', error: '✗' }[type] || '→';
30
+ console.log(`${colors[type] || ''}${prefix} ${message}${colors.reset}`);
31
+ }
32
+
33
+ /**
34
+ * Pre-Capture Validation
35
+ * Validates configuration before starting capture
36
+ */
37
+ validatePreCapture(config) {
38
+ this.log('Running pre-capture validation...', 'info');
39
+ const checks = [];
40
+
41
+ // Check platform URL
42
+ checks.push({
43
+ name: 'Platform URL valid',
44
+ passed: this.isValidUrl(config.platform?.baseUrl),
45
+ message: config.platform?.baseUrl ? `URL: ${config.platform.baseUrl}` : 'Missing platform.baseUrl'
46
+ });
47
+
48
+ // Check credentials available
49
+ if (config.auth?.type === 'form') {
50
+ const hasEmail = process.env.PLATFORM_EMAIL || config.auth?.credentials?.email;
51
+ const hasPassword = process.env.PLATFORM_PASSWORD || config.auth?.credentials?.password;
52
+ checks.push({
53
+ name: 'Credentials available',
54
+ passed: !!(hasEmail && hasPassword),
55
+ message: hasEmail && hasPassword ? 'Credentials found' : 'Missing PLATFORM_EMAIL or PLATFORM_PASSWORD'
56
+ });
57
+ }
58
+
59
+ // Check output directory writable
60
+ const outputDir = config.output?.directory || './references';
61
+ checks.push({
62
+ name: 'Output directory accessible',
63
+ passed: this.isDirectoryWritable(outputDir),
64
+ message: `Directory: ${outputDir}`
65
+ });
66
+
67
+ return this.summarizeChecks('Pre-Capture', checks);
68
+ }
69
+
70
+ /**
71
+ * Post-Capture Validation
72
+ * Validates capture completeness
73
+ */
74
+ validatePostCapture(config = {}) {
75
+ this.log('Running post-capture validation...', 'info');
76
+ const checks = [];
77
+
78
+ // Check manifest exists
79
+ const manifestPath = path.join(this.referenceDir, 'manifest.json');
80
+ const manifestExists = fs.existsSync(manifestPath);
81
+ checks.push({
82
+ name: 'Manifest exists',
83
+ passed: manifestExists,
84
+ message: manifestExists ? manifestPath : 'manifest.json not found'
85
+ });
86
+
87
+ if (!manifestExists) {
88
+ return this.summarizeChecks('Post-Capture', checks);
89
+ }
90
+
91
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
92
+
93
+ // Check minimum pages
94
+ const minPages = config.validation?.minPages || 5;
95
+ checks.push({
96
+ name: 'Minimum pages captured',
97
+ passed: manifest.pages?.length >= minPages,
98
+ message: `${manifest.pages?.length || 0} pages (minimum: ${minPages})`
99
+ });
100
+
101
+ // Check screenshots exist
102
+ const missingScreenshots = [];
103
+ manifest.pages?.forEach(page => {
104
+ if (page.screenshot) {
105
+ const screenshotPath = path.join(this.referenceDir, page.screenshot);
106
+ if (!fs.existsSync(screenshotPath)) {
107
+ missingScreenshots.push(page.screenshot);
108
+ }
109
+ }
110
+ });
111
+ checks.push({
112
+ name: 'All screenshots exist',
113
+ passed: missingScreenshots.length === 0,
114
+ message: missingScreenshots.length === 0
115
+ ? `${manifest.pages?.length || 0} screenshots verified`
116
+ : `Missing: ${missingScreenshots.slice(0, 3).join(', ')}${missingScreenshots.length > 3 ? '...' : ''}`
117
+ });
118
+
119
+ // Check design tokens
120
+ const tokensPath = path.join(this.referenceDir, 'design-tokens.json');
121
+ const tokensExist = fs.existsSync(tokensPath);
122
+ checks.push({
123
+ name: 'Design tokens extracted',
124
+ passed: tokensExist,
125
+ message: tokensExist ? 'design-tokens.json found' : 'design-tokens.json missing'
126
+ });
127
+
128
+ if (tokensExist) {
129
+ const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
130
+ const minColors = config.validation?.minColors || 10;
131
+ checks.push({
132
+ name: 'Sufficient colors extracted',
133
+ passed: tokens.totalColorsFound >= minColors,
134
+ message: `${tokens.totalColorsFound} colors (minimum: ${minColors})`
135
+ });
136
+
137
+ // Check primary color exists
138
+ checks.push({
139
+ name: 'Primary color identified',
140
+ passed: !!tokens.colors?.primary,
141
+ message: tokens.colors?.primary || 'Primary color not identified'
142
+ });
143
+ }
144
+
145
+ // Validate page entries have required fields
146
+ const pagesWithMissingNames = (manifest.pages || []).filter(p => !p.name);
147
+ if (pagesWithMissingNames.length > 0) {
148
+ checks.push({
149
+ name: 'Page entries have names',
150
+ passed: false,
151
+ message: `${pagesWithMissingNames.length} pages missing 'name' field. Check manifest.json structure.`
152
+ });
153
+ this.warnings.push(
154
+ 'Pages missing name field - manifest.json may be malformed. ' +
155
+ 'Each page entry requires: { name: "page-name", url: "/path", screenshot: "screenshots/file.png" }'
156
+ );
157
+ }
158
+
159
+ // Check for common page patterns (safely handle missing names)
160
+ const pageNames = (manifest.pages || [])
161
+ .filter(p => p && p.name)
162
+ .map(p => p.name.toLowerCase());
163
+
164
+ // List pages should have detail pages
165
+ const listPages = pageNames.filter(n => n.includes('list') || n.includes('accounts') || n.includes('contacts'));
166
+ const detailPages = pageNames.filter(n => n.includes('detail'));
167
+
168
+ if (listPages.length > 0) {
169
+ checks.push({
170
+ name: 'List-Detail pattern complete',
171
+ passed: detailPages.length > 0,
172
+ message: detailPages.length > 0
173
+ ? `${listPages.length} list pages, ${detailPages.length} detail pages`
174
+ : `${listPages.length} list pages found but no detail pages captured`
175
+ });
176
+ }
177
+
178
+ // Check for common pages
179
+ const commonPages = ['settings', 'search'];
180
+ commonPages.forEach(pageName => {
181
+ const found = pageNames.some(n => n.includes(pageName));
182
+ if (!found) {
183
+ this.warnings.push(`Common page '${pageName}' not found - may be missing`);
184
+ }
185
+ });
186
+
187
+ // Check HTML files if configured
188
+ if (config.output?.html !== false) {
189
+ const htmlDir = path.join(this.referenceDir, 'html');
190
+ const htmlCount = fs.existsSync(htmlDir)
191
+ ? fs.readdirSync(htmlDir).filter(f => f.endsWith('.html')).length
192
+ : 0;
193
+ checks.push({
194
+ name: 'HTML files captured',
195
+ passed: htmlCount > 0,
196
+ message: `${htmlCount} HTML files`
197
+ });
198
+ }
199
+
200
+ return this.summarizeChecks('Post-Capture', checks);
201
+ }
202
+
203
+ /**
204
+ * Pre-Generation Validation
205
+ * Validates prerequisites before prototype generation
206
+ */
207
+ validatePreGeneration() {
208
+ this.log('Running pre-generation validation...', 'info');
209
+ const checks = [];
210
+
211
+ // Check manifest
212
+ const manifestPath = path.join(this.referenceDir, 'manifest.json');
213
+ checks.push({
214
+ name: 'Manifest exists',
215
+ passed: fs.existsSync(manifestPath),
216
+ message: fs.existsSync(manifestPath) ? 'Found' : 'Missing - run capture first'
217
+ });
218
+
219
+ // Check design tokens
220
+ const tokensPath = path.join(this.referenceDir, 'design-tokens.json');
221
+ const tokensExist = fs.existsSync(tokensPath);
222
+ checks.push({
223
+ name: 'Design tokens exist',
224
+ passed: tokensExist,
225
+ message: tokensExist ? 'Found' : 'Missing - colors will be incorrect'
226
+ });
227
+
228
+ if (tokensExist) {
229
+ const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
230
+
231
+ // Validate required color categories
232
+ const requiredColors = ['primary', 'text', 'background', 'border'];
233
+ const missingColors = requiredColors.filter(c => !tokens.colors?.[c]);
234
+
235
+ checks.push({
236
+ name: 'Required color categories',
237
+ passed: missingColors.length === 0,
238
+ message: missingColors.length === 0
239
+ ? 'All required colors present'
240
+ : `Missing: ${missingColors.join(', ')}`
241
+ });
242
+ }
243
+
244
+ // Check screenshots directory
245
+ const screenshotsDir = path.join(this.referenceDir, 'screenshots');
246
+ const screenshotCount = fs.existsSync(screenshotsDir)
247
+ ? fs.readdirSync(screenshotsDir).filter(f => f.endsWith('.png')).length
248
+ : 0;
249
+
250
+ checks.push({
251
+ name: 'Screenshots available',
252
+ passed: screenshotCount > 0,
253
+ message: `${screenshotCount} screenshots found`
254
+ });
255
+
256
+ return this.summarizeChecks('Pre-Generation', checks);
257
+ }
258
+
259
+ /**
260
+ * Post-Generation Validation
261
+ * Validates generated prototype for accuracy
262
+ */
263
+ validatePostGeneration() {
264
+ this.log('Running post-generation validation...', 'info');
265
+ const checks = [];
266
+
267
+ // Check prototype directory exists
268
+ checks.push({
269
+ name: 'Prototype directory exists',
270
+ passed: fs.existsSync(this.prototypeDir),
271
+ message: this.prototypeDir
272
+ });
273
+
274
+ if (!fs.existsSync(this.prototypeDir)) {
275
+ return this.summarizeChecks('Post-Generation', checks);
276
+ }
277
+
278
+ // Check package.json
279
+ const packagePath = path.join(this.prototypeDir, 'package.json');
280
+ checks.push({
281
+ name: 'package.json exists',
282
+ passed: fs.existsSync(packagePath),
283
+ message: fs.existsSync(packagePath) ? 'Found' : 'Missing'
284
+ });
285
+
286
+ // Check Tailwind config
287
+ const tailwindPath = path.join(this.prototypeDir, 'tailwind.config.js');
288
+ const tailwindTsPath = path.join(this.prototypeDir, 'tailwind.config.ts');
289
+ checks.push({
290
+ name: 'Tailwind config exists',
291
+ passed: fs.existsSync(tailwindPath) || fs.existsSync(tailwindTsPath),
292
+ message: fs.existsSync(tailwindPath) || fs.existsSync(tailwindTsPath) ? 'Found' : 'Missing'
293
+ });
294
+
295
+ // Validate colors in generated code
296
+ const colorValidation = this.validateGeneratedColors();
297
+ checks.push({
298
+ name: 'Colors match design tokens',
299
+ passed: colorValidation.passed,
300
+ message: colorValidation.message
301
+ });
302
+
303
+ // Check for Tailwind default colors (should not be used)
304
+ const defaultColorCheck = this.checkForDefaultColors();
305
+ checks.push({
306
+ name: 'No Tailwind default colors',
307
+ passed: defaultColorCheck.passed,
308
+ message: defaultColorCheck.message
309
+ });
310
+
311
+ // Check component files exist
312
+ const componentsDir = path.join(this.prototypeDir, 'src', 'components');
313
+ const componentCount = fs.existsSync(componentsDir)
314
+ ? fs.readdirSync(componentsDir).filter(f => f.endsWith('.tsx')).length
315
+ : 0;
316
+
317
+ checks.push({
318
+ name: 'Components generated',
319
+ passed: componentCount > 0,
320
+ message: `${componentCount} components`
321
+ });
322
+
323
+ return this.summarizeChecks('Post-Generation', checks);
324
+ }
325
+
326
+ /**
327
+ * Validate that generated colors match design tokens
328
+ */
329
+ validateGeneratedColors() {
330
+ const tokensPath = path.join(this.referenceDir, 'design-tokens.json');
331
+ if (!fs.existsSync(tokensPath)) {
332
+ return { passed: false, message: 'Design tokens not found' };
333
+ }
334
+
335
+ const tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
336
+ const allowedColors = new Set();
337
+
338
+ // Add all colors from tokens
339
+ const addColors = (obj) => {
340
+ Object.values(obj).forEach(value => {
341
+ if (typeof value === 'string' && value.startsWith('#')) {
342
+ allowedColors.add(value.toLowerCase());
343
+ } else if (typeof value === 'object') {
344
+ addColors(value);
345
+ }
346
+ });
347
+ };
348
+ addColors(tokens.colors || {});
349
+ tokens.rawColors?.forEach(([color]) => allowedColors.add(color.toLowerCase()));
350
+
351
+ // Add common acceptable colors
352
+ ['#fff', '#ffffff', '#000', '#000000', 'transparent', 'inherit', 'currentColor'].forEach(c =>
353
+ allowedColors.add(c.toLowerCase())
354
+ );
355
+
356
+ // Scan generated files for colors
357
+ const srcDir = path.join(this.prototypeDir, 'src');
358
+ if (!fs.existsSync(srcDir)) {
359
+ return { passed: false, message: 'Source directory not found' };
360
+ }
361
+
362
+ const invalidColors = new Set();
363
+ this.scanFilesForColors(srcDir, allowedColors, invalidColors);
364
+
365
+ if (invalidColors.size === 0) {
366
+ return { passed: true, message: 'All colors from design tokens' };
367
+ } else {
368
+ return {
369
+ passed: false,
370
+ message: `Invalid colors found: ${[...invalidColors].slice(0, 5).join(', ')}${invalidColors.size > 5 ? '...' : ''}`
371
+ };
372
+ }
373
+ }
374
+
375
+ scanFilesForColors(dir, allowedColors, invalidColors) {
376
+ const files = fs.readdirSync(dir);
377
+
378
+ for (const file of files) {
379
+ const filePath = path.join(dir, file);
380
+ const stat = fs.statSync(filePath);
381
+
382
+ if (stat.isDirectory()) {
383
+ this.scanFilesForColors(filePath, allowedColors, invalidColors);
384
+ } else if (file.endsWith('.tsx') || file.endsWith('.ts') || file.endsWith('.css')) {
385
+ const content = fs.readFileSync(filePath, 'utf-8');
386
+
387
+ // Find hex colors
388
+ const hexColors = content.match(/#[0-9a-fA-F]{3,8}\b/g) || [];
389
+ hexColors.forEach(color => {
390
+ const normalized = color.toLowerCase();
391
+ if (!allowedColors.has(normalized)) {
392
+ invalidColors.add(normalized);
393
+ }
394
+ });
395
+ }
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Check for Tailwind default color classes
401
+ */
402
+ checkForDefaultColors() {
403
+ const srcDir = path.join(this.prototypeDir, 'src');
404
+ if (!fs.existsSync(srcDir)) {
405
+ return { passed: true, message: 'Source directory not found' };
406
+ }
407
+
408
+ // Tailwind default color patterns that shouldn't be used
409
+ const defaultColorPatterns = [
410
+ /\b(bg|text|border|ring)-(red|blue|green|yellow|purple|pink|indigo|gray|slate|zinc|neutral|stone|orange|amber|lime|emerald|teal|cyan|sky|violet|fuchsia|rose)-\d{2,3}\b/g
411
+ ];
412
+
413
+ const foundDefaults = new Set();
414
+ this.scanFilesForPatterns(srcDir, defaultColorPatterns, foundDefaults);
415
+
416
+ if (foundDefaults.size === 0) {
417
+ return { passed: true, message: 'No default Tailwind colors used' };
418
+ } else {
419
+ return {
420
+ passed: false,
421
+ message: `Default colors found: ${[...foundDefaults].slice(0, 5).join(', ')}`
422
+ };
423
+ }
424
+ }
425
+
426
+ scanFilesForPatterns(dir, patterns, matches) {
427
+ const files = fs.readdirSync(dir);
428
+
429
+ for (const file of files) {
430
+ const filePath = path.join(dir, file);
431
+ const stat = fs.statSync(filePath);
432
+
433
+ if (stat.isDirectory()) {
434
+ this.scanFilesForPatterns(filePath, patterns, matches);
435
+ } else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
436
+ const content = fs.readFileSync(filePath, 'utf-8');
437
+
438
+ patterns.forEach(pattern => {
439
+ const found = content.match(pattern) || [];
440
+ found.forEach(match => matches.add(match));
441
+ });
442
+ }
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Helper methods
448
+ */
449
+ isValidUrl(url) {
450
+ try {
451
+ new URL(url);
452
+ return true;
453
+ } catch {
454
+ return false;
455
+ }
456
+ }
457
+
458
+ isDirectoryWritable(dir) {
459
+ try {
460
+ if (!fs.existsSync(dir)) {
461
+ fs.mkdirSync(dir, { recursive: true });
462
+ }
463
+ const testFile = path.join(dir, '.write-test');
464
+ fs.writeFileSync(testFile, 'test');
465
+ fs.unlinkSync(testFile);
466
+ return true;
467
+ } catch {
468
+ return false;
469
+ }
470
+ }
471
+
472
+ summarizeChecks(phase, checks) {
473
+ console.log(`\n${phase} Validation Results:`);
474
+ console.log('─'.repeat(50));
475
+
476
+ let allPassed = true;
477
+ checks.forEach(check => {
478
+ const icon = check.passed ? '✓' : '✗';
479
+ const color = check.passed ? '\x1b[32m' : '\x1b[31m';
480
+ console.log(`${color}${icon}\x1b[0m ${check.name}`);
481
+ console.log(` ${check.message}`);
482
+ if (!check.passed) allPassed = false;
483
+ });
484
+
485
+ if (this.warnings.length > 0) {
486
+ console.log('\nWarnings:');
487
+ this.warnings.forEach(w => console.log(`\x1b[33m⚠\x1b[0m ${w}`));
488
+ }
489
+
490
+ console.log('─'.repeat(50));
491
+ console.log(`Status: ${allPassed ? '\x1b[32mPASSED\x1b[0m' : '\x1b[31mFAILED\x1b[0m'}`);
492
+
493
+ return {
494
+ passed: allPassed,
495
+ checks,
496
+ warnings: this.warnings,
497
+ errors: this.errors
498
+ };
499
+ }
500
+ }
501
+
502
+ // CLI
503
+ if (require.main === module) {
504
+ const args = process.argv.slice(2);
505
+ const phase = args[0] || 'all';
506
+
507
+ const validator = new ValidationEngine({
508
+ referenceDir: args[1] || './references',
509
+ prototypeDir: args[2] || './prototype'
510
+ });
511
+
512
+ let result;
513
+ switch (phase) {
514
+ case 'pre-capture':
515
+ const config = args[1] ? JSON.parse(fs.readFileSync(args[1], 'utf-8')) : {};
516
+ result = validator.validatePreCapture(config);
517
+ break;
518
+ case 'post-capture':
519
+ result = validator.validatePostCapture();
520
+ break;
521
+ case 'pre-generation':
522
+ result = validator.validatePreGeneration();
523
+ break;
524
+ case 'post-generation':
525
+ result = validator.validatePostGeneration();
526
+ break;
527
+ case 'all':
528
+ console.log('\n' + '='.repeat(60));
529
+ console.log('RUNNING ALL VALIDATIONS');
530
+ console.log('='.repeat(60));
531
+
532
+ const results = [];
533
+ results.push(validator.validatePostCapture());
534
+ results.push(validator.validatePreGeneration());
535
+ results.push(validator.validatePostGeneration());
536
+
537
+ result = { passed: results.every(r => r.passed) };
538
+ break;
539
+ default:
540
+ console.log(`
541
+ Validation Engine
542
+
543
+ Usage:
544
+ node validation-engine.js <phase> [referenceDir] [prototypeDir]
545
+
546
+ Phases:
547
+ pre-capture Validate before capture (requires config file path)
548
+ post-capture Validate after capture
549
+ pre-generation Validate before prototype generation
550
+ post-generation Validate after prototype generation
551
+ all Run all applicable validations
552
+ `);
553
+ process.exit(0);
554
+ }
555
+
556
+ process.exit(result.passed ? 0 : 1);
557
+ }
558
+
559
+ module.exports = { ValidationEngine };
package/.env.example ADDED
@@ -0,0 +1,74 @@
1
+ # ============================================
2
+ # Real Prototypes - Configuration Template
3
+ # ============================================
4
+ # Copy this file to CLAUDE.md in your project root,
5
+ # or set these as environment variables.
6
+ #
7
+ # IMPORTANT: Never commit credentials to version control!
8
+ # ============================================
9
+
10
+ # ----------------------
11
+ # Platform Credentials
12
+ # ----------------------
13
+ # The target platform URL to capture
14
+ PLATFORM_URL=https://www.example.com
15
+
16
+ # Login credentials (if the platform requires authentication)
17
+ PLATFORM_EMAIL=your@email.com
18
+ PLATFORM_PASSWORD=your-password
19
+
20
+ # ----------------------
21
+ # Capture Settings
22
+ # ----------------------
23
+
24
+ # Pages to capture
25
+ # Option 1: Specify pages manually (comma-separated paths)
26
+ PAGES_TO_CAPTURE=/homepage,/products,/checkout,/account
27
+
28
+ # Option 2: Auto-discover all pages
29
+ # PAGES_TO_CAPTURE=auto
30
+
31
+ # Option 3: Disable auto-capture (only capture specified pages)
32
+ # PAGES_TO_CAPTURE=off
33
+
34
+ # Capture mode
35
+ # "full" = capture all discovered pages up to MAX_PAGES
36
+ # "manual" = only capture pages listed in PAGES_TO_CAPTURE
37
+ CAPTURE_MODE=manual
38
+
39
+ # Maximum pages to capture in auto/full mode
40
+ MAX_PAGES=50
41
+
42
+ # Viewport dimensions (pixels)
43
+ VIEWPORT_WIDTH=1920
44
+ VIEWPORT_HEIGHT=1080
45
+
46
+ # Wait time after page load before capturing (milliseconds)
47
+ # Increase this if pages have slow-loading content or animations
48
+ WAIT_AFTER_LOAD=2000
49
+
50
+ # ----------------------
51
+ # Advanced Settings
52
+ # ----------------------
53
+
54
+ # Capture multiple viewports (comma-separated: width:height)
55
+ # VIEWPORTS=1920:1080,768:1024,375:812
56
+
57
+ # Exclude URL patterns from capture (comma-separated)
58
+ # EXCLUDE_PATTERNS=/logout,/signout,/delete,/admin
59
+
60
+ # Enable interaction capture (click buttons, tabs, dropdowns)
61
+ # CAPTURE_INTERACTIONS=true
62
+
63
+ # ----------------------
64
+ # Output Settings
65
+ # ----------------------
66
+
67
+ # Output directory for captured references
68
+ # OUTPUT_DIR=./references
69
+
70
+ # Save HTML source alongside screenshots
71
+ # SAVE_HTML=true
72
+
73
+ # Extract and save design tokens (colors, fonts, spacing)
74
+ # EXTRACT_DESIGN_TOKENS=true
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kaidhar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.