real-prototypes-skill 2.0.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 +329 -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,338 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * create-manifest.js
5
+ *
6
+ * Generates a manifest.json file for platform prototype references.
7
+ * Scans references/screenshots and references/html directories to auto-discover files.
8
+ *
9
+ * Usage:
10
+ * node create-manifest.js <platform-name> <platform-url> [output-dir]
11
+ *
12
+ * Arguments:
13
+ * platform-name Name of the platform (e.g., "Linear", "Notion")
14
+ * platform-url Base URL of the platform (e.g., "https://linear.app")
15
+ * output-dir Optional output directory (defaults to current directory)
16
+ *
17
+ * Example:
18
+ * node create-manifest.js "Linear" "https://linear.app" ./references
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ // Supported file extensions
25
+ const SCREENSHOT_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
26
+ const HTML_EXTENSIONS = ['.html', '.htm'];
27
+
28
+ /**
29
+ * Parse command line arguments
30
+ */
31
+ function parseArgs() {
32
+ const args = process.argv.slice(2);
33
+
34
+ if (args.length < 2) {
35
+ console.error('Usage: node create-manifest.js <platform-name> <platform-url> [output-dir]');
36
+ console.error('');
37
+ console.error('Arguments:');
38
+ console.error(' platform-name Name of the platform (e.g., "Linear")');
39
+ console.error(' platform-url Base URL of the platform (e.g., "https://linear.app")');
40
+ console.error(' output-dir Optional output directory (defaults to current directory)');
41
+ process.exit(1);
42
+ }
43
+
44
+ return {
45
+ platformName: args[0],
46
+ platformUrl: args[1],
47
+ outputDir: args[2] || process.cwd()
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Check if a file has a matching extension
53
+ */
54
+ function hasExtension(filename, extensions) {
55
+ const ext = path.extname(filename).toLowerCase();
56
+ return extensions.includes(ext);
57
+ }
58
+
59
+ /**
60
+ * Scan a directory for files with specific extensions
61
+ */
62
+ function scanDirectory(dirPath, extensions) {
63
+ const files = [];
64
+
65
+ if (!fs.existsSync(dirPath)) {
66
+ return files;
67
+ }
68
+
69
+ try {
70
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
71
+
72
+ for (const entry of entries) {
73
+ if (entry.isFile() && hasExtension(entry.name, extensions)) {
74
+ const filePath = path.join(dirPath, entry.name);
75
+ const stats = fs.statSync(filePath);
76
+ files.push({
77
+ name: entry.name,
78
+ path: filePath,
79
+ mtime: stats.mtime
80
+ });
81
+ }
82
+ }
83
+ } catch (err) {
84
+ console.warn(`Warning: Could not read directory ${dirPath}: ${err.message}`);
85
+ }
86
+
87
+ return files.sort((a, b) => a.name.localeCompare(b.name));
88
+ }
89
+
90
+ /**
91
+ * Extract page ID from filename
92
+ * Converts "my-page-name.png" to "my-page-name"
93
+ * Handles patterns like "page-name-desktop.png" or "page-name-1.png"
94
+ */
95
+ function extractPageId(filename) {
96
+ // Remove extension
97
+ let id = path.basename(filename, path.extname(filename));
98
+
99
+ // Normalize to lowercase with hyphens
100
+ id = id.toLowerCase().replace(/[_\s]+/g, '-');
101
+
102
+ return id;
103
+ }
104
+
105
+ /**
106
+ * Convert page ID to human-readable name
107
+ * Converts "my-page-name" to "My Page Name"
108
+ */
109
+ function pageIdToName(pageId) {
110
+ return pageId
111
+ .split('-')
112
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
113
+ .join(' ');
114
+ }
115
+
116
+ /**
117
+ * Detect viewport/variant from filename
118
+ * Looks for patterns like "-desktop", "-mobile", "-hover", "-1920x1080"
119
+ */
120
+ function detectScreenshotMeta(filename) {
121
+ const basename = path.basename(filename, path.extname(filename)).toLowerCase();
122
+ const meta = {};
123
+
124
+ // Check for viewport indicators
125
+ if (basename.includes('desktop') || basename.includes('-lg') || basename.includes('-large')) {
126
+ meta.viewport = 'desktop';
127
+ } else if (basename.includes('mobile') || basename.includes('-sm') || basename.includes('-small')) {
128
+ meta.viewport = 'mobile';
129
+ } else if (basename.includes('tablet') || basename.includes('-md') || basename.includes('-medium')) {
130
+ meta.viewport = 'tablet';
131
+ }
132
+
133
+ // Check for resolution pattern (e.g., 1920x1080)
134
+ const resMatch = basename.match(/(\d{3,4})x(\d{3,4})/);
135
+ if (resMatch) {
136
+ meta.viewport = `${resMatch[1]}x${resMatch[2]}`;
137
+ }
138
+
139
+ // Check for state indicators
140
+ if (basename.includes('hover')) {
141
+ meta.state = 'hover';
142
+ } else if (basename.includes('active') || basename.includes('pressed')) {
143
+ meta.state = 'active';
144
+ } else if (basename.includes('focus')) {
145
+ meta.state = 'focus';
146
+ } else if (basename.includes('expanded')) {
147
+ meta.state = 'expanded';
148
+ } else if (basename.includes('collapsed')) {
149
+ meta.state = 'collapsed';
150
+ } else if (basename.includes('loading')) {
151
+ meta.state = 'loading';
152
+ } else if (basename.includes('empty')) {
153
+ meta.state = 'empty';
154
+ } else if (basename.includes('error')) {
155
+ meta.state = 'error';
156
+ }
157
+
158
+ return meta;
159
+ }
160
+
161
+ /**
162
+ * Group screenshots by base page name
163
+ * Handles multiple screenshots per page (different viewports, states)
164
+ */
165
+ function groupScreenshots(files) {
166
+ const groups = new Map();
167
+
168
+ for (const file of files) {
169
+ const basename = path.basename(file.name, path.extname(file.name)).toLowerCase();
170
+
171
+ // Try to extract base page name (remove viewport/state suffixes)
172
+ let baseName = basename
173
+ .replace(/[-_](desktop|mobile|tablet|lg|md|sm|large|medium|small)$/i, '')
174
+ .replace(/[-_](hover|active|pressed|focus|expanded|collapsed|loading|empty|error)$/i, '')
175
+ .replace(/[-_]\d{3,4}x\d{3,4}$/i, '')
176
+ .replace(/[-_]\d+$/i, ''); // Remove trailing numbers
177
+
178
+ if (!groups.has(baseName)) {
179
+ groups.set(baseName, []);
180
+ }
181
+ groups.get(baseName).push(file);
182
+ }
183
+
184
+ return groups;
185
+ }
186
+
187
+ /**
188
+ * Match HTML files to screenshot groups
189
+ */
190
+ function matchHtmlFiles(htmlFiles, screenshotGroups) {
191
+ const matches = new Map();
192
+
193
+ for (const html of htmlFiles) {
194
+ const basename = path.basename(html.name, path.extname(html.name)).toLowerCase();
195
+
196
+ // Look for exact or partial match in screenshot groups
197
+ for (const [groupName] of screenshotGroups) {
198
+ if (basename === groupName || basename.includes(groupName) || groupName.includes(basename)) {
199
+ matches.set(groupName, html);
200
+ break;
201
+ }
202
+ }
203
+
204
+ // If no match found, create standalone entry
205
+ if (!matches.has(basename)) {
206
+ matches.set(basename, html);
207
+ }
208
+ }
209
+
210
+ return matches;
211
+ }
212
+
213
+ /**
214
+ * Generate manifest from discovered files
215
+ */
216
+ function generateManifest(platformName, platformUrl, outputDir) {
217
+ const screenshotsDir = path.join(outputDir, 'references', 'screenshots');
218
+ const htmlDir = path.join(outputDir, 'references', 'html');
219
+
220
+ // Scan directories
221
+ const screenshotFiles = scanDirectory(screenshotsDir, SCREENSHOT_EXTENSIONS);
222
+ const htmlFiles = scanDirectory(htmlDir, HTML_EXTENSIONS);
223
+
224
+ console.log(`Found ${screenshotFiles.length} screenshot(s) in ${screenshotsDir}`);
225
+ console.log(`Found ${htmlFiles.length} HTML file(s) in ${htmlDir}`);
226
+
227
+ // Group screenshots by page
228
+ const screenshotGroups = groupScreenshots(screenshotFiles);
229
+
230
+ // Match HTML files to groups
231
+ const htmlMatches = matchHtmlFiles(htmlFiles, screenshotGroups);
232
+
233
+ // Collect all unique page identifiers
234
+ const pageIds = new Set([...screenshotGroups.keys(), ...htmlMatches.keys()]);
235
+
236
+ // Build pages array
237
+ const pages = [];
238
+
239
+ for (const pageId of pageIds) {
240
+ const page = {
241
+ id: pageId,
242
+ name: pageIdToName(pageId),
243
+ path: `/${pageId}`
244
+ };
245
+
246
+ // Add screenshots
247
+ const screenshots = screenshotGroups.get(pageId);
248
+ if (screenshots && screenshots.length > 0) {
249
+ page.screenshots = screenshots.map(file => {
250
+ const meta = detectScreenshotMeta(file.name);
251
+ return {
252
+ file: file.name,
253
+ ...meta
254
+ };
255
+ });
256
+ }
257
+
258
+ // Add HTML reference
259
+ const html = htmlMatches.get(pageId);
260
+ if (html) {
261
+ page.html = {
262
+ file: html.name,
263
+ capturedAt: html.mtime.toISOString()
264
+ };
265
+ }
266
+
267
+ pages.push(page);
268
+ }
269
+
270
+ // Sort pages alphabetically by ID
271
+ pages.sort((a, b) => a.id.localeCompare(b.id));
272
+
273
+ // Build manifest
274
+ const manifest = {
275
+ platform: {
276
+ name: platformName,
277
+ url: platformUrl,
278
+ capturedAt: new Date().toISOString()
279
+ },
280
+ pages: pages,
281
+ designTokens: {
282
+ colors: {},
283
+ typography: {},
284
+ spacing: {}
285
+ },
286
+ metadata: {
287
+ generatedAt: new Date().toISOString(),
288
+ generatedBy: 'create-manifest.js'
289
+ }
290
+ };
291
+
292
+ return manifest;
293
+ }
294
+
295
+ /**
296
+ * Main execution
297
+ */
298
+ function main() {
299
+ const { platformName, platformUrl, outputDir } = parseArgs();
300
+
301
+ // Resolve output directory to absolute path
302
+ const resolvedOutputDir = path.resolve(outputDir);
303
+
304
+ console.log(`Generating manifest for: ${platformName}`);
305
+ console.log(`Platform URL: ${platformUrl}`);
306
+ console.log(`Output directory: ${resolvedOutputDir}`);
307
+ console.log('');
308
+
309
+ // Generate manifest
310
+ const manifest = generateManifest(platformName, platformUrl, resolvedOutputDir);
311
+
312
+ // Write manifest file
313
+ const manifestPath = path.join(resolvedOutputDir, 'manifest.json');
314
+
315
+ try {
316
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
317
+ console.log('');
318
+ console.log(`Manifest written to: ${manifestPath}`);
319
+ console.log(`Total pages: ${manifest.pages.length}`);
320
+ } catch (err) {
321
+ console.error(`Error writing manifest: ${err.message}`);
322
+ process.exit(1);
323
+ }
324
+ }
325
+
326
+ // Run if executed directly
327
+ if (require.main === module) {
328
+ main();
329
+ }
330
+
331
+ // Export for testing
332
+ module.exports = {
333
+ generateManifest,
334
+ extractPageId,
335
+ pageIdToName,
336
+ detectScreenshotMeta,
337
+ groupScreenshots
338
+ };