@texturehq/edges 1.7.1 → 1.7.3

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,970 @@
1
+ // generate-edges-docs.js
2
+ // Unified AI Context Generator for @texturehq/edges
3
+ //
4
+ // Consolidates and enhances:
5
+ // - generate-components-manifest.js
6
+ // - generate-utilities-manifest.js
7
+ //
8
+ // Generates:
9
+ // - Component, utility, and hook manifests
10
+ // - docs/ai/edges/ markdown documentation
11
+ // - .agent-fragments/edges.md (for merging with other fragments)
12
+ // - .cursor/rules/edges-components.mdc
13
+ // - Distributable context files for consuming apps
14
+
15
+ import { execSync } from "child_process";
16
+ import fs from "fs";
17
+ import path from "path";
18
+
19
+ const ROOT = process.cwd();
20
+ const SRC_DIR = path.join(ROOT, "src");
21
+ const DIST_DIR = path.join(ROOT, "dist");
22
+ const MONO_ROOT = path.join(ROOT, "../..");
23
+ const DOCS_AI_EDGES = path.join(MONO_ROOT, "docs/ai/edges");
24
+
25
+ // ============================================================================
26
+ // UTILITIES
27
+ // ============================================================================
28
+
29
+ function read(file) {
30
+ return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
31
+ }
32
+
33
+ function ensureDir(dir) {
34
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
35
+ }
36
+
37
+ function writeFile(filePath, content) {
38
+ ensureDir(path.dirname(filePath));
39
+ fs.writeFileSync(filePath, content, "utf8");
40
+ console.log(`✓ Generated: ${filePath}`);
41
+ }
42
+
43
+ // ============================================================================
44
+ // COMPONENT MANIFEST GENERATION
45
+ // ============================================================================
46
+
47
+ function extractExportsFromIndex(indexContent) {
48
+ const re = /export\s+\{\s*([A-Za-z0-9_,\s]+)\s*\}\s+from\s+"\.\/components\/([A-Za-z0-9_/.-]+)"/g;
49
+ const results = [];
50
+ let m;
51
+ while ((m = re.exec(indexContent))) {
52
+ const exported = m[1]
53
+ .split(",")
54
+ .map((s) => s.trim())
55
+ .filter(Boolean);
56
+ const rel = m[2];
57
+ for (const name of exported) {
58
+ results.push({ name, relPath: rel });
59
+ }
60
+ }
61
+ return results;
62
+ }
63
+
64
+ function findComponentFile(relPath) {
65
+ const base = path.join(SRC_DIR, "components", relPath);
66
+ const componentName = relPath.split("/").pop();
67
+ const candidates = [
68
+ path.join(base, `${componentName}.tsx`),
69
+ path.join(base, `${componentName}.ts`),
70
+ path.join(base, `index.tsx`),
71
+ path.join(base, `index.ts`),
72
+ ];
73
+ for (const f of candidates) {
74
+ if (fs.existsSync(f)) return f;
75
+ }
76
+ return null;
77
+ }
78
+
79
+ function findStorybookFile(relPath) {
80
+ const base = path.join(SRC_DIR, "components", relPath);
81
+ const componentName = relPath.split("/").pop();
82
+ const candidates = [path.join(base, `${componentName}.stories.tsx`), path.join(base, `${componentName}.stories.ts`)];
83
+ for (const f of candidates) {
84
+ if (fs.existsSync(f)) return f;
85
+ }
86
+ return null;
87
+ }
88
+
89
+ function extractStorybookCategory(storyContent) {
90
+ // Extract: title: "Form Controls/Button"
91
+ const titleMatch = storyContent.match(/title:\s*["']([^"']+)["']/);
92
+ if (titleMatch) {
93
+ const fullTitle = titleMatch[1];
94
+ const parts = fullTitle.split("/");
95
+ if (parts.length > 1) {
96
+ return parts[0]; // "Form Controls"
97
+ }
98
+ }
99
+ return null;
100
+ }
101
+
102
+ function extractRelatedComponents(storyContent) {
103
+ // Find other component imports in the story file
104
+ const importMatch = storyContent.match(/import\s*\{([^}]+)\}\s*from\s*["']\.\.\/(\w+)["']/g);
105
+ const related = new Set();
106
+ if (importMatch) {
107
+ for (const imp of importMatch) {
108
+ const m = imp.match(/import\s*\{([^}]+)\}/);
109
+ if (m) {
110
+ const imports = m[1].split(",").map((s) => s.trim());
111
+ for (const name of imports) {
112
+ if (/^[A-Z]/.test(name)) {
113
+ related.add(name);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ return Array.from(related);
120
+ }
121
+
122
+ function getJsdocAbove(content, idx) {
123
+ const upTo = content.slice(0, idx);
124
+ const start = upTo.lastIndexOf("/**");
125
+ const end = upTo.lastIndexOf("*/");
126
+ if (start !== -1 && end !== -1 && end > start) {
127
+ const block = upTo.slice(start + 3, end).trim();
128
+ return block
129
+ .split("\n")
130
+ .map((l) => l.replace(/^\s*\*\s?/, "").trim())
131
+ .join(" ")
132
+ .trim();
133
+ }
134
+ return "";
135
+ }
136
+
137
+ function extractProps(content, componentName) {
138
+ const ifaceRe = new RegExp(
139
+ `export\\s+interface\\s+${componentName}Props\\s*(?:extends\\s+[^{]+)?\\{([\\s\\S]*?)\\}`,
140
+ "m",
141
+ );
142
+ let m = ifaceRe.exec(content);
143
+ if (!m) {
144
+ const anyIfaceRe = /export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
145
+ m = anyIfaceRe.exec(content);
146
+ }
147
+ if (!m) {
148
+ const typeRe = new RegExp(`export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`, "m");
149
+ m = typeRe.exec(content);
150
+ }
151
+ const props = [];
152
+ if (m) {
153
+ const body = m[2] || m[1] || "";
154
+ // Parse props more carefully to handle nested types
155
+ let currentProp = "";
156
+ let braceDepth = 0;
157
+ const lines = body.split("\n");
158
+
159
+ for (const line of lines) {
160
+ const trimmed = line.trim();
161
+ if (!trimmed || trimmed.startsWith("//")) continue;
162
+
163
+ // Count braces to track nested objects
164
+ for (const char of trimmed) {
165
+ if (char === "{") braceDepth++;
166
+ if (char === "}") braceDepth--;
167
+ }
168
+
169
+ currentProp += (currentProp ? " " : "") + trimmed;
170
+
171
+ // If we're at depth 0 and the line ends with semicolon or we're complete, process the prop
172
+ if (braceDepth === 0 && (trimmed.endsWith(";") || trimmed.endsWith(","))) {
173
+ const pm = /^(readonly\s+)?([A-Za-z0-9_]+)\??:\s*(.+?)[;,]?\s*$/.exec(currentProp.trim());
174
+ if (pm) {
175
+ const name = pm[2];
176
+ let type = pm[3].trim();
177
+ // Clean up type - remove trailing semicolon/comma
178
+ type = type.replace(/[;,]\s*$/, "");
179
+ // Simplify very long nested types for readability
180
+ if (type.includes("{") && type.length > 100) {
181
+ // For nested object types, show simplified version
182
+ type = type.substring(0, 80) + "...}";
183
+ }
184
+ props.push({ name, type });
185
+ }
186
+ currentProp = "";
187
+ braceDepth = 0;
188
+ }
189
+ }
190
+ }
191
+ return props;
192
+ }
193
+
194
+ function generateComponentsManifest() {
195
+ console.log("\n📦 Generating components manifest...");
196
+
197
+ const indexPath = path.join(SRC_DIR, "index.ts");
198
+ const indexContent = read(indexPath);
199
+ const exports = extractExportsFromIndex(indexContent);
200
+
201
+ const components = [];
202
+ const categories = {};
203
+
204
+ for (const { name, relPath } of exports) {
205
+ if (!/^[A-Z]/.test(name)) continue;
206
+
207
+ const componentFile = findComponentFile(relPath);
208
+ if (!componentFile) continue;
209
+ const content = read(componentFile);
210
+
211
+ // Extract description from JSDoc
212
+ let idx = -1;
213
+ let description = "";
214
+ const patterns = [
215
+ `export function ${name}`,
216
+ `export const ${name}:`,
217
+ `export const ${name} =`,
218
+ `export class ${name}`,
219
+ new RegExp(`export\\s+const\\s+${name}\\s*[:=]`),
220
+ new RegExp(`export\\s+function\\s+${name}\\s*[(<]`),
221
+ ];
222
+
223
+ for (const pattern of patterns) {
224
+ if (typeof pattern === "string") {
225
+ idx = content.indexOf(pattern);
226
+ } else {
227
+ const match = content.search(pattern);
228
+ if (match !== -1) idx = match;
229
+ }
230
+ if (idx !== -1) break;
231
+ }
232
+
233
+ if (idx !== -1) {
234
+ description = getJsdocAbove(content, idx);
235
+ }
236
+
237
+ // Extract props
238
+ const props = extractProps(content, name);
239
+
240
+ // Extract Storybook metadata
241
+ const storyFile = findStorybookFile(relPath);
242
+ let category = "Uncategorized";
243
+ let relatedComponents = [];
244
+
245
+ if (storyFile) {
246
+ const storyContent = read(storyFile);
247
+ const storyCategory = extractStorybookCategory(storyContent);
248
+ if (storyCategory) category = storyCategory;
249
+ relatedComponents = extractRelatedComponents(storyContent);
250
+ }
251
+
252
+ const component = {
253
+ name,
254
+ category,
255
+ description,
256
+ importRoot: "@texturehq/edges",
257
+ importPath: `@texturehq/edges/components/${name}`,
258
+ props,
259
+ relatedComponents,
260
+ storybookPath: category + "/" + name,
261
+ };
262
+
263
+ components.push(component);
264
+
265
+ // Organize by category
266
+ if (!categories[category]) {
267
+ categories[category] = [];
268
+ }
269
+ categories[category].push(name);
270
+ }
271
+
272
+ // Sort components by name
273
+ components.sort((a, b) => a.name.localeCompare(b.name));
274
+
275
+ // Sort category arrays
276
+ for (const cat in categories) {
277
+ categories[cat].sort();
278
+ }
279
+
280
+ const manifest = {
281
+ version: process.env.npm_package_version || "unknown",
282
+ generatedAt: new Date().toISOString(),
283
+ components: components,
284
+ categories: categories,
285
+ };
286
+
287
+ ensureDir(DIST_DIR);
288
+ writeFile(path.join(DIST_DIR, "components.manifest.json"), JSON.stringify(manifest, null, 2));
289
+
290
+ console.log(` Found ${components.length} components in ${Object.keys(categories).length} categories`);
291
+ return manifest;
292
+ }
293
+
294
+ // ============================================================================
295
+ // UTILITIES & HOOKS MANIFEST GENERATION
296
+ // ============================================================================
297
+
298
+ function extractJSDoc(content, functionName) {
299
+ const patterns = [
300
+ new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`, "s"),
301
+ new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
302
+ ];
303
+
304
+ for (const pattern of patterns) {
305
+ const match = content.match(pattern);
306
+ if (match) {
307
+ const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
308
+ if (jsdocMatch) {
309
+ const cleaned = jsdocMatch[1]
310
+ .split("\n")
311
+ .map((line) => line.replace(/^\s*\*\s?/, "").trim())
312
+ .filter((line) => line && !line.startsWith("@"))
313
+ .join(" ")
314
+ .trim();
315
+ return cleaned;
316
+ }
317
+ }
318
+ }
319
+ return "";
320
+ }
321
+
322
+ function extractExportsFromFile(filePath) {
323
+ const content = read(filePath);
324
+ if (!content) return [];
325
+
326
+ const utilities = [];
327
+
328
+ // Match export function declarations
329
+ const functionPattern = /export\s+(?:async\s+)?function\s+(\w+)/g;
330
+ let match;
331
+ while ((match = functionPattern.exec(content))) {
332
+ const name = match[1];
333
+ utilities.push({
334
+ name,
335
+ type: "function",
336
+ description: extractJSDoc(content, name),
337
+ });
338
+ }
339
+
340
+ // Match export const arrow functions and constants
341
+ const constPattern = /export\s+const\s+(\w+)\s*(?::\s*[^=]+)?\s*=/g;
342
+ while ((match = constPattern.exec(content))) {
343
+ const name = match[1];
344
+ const afterMatch = content.slice(match.index + match[0].length, match.index + match[0].length + 50);
345
+ const isFunction = afterMatch.includes("=>") || afterMatch.includes("function");
346
+
347
+ utilities.push({
348
+ name,
349
+ type: isFunction ? "function" : "constant",
350
+ description: extractJSDoc(content, name),
351
+ });
352
+ }
353
+
354
+ return utilities;
355
+ }
356
+
357
+ function scanDirectory(dir, category) {
358
+ const utilities = [];
359
+
360
+ if (!fs.existsSync(dir)) return utilities;
361
+
362
+ const files = fs.readdirSync(dir);
363
+
364
+ for (const file of files) {
365
+ const filePath = path.join(dir, file);
366
+ const stat = fs.statSync(filePath);
367
+
368
+ if (stat.isDirectory() && file !== "__tests__" && file !== "tests") {
369
+ utilities.push(...scanDirectory(filePath, `${category}/${file}`));
370
+ } else if ((file.endsWith(".ts") || file.endsWith(".tsx")) && !file.endsWith(".d.ts")) {
371
+ if (file.includes(".test.") || file.includes(".spec.") || file.includes(".stories.")) {
372
+ continue;
373
+ }
374
+
375
+ const funcs = extractExportsFromFile(filePath);
376
+ for (const func of funcs) {
377
+ utilities.push({
378
+ ...func,
379
+ category,
380
+ file: path.relative(SRC_DIR, filePath),
381
+ });
382
+ }
383
+ }
384
+ }
385
+
386
+ return utilities;
387
+ }
388
+
389
+ function generateUtilitiesManifest() {
390
+ console.log("\n🔧 Generating utilities manifest...");
391
+
392
+ const manifest = {
393
+ version: process.env.npm_package_version || "unknown",
394
+ generatedAt: new Date().toISOString(),
395
+ categories: {},
396
+ };
397
+
398
+ // Scan hooks directory
399
+ const hooksDir = path.join(SRC_DIR, "hooks");
400
+ const hooks = scanDirectory(hooksDir, "hooks");
401
+ if (hooks.length > 0) {
402
+ manifest.categories.hooks = {
403
+ description:
404
+ "React hooks for common functionality like breakpoints, debouncing, local storage, and media queries",
405
+ utilities: hooks.sort((a, b) => a.name.localeCompare(b.name)),
406
+ };
407
+ }
408
+
409
+ // Scan formatting utilities
410
+ const formattingDir = path.join(SRC_DIR, "utils/formatting");
411
+ const formatting = scanDirectory(formattingDir, "formatting");
412
+ if (formatting.length > 0) {
413
+ manifest.categories.formatting = {
414
+ description:
415
+ "Comprehensive formatting utilities for text, numbers, dates, currency, energy, temperature, distance, and more",
416
+ utilities: formatting.sort((a, b) => a.name.localeCompare(b.name)),
417
+ };
418
+ }
419
+
420
+ // Chart utilities
421
+ const chartsDir = path.join(SRC_DIR, "utils/charts");
422
+ const charts = scanDirectory(chartsDir, "charts");
423
+ const chartExportUtils = extractExportsFromFile(path.join(SRC_DIR, "utils/chartExport.ts"));
424
+ const chartUtils = extractExportsFromFile(path.join(SRC_DIR, "utils/charts.ts"));
425
+ const allChartUtils = [
426
+ ...charts,
427
+ ...chartExportUtils.map((f) => ({ ...f, category: "charts", file: "utils/chartExport.ts" })),
428
+ ...chartUtils.map((f) => ({ ...f, category: "charts", file: "utils/charts.ts" })),
429
+ ];
430
+
431
+ if (allChartUtils.length > 0) {
432
+ manifest.categories.charts = {
433
+ description: "Chart utilities for data visualization, scaling, and export functionality",
434
+ utilities: allChartUtils.sort((a, b) => a.name.localeCompare(b.name)),
435
+ };
436
+ }
437
+
438
+ // Color utilities
439
+ const colorUtils = extractExportsFromFile(path.join(SRC_DIR, "utils/colors.ts"));
440
+ if (colorUtils.length > 0) {
441
+ manifest.categories.colors = {
442
+ description:
443
+ "Color management utilities for theme-aware color resolution, contrast calculation, and palette generation",
444
+ utilities: colorUtils
445
+ .map((f) => ({ ...f, category: "colors", file: "utils/colors.ts" }))
446
+ .sort((a, b) => a.name.localeCompare(b.name)),
447
+ };
448
+ }
449
+
450
+ const totalUtilities = Object.values(manifest.categories).reduce((sum, cat) => sum + cat.utilities.length, 0);
451
+
452
+ ensureDir(DIST_DIR);
453
+ writeFile(path.join(DIST_DIR, "utilities.manifest.json"), JSON.stringify(manifest, null, 2));
454
+
455
+ console.log(` Found ${totalUtilities} utilities in ${Object.keys(manifest.categories).length} categories`);
456
+ return manifest;
457
+ }
458
+
459
+ // ============================================================================
460
+ // THEME PARSING
461
+ // ============================================================================
462
+
463
+ function parseThemeColors() {
464
+ console.log("\n🎨 Parsing theme colors...");
465
+
466
+ const themePath = path.join(DIST_DIR, "theme.css");
467
+ if (!fs.existsSync(themePath)) {
468
+ console.log(" ⚠️ theme.css not found, skipping color extraction");
469
+ return {};
470
+ }
471
+
472
+ const themeContent = read(themePath);
473
+ const colorVars = {};
474
+ const colorRegex = /--color-([a-z-]+):\s*([^;]+);/g;
475
+ let match;
476
+
477
+ while ((match = colorRegex.exec(themeContent))) {
478
+ const varName = match[1];
479
+ const value = match[2].trim();
480
+ colorVars[varName] = value;
481
+ }
482
+
483
+ console.log(` Found ${Object.keys(colorVars).length} color variables`);
484
+ return colorVars;
485
+ }
486
+
487
+ // ============================================================================
488
+ // MARKDOWN DOCUMENTATION GENERATION
489
+ // ============================================================================
490
+
491
+ function generateComponentsReference(componentsManifest) {
492
+ console.log("\n📝 Generating components reference...");
493
+
494
+ const { components, categories } = componentsManifest;
495
+
496
+ let content = `# Edges Components Reference
497
+
498
+ > **Auto-generated** - Do not edit manually. Run \`yarn generate:edges-docs\` to regenerate.
499
+
500
+ This document provides a complete reference of all ${components.length} components in the @texturehq/edges design system, organized by functional category.
501
+
502
+ ## Quick Navigation
503
+
504
+ `;
505
+
506
+ // Table of contents by category
507
+ const sortedCategories = Object.keys(categories).sort();
508
+ for (const category of sortedCategories) {
509
+ const count = categories[category].length;
510
+ content += `- [${category}](#${category.toLowerCase().replace(/\s+/g, "-")}) (${count} component${count > 1 ? "s" : ""})\n`;
511
+ }
512
+
513
+ content += `\n---\n\n`;
514
+
515
+ // Generate content by category
516
+ for (const category of sortedCategories) {
517
+ content += `## ${category}\n\n`;
518
+
519
+ const categoryComponents = components.filter((c) => c.category === category);
520
+ categoryComponents.sort((a, b) => a.name.localeCompare(b.name));
521
+
522
+ for (const component of categoryComponents) {
523
+ content += `### ${component.name}\n\n`;
524
+
525
+ if (component.description) {
526
+ content += `${component.description}\n\n`;
527
+ }
528
+
529
+ content += `**Import:**\n\n`;
530
+ content += `\`\`\`typescript\n`;
531
+ content += `import { ${component.name} } from "${component.importRoot}";\n`;
532
+ content += `// or\n`;
533
+ content += `import { ${component.name} } from "${component.importPath}";\n`;
534
+ content += `\`\`\`\n\n`;
535
+
536
+ if (component.props && component.props.length > 0) {
537
+ content += `**Props:**\n\n`;
538
+ content += `| Prop | Type |\n`;
539
+ content += `|------|------|\n`;
540
+ for (const prop of component.props) {
541
+ content += `| \`${prop.name}\` | \`${prop.type}\` |\n`;
542
+ }
543
+ content += `\n`;
544
+ }
545
+
546
+ if (component.relatedComponents && component.relatedComponents.length > 0) {
547
+ content += `**Related:** ${component.relatedComponents.join(", ")}\n\n`;
548
+ }
549
+
550
+ if (component.storybookPath) {
551
+ content += `**Storybook:** \`${component.storybookPath}\`\n\n`;
552
+ }
553
+
554
+ content += `---\n\n`;
555
+ }
556
+ }
557
+
558
+ writeFile(path.join(DOCS_AI_EDGES, "components-reference.md"), content);
559
+ }
560
+
561
+ function generateHooksReference(utilitiesManifest) {
562
+ console.log("\n🪝 Generating hooks reference...");
563
+
564
+ const hooksCategory = utilitiesManifest.categories.hooks;
565
+ if (!hooksCategory) {
566
+ console.log(" No hooks found");
567
+ return;
568
+ }
569
+
570
+ const hooks = hooksCategory.utilities;
571
+
572
+ let content = `# Edges Hooks Reference
573
+
574
+ > **Auto-generated** - Do not edit manually. Run \`yarn generate:edges-docs\` to regenerate.
575
+
576
+ This document provides a complete reference of all ${hooks.length} React hooks in the @texturehq/edges design system.
577
+
578
+ ${hooksCategory.description}
579
+
580
+ ## Available Hooks
581
+
582
+ `;
583
+
584
+ for (const hook of hooks) {
585
+ content += `### ${hook.name}\n\n`;
586
+
587
+ if (hook.description) {
588
+ content += `${hook.description}\n\n`;
589
+ }
590
+
591
+ content += `**Import:**\n\n`;
592
+ content += `\`\`\`typescript\n`;
593
+ content += `import { ${hook.name} } from "@texturehq/edges";\n`;
594
+ content += `\`\`\`\n\n`;
595
+
596
+ content += `**Source:** \`src/${hook.file}\`\n\n`;
597
+ content += `---\n\n`;
598
+ }
599
+
600
+ writeFile(path.join(DOCS_AI_EDGES, "hooks-reference.md"), content);
601
+ }
602
+
603
+ function generateUtilitiesReference(utilitiesManifest) {
604
+ console.log("\n🛠️ Generating utilities reference...");
605
+
606
+ const categories = Object.keys(utilitiesManifest.categories).filter((c) => c !== "hooks");
607
+ if (categories.length === 0) {
608
+ console.log(" No utilities found");
609
+ return;
610
+ }
611
+
612
+ let content = `# Edges Utilities Reference
613
+
614
+ > **Auto-generated** - Do not edit manually. Run \`yarn generate:edges-docs\` to regenerate.
615
+
616
+ This document provides a complete reference of all utility functions in the @texturehq/edges design system.
617
+
618
+ ## Categories
619
+
620
+ `;
621
+
622
+ for (const category of categories) {
623
+ const cat = utilitiesManifest.categories[category];
624
+ content += `- [${category}](#${category}) (${cat.utilities.length} utilities)\n`;
625
+ }
626
+
627
+ content += `\n---\n\n`;
628
+
629
+ for (const category of categories) {
630
+ const cat = utilitiesManifest.categories[category];
631
+ content += `## ${category}\n\n`;
632
+ content += `${cat.description}\n\n`;
633
+
634
+ for (const util of cat.utilities) {
635
+ content += `### ${util.name}\n\n`;
636
+
637
+ if (util.description) {
638
+ content += `${util.description}\n\n`;
639
+ }
640
+
641
+ content += `**Type:** ${util.type}\n\n`;
642
+ content += `**Import:**\n\n`;
643
+ content += `\`\`\`typescript\n`;
644
+ content += `import { ${util.name} } from "@texturehq/edges";\n`;
645
+ content += `\`\`\`\n\n`;
646
+
647
+ content += `**Source:** \`src/${util.file}\`\n\n`;
648
+ content += `---\n\n`;
649
+ }
650
+ }
651
+
652
+ writeFile(path.join(DOCS_AI_EDGES, "utilities-reference.md"), content);
653
+ }
654
+
655
+ function generateThemeSystemDoc(colorVars) {
656
+ console.log("\n🎨 Generating theme system documentation...");
657
+
658
+ let content = `# Edges Theme System
659
+
660
+ > **Auto-generated** - Do not edit manually. Run \`yarn generate:edges-docs\` to regenerate.
661
+
662
+ ## Overview
663
+
664
+ The @texturehq/edges design system uses Tailwind 4 with CSS variables. All design tokens are defined as CSS variables and automatically become available as Tailwind utility classes.
665
+
666
+ ## Setup
667
+
668
+ \`\`\`css
669
+ @import "@texturehq/edges/theme.css";
670
+ \`\`\`
671
+
672
+ ## How It Works
673
+
674
+ CSS variables automatically become Tailwind classes:
675
+ - \`--color-brand-primary\` → \`bg-brand-primary\`, \`text-brand-primary\`, \`border-brand-primary\`
676
+ - \`--spacing-md\` → \`p-md\`, \`m-md\`, \`gap-md\`
677
+ - \`--text-lg\` → \`text-lg\`
678
+ - \`--radius-lg\` → \`rounded-lg\`
679
+
680
+ ## Color System
681
+
682
+ The theme includes ${Object.keys(colorVars).length} color variables organized by purpose:
683
+
684
+ ### Brand Colors
685
+ - \`brand-primary\` - Primary brand color
686
+ - \`brand-light\` - Light brand variant
687
+ - \`brand-dark\` - Dark brand variant
688
+
689
+ ### Text Colors
690
+ - \`text-body\` - Body text
691
+ - \`text-heading\` - Heading text
692
+ - \`text-muted\` - Muted/secondary text
693
+ - \`text-caption\` - Caption/helper text
694
+ - \`text-on-primary\` - Text on primary backgrounds
695
+
696
+ ### Background Colors
697
+ - \`background-body\` - Main page background
698
+ - \`background-surface\` - Card/surface background
699
+ - \`background-muted\` - Muted background
700
+ - \`background-subtle\` - Subtle background
701
+
702
+ ### Border Colors
703
+ - \`border-default\` - Default border
704
+ - \`border-focus\` - Focused border
705
+ - \`border-muted\` - Muted border
706
+ - \`border-subtle\` - Subtle border
707
+
708
+ ### Action Colors
709
+ - \`action-primary\` - Primary actions
710
+ - \`action-secondary\` - Secondary actions
711
+ - \`action-destructive\` - Destructive actions
712
+
713
+ ### Feedback Colors
714
+ - \`feedback-success\` - Success state
715
+ - \`feedback-error\` - Error state
716
+ - \`feedback-warning\` - Warning state
717
+ - \`feedback-info\` - Info state
718
+
719
+ ## Usage Guidelines
720
+
721
+ **✅ Good - Use semantic classes:**
722
+ \`\`\`tsx
723
+ <div className="bg-brand-primary text-text-on-primary p-md rounded-lg shadow-md">
724
+ <h2 className="text-text-heading text-lg font-medium">Title</h2>
725
+ <p className="text-text-body text-base">Content</p>
726
+ </div>
727
+ \`\`\`
728
+
729
+ **❌ Avoid - Arbitrary values:**
730
+ \`\`\`tsx
731
+ <div className="bg-[#444ae1] text-[#ffffff] p-[1rem] rounded-[0.5rem]">
732
+ <h2 className="text-[#111827] text-[1.125rem] font-[500]">Title</h2>
733
+ <p className="text-[#333333] text-[1rem]">Content</p>
734
+ </div>
735
+ \`\`\`
736
+
737
+ ## Spacing Scale
738
+
739
+ - \`xs\`, \`sm\`, \`md\`, \`lg\`, \`xl\`, \`2xl\`, \`3xl\`, \`4xl\`
740
+
741
+ ## Typography Scale
742
+
743
+ - \`xs\`, \`sm\`, \`base\`, \`lg\`, \`xl\`, \`2xl\`, \`3xl\`, \`4xl\`
744
+
745
+ ## Border Radius Scale
746
+
747
+ - \`xs\`, \`sm\`, \`md\`, \`lg\`, \`xl\`, \`2xl\`, \`3xl\`, \`4xl\`
748
+
749
+ ## Dark Mode
750
+
751
+ All colors automatically adapt to dark mode when the \`.theme-dark\` class is present on a parent element.
752
+
753
+ ## Available Variables
754
+
755
+ The theme includes:
756
+ - Complete color system (${Object.keys(colorVars).length} color variables)
757
+ - Spacing scale
758
+ - Typography scale
759
+ - Border radius scale
760
+ - Shadow system
761
+ - Animation definitions
762
+ - Form control specifications
763
+ `;
764
+
765
+ writeFile(path.join(DOCS_AI_EDGES, "theme-system.md"), content);
766
+ }
767
+
768
+ function generateEdgesReadme(componentsManifest, utilitiesManifest) {
769
+ console.log("\n📄 Generating edges README...");
770
+
771
+ const componentCount = componentsManifest.components.length;
772
+ const categoryCount = Object.keys(componentsManifest.categories).length;
773
+ const hookCount = utilitiesManifest.categories.hooks?.utilities.length || 0;
774
+
775
+ let utilityCount = 0;
776
+ for (const cat in utilitiesManifest.categories) {
777
+ if (cat !== "hooks") {
778
+ utilityCount += utilitiesManifest.categories[cat].utilities.length;
779
+ }
780
+ }
781
+
782
+ let content = `# Edges Design System - AI Context
783
+
784
+ > **Auto-generated** - Do not edit manually. Run \`yarn generate:edges-docs\` to regenerate.
785
+
786
+ 👋 **AI Agents: Start here!**
787
+
788
+ Complete documentation for the @texturehq/edges design system.
789
+
790
+ ## What's Inside
791
+
792
+ - **${componentCount} Components** across ${categoryCount} categories
793
+ - **${hookCount} React Hooks** for common functionality
794
+ - **${utilityCount} Utility Functions** for formatting, charts, colors, and more
795
+ - **Complete Theme System** with Tailwind 4 + CSS variables
796
+
797
+ ## Quick Links
798
+
799
+ ### Component Library
800
+ - [Components Reference](components-reference.md) - All ${componentCount} components organized by category
801
+ - [Component Categories](#component-categories) - Quick navigation by function
802
+
803
+ ### Utilities & Hooks
804
+ - [Hooks Reference](hooks-reference.md) - All ${hookCount} React hooks
805
+ - [Utilities Reference](utilities-reference.md) - All ${utilityCount} utility functions
806
+
807
+ ### Design System
808
+ - [Theme System](theme-system.md) - Colors, spacing, typography with Tailwind 4
809
+ - [Patterns](patterns.md) - Common UI patterns and best practices
810
+
811
+ ## Component Categories
812
+
813
+ `;
814
+
815
+ const sortedCategories = Object.keys(componentsManifest.categories).sort();
816
+ for (const category of sortedCategories) {
817
+ const components = componentsManifest.categories[category];
818
+ content += `### ${category}\n\n`;
819
+ content += `${components.join(", ")}\n\n`;
820
+ }
821
+
822
+ content += `## Installation
823
+
824
+ \`\`\`bash
825
+ yarn add @texturehq/edges
826
+ \`\`\`
827
+
828
+ ## Setup
829
+
830
+ \`\`\`css
831
+ @import "@texturehq/edges/theme.css";
832
+ \`\`\`
833
+
834
+ \`\`\`typescript
835
+ import { Button, TextField, DataTable } from "@texturehq/edges";
836
+ \`\`\`
837
+
838
+ ## For Dashboard Development
839
+
840
+ When building UI in \`apps/dashboard\`, reference:
841
+ - **Components**: \`docs/ai/edges/components-reference.md\`
842
+ - **Hooks**: \`docs/ai/edges/hooks-reference.md\`
843
+ - **Utilities**: \`docs/ai/edges/utilities-reference.md\`
844
+ - **Theme**: \`docs/ai/edges/theme-system.md\`
845
+ - **Patterns**: \`docs/ai/edges/patterns.md\`
846
+
847
+ ---
848
+
849
+ **Package Version:** ${componentsManifest.version}
850
+ **Generated:** ${new Date().toISOString()}
851
+ `;
852
+
853
+ writeFile(path.join(DOCS_AI_EDGES, "README.md"), content);
854
+ }
855
+
856
+ // ============================================================================
857
+ // AGENT INSTRUCTION FRAGMENT
858
+ // ============================================================================
859
+
860
+ function generateEdgesFragment(componentsManifest, utilitiesManifest) {
861
+ console.log("\n📄 Generating edges fragment...");
862
+
863
+ const componentCount = componentsManifest.components.length;
864
+ const hookCount = utilitiesManifest.categories.hooks?.utilities.length || 0;
865
+
866
+ let utilityCount = 0;
867
+ for (const cat in utilitiesManifest.categories) {
868
+ if (cat !== "hooks") {
869
+ utilityCount += utilitiesManifest.categories[cat].utilities.length;
870
+ }
871
+ }
872
+
873
+ const content = `<!-- priority: 20 -->
874
+ ## Edges Design System Context
875
+
876
+ The @texturehq/edges design system provides ${componentCount} React components, ${hookCount} hooks, and ${utilityCount} utility functions.
877
+
878
+ **Complete component documentation is in**: \`docs/ai/edges/\`
879
+
880
+ ### Quick Start
881
+
882
+ 1. Read \`docs/ai/edges/README.md\` for an overview and component categories
883
+ 2. Reference \`docs/ai/edges/components-reference.md\` for detailed component API
884
+ 3. Review \`docs/ai/edges/patterns.md\` for common UI patterns and best practices
885
+ 4. Check \`docs/ai/edges/theme-system.md\` for Tailwind 4 color tokens and styling
886
+
887
+ ### Additional Resources
888
+
889
+ - Hooks: \`docs/ai/edges/hooks-reference.md\`
890
+ - Utilities: \`docs/ai/edges/utilities-reference.md\`
891
+
892
+ ### For Dashboard Development
893
+
894
+ **UI (Edges)**:
895
+ - All components, hooks, and utilities from \`@texturehq/edges\`
896
+ - Use semantic Tailwind classes from theme (see theme-system.md)
897
+ - Follow patterns from \`docs/ai/edges/patterns.md\`
898
+
899
+ ### Common Patterns
900
+
901
+ **Import**: \`import { Button, TextField } from "@texturehq/edges"\`
902
+ **Styling**: Use semantic classes like \`bg-brand-primary\`, \`text-text-body\`
903
+ **Forms**: Wrap in \`<Form>\`, use validation with \`errorMessage\`
904
+ **Loading**: Show \`<Skeleton>\` during data fetching
905
+ **Responsive**: Use \`useBreakpoint()\` hook or responsive grid cols
906
+ `;
907
+
908
+ const fragmentsDir = path.join(MONO_ROOT, ".agent-fragments");
909
+ ensureDir(fragmentsDir);
910
+ writeFile(path.join(fragmentsDir, "edges.md"), content);
911
+ }
912
+
913
+ // ============================================================================
914
+ // MAIN
915
+ // ============================================================================
916
+
917
+ async function main() {
918
+ console.log("🚀 Generating Edges AI Context...\n");
919
+
920
+ // Ensure output directories exist
921
+ ensureDir(DOCS_AI_EDGES);
922
+ ensureDir(DIST_DIR);
923
+
924
+ // Generate manifests
925
+ const componentsManifest = generateComponentsManifest();
926
+ const utilitiesManifest = generateUtilitiesManifest();
927
+
928
+ // Parse theme
929
+ const colorVars = parseThemeColors();
930
+
931
+ // Generate markdown documentation
932
+ generateComponentsReference(componentsManifest);
933
+ generateHooksReference(utilitiesManifest);
934
+ generateUtilitiesReference(utilitiesManifest);
935
+ generateThemeSystemDoc(colorVars);
936
+ generateEdgesReadme(componentsManifest, utilitiesManifest);
937
+
938
+ // Generate agent instruction fragment
939
+ generateEdgesFragment(componentsManifest, utilitiesManifest);
940
+
941
+ // Merge all agent instruction fragments
942
+ console.log("\n🔀 Merging agent instruction fragments...");
943
+ try {
944
+ execSync("ts-node scripts/merge-agent-instructions.ts", {
945
+ cwd: MONO_ROOT,
946
+ stdio: "inherit",
947
+ });
948
+ } catch (err) {
949
+ console.log("⚠️ Note: Could not merge agent instructions (merge script may not be available yet)");
950
+ }
951
+
952
+ console.log("\n✅ Edges AI context generation complete!");
953
+ console.log("\nGenerated files:");
954
+ console.log(" - dist/components.manifest.json");
955
+ console.log(" - dist/utilities.manifest.json");
956
+ console.log(" - docs/ai/edges/README.md");
957
+ console.log(" - docs/ai/edges/components-reference.md");
958
+ console.log(" - docs/ai/edges/hooks-reference.md");
959
+ console.log(" - docs/ai/edges/utilities-reference.md");
960
+ console.log(" - docs/ai/edges/theme-system.md");
961
+ console.log(" - .agent-fragments/edges.md");
962
+ console.log(" - .claude/instructions.md (merged)");
963
+ console.log(" - .codex/instructions.md (merged)");
964
+ console.log(" - .github/copilot-instructions.md (merged)");
965
+ }
966
+
967
+ main().catch((err) => {
968
+ console.error("❌ Failed to generate AI context:", err);
969
+ process.exit(1);
970
+ });