@fragments-sdk/cli 0.2.2

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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +4783 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chunk-4FDQSGKX.js +786 -0
  7. package/dist/chunk-4FDQSGKX.js.map +1 -0
  8. package/dist/chunk-7H2MMGYG.js +369 -0
  9. package/dist/chunk-7H2MMGYG.js.map +1 -0
  10. package/dist/chunk-BSCG3IP7.js +619 -0
  11. package/dist/chunk-BSCG3IP7.js.map +1 -0
  12. package/dist/chunk-LY2CFFPY.js +898 -0
  13. package/dist/chunk-LY2CFFPY.js.map +1 -0
  14. package/dist/chunk-MUZ6CM66.js +6636 -0
  15. package/dist/chunk-MUZ6CM66.js.map +1 -0
  16. package/dist/chunk-OAENNG3G.js +1489 -0
  17. package/dist/chunk-OAENNG3G.js.map +1 -0
  18. package/dist/chunk-XHNKNI6J.js +235 -0
  19. package/dist/chunk-XHNKNI6J.js.map +1 -0
  20. package/dist/core-DWKLGY4N.js +68 -0
  21. package/dist/core-DWKLGY4N.js.map +1 -0
  22. package/dist/generate-4LQNJ7SX.js +249 -0
  23. package/dist/generate-4LQNJ7SX.js.map +1 -0
  24. package/dist/index.d.ts +775 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init-EMVI47QG.js +416 -0
  28. package/dist/init-EMVI47QG.js.map +1 -0
  29. package/dist/mcp-bin.d.ts +1 -0
  30. package/dist/mcp-bin.js +1117 -0
  31. package/dist/mcp-bin.js.map +1 -0
  32. package/dist/scan-4YPRF7FV.js +12 -0
  33. package/dist/scan-4YPRF7FV.js.map +1 -0
  34. package/dist/service-QSZMZJBJ.js +208 -0
  35. package/dist/service-QSZMZJBJ.js.map +1 -0
  36. package/dist/static-viewer-MIPGZ4Z7.js +12 -0
  37. package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
  38. package/dist/test-SQ5ZHXWU.js +1067 -0
  39. package/dist/test-SQ5ZHXWU.js.map +1 -0
  40. package/dist/tokens-HSGMYK64.js +173 -0
  41. package/dist/tokens-HSGMYK64.js.map +1 -0
  42. package/dist/viewer-YRF4SQE4.js +11101 -0
  43. package/dist/viewer-YRF4SQE4.js.map +1 -0
  44. package/package.json +107 -0
  45. package/src/ai.ts +266 -0
  46. package/src/analyze.ts +265 -0
  47. package/src/bin.ts +916 -0
  48. package/src/build.ts +248 -0
  49. package/src/commands/a11y.ts +302 -0
  50. package/src/commands/add.ts +313 -0
  51. package/src/commands/audit.ts +195 -0
  52. package/src/commands/baseline.ts +221 -0
  53. package/src/commands/build.ts +144 -0
  54. package/src/commands/compare.ts +337 -0
  55. package/src/commands/context.ts +107 -0
  56. package/src/commands/dev.ts +107 -0
  57. package/src/commands/enhance.ts +858 -0
  58. package/src/commands/generate.ts +391 -0
  59. package/src/commands/init.ts +531 -0
  60. package/src/commands/link/figma.ts +645 -0
  61. package/src/commands/link/index.ts +10 -0
  62. package/src/commands/link/storybook.ts +267 -0
  63. package/src/commands/list.ts +49 -0
  64. package/src/commands/metrics.ts +114 -0
  65. package/src/commands/reset.ts +242 -0
  66. package/src/commands/scan.ts +537 -0
  67. package/src/commands/storygen.ts +207 -0
  68. package/src/commands/tokens.ts +251 -0
  69. package/src/commands/validate.ts +93 -0
  70. package/src/commands/verify.ts +215 -0
  71. package/src/core/composition.test.ts +262 -0
  72. package/src/core/composition.ts +255 -0
  73. package/src/core/config.ts +84 -0
  74. package/src/core/constants.ts +111 -0
  75. package/src/core/context.ts +380 -0
  76. package/src/core/defineSegment.ts +137 -0
  77. package/src/core/discovery.ts +337 -0
  78. package/src/core/figma.ts +263 -0
  79. package/src/core/fragment-types.ts +214 -0
  80. package/src/core/generators/context.ts +389 -0
  81. package/src/core/generators/index.ts +23 -0
  82. package/src/core/generators/registry.ts +364 -0
  83. package/src/core/generators/typescript-extractor.ts +374 -0
  84. package/src/core/importAnalyzer.ts +217 -0
  85. package/src/core/index.ts +149 -0
  86. package/src/core/loader.ts +155 -0
  87. package/src/core/node.ts +63 -0
  88. package/src/core/parser.ts +551 -0
  89. package/src/core/previewLoader.ts +172 -0
  90. package/src/core/schema/fragment.schema.json +189 -0
  91. package/src/core/schema/registry.schema.json +137 -0
  92. package/src/core/schema.ts +182 -0
  93. package/src/core/storyAdapter.test.ts +571 -0
  94. package/src/core/storyAdapter.ts +761 -0
  95. package/src/core/token-types.ts +287 -0
  96. package/src/core/types.ts +754 -0
  97. package/src/diff.ts +323 -0
  98. package/src/index.ts +43 -0
  99. package/src/mcp/__tests__/projectFields.test.ts +130 -0
  100. package/src/mcp/bin.ts +36 -0
  101. package/src/mcp/index.ts +8 -0
  102. package/src/mcp/server.ts +1310 -0
  103. package/src/mcp/utils.ts +54 -0
  104. package/src/mcp-bin.ts +36 -0
  105. package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
  106. package/src/migrate/__tests__/args/args.test.ts +452 -0
  107. package/src/migrate/__tests__/meta/meta.test.ts +198 -0
  108. package/src/migrate/__tests__/stories/stories.test.ts +278 -0
  109. package/src/migrate/__tests__/utils/utils.test.ts +371 -0
  110. package/src/migrate/__tests__/values/values.test.ts +303 -0
  111. package/src/migrate/bin.ts +108 -0
  112. package/src/migrate/converter.ts +658 -0
  113. package/src/migrate/detect.ts +196 -0
  114. package/src/migrate/index.ts +45 -0
  115. package/src/migrate/migrate.ts +163 -0
  116. package/src/migrate/parser.ts +1136 -0
  117. package/src/migrate/report.ts +624 -0
  118. package/src/migrate/types.ts +169 -0
  119. package/src/screenshot.ts +249 -0
  120. package/src/service/__tests__/ast-utils.test.ts +426 -0
  121. package/src/service/__tests__/enhance-scanner.test.ts +200 -0
  122. package/src/service/__tests__/figma/figma.test.ts +652 -0
  123. package/src/service/__tests__/metrics-store.test.ts +409 -0
  124. package/src/service/__tests__/patch-generator.test.ts +186 -0
  125. package/src/service/__tests__/props-extractor.test.ts +365 -0
  126. package/src/service/__tests__/token-registry.test.ts +267 -0
  127. package/src/service/analytics.ts +659 -0
  128. package/src/service/ast-utils.ts +444 -0
  129. package/src/service/browser-pool.ts +339 -0
  130. package/src/service/capture.ts +267 -0
  131. package/src/service/diff.ts +279 -0
  132. package/src/service/enhance/aggregator.ts +489 -0
  133. package/src/service/enhance/cache.ts +275 -0
  134. package/src/service/enhance/codebase-scanner.ts +357 -0
  135. package/src/service/enhance/context-generator.ts +529 -0
  136. package/src/service/enhance/doc-extractor.ts +523 -0
  137. package/src/service/enhance/index.ts +131 -0
  138. package/src/service/enhance/props-extractor.ts +665 -0
  139. package/src/service/enhance/scanner.ts +445 -0
  140. package/src/service/enhance/storybook-parser.ts +552 -0
  141. package/src/service/enhance/types.ts +346 -0
  142. package/src/service/enhance/variant-renderer.ts +479 -0
  143. package/src/service/figma.ts +1008 -0
  144. package/src/service/index.ts +249 -0
  145. package/src/service/metrics-store.ts +333 -0
  146. package/src/service/patch-generator.ts +349 -0
  147. package/src/service/report.ts +854 -0
  148. package/src/service/storage.ts +401 -0
  149. package/src/service/token-fixes.ts +281 -0
  150. package/src/service/token-parser.ts +504 -0
  151. package/src/service/token-registry.ts +721 -0
  152. package/src/service/utils.ts +172 -0
  153. package/src/setup.ts +241 -0
  154. package/src/shared/command-wrapper.ts +81 -0
  155. package/src/shared/dev-server-client.ts +199 -0
  156. package/src/shared/index.ts +8 -0
  157. package/src/shared/segment-loader.ts +59 -0
  158. package/src/shared/types.ts +147 -0
  159. package/src/static-viewer.ts +715 -0
  160. package/src/test/discovery.ts +172 -0
  161. package/src/test/index.ts +281 -0
  162. package/src/test/reporters/console.ts +194 -0
  163. package/src/test/reporters/json.ts +190 -0
  164. package/src/test/reporters/junit.ts +186 -0
  165. package/src/test/runner.ts +598 -0
  166. package/src/test/types.ts +245 -0
  167. package/src/test/watch.ts +200 -0
  168. package/src/validators.ts +152 -0
  169. package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
  170. package/src/viewer/__tests__/render-utils.test.ts +232 -0
  171. package/src/viewer/__tests__/style-utils.test.ts +404 -0
  172. package/src/viewer/bin.ts +86 -0
  173. package/src/viewer/cli/health.ts +256 -0
  174. package/src/viewer/cli/index.ts +33 -0
  175. package/src/viewer/cli/scan.ts +124 -0
  176. package/src/viewer/cli/utils.ts +174 -0
  177. package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
  178. package/src/viewer/components/ActionCapture.tsx +172 -0
  179. package/src/viewer/components/ActionsPanel.tsx +371 -0
  180. package/src/viewer/components/App.tsx +638 -0
  181. package/src/viewer/components/BottomPanel.tsx +224 -0
  182. package/src/viewer/components/CodePanel.tsx +589 -0
  183. package/src/viewer/components/CommandPalette.tsx +336 -0
  184. package/src/viewer/components/ComponentGraph.tsx +394 -0
  185. package/src/viewer/components/ComponentHeader.tsx +85 -0
  186. package/src/viewer/components/ContractPanel.tsx +234 -0
  187. package/src/viewer/components/ErrorBoundary.tsx +85 -0
  188. package/src/viewer/components/FigmaEmbed.tsx +231 -0
  189. package/src/viewer/components/FragmentEditor.tsx +485 -0
  190. package/src/viewer/components/HealthDashboard.tsx +452 -0
  191. package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
  192. package/src/viewer/components/Icons.tsx +417 -0
  193. package/src/viewer/components/InteractionsPanel.tsx +720 -0
  194. package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
  195. package/src/viewer/components/IsolatedRender.tsx +111 -0
  196. package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
  197. package/src/viewer/components/LandingPage.tsx +441 -0
  198. package/src/viewer/components/Layout.tsx +22 -0
  199. package/src/viewer/components/LeftSidebar.tsx +391 -0
  200. package/src/viewer/components/MultiViewportPreview.tsx +429 -0
  201. package/src/viewer/components/PreviewArea.tsx +404 -0
  202. package/src/viewer/components/PreviewFrameHost.tsx +310 -0
  203. package/src/viewer/components/PreviewPane.tsx +150 -0
  204. package/src/viewer/components/PreviewToolbar.tsx +176 -0
  205. package/src/viewer/components/PropsEditor.tsx +512 -0
  206. package/src/viewer/components/PropsTable.tsx +98 -0
  207. package/src/viewer/components/RelationsSection.tsx +57 -0
  208. package/src/viewer/components/ResizablePanel.tsx +328 -0
  209. package/src/viewer/components/RightSidebar.tsx +118 -0
  210. package/src/viewer/components/ScreenshotButton.tsx +90 -0
  211. package/src/viewer/components/Sidebar.tsx +169 -0
  212. package/src/viewer/components/SkeletonLoader.tsx +156 -0
  213. package/src/viewer/components/StoryRenderer.tsx +128 -0
  214. package/src/viewer/components/ThemeProvider.tsx +96 -0
  215. package/src/viewer/components/Toast.tsx +67 -0
  216. package/src/viewer/components/TokenStylePanel.tsx +708 -0
  217. package/src/viewer/components/UsageSection.tsx +95 -0
  218. package/src/viewer/components/VariantMatrix.tsx +350 -0
  219. package/src/viewer/components/VariantRenderer.tsx +131 -0
  220. package/src/viewer/components/VariantTabs.tsx +84 -0
  221. package/src/viewer/components/ViewportSelector.tsx +165 -0
  222. package/src/viewer/components/_future/CreatePage.tsx +836 -0
  223. package/src/viewer/composition-renderer.ts +381 -0
  224. package/src/viewer/constants/index.ts +1 -0
  225. package/src/viewer/constants/ui.ts +185 -0
  226. package/src/viewer/entry.tsx +299 -0
  227. package/src/viewer/hooks/index.ts +2 -0
  228. package/src/viewer/hooks/useA11yCache.ts +383 -0
  229. package/src/viewer/hooks/useA11yService.ts +498 -0
  230. package/src/viewer/hooks/useActions.ts +138 -0
  231. package/src/viewer/hooks/useAppState.ts +124 -0
  232. package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
  233. package/src/viewer/hooks/useHmrStatus.ts +109 -0
  234. package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
  235. package/src/viewer/hooks/usePreviewBridge.ts +347 -0
  236. package/src/viewer/hooks/useScrollSpy.ts +78 -0
  237. package/src/viewer/hooks/useUrlState.ts +330 -0
  238. package/src/viewer/hooks/useViewSettings.ts +125 -0
  239. package/src/viewer/index.html +28 -0
  240. package/src/viewer/index.ts +14 -0
  241. package/src/viewer/intelligence/healthReport.ts +505 -0
  242. package/src/viewer/intelligence/styleDrift.ts +340 -0
  243. package/src/viewer/intelligence/usageScanner.ts +309 -0
  244. package/src/viewer/jsx-parser.ts +485 -0
  245. package/src/viewer/postcss.config.js +6 -0
  246. package/src/viewer/preview-frame-entry.tsx +25 -0
  247. package/src/viewer/preview-frame.html +109 -0
  248. package/src/viewer/render-template.html +68 -0
  249. package/src/viewer/render-utils.ts +170 -0
  250. package/src/viewer/server.ts +276 -0
  251. package/src/viewer/style-utils.ts +414 -0
  252. package/src/viewer/styles/globals.css +355 -0
  253. package/src/viewer/tailwind.config.js +37 -0
  254. package/src/viewer/types/a11y.ts +197 -0
  255. package/src/viewer/utils/a11y-fixes.ts +471 -0
  256. package/src/viewer/utils/actionExport.ts +372 -0
  257. package/src/viewer/utils/colorSchemes.ts +201 -0
  258. package/src/viewer/utils/detectRelationships.ts +256 -0
  259. package/src/viewer/vite-plugin.ts +2143 -0
@@ -0,0 +1,391 @@
1
+ /**
2
+ * fragments generate - AI-assisted fragment generation
3
+ *
4
+ * Analyzes component source code and generates fragment files with:
5
+ * - Extracted props from TypeScript
6
+ * - Inferred usage patterns from story names
7
+ * - Generated description
8
+ */
9
+
10
+ import { readFile, writeFile, mkdir, access } from "node:fs/promises";
11
+ import { resolve, join, basename, dirname, relative } from "node:path";
12
+ import pc from "picocolors";
13
+ import fg from "fast-glob";
14
+ import { BRAND, type Fragment } from "../core/index.js";
15
+ import { extractPropsFromFile } from "../core/node.js";
16
+
17
+ export interface GenerateOptions {
18
+ /** Project root directory */
19
+ projectRoot?: string;
20
+ /** Specific component name to generate (optional) */
21
+ component?: string;
22
+ /** Overwrite existing fragment files */
23
+ force?: boolean;
24
+ /** Pattern for component files */
25
+ componentPattern?: string;
26
+ }
27
+
28
+ export interface GenerateResult {
29
+ success: boolean;
30
+ generated: Array<{ name: string; path: string }>;
31
+ skipped: Array<{ name: string; reason: string }>;
32
+ errors: Array<{ name: string; error: string }>;
33
+ }
34
+
35
+ /**
36
+ * Generate fragment files from component source code
37
+ */
38
+ export async function generate(options: GenerateOptions = {}): Promise<GenerateResult> {
39
+ const projectRoot = resolve(options.projectRoot || process.cwd());
40
+ const generated: Array<{ name: string; path: string }> = [];
41
+ const skipped: Array<{ name: string; reason: string }> = [];
42
+ const errors: Array<{ name: string; error: string }> = [];
43
+
44
+ console.log(pc.cyan(`\n${BRAND.name} Generate\n`));
45
+
46
+ // Ensure .fragments/components directory exists
47
+ const fragmentsDir = join(projectRoot, BRAND.dataDir, BRAND.componentsDir);
48
+ await mkdir(fragmentsDir, { recursive: true });
49
+
50
+ // Find component files
51
+ const componentPattern =
52
+ options.componentPattern ||
53
+ "src/components/**/*.tsx";
54
+
55
+ const componentFiles = await fg(componentPattern, {
56
+ cwd: projectRoot,
57
+ ignore: [
58
+ "**/node_modules/**",
59
+ "**/*.stories.*",
60
+ "**/*.segment.*",
61
+ "**/*.test.*",
62
+ "**/*.spec.*",
63
+ "**/*.d.ts",
64
+ "**/index.tsx", // Often just re-exports
65
+ ],
66
+ absolute: true,
67
+ });
68
+
69
+ // Find story files for pattern inference
70
+ const storyFiles = await fg(["src/**/*.stories.tsx", "src/**/*.stories.ts"], {
71
+ cwd: projectRoot,
72
+ ignore: ["**/node_modules/**"],
73
+ absolute: true,
74
+ });
75
+
76
+ // Build story map for pattern inference
77
+ const storyMap = new Map<string, string[]>();
78
+ for (const storyFile of storyFiles) {
79
+ try {
80
+ const content = await readFile(storyFile, "utf-8");
81
+ const componentName = extractComponentNameFromStory(content, storyFile);
82
+ if (componentName) {
83
+ const storyNames = extractStoryNames(content);
84
+ storyMap.set(componentName, storyNames);
85
+ }
86
+ } catch {
87
+ // Ignore parsing errors
88
+ }
89
+ }
90
+
91
+ console.log(pc.dim(`Found ${componentFiles.length} component files\n`));
92
+
93
+ // Process each component file
94
+ for (const filePath of componentFiles) {
95
+ try {
96
+ const extracted = extractPropsFromFile(filePath);
97
+
98
+ if (!extracted || !extracted.componentName) {
99
+ continue;
100
+ }
101
+
102
+ const componentName = extracted.componentName;
103
+
104
+ // Filter by component name if specified
105
+ if (options.component && componentName !== options.component) {
106
+ continue;
107
+ }
108
+
109
+ // Check if fragment already exists
110
+ const fragmentPath = join(fragmentsDir, `${componentName}${BRAND.fileExtension}`);
111
+ let fragmentExists = false;
112
+ try {
113
+ await access(fragmentPath);
114
+ fragmentExists = true;
115
+ } catch {
116
+ // Fragment doesn't exist
117
+ }
118
+
119
+ if (fragmentExists && !options.force) {
120
+ skipped.push({ name: componentName, reason: "Fragment already exists" });
121
+ console.log(pc.dim(` Skipping ${componentName} (fragment exists)`));
122
+ continue;
123
+ }
124
+
125
+ // Generate fragment from analysis
126
+ const fragment = generateFragmentFromComponent(
127
+ componentName,
128
+ extracted,
129
+ filePath,
130
+ storyMap.get(componentName) || []
131
+ );
132
+
133
+ // Write fragment file
134
+ await writeFile(fragmentPath, JSON.stringify(fragment, null, 2), "utf-8");
135
+ generated.push({ name: componentName, path: relative(projectRoot, fragmentPath) });
136
+ console.log(pc.green(` ✓ Generated ${componentName}${BRAND.fileExtension}`));
137
+ } catch (e) {
138
+ const fileName = basename(filePath);
139
+ errors.push({
140
+ name: fileName,
141
+ error: e instanceof Error ? e.message : String(e),
142
+ });
143
+ console.log(pc.red(` ✗ Failed: ${fileName}`));
144
+ }
145
+ }
146
+
147
+ // Summary
148
+ console.log();
149
+ if (generated.length > 0) {
150
+ console.log(pc.green(`✓ Generated ${generated.length} fragment(s)`));
151
+ }
152
+ if (skipped.length > 0) {
153
+ console.log(pc.dim(` Skipped ${skipped.length} (use --force to overwrite)`));
154
+ }
155
+ if (errors.length > 0) {
156
+ console.log(pc.yellow(` ${errors.length} error(s)`));
157
+ }
158
+ console.log();
159
+
160
+ return {
161
+ success: errors.length === 0,
162
+ generated,
163
+ skipped,
164
+ errors,
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Generate a Fragment from extracted component data
170
+ */
171
+ function generateFragmentFromComponent(
172
+ componentName: string,
173
+ extracted: ReturnType<typeof extractPropsFromFile> & { componentName: string },
174
+ filePath: string,
175
+ storyNames: string[]
176
+ ): Fragment {
177
+ // Infer usage from story names
178
+ const whenToUse = inferUsageFromStories(storyNames);
179
+
180
+ // Generate description from component name
181
+ const description = generateDescription(componentName, extracted.props);
182
+
183
+ // Infer accessibility from props
184
+ const accessibility = inferAccessibility(extracted.props);
185
+
186
+ // Infer status from file path
187
+ const status = inferStatus(filePath);
188
+
189
+ const fragment: Fragment = {
190
+ $schema: "https://fragments.dev/schema/v1.json",
191
+ name: componentName,
192
+ description,
193
+ usage: {
194
+ when: whenToUse,
195
+ doNot: [],
196
+ },
197
+ meta: {
198
+ status,
199
+ },
200
+ };
201
+
202
+ // Add accessibility if any was inferred
203
+ if (accessibility.role || (accessibility.requirements && accessibility.requirements.length > 0)) {
204
+ fragment.accessibility = accessibility;
205
+ }
206
+
207
+ return fragment;
208
+ }
209
+
210
+ /**
211
+ * Extract component name from story file
212
+ */
213
+ function extractComponentNameFromStory(content: string, filePath: string): string | null {
214
+ // Try to extract from title in default export
215
+ // e.g., export default { title: 'Components/Button' }
216
+ const titleMatch = content.match(/title:\s*['"](?:[^'"]+\/)?([^'"]+)['"]/);
217
+ if (titleMatch) {
218
+ return titleMatch[1];
219
+ }
220
+
221
+ // Try to extract from file name
222
+ const fileName = basename(filePath);
223
+ const componentName = fileName.replace(/\.stories\.(tsx?|jsx?)$/, "");
224
+ if (/^[A-Z]/.test(componentName)) {
225
+ return componentName;
226
+ }
227
+
228
+ return null;
229
+ }
230
+
231
+ /**
232
+ * Extract story names from story file content
233
+ */
234
+ function extractStoryNames(content: string): string[] {
235
+ const names: string[] = [];
236
+
237
+ // Match named exports that look like stories
238
+ // e.g., export const Primary = ...
239
+ const exportMatches = content.matchAll(
240
+ /export\s+const\s+([A-Z][a-zA-Z0-9]*)\s*[=:]/g
241
+ );
242
+
243
+ for (const match of exportMatches) {
244
+ const name = match[1];
245
+ if (name !== "default" && !name.endsWith("Args") && !name.endsWith("Meta")) {
246
+ names.push(name);
247
+ }
248
+ }
249
+
250
+ return names;
251
+ }
252
+
253
+ /**
254
+ * Infer usage scenarios from story names
255
+ */
256
+ function inferUsageFromStories(storyNames: string[]): string[] {
257
+ const usage: string[] = [];
258
+
259
+ for (const name of storyNames) {
260
+ // Convert PascalCase to sentence
261
+ const sentence = name
262
+ .replace(/([A-Z])/g, " $1")
263
+ .trim()
264
+ .toLowerCase();
265
+
266
+ // Skip generic names
267
+ if (
268
+ ["default", "primary", "basic", "example", "playground"].includes(
269
+ sentence.toLowerCase()
270
+ )
271
+ ) {
272
+ continue;
273
+ }
274
+
275
+ // Generate usage from story name
276
+ if (sentence.includes("loading")) {
277
+ usage.push("Showing loading states");
278
+ } else if (sentence.includes("disabled")) {
279
+ usage.push("Preventing user interaction");
280
+ } else if (sentence.includes("error")) {
281
+ usage.push("Displaying error states");
282
+ } else if (sentence.includes("success")) {
283
+ usage.push("Showing success feedback");
284
+ } else if (sentence.includes("empty")) {
285
+ usage.push("Handling empty states");
286
+ } else if (sentence.includes("with")) {
287
+ // "WithIcon" -> "Adding icons"
288
+ const withPart = sentence.replace("with ", "");
289
+ usage.push(`Displaying with ${withPart}`);
290
+ }
291
+ }
292
+
293
+ return usage;
294
+ }
295
+
296
+ /**
297
+ * Generate description from component name and props
298
+ */
299
+ function generateDescription(
300
+ componentName: string,
301
+ props: Record<string, unknown>
302
+ ): string {
303
+ // Convert PascalCase to words
304
+ const words = componentName
305
+ .replace(/([A-Z])/g, " $1")
306
+ .trim()
307
+ .toLowerCase();
308
+
309
+ // Detect component type from name or props
310
+ const hasOnClick = "onClick" in props || "onPress" in props;
311
+ const hasValue = "value" in props || "defaultValue" in props;
312
+ const hasChildren = "children" in props;
313
+
314
+ if (hasOnClick && !hasValue) {
315
+ return `Interactive ${words} element for triggering actions`;
316
+ }
317
+
318
+ if (hasValue) {
319
+ return `Form ${words} for user input`;
320
+ }
321
+
322
+ if (hasChildren) {
323
+ return `Container ${words} for grouping content`;
324
+ }
325
+
326
+ return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;
327
+ }
328
+
329
+ /**
330
+ * Infer accessibility from props
331
+ */
332
+ function inferAccessibility(props: Record<string, unknown>): {
333
+ role?: string;
334
+ requirements?: string[];
335
+ } {
336
+ const accessibility: { role?: string; requirements?: string[] } = {};
337
+
338
+ const hasOnClick = "onClick" in props || "onPress" in props;
339
+ const hasAriaLabel = "ariaLabel" in props || "aria-label" in props;
340
+ const hasRole = "role" in props;
341
+ const hasDisabled = "disabled" in props;
342
+ const hasHref = "href" in props;
343
+
344
+ // Infer role
345
+ if (hasOnClick && !hasHref) {
346
+ accessibility.role = "button";
347
+ } else if (hasHref) {
348
+ accessibility.role = "link";
349
+ }
350
+
351
+ // Infer requirements
352
+ const requirements: string[] = [];
353
+
354
+ if (hasOnClick && !hasAriaLabel) {
355
+ requirements.push("Should have visible text or aria-label");
356
+ }
357
+
358
+ if (hasDisabled) {
359
+ requirements.push("Disabled state should be conveyed to assistive technology");
360
+ }
361
+
362
+ if (requirements.length > 0) {
363
+ accessibility.requirements = requirements;
364
+ }
365
+
366
+ return accessibility;
367
+ }
368
+
369
+ /**
370
+ * Infer status from file path
371
+ */
372
+ function inferStatus(
373
+ filePath: string
374
+ ): "draft" | "experimental" | "beta" | "stable" | "deprecated" {
375
+ const lowerPath = filePath.toLowerCase();
376
+
377
+ if (lowerPath.includes("/experimental/") || lowerPath.includes("/labs/")) {
378
+ return "experimental";
379
+ }
380
+ if (lowerPath.includes("/beta/")) {
381
+ return "beta";
382
+ }
383
+ if (lowerPath.includes("/deprecated/") || lowerPath.includes("/legacy/")) {
384
+ return "deprecated";
385
+ }
386
+ if (lowerPath.includes("/draft/") || lowerPath.includes("/wip/")) {
387
+ return "draft";
388
+ }
389
+
390
+ return "stable";
391
+ }