@tpitre/story-ui 4.7.0 → 4.8.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 (31) hide show
  1. package/dist/cli/index.js +50 -9
  2. package/dist/mcp-server/index.js +2 -2
  3. package/dist/mcp-server/mcp-stdio-server.js +15 -2
  4. package/dist/mcp-server/routes/components.d.ts.map +1 -1
  5. package/dist/mcp-server/routes/components.js +30 -12
  6. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -1
  7. package/dist/mcp-server/routes/generateStory.js +7 -1
  8. package/dist/story-generator/componentDiscovery.d.ts +14 -0
  9. package/dist/story-generator/componentDiscovery.d.ts.map +1 -1
  10. package/dist/story-generator/enhancedComponentDiscovery.d.ts +34 -0
  11. package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -1
  12. package/dist/story-generator/enhancedComponentDiscovery.js +461 -1
  13. package/dist/story-generator/framework-adapters/angular-adapter.d.ts +0 -4
  14. package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -1
  15. package/dist/story-generator/framework-adapters/angular-adapter.js +1 -9
  16. package/dist/story-generator/framework-adapters/base-adapter.d.ts +8 -0
  17. package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -1
  18. package/dist/story-generator/framework-adapters/base-adapter.js +89 -2
  19. package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -1
  20. package/dist/story-generator/framework-adapters/react-adapter.js +68 -10
  21. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +0 -4
  22. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -1
  23. package/dist/story-generator/framework-adapters/web-components-adapter.js +1 -9
  24. package/dist/story-generator/postProcessStory.d.ts +23 -0
  25. package/dist/story-generator/postProcessStory.d.ts.map +1 -1
  26. package/dist/story-generator/postProcessStory.js +134 -0
  27. package/dist/story-generator/promptGenerator.d.ts.map +1 -1
  28. package/dist/story-generator/promptGenerator.js +165 -11
  29. package/dist/story-ui.config.d.ts +16 -0
  30. package/dist/story-ui.config.d.ts.map +1 -1
  31. package/package.json +1 -1
@@ -112,17 +112,54 @@ export const MyStory: Story = {
112
112
  IMPORTANT: Without ChakraProvider, you will see "useContext returned undefined" errors.
113
113
  `;
114
114
  }
115
+ // Build import instructions based on importStyle
116
+ const importPath = config.importPath || 'your-library';
117
+ const useIndividualImports = config.importStyle === 'individual';
118
+ let importInstructions;
119
+ let additionalImportRules;
120
+ if (useIndividualImports) {
121
+ importInstructions = `MANDATORY IMPORTS - First lines of every story file:
122
+ 1. import React from 'react';
123
+ 2. import type { Meta, StoryObj } from '@storybook/react';
124
+ 3. import { ComponentName } from '${importPath}/component-name'; // INDIVIDUAL FILE IMPORTS REQUIRED
125
+
126
+ 🚫 INDIVIDUAL FILE IMPORTS REQUIRED 🚫
127
+ This library does NOT have a barrel export. Each component MUST be imported from its own file:
128
+ - import { Button } from '${importPath}/button';
129
+ - import { Card, CardHeader, CardContent } from '${importPath}/card';
130
+ - import { Dialog, DialogTrigger, DialogContent } from '${importPath}/dialog';
131
+
132
+ WRONG - DO NOT USE (will cause "Failed to fetch dynamically imported module" error):
133
+ - import { Button, Card, Dialog } from '${importPath}'; // ❌ NO BARREL EXPORT EXISTS
134
+
135
+ File naming convention:
136
+ - Components are PascalCase: Button, AlertDialog, NavigationMenu
137
+ - Files are kebab-case: button, alert-dialog, navigation-menu
138
+ - Sub-components share files: CardHeader → card, DialogTrigger → dialog`;
139
+ additionalImportRules = `- 🚫 INDIVIDUAL IMPORTS REQUIRED: Each component from its own file
140
+ - Sub-components share the same file (CardHeader, CardContent → '${importPath}/card')
141
+ - File names use kebab-case (AlertDialog → alert-dialog)`;
142
+ }
143
+ else {
144
+ importInstructions = `MANDATORY IMPORTS - First lines of every story file:
145
+ 1. import React from 'react';
146
+ 2. import type { Meta, StoryObj } from '@storybook/react';
147
+ 3. import { ComponentName } from '${importPath}';`;
148
+ additionalImportRules = '';
149
+ }
150
+ // Build the example import based on importStyle
151
+ const exampleImport = useIndividualImports
152
+ ? `import { Button } from '${importPath}/button';`
153
+ : `import { Button } from '${importPath}';`;
115
154
  return `You are an expert React developer creating Storybook stories using CSF 3.0 format.
116
155
  Use ONLY the React components from the ${componentSystemName} listed below.
117
156
 
118
- MANDATORY IMPORTS - First lines of every story file:
119
- 1. import React from 'react';
120
- 2. import type { Meta, StoryObj } from '@storybook/react';
121
- 3. import { ComponentName } from '${config.importPath || 'your-library'}';
157
+ ${importInstructions}
122
158
 
123
159
  ${typescript ? 'Use TypeScript with proper type annotations.' : 'Use JavaScript.'}
124
160
  ${chakraInstructions}
125
161
  COMPONENT IMPORT RULES:
162
+ ${additionalImportRules}
126
163
  - ONLY import components listed in the "Available Components" section
127
164
  - Use the EXACT import path shown after each component name
128
165
  - Components not in the list DO NOT EXIST
@@ -141,7 +178,7 @@ Example structure:
141
178
  \`\`\`tsx
142
179
  import React from 'react';
143
180
  import type { Meta, StoryObj } from '@storybook/react';
144
- import { Button } from '${config.importPath || 'your-library'}';
181
+ ${exampleImport}
145
182
 
146
183
  const meta: Meta<typeof Button> = {
147
184
  title: 'Components/Button',
@@ -179,6 +216,19 @@ ${this.getCommonRules()}`;
179
216
  }
180
217
  generateExamples(config) {
181
218
  const lib = config.importPath || 'your-library';
219
+ const useIndividualImports = config.importStyle === 'individual';
220
+ // Generate import statements based on importStyle
221
+ let singleComponentImport;
222
+ let multiComponentImport;
223
+ if (useIndividualImports) {
224
+ singleComponentImport = `import { Button } from '${lib}/button';`;
225
+ multiComponentImport = `import { Card } from '${lib}/card';
226
+ import { Button } from '${lib}/button';`;
227
+ }
228
+ else {
229
+ singleComponentImport = `import { Button } from '${lib}';`;
230
+ multiComponentImport = `import { Card, Button, Text } from '${lib}';`;
231
+ }
182
232
  return `
183
233
  ## Example Stories
184
234
 
@@ -186,7 +236,7 @@ ${this.getCommonRules()}`;
186
236
  \`\`\`tsx
187
237
  import React from 'react';
188
238
  import type { Meta, StoryObj } from '@storybook/react';
189
- import { Button } from '${lib}';
239
+ ${singleComponentImport}
190
240
 
191
241
  const meta: Meta<typeof Button> = {
192
242
  title: 'Components/Button',
@@ -217,7 +267,7 @@ export const Primary: Story = {
217
267
  \`\`\`tsx
218
268
  import React from 'react';
219
269
  import type { Meta, StoryObj } from '@storybook/react';
220
- import { Card, Button, Text } from '${lib}';
270
+ ${multiComponentImport}
221
271
 
222
272
  const meta: Meta = {
223
273
  title: 'Examples/Card Layout',
@@ -232,8 +282,8 @@ export const ProductCard: Story = {
232
282
  render: () => (
233
283
  <Card style={{ width: 300 }}>
234
284
  <img src="https://picsum.photos/300/200" alt="Product" />
235
- <Text variant="heading">Product Name</Text>
236
- <Text>$99.00</Text>
285
+ <div>Product Name</div>
286
+ <div>$99.00</div>
237
287
  <Button variant="primary">Add to Cart</Button>
238
288
  </Card>
239
289
  ),
@@ -243,6 +293,7 @@ export const ProductCard: Story = {
243
293
  }
244
294
  generateSampleStory(config, components) {
245
295
  const lib = config.importPath || 'your-library';
296
+ const useIndividualImports = config.importStyle === 'individual';
246
297
  const firstComponent = components[0];
247
298
  if (!firstComponent) {
248
299
  return `
@@ -262,10 +313,17 @@ export const Default: Story = {
262
313
  };
263
314
  `;
264
315
  }
316
+ // Convert PascalCase to kebab-case for individual imports
317
+ const kebabName = firstComponent.name
318
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
319
+ .toLowerCase();
320
+ const importStatement = useIndividualImports
321
+ ? `import { ${firstComponent.name} } from '${lib}/${kebabName}';`
322
+ : `import { ${firstComponent.name} } from '${lib}';`;
265
323
  return `
266
324
  import React from 'react';
267
325
  import type { Meta, StoryObj } from '@storybook/react';
268
- import { ${firstComponent.name} } from '${lib}';
326
+ ${importStatement}
269
327
 
270
328
  const meta: Meta<typeof ${firstComponent.name}> = {
271
329
  title: 'Components/${firstComponent.name}',
@@ -30,10 +30,6 @@ export declare class WebComponentsAdapter extends BaseFrameworkAdapter {
30
30
  generateExamples(config: StoryUIConfig): string;
31
31
  generateSampleStory(config: StoryUIConfig, components: DiscoveredComponent[]): string;
32
32
  getStoryTemplate(options?: StoryGenerationOptions): string;
33
- /**
34
- * Convert PascalCase to kebab-case
35
- */
36
- private toKebabCase;
37
33
  /**
38
34
  * Post-process Web Components stories
39
35
  */
@@ -1 +1 @@
1
- {"version":3,"file":"web-components-adapter.d.ts","sourceRoot":"","sources":["../../../story-generator/framework-adapters/web-components-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,aAAa,EACb,cAAc,EACd,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qBAAa,oBAAqB,SAAQ,oBAAoB;IAC5D,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAoB;IAChD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,wBAAwB,EAAE,cAAc,EAAE,CAGjD;IACF,QAAQ,CAAC,gBAAgB,iBAAiB;IAE1C;;OAEG;IACH,wBAAwB,IAAI,MAAM,EAAE;IAIpC;;;OAGG;IACH,6BAA6B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAoE1E;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB,oBAAoB,CAClB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM;IA4FT,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM;IA0G/C,mBAAmB,CACjB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,MAAM;IA8CT,gBAAgB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM;IAsB1D;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAuCzC;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;IAiCjC;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAwBpE;;OAEG;IACH,eAAe,CACb,UAAU,EAAE,mBAAmB,EAAE,EACjC,MAAM,EAAE,aAAa,GACpB,MAAM;CAYV;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,oBAAoB,CAEjE"}
1
+ {"version":3,"file":"web-components-adapter.d.ts","sourceRoot":"","sources":["../../../story-generator/framework-adapters/web-components-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,aAAa,EACb,cAAc,EACd,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qBAAa,oBAAqB,SAAQ,oBAAoB;IAC5D,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAoB;IAChD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,wBAAwB,EAAE,cAAc,EAAE,CAGjD;IACF,QAAQ,CAAC,gBAAgB,iBAAiB;IAE1C;;OAEG;IACH,wBAAwB,IAAI,MAAM,EAAE;IAIpC;;;OAGG;IACH,6BAA6B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAoE1E;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB,oBAAoB,CAClB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM;IA4FT,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM;IA0G/C,mBAAmB,CACjB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,MAAM;IA8CT,gBAAgB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM;IAwB1D;;OAEG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAuCzC;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;IAiCjC;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAwBpE;;OAEG;IACH,eAAe,CACb,UAAU,EAAE,mBAAmB,EAAE,EACjC,MAAM,EAAE,aAAa,GACpB,MAAM;CAYV;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,oBAAoB,CAEjE"}
@@ -352,15 +352,7 @@ export const Default: Story = {
352
352
  };
353
353
  `;
354
354
  }
355
- /**
356
- * Convert PascalCase to kebab-case
357
- */
358
- toKebabCase(str) {
359
- return str
360
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
361
- .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
362
- .toLowerCase();
363
- }
355
+ // toKebabCase inherited from BaseFrameworkAdapter
364
356
  /**
365
357
  * Post-process Web Components stories
366
358
  */
@@ -1,3 +1,26 @@
1
+ /**
2
+ * Interface for discovered component data
3
+ * This matches the DiscoveredComponent interface from componentDiscovery.ts
4
+ */
5
+ export interface ComponentInfo {
6
+ name: string;
7
+ filePath: string;
8
+ }
9
+ /**
10
+ * Fix barrel imports to individual file imports
11
+ * DESIGN-SYSTEM AGNOSTIC: Uses actual component discovery data
12
+ *
13
+ * Converts: import { Button, Card, CardHeader } from '@/components/ui';
14
+ * To: import { Button } from '@/components/ui/button';
15
+ * import { Card, CardHeader } from '@/components/ui/card';
16
+ *
17
+ * @param code - The generated story code
18
+ * @param importPath - The import path alias (e.g., '@/components/ui')
19
+ * @param importStyle - Import style ('individual' or 'barrel')
20
+ * @param componentsPath - Optional: Actual filesystem path (e.g., './src/components/ui')
21
+ * @param discoveredComponents - Optional: Array of discovered components with file paths
22
+ */
23
+ export declare function fixBarrelImports(code: string, importPath: string, importStyle?: string, componentsPath?: string, discoveredComponents?: ComponentInfo[]): string;
1
24
  /**
2
25
  * Post-process generated stories to fix common issues
3
26
  * This module is completely design-system agnostic
@@ -1 +1 @@
1
- {"version":3,"file":"postProcessStory.d.ts","sourceRoot":"","sources":["../../story-generator/postProcessStory.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E"}
1
+ {"version":3,"file":"postProcessStory.d.ts","sourceRoot":"","sources":["../../story-generator/postProcessStory.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAyDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EACvB,oBAAoB,CAAC,EAAE,aAAa,EAAE,GACrC,MAAM,CA2FR;AASD;;;GAGG;AAEH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E"}
@@ -1,4 +1,138 @@
1
1
  import { logger } from './logger.js';
2
+ /**
3
+ * Convert PascalCase to kebab-case
4
+ * Used as fallback when component is not in discovery
5
+ */
6
+ function toKebabCase(str) {
7
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
8
+ }
9
+ /**
10
+ * Normalize a file path for comparison
11
+ * Removes leading ./ and trailing extensions
12
+ */
13
+ function normalizePath(filePath) {
14
+ return filePath
15
+ .replace(/^\.\//, '') // Remove leading ./
16
+ .replace(/\.(tsx?|jsx?|vue|svelte)$/, ''); // Remove extension
17
+ }
18
+ /**
19
+ * Build a map of component names to their import paths
20
+ * Uses actual discovery data rather than hardcoded patterns
21
+ */
22
+ function buildComponentToImportMap(discoveredComponents, componentsPath, importPath) {
23
+ const componentMap = new Map();
24
+ // Normalize the componentsPath for comparison
25
+ const normalizedComponentsPath = normalizePath(componentsPath);
26
+ for (const component of discoveredComponents) {
27
+ const normalizedFilePath = normalizePath(component.filePath);
28
+ // Check if this component is from our configured components path
29
+ if (normalizedFilePath.startsWith(normalizedComponentsPath)) {
30
+ // Extract the relative path after componentsPath
31
+ const relativePath = normalizedFilePath.slice(normalizedComponentsPath.length);
32
+ // Remove leading slash if present
33
+ const cleanRelativePath = relativePath.replace(/^\//, '');
34
+ // Build the import path
35
+ const componentImportPath = cleanRelativePath
36
+ ? `${importPath}/${cleanRelativePath}`
37
+ : importPath;
38
+ componentMap.set(component.name, componentImportPath);
39
+ logger.log(`[DISCOVERY] ${component.name} -> ${componentImportPath}`);
40
+ }
41
+ }
42
+ return componentMap;
43
+ }
44
+ /**
45
+ * Fix barrel imports to individual file imports
46
+ * DESIGN-SYSTEM AGNOSTIC: Uses actual component discovery data
47
+ *
48
+ * Converts: import { Button, Card, CardHeader } from '@/components/ui';
49
+ * To: import { Button } from '@/components/ui/button';
50
+ * import { Card, CardHeader } from '@/components/ui/card';
51
+ *
52
+ * @param code - The generated story code
53
+ * @param importPath - The import path alias (e.g., '@/components/ui')
54
+ * @param importStyle - Import style ('individual' or 'barrel')
55
+ * @param componentsPath - Optional: Actual filesystem path (e.g., './src/components/ui')
56
+ * @param discoveredComponents - Optional: Array of discovered components with file paths
57
+ */
58
+ export function fixBarrelImports(code, importPath, importStyle, componentsPath, discoveredComponents) {
59
+ // Early return if not using individual imports
60
+ if (importStyle !== 'individual') {
61
+ return code;
62
+ }
63
+ logger.log('🔧 Fixing barrel imports to individual file imports (design-system agnostic)');
64
+ logger.log(`[DEBUG] importPath: ${importPath}`);
65
+ logger.log(`[DEBUG] componentsPath: ${componentsPath || 'not provided'}`);
66
+ logger.log(`[DEBUG] discoveredComponents count: ${discoveredComponents?.length || 0}`);
67
+ // Build component to import path mapping from discovery data
68
+ let componentToImport = null;
69
+ if (componentsPath && discoveredComponents && discoveredComponents.length > 0) {
70
+ componentToImport = buildComponentToImportMap(discoveredComponents, componentsPath, importPath);
71
+ logger.log(`[DEBUG] Built component map with ${componentToImport.size} entries`);
72
+ }
73
+ // Match imports from the barrel path (without a subpath)
74
+ // e.g., import { Button, Card } from '@/components/ui';
75
+ const barrelImportRegex = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*['"]${escapeRegExp(importPath)}['"];?`, 'g');
76
+ logger.log(`[DEBUG] regex pattern: ${barrelImportRegex.source}`);
77
+ let processedCode = code;
78
+ let match;
79
+ let foundMatches = false;
80
+ while ((match = barrelImportRegex.exec(code)) !== null) {
81
+ foundMatches = true;
82
+ const fullMatch = match[0];
83
+ const componentList = match[1];
84
+ logger.log(`[DEBUG] Found match: "${fullMatch}"`);
85
+ logger.log(`[DEBUG] Component list: "${componentList}"`);
86
+ // Parse component names
87
+ const components = componentList
88
+ .split(',')
89
+ .map(c => c.trim())
90
+ .filter(c => c.length > 0);
91
+ logger.log(`[DEBUG] Parsed components: ${components.join(', ')}`);
92
+ // Group components by their import path
93
+ const groupedByImportPath = new Map();
94
+ for (const component of components) {
95
+ let componentImportPath;
96
+ if (componentToImport && componentToImport.has(component)) {
97
+ // Use discovery-based mapping
98
+ componentImportPath = componentToImport.get(component);
99
+ logger.log(`[DISCOVERY] ${component} -> ${componentImportPath}`);
100
+ }
101
+ else {
102
+ // Fallback: Use simple PascalCase to kebab-case conversion
103
+ // This handles components not found in discovery
104
+ const fileName = toKebabCase(component);
105
+ componentImportPath = `${importPath}/${fileName}`;
106
+ logger.log(`[FALLBACK] ${component} -> ${componentImportPath} (not in discovery)`);
107
+ }
108
+ if (!groupedByImportPath.has(componentImportPath)) {
109
+ groupedByImportPath.set(componentImportPath, []);
110
+ }
111
+ groupedByImportPath.get(componentImportPath).push(component);
112
+ }
113
+ // Build individual imports
114
+ const individualImports = Array.from(groupedByImportPath.entries())
115
+ .map(([path, comps]) => {
116
+ return `import { ${comps.join(', ')} } from '${path}';`;
117
+ })
118
+ .join('\n');
119
+ logger.log(`[DEBUG] Replacement: "${individualImports}"`);
120
+ // Replace the barrel import with individual imports
121
+ processedCode = processedCode.replace(fullMatch, individualImports);
122
+ logger.log(`✅ Converted barrel import to ${groupedByImportPath.size} individual import(s)`);
123
+ }
124
+ if (!foundMatches) {
125
+ logger.log('[DEBUG] No barrel imports found to fix');
126
+ }
127
+ logger.log(`[DEBUG] First 500 chars of processed code: ${processedCode.substring(0, 500)}`);
128
+ return processedCode;
129
+ }
130
+ /**
131
+ * Escape special regex characters
132
+ */
133
+ function escapeRegExp(string) {
134
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
135
+ }
2
136
  /**
3
137
  * Post-process generated stories to fix common issues
4
138
  * This module is completely design-system agnostic
@@ -1 +1 @@
1
- {"version":3,"file":"promptGenerator.d.ts","sourceRoot":"","sources":["../../story-generator/promptGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAI9D,OAAO,EAEL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAyOvC;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,eAAe,EAAE,oBAAoB,CAAC;IACvF,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,eAAe,CAcxG;AAqTD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CA6D1E;AA0MD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,OAAO,CAAC,MAAM,CAAC,CA8JjB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAiHjB;AA4ED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAIzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,gBAAgB,CAG9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,EAAE,CAGxD"}
1
+ {"version":3,"file":"promptGenerator.d.ts","sourceRoot":"","sources":["../../story-generator/promptGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAY,MAAM,yBAAyB,CAAC;AAIxE,OAAO,EAEL,eAAe,EACf,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AAyOvC;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,eAAe,EAAE,oBAAoB,CAAC;IACvF,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,GAAG,eAAe,CAkBxG;AA6bD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CA6D1E;AAyND;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,GAChC,OAAO,CAAC,MAAM,CAAC,CAyKjB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA4HjB;AA4ED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAIzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,gBAAgB,CAG9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,EAAE,CAGxD"}
@@ -169,6 +169,9 @@ export function generatePrompt(config, components) {
169
169
  const examples = generateExamples(config);
170
170
  const systemPrompt = generateSystemPrompt(config);
171
171
  const sampleStory = config.sampleStory || generateDefaultSampleStory(config, components);
172
+ // DEBUG: Log component reference to see import paths
173
+ console.log('[DEBUG] Import style:', config.importStyle);
174
+ console.log('[DEBUG] Sample component reference (first 500 chars):', componentReference.substring(0, 500));
172
175
  return {
173
176
  systemPrompt,
174
177
  componentReference,
@@ -306,6 +309,46 @@ CRITICAL IMPORT RULES:
306
309
  import type { Meta, StoryObj } from '${storybookFramework}';
307
310
  import { ComponentName } from '[your-import-path]';`;
308
311
  }
312
+ // Determine import style - individual file imports vs barrel imports
313
+ const useIndividualImports = config.importStyle === 'individual';
314
+ // Generate appropriate import instruction based on importStyle
315
+ let importInstructionText;
316
+ let importExampleText;
317
+ if (useIndividualImports) {
318
+ // Individual file imports - each component from its own file
319
+ importInstructionText = `║ IMPORT STYLE: INDIVIDUAL FILE IMPORTS ║
320
+ ╠════════════════════════════════════════════════════════════════════╣
321
+ ║ Each component MUST be imported from its own file: ║
322
+ ║ import { Button } from '${importPath}/button';${' '.repeat(Math.max(0, 20 - importPath.length))}║
323
+ ║ import { Card, CardHeader } from '${importPath}/card';${' '.repeat(Math.max(0, 11 - importPath.length))}║
324
+ ║ import { Input } from '${importPath}/input';${' '.repeat(Math.max(0, 21 - importPath.length))}║
325
+ ╠════════════════════════════════════════════════════════════════════╣
326
+ ║ 🚫 DO NOT use barrel imports like: ║
327
+ ║ import { Button, Card } from '${importPath}';${' '.repeat(Math.max(0, 18 - importPath.length))}║`;
328
+ importExampleText = `
329
+ 🔴 CRITICAL: INDIVIDUAL FILE IMPORTS REQUIRED 🔴
330
+ This library does NOT have a barrel export (no index.ts). Each component MUST be imported from its own file!
331
+
332
+ CORRECT import pattern:
333
+ import { Button } from '${importPath}/button';
334
+ import { Card, CardHeader, CardContent } from '${importPath}/card';
335
+ import { Dialog, DialogContent, DialogTrigger } from '${importPath}/dialog';
336
+ import { Input } from '${importPath}/input';
337
+
338
+ WRONG - DO NOT USE (will cause "Failed to fetch dynamically imported module" error):
339
+ import { Button, Card, Input } from '${importPath}';
340
+
341
+ File naming convention:
342
+ - Component names are PascalCase: Button, AlertDialog, NavigationMenu
343
+ - File names are kebab-case: button, alert-dialog, navigation-menu
344
+ - Sub-components share the same file: CardHeader, CardContent → card`;
345
+ }
346
+ else {
347
+ // Barrel imports - all components from single entry point (default)
348
+ importInstructionText = `║ ALL component imports MUST use: ║
349
+ ║ import { ComponentName } from '${importPath}';${' '.repeat(Math.max(0, 32 - importPath.length))}║`;
350
+ importExampleText = '';
351
+ }
309
352
  return `
310
353
  ╔════════════════════════════════════════════════════════════════════╗
311
354
  ║ 🚨 MANDATORY LIBRARY CONSTRAINT 🚨 ║
@@ -314,8 +357,7 @@ import { ComponentName } from '[your-import-path]';`;
314
357
  ║ IMPORT PATH: ${importPath.padEnd(46)}║
315
358
  ║ FRAMEWORK: ${framework.padEnd(46)}║
316
359
  ╠════════════════════════════════════════════════════════════════════╣
317
- ║ ALL component imports MUST use: ║
318
- ║ import { ComponentName } from '${importPath}';${' '.repeat(Math.max(0, 32 - importPath.length))}║
360
+ ${importInstructionText}
319
361
  ╠════════════════════════════════════════════════════════════════════╣
320
362
  ║ 🚫 FORBIDDEN LIBRARIES - DO NOT USE: ║
321
363
  ║ - tamagui, @tamagui/core (NEVER USE) ║
@@ -323,7 +365,7 @@ import { ComponentName } from '[your-import-path]';`;
323
365
  ║ - @mui/material (unless configured) ║
324
366
  ║ - antd (unless configured) ║
325
367
  ║ - Any library NOT matching: ${importPath.padEnd(36)}║
326
- ╚════════════════════════════════════════════════════════════════════╝
368
+ ╚════════════════════════════════════════════════════════════════════╝${importExampleText}
327
369
  ${frameworkImportInstructions}
328
370
 
329
371
  🔴 CRITICAL RULE: NEVER use children in args for ANY component or layout. Always use render functions. 🔴
@@ -434,16 +476,104 @@ function generateComponentReference(components, config) {
434
476
  }
435
477
  return reference;
436
478
  }
479
+ /**
480
+ * Convert PascalCase to kebab-case
481
+ */
482
+ function toKebabCase(str) {
483
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
484
+ }
485
+ /**
486
+ * Get the base component name (for sub-components like CardHeader, returns 'Card')
487
+ */
488
+ function getBaseComponentName(componentName) {
489
+ // Common sub-component patterns in shadcn/ui and other design systems
490
+ const subComponentPatterns = [
491
+ /^(Card)(Header|Footer|Title|Action|Description|Content)$/,
492
+ /^(Dialog)(Close|Content|Description|Footer|Header|Overlay|Portal|Title|Trigger)$/,
493
+ /^(AlertDialog)(Portal|Overlay|Trigger|Content|Header|Footer|Title|Description|Action|Cancel)$/,
494
+ /^(DropdownMenu)(Portal|Trigger|Content|Group|Label|Item|CheckboxItem|RadioGroup|RadioItem|Separator|Shortcut|Sub|SubTrigger|SubContent)$/,
495
+ /^(ContextMenu)(Trigger|Content|Item|CheckboxItem|RadioItem|Label|Separator|Shortcut|Group|Portal|Sub|SubContent|SubTrigger|RadioGroup)$/,
496
+ /^(NavigationMenu)(List|Item|Content|Trigger|Link|Indicator|Viewport)$/,
497
+ /^(Select)(Content|Group|Item|Label|ScrollDownButton|ScrollUpButton|Separator|Trigger|Value)$/,
498
+ /^(Menubar)(Portal|Menu|Trigger|Content|Group|Separator|Label|Item|Shortcut|CheckboxItem|RadioGroup|RadioItem|Sub|SubTrigger|SubContent)$/,
499
+ /^(Accordion)(Item|Trigger|Content)$/,
500
+ /^(Tabs)(List|Trigger|Content)$/,
501
+ /^(Sheet)(Trigger|Close|Content|Header|Footer|Title|Description)$/,
502
+ /^(Avatar)(Image|Fallback)$/,
503
+ /^(Breadcrumb)(List|Item|Link|Page|Separator|Ellipsis)$/,
504
+ /^(Command)(Dialog|Input|List|Empty|Group|Item|Shortcut|Separator)$/,
505
+ /^(HoverCard)(Trigger|Content)$/,
506
+ /^(Popover)(Trigger|Content|Anchor)$/,
507
+ /^(Collapsible)(Trigger|Content)$/,
508
+ /^(Drawer)(Portal|Overlay|Trigger|Close|Content|Header|Footer|Title|Description)$/,
509
+ /^(RadioGroup)(Item)$/,
510
+ /^(ToggleGroup)(Item)$/,
511
+ /^(Tooltip)(Trigger|Content|Provider)$/,
512
+ /^(Table)(Header|Body|Footer|Head|Row|Cell|Caption)$/,
513
+ /^(InputOTP)(Group|Slot|Separator)$/,
514
+ /^(Resizable)(PanelGroup|Panel|Handle)$/,
515
+ /^(ScrollArea|ScrollBar)$/,
516
+ /^(Pagination)(Content|Link|Item|Previous|Next|Ellipsis)$/,
517
+ /^(Alert)(Title|Description)$/,
518
+ ];
519
+ for (const pattern of subComponentPatterns) {
520
+ const match = componentName.match(pattern);
521
+ if (match) {
522
+ return match[1];
523
+ }
524
+ }
525
+ return componentName;
526
+ }
437
527
  /**
438
528
  * Formats a single component reference
439
529
  */
440
530
  function formatComponentReference(component, config) {
441
531
  let reference = `- ${component.name}`;
442
- // Add import path information if available
532
+ // Add import path information
533
+ let importPath;
443
534
  if (component.__componentPath) {
444
- reference += ` (import from '${component.__componentPath}')`;
535
+ importPath = component.__componentPath;
445
536
  }
446
- if (component.props && component.props.length > 0) {
537
+ else if (config.importStyle === 'individual') {
538
+ // Generate individual import path based on component name
539
+ const basePath = config.importPath || 'unknown';
540
+ const baseComponentName = getBaseComponentName(component.name);
541
+ const kebabName = toKebabCase(baseComponentName);
542
+ importPath = `${basePath}/${kebabName}`;
543
+ }
544
+ else {
545
+ importPath = config.importPath || '';
546
+ }
547
+ if (importPath) {
548
+ reference += ` (import from '${importPath}')`;
549
+ }
550
+ // Use rich prop types if available, otherwise fall back to simple prop names
551
+ if (component.propTypes && component.propTypes.length > 0) {
552
+ reference += '\n Props:';
553
+ for (const prop of component.propTypes) {
554
+ let propLine = `\n - ${prop.name}`;
555
+ // Add type information
556
+ if (prop.type !== 'unknown') {
557
+ propLine += `: ${prop.type}`;
558
+ }
559
+ // Add options for select/radio types
560
+ if (prop.options && prop.options.length > 0) {
561
+ const optionsStr = prop.options.slice(0, 5).map(o => `"${o}"`).join(' | ');
562
+ propLine += ` [${optionsStr}${prop.options.length > 5 ? '...' : ''}]`;
563
+ }
564
+ // Add description
565
+ if (prop.description) {
566
+ propLine += ` - ${prop.description}`;
567
+ }
568
+ // Add default value
569
+ if (prop.defaultValue !== undefined) {
570
+ propLine += ` (default: ${JSON.stringify(prop.defaultValue)})`;
571
+ }
572
+ reference += propLine;
573
+ }
574
+ }
575
+ else if (component.props && component.props.length > 0) {
576
+ // Fall back to simple prop names
447
577
  reference += `: Props: ${component.props.join(', ')}`;
448
578
  }
449
579
  if (component.slots && component.slots.length > 0) {
@@ -608,6 +738,8 @@ function generateExamples(config) {
608
738
  */
609
739
  function generateImportStatements(config, components, componentNames) {
610
740
  const importMap = new Map();
741
+ // Check if individual file imports are configured (for libraries without barrel exports)
742
+ const useIndividualImports = config.importStyle === 'individual';
611
743
  for (const componentName of componentNames) {
612
744
  // Find the component in our discovered components to get its specific import path
613
745
  const component = components.find(c => c.name === componentName);
@@ -619,8 +751,20 @@ function generateImportStatements(config, components, componentNames) {
619
751
  }
620
752
  importMap.get(importPath).push(componentName);
621
753
  }
754
+ else if (useIndividualImports) {
755
+ // Generate individual file imports for libraries without barrel exports
756
+ // Group by base component: CardHeader, CardContent -> card.tsx
757
+ // This pattern works for React (shadcn/ui), Vue (PrimeVue), Angular (Material), Web Components (Shoelace)
758
+ const baseComponent = getBaseComponentName(componentName);
759
+ const fileName = toKebabCase(baseComponent);
760
+ const importPath = `${config.importPath}/${fileName}`;
761
+ if (!importMap.has(importPath)) {
762
+ importMap.set(importPath, []);
763
+ }
764
+ importMap.get(importPath).push(componentName);
765
+ }
622
766
  else {
623
- // Fallback to the main package import path
767
+ // Use barrel import (default) - import all from single entry point
624
768
  if (!importMap.has(config.importPath)) {
625
769
  importMap.set(config.importPath, []);
626
770
  }
@@ -629,8 +773,8 @@ function generateImportStatements(config, components, componentNames) {
629
773
  }
630
774
  // Generate import statements
631
775
  const importLines = [];
632
- for (const [importPath, components] of importMap) {
633
- importLines.push(`import { ${components.join(', ')} } from '${importPath}';`);
776
+ for (const [importPath, comps] of importMap) {
777
+ importLines.push(`import { ${comps.join(', ')} } from '${importPath}';`);
634
778
  }
635
779
  return importLines.join('\n');
636
780
  }
@@ -815,7 +959,12 @@ export async function buildClaudePrompt(userPrompt, config, components) {
815
959
  frameworkFirstLineRules = `🚨 FINAL CRITICAL REMINDERS 🚨
816
960
  Follow the framework-specific import patterns shown above.`;
817
961
  }
818
- promptParts.push(`Output a complete Storybook story file in TypeScript. Import components as shown in the sample template below. Use the following sample as a template. Respond ONLY with a single code block containing the full file, and nothing else.`, '', '<rules>', frameworkFirstLineRules, '', 'OTHER CRITICAL RULES:', '- Story title MUST always start with "Generated/" (e.g., title: "Generated/Recipe Card")', '- Do NOT use prefixes like "Content/", "Components/", or any other section name', '- ONLY import components that are listed in the "Available components" section', '- ALWAYS use the exact import path shown in parentheses after each component', '- NEVER use main package imports when specific subpath imports are shown', '- Do NOT import story exports - these are NOT real components', '- Check every import against the Available components list before using it', '- FORBIDDEN: Any component not explicitly listed in the Available components section', '- FORBIDDEN: Theme setup components (providers should be configured at the app level, not in individual stories)', '- All images MUST have a src attribute with placeholder URLs (use https://picsum.photos/)', '- Never create <img> tags without src attributes', '- MUST use ES modules syntax: "export default meta;" NOT "module.exports = meta;"', '- The file MUST have a default export for the meta object', '- Keep the story concise and focused - avoid overly complex layouts that might exceed token limits', '- Ensure all JSX tags are properly closed', '- Story must be complete and syntactically valid', '- CRITICAL: Never put ANY content in args.children - always use render function', '- Use render functions for ALL layouts and component compositions', '- For layouts: DO NOT set component in meta', '- Only set component in meta when showcasing a SINGLE component', '- Use appropriate styling for the component library (design tokens, className, or inline styles as needed)', '</rules>', '', 'Sample story format:', generated.sampleStory, '', 'User request:', userPrompt);
962
+ // Add import style specific rules
963
+ const importStyleRules = [];
964
+ if (config.importStyle === 'individual') {
965
+ importStyleRules.push(`- 🚫 INDIVIDUAL IMPORTS REQUIRED: Import each component from its own file (e.g., '${config.importPath}/button', NOT '${config.importPath}')`, `- Sub-components share files: CardHeader, CardContent → '${config.importPath}/card'`, '- File names use kebab-case: AlertDialog → alert-dialog, NavigationMenu → navigation-menu');
966
+ }
967
+ promptParts.push(`Output a complete Storybook story file in TypeScript. Import components as shown in the sample template below. Use the following sample as a template. Respond ONLY with a single code block containing the full file, and nothing else.`, '', '<rules>', frameworkFirstLineRules, '', 'OTHER CRITICAL RULES:', ...importStyleRules, '- Story title MUST always start with "Generated/" (e.g., title: "Generated/Recipe Card")', '- Do NOT use prefixes like "Content/", "Components/", or any other section name', '- ONLY import components that are listed in the "Available components" section', '- ALWAYS use the exact import path shown in parentheses after each component', '- NEVER use main package imports when specific subpath imports are shown', '- Do NOT import story exports - these are NOT real components', '- Check every import against the Available components list before using it', '- FORBIDDEN: Any component not explicitly listed in the Available components section', '- FORBIDDEN: Theme setup components (providers should be configured at the app level, not in individual stories)', '- All images MUST have a src attribute with placeholder URLs (use https://picsum.photos/)', '- Never create <img> tags without src attributes', '- MUST use ES modules syntax: "export default meta;" NOT "module.exports = meta;"', '- The file MUST have a default export for the meta object', '- Keep the story concise and focused - avoid overly complex layouts that might exceed token limits', '- Ensure all JSX tags are properly closed', '- Story must be complete and syntactically valid', '- CRITICAL: Never put ANY content in args.children - always use render function', '- Use render functions for ALL layouts and component compositions', '- For layouts: DO NOT set component in meta', '- Only set component in meta when showcasing a SINGLE component', '- Use appropriate styling for the component library (design tokens, className, or inline styles as needed)', '</rules>', '', 'Sample story format:', generated.sampleStory, '', 'User request:', userPrompt);
819
968
  return promptParts.join('\n');
820
969
  }
821
970
  /**
@@ -913,7 +1062,12 @@ export async function buildFrameworkAwarePrompt(userPrompt, config, components,
913
1062
  promptParts.push(`${frameworkType.toUpperCase()} SPECIFIC RULES:`);
914
1063
  promptParts.push(...frameworkRules);
915
1064
  }
916
- promptParts.push('', `Output a complete Storybook story file in TypeScript. Import components as shown in the sample template below. Use the following sample as a template. Respond ONLY with a single code block containing the full file, and nothing else.`, '', '<rules>', 'CRITICAL REMINDERS:', '- Story title MUST always start with "Generated/" (e.g., title: "Generated/Recipe Card")', '- ONLY import components that are listed in the "Available components" section', '- ALWAYS use the exact import path shown in parentheses after each component', '- NEVER use main package imports when specific subpath imports are shown', '- Do NOT import story exports - these are NOT real components', '- All images MUST have a src attribute with placeholder URLs (use https://picsum.photos/)', '- MUST use ES modules syntax: "export default meta;" NOT "module.exports = meta;"', '- The file MUST have a default export for the meta object', '- Keep the story concise and focused - avoid overly complex layouts', '- Ensure all tags are properly closed and syntax is valid', '- Story must be complete and syntactically valid', '</rules>', '', 'Sample story format:', generated.sampleStory, '', 'User request:', userPrompt);
1065
+ // Add import style specific rules for framework-aware prompts
1066
+ const importStyleRulesFramework = [];
1067
+ if (config.importStyle === 'individual') {
1068
+ importStyleRulesFramework.push(`- 🚫 INDIVIDUAL IMPORTS REQUIRED: Import each component from its own file (e.g., '${config.importPath}/button', NOT '${config.importPath}')`, `- Sub-components share files: CardHeader, CardContent → '${config.importPath}/card'`, '- File names use kebab-case: AlertDialog → alert-dialog, NavigationMenu → navigation-menu');
1069
+ }
1070
+ promptParts.push('', `Output a complete Storybook story file in TypeScript. Import components as shown in the sample template below. Use the following sample as a template. Respond ONLY with a single code block containing the full file, and nothing else.`, '', '<rules>', 'CRITICAL REMINDERS:', ...importStyleRulesFramework, '- Story title MUST always start with "Generated/" (e.g., title: "Generated/Recipe Card")', '- ONLY import components that are listed in the "Available components" section', '- ALWAYS use the exact import path shown in parentheses after each component', '- NEVER use main package imports when specific subpath imports are shown', '- Do NOT import story exports - these are NOT real components', '- All images MUST have a src attribute with placeholder URLs (use https://picsum.photos/)', '- MUST use ES modules syntax: "export default meta;" NOT "module.exports = meta;"', '- The file MUST have a default export for the meta object', '- Keep the story concise and focused - avoid overly complex layouts', '- Ensure all tags are properly closed and syntax is valid', '- Story must be complete and syntactically valid', '</rules>', '', 'Sample story format:', generated.sampleStory, '', 'User request:', userPrompt);
917
1071
  return promptParts.join('\n');
918
1072
  }
919
1073
  /**
@@ -81,6 +81,22 @@ export interface StoryUIConfig {
81
81
  additionalImports?: AdditionalImport[];
82
82
  considerationsPath?: string;
83
83
  storybookFramework?: string;
84
+ /**
85
+ * Import style for generated stories:
86
+ * - 'barrel': Use barrel imports from a single entry point (e.g., `import { Button } from 'library'`)
87
+ * - 'individual': Use individual file imports (e.g., `import { Button } from 'library/button'`)
88
+ *
89
+ * Use 'individual' for libraries without barrel exports (index.ts), such as:
90
+ * - shadcn/ui, Radix Vue, PrimeVue (Vue)
91
+ * - Angular Material, PrimeNG (Angular)
92
+ * - Shoelace, Lion (Web Components)
93
+ *
94
+ * Use 'barrel' (default) for libraries with barrel exports, such as:
95
+ * - Chakra UI, Mantine, Ant Design (React)
96
+ * - Vuetify, Quasar (Vue)
97
+ * - Skeleton UI (Svelte)
98
+ */
99
+ importStyle?: 'barrel' | 'individual';
84
100
  designSystemGuidelines?: DesignSystemGuidelines;
85
101
  /** Icon imports configuration (auto-detected from package.json or manually configured) */
86
102
  iconImports?: IconImportsConfig;