@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,537 @@
1
+ /**
2
+ * fragments scan - Zero-config segments.json generation from source code
3
+ *
4
+ * Automatically extracts component documentation by:
5
+ * 1. Discovering components from source files and barrel exports
6
+ * 2. Extracting props from TypeScript interfaces
7
+ * 3. Scanning codebase for usage patterns
8
+ * 4. Parsing Storybook stories for examples
9
+ * 5. Inferring component relationships from usage data
10
+ * 6. Generating complete segments.json without manual documentation
11
+ */
12
+
13
+ import { writeFile, mkdir } from "node:fs/promises";
14
+ import { resolve, join, dirname, relative } from "node:path";
15
+ import pc from "picocolors";
16
+ import {
17
+ BRAND,
18
+ type CompiledSegmentsFile,
19
+ type CompiledSegment,
20
+ type PropDefinition,
21
+ } from "../core/index.js";
22
+ import {
23
+ loadConfig,
24
+ discoverAllComponents,
25
+ type DiscoveredComponent,
26
+ } from "../core/node.js";
27
+ import {
28
+ scanCodebase,
29
+ extractPropsFromFile,
30
+ parseAllStories,
31
+ inferAllRelations,
32
+ generateComponentContext,
33
+ generateEnhancementSuggestions,
34
+ filterBoilerplate,
35
+ convertToSegmentProps,
36
+ type UsageAnalysis,
37
+ type ComponentRelation,
38
+ type PropsExtractionResult,
39
+ type ParsedStoryFile,
40
+ } from "../service/index.js";
41
+
42
+ export interface ScanOptions {
43
+ /** Path to config file */
44
+ config?: string;
45
+ /** Output file path (default: segments.json) */
46
+ output?: string;
47
+ /** Component patterns to scan */
48
+ componentPatterns?: string[];
49
+ /** Barrel export files to parse */
50
+ barrelFiles?: string[];
51
+ /** Directory to scan for usage patterns */
52
+ usageDir?: string;
53
+ /** Skip usage analysis */
54
+ skipUsage?: boolean;
55
+ /** Skip Storybook parsing */
56
+ skipStorybook?: boolean;
57
+ /** Verbose output */
58
+ verbose?: boolean;
59
+ }
60
+
61
+ export interface ScanResult {
62
+ success: boolean;
63
+ outputPath: string;
64
+ componentCount: number;
65
+ propsExtracted: number;
66
+ usagesFound: number;
67
+ relationsInferred: number;
68
+ storiesParsed: number;
69
+ errors: Array<{ component: string; error: string }>;
70
+ warnings: Array<{ component: string; warning: string }>;
71
+ }
72
+
73
+ /**
74
+ * Scan codebase and generate segments.json directly from source
75
+ */
76
+ export async function scan(options: ScanOptions = {}): Promise<ScanResult> {
77
+ const startTime = Date.now();
78
+ const errors: Array<{ component: string; error: string }> = [];
79
+ const warnings: Array<{ component: string; warning: string }> = [];
80
+
81
+ // Load config or use defaults
82
+ let configDir: string;
83
+ let outputFile: string;
84
+ let componentPatterns: string[] | undefined;
85
+
86
+ try {
87
+ const loaded = await loadConfig(options.config);
88
+ configDir = loaded.configDir;
89
+ outputFile = options.output || loaded.config.outFile || "segments.json";
90
+ componentPatterns = options.componentPatterns || loaded.config.components;
91
+ } catch {
92
+ // No config file, use defaults
93
+ configDir = process.cwd();
94
+ outputFile = options.output || "segments.json";
95
+ componentPatterns = options.componentPatterns;
96
+ }
97
+
98
+ console.log(pc.cyan(`\n${BRAND.name} Scan\n`));
99
+ console.log(pc.dim("Zero-config segments.json generation from source code\n"));
100
+
101
+ // Phase 1: Discover components
102
+ console.log(pc.dim("Phase 1: Discovering components..."));
103
+ const components = await discoverAllComponents(configDir, {
104
+ patterns: componentPatterns,
105
+ exclude: ["**/*.test.*", "**/*.spec.*", "**/__tests__/**"],
106
+ barrelFiles: options.barrelFiles,
107
+ });
108
+
109
+ if (components.length === 0) {
110
+ console.log(pc.yellow("No components found. Check your patterns or config."));
111
+ return {
112
+ success: false,
113
+ outputPath: resolve(configDir, outputFile),
114
+ componentCount: 0,
115
+ propsExtracted: 0,
116
+ usagesFound: 0,
117
+ relationsInferred: 0,
118
+ storiesParsed: 0,
119
+ errors: [{ component: "*", error: "No components found" }],
120
+ warnings: [],
121
+ };
122
+ }
123
+
124
+ console.log(pc.green(` Found ${components.length} components`));
125
+ if (options.verbose) {
126
+ for (const comp of components.slice(0, 10)) {
127
+ console.log(pc.dim(` - ${comp.name}: ${comp.relativePath}`));
128
+ }
129
+ if (components.length > 10) {
130
+ console.log(pc.dim(` ... and ${components.length - 10} more`));
131
+ }
132
+ }
133
+
134
+ // Phase 2: Extract props from TypeScript
135
+ console.log(pc.dim("\nPhase 2: Extracting props from TypeScript..."));
136
+ const propsMap = new Map<string, ReturnType<typeof convertToSegmentProps>>();
137
+ const propsResults = new Map<string, PropsExtractionResult>();
138
+ let propsExtracted = 0;
139
+
140
+ for (const comp of components) {
141
+ try {
142
+ const extraction = await extractPropsFromFile(comp.sourcePath, {
143
+ propsTypeName: `${comp.name}Props`,
144
+ });
145
+
146
+ propsResults.set(comp.name, extraction);
147
+
148
+ if (extraction.success && extraction.props.length > 0) {
149
+ propsMap.set(comp.name, convertToSegmentProps(extraction.props));
150
+ propsExtracted++;
151
+ }
152
+ } catch (e) {
153
+ if (options.verbose) {
154
+ warnings.push({
155
+ component: comp.name,
156
+ warning: `Props extraction failed: ${e instanceof Error ? e.message : String(e)}`,
157
+ });
158
+ }
159
+ }
160
+ }
161
+
162
+ console.log(pc.green(` Extracted props for ${propsExtracted} components`));
163
+
164
+ // Phase 3: Scan for usage patterns
165
+ let usageAnalysis: UsageAnalysis | undefined;
166
+ let usagesFound = 0;
167
+ let allRelations = new Map<string, ComponentRelation[]>();
168
+
169
+ if (!options.skipUsage) {
170
+ console.log(pc.dim("\nPhase 3: Scanning for usage patterns..."));
171
+ const usageDir = options.usageDir || configDir;
172
+
173
+ try {
174
+ // Get component names for filtering
175
+ const componentNames = components.map((c) => c.name);
176
+
177
+ usageAnalysis = await scanCodebase({
178
+ rootDir: usageDir,
179
+ include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
180
+ exclude: [
181
+ "**/node_modules/**",
182
+ "**/dist/**",
183
+ "**/*.stories.*",
184
+ "**/*.test.*",
185
+ "**/*.spec.*",
186
+ ],
187
+ componentNames,
188
+ useCache: true,
189
+ });
190
+
191
+ // Count total usages across all components
192
+ usagesFound = Object.values(usageAnalysis.components).reduce(
193
+ (sum, comp) => sum + comp.totalUsages,
194
+ 0
195
+ );
196
+
197
+ // Infer relations
198
+ allRelations = inferAllRelations(usageAnalysis);
199
+ console.log(pc.green(` Found ${usagesFound} usages across ${usageAnalysis.totalFiles} files`));
200
+ console.log(pc.green(` Inferred relations for ${allRelations.size} components`));
201
+ } catch (e) {
202
+ warnings.push({
203
+ component: "*",
204
+ warning: `Usage scanning failed: ${e instanceof Error ? e.message : String(e)}`,
205
+ });
206
+ console.log(pc.yellow(` Usage scanning failed: ${e instanceof Error ? e.message : "unknown error"}`));
207
+ }
208
+ } else {
209
+ console.log(pc.dim("\nPhase 3: Skipping usage analysis"));
210
+ }
211
+
212
+ // Phase 4: Parse Storybook stories
213
+ const storiesMap = new Map<string, ParsedStoryFile>();
214
+ let storiesParsed = 0;
215
+
216
+ if (!options.skipStorybook) {
217
+ console.log(pc.dim("\nPhase 4: Parsing Storybook stories..."));
218
+
219
+ try {
220
+ const allStories = await parseAllStories(configDir);
221
+
222
+ for (const [name, stories] of Object.entries(allStories)) {
223
+ if (stories && stories.stories.length > 0) {
224
+ storiesMap.set(name, stories);
225
+ storiesParsed++;
226
+ }
227
+ }
228
+
229
+ console.log(pc.green(` Parsed stories for ${storiesParsed} components`));
230
+ } catch (e) {
231
+ warnings.push({
232
+ component: "*",
233
+ warning: `Storybook parsing failed: ${e instanceof Error ? e.message : String(e)}`,
234
+ });
235
+ console.log(pc.yellow(` Storybook parsing failed: ${e instanceof Error ? e.message : "unknown error"}`));
236
+ }
237
+ } else {
238
+ console.log(pc.dim("\nPhase 4: Skipping Storybook parsing"));
239
+ }
240
+
241
+ // Phase 5: Generate segments
242
+ console.log(pc.dim("\nPhase 5: Generating segments..."));
243
+ const segments: Record<string, CompiledSegment> = {};
244
+
245
+ for (const comp of components) {
246
+ try {
247
+ const segment = generateSegmentFromData(
248
+ comp,
249
+ configDir,
250
+ propsMap.get(comp.name),
251
+ usageAnalysis?.components[comp.name],
252
+ allRelations.get(comp.name),
253
+ storiesMap.get(comp.name)
254
+ );
255
+
256
+ segments[comp.name] = segment;
257
+ } catch (e) {
258
+ errors.push({
259
+ component: comp.name,
260
+ error: e instanceof Error ? e.message : String(e),
261
+ });
262
+ }
263
+ }
264
+
265
+ // Write output
266
+ const outputPath = resolve(configDir, outputFile);
267
+ await mkdir(dirname(outputPath), { recursive: true });
268
+
269
+ const output: CompiledSegmentsFile = {
270
+ version: "1.0.0",
271
+ generatedAt: new Date().toISOString(),
272
+ segments,
273
+ };
274
+
275
+ await writeFile(outputPath, JSON.stringify(output, null, 2));
276
+
277
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
278
+
279
+ // Summary
280
+ console.log(pc.dim("\n────────────────────────────────────────"));
281
+ console.log(pc.green(`\n✓ Generated segments.json in ${elapsed}s`));
282
+ console.log(pc.dim(` Output: ${relative(process.cwd(), outputPath)}`));
283
+ console.log(pc.dim(` Components: ${Object.keys(segments).length}`));
284
+ console.log(pc.dim(` Props extracted: ${propsExtracted}`));
285
+ console.log(pc.dim(` Usages found: ${usagesFound}`));
286
+ console.log(pc.dim(` Relations inferred: ${allRelations.size}`));
287
+ console.log(pc.dim(` Stories parsed: ${storiesParsed}`));
288
+
289
+ if (warnings.length > 0) {
290
+ console.log(pc.yellow(`\n ${warnings.length} warning(s)`));
291
+ if (options.verbose) {
292
+ for (const w of warnings) {
293
+ console.log(pc.dim(` ${w.component}: ${w.warning}`));
294
+ }
295
+ }
296
+ }
297
+
298
+ if (errors.length > 0) {
299
+ console.log(pc.red(`\n ${errors.length} error(s)`));
300
+ for (const e of errors) {
301
+ console.log(pc.dim(` ${e.component}: ${e.error}`));
302
+ }
303
+ }
304
+
305
+ console.log();
306
+
307
+ return {
308
+ success: errors.length === 0,
309
+ outputPath,
310
+ componentCount: Object.keys(segments).length,
311
+ propsExtracted,
312
+ usagesFound,
313
+ relationsInferred: allRelations.size,
314
+ storiesParsed,
315
+ errors,
316
+ warnings,
317
+ };
318
+ }
319
+
320
+ /**
321
+ * Generate a CompiledSegment from extracted data
322
+ */
323
+ function generateSegmentFromData(
324
+ comp: DiscoveredComponent,
325
+ configDir: string,
326
+ props: Record<string, PropDefinition> | undefined,
327
+ usageAnalysis: UsageAnalysis["components"][string] | undefined,
328
+ relations: ComponentRelation[] | undefined,
329
+ storyFile: ParsedStoryFile | undefined
330
+ ): CompiledSegment {
331
+ // Generate context for AI suggestions
332
+ const context = generateComponentContext(
333
+ comp.name,
334
+ usageAnalysis,
335
+ undefined, // No extracted docs yet
336
+ storyFile,
337
+ undefined, // No props extraction result (we have the converted props already)
338
+ relations
339
+ );
340
+
341
+ // Generate enhancement suggestions from context
342
+ const suggestions = generateEnhancementSuggestions(context);
343
+
344
+ // Filter boilerplate from suggestions
345
+ const when = filterBoilerplate(suggestions.suggestions.when);
346
+ const whenNot = filterBoilerplate(suggestions.suggestions.whenNot);
347
+
348
+ // Infer category from path
349
+ const category = inferCategory(comp.relativePath);
350
+
351
+ // Infer status from path
352
+ const status = inferStatus(comp.relativePath);
353
+
354
+ // Build variants from stories
355
+ const variants: CompiledSegment["variants"] = [];
356
+ if (storyFile?.stories) {
357
+ for (const story of storyFile.stories) {
358
+ variants.push({
359
+ name: story.name,
360
+ description: story.description || `${story.displayName} variant`,
361
+ code: story.code,
362
+ });
363
+ }
364
+ }
365
+
366
+ // Build relations
367
+ const compiledRelations: CompiledSegment["relations"] = [];
368
+ if (relations && relations.length > 0) {
369
+ for (const rel of relations.slice(0, 10)) {
370
+ compiledRelations.push({
371
+ component: rel.component,
372
+ relationship: rel.relationship,
373
+ note: `Appears together ${rel.frequency} times`,
374
+ });
375
+ }
376
+ }
377
+
378
+ // Generate description
379
+ const description = generateDescription(comp.name, props, usageAnalysis);
380
+
381
+ return {
382
+ filePath: comp.relativePath,
383
+ meta: {
384
+ name: comp.name,
385
+ description,
386
+ category,
387
+ status,
388
+ },
389
+ usage: {
390
+ when: when.length > 0 ? when : [`Use ${comp.name} for its intended purpose`],
391
+ whenNot: whenNot,
392
+ },
393
+ props: props || {},
394
+ relations: compiledRelations.length > 0 ? compiledRelations : undefined,
395
+ variants,
396
+ _generated: {
397
+ source: "ai",
398
+ sourceFile: comp.relativePath,
399
+ confidence: calculateConfidence(props, usageAnalysis, storyFile),
400
+ timestamp: new Date().toISOString(),
401
+ },
402
+ };
403
+ }
404
+
405
+ /**
406
+ * Infer category from file path
407
+ */
408
+ function inferCategory(relativePath: string): string {
409
+ const parts = relativePath.toLowerCase().split("/");
410
+
411
+ // Common category patterns
412
+ const categoryPatterns: Record<string, string[]> = {
413
+ "Actions": ["button", "action", "cta"],
414
+ "Forms": ["form", "input", "select", "checkbox", "radio", "textarea", "field"],
415
+ "Layout": ["layout", "container", "grid", "flex", "stack", "box", "divider", "spacer"],
416
+ "Navigation": ["nav", "menu", "breadcrumb", "tab", "link", "pagination"],
417
+ "Feedback": ["alert", "toast", "notification", "message", "badge", "indicator", "progress", "spinner", "loading"],
418
+ "Data Display": ["table", "list", "card", "avatar", "stat", "timeline", "tree"],
419
+ "Overlays": ["modal", "dialog", "drawer", "popover", "tooltip", "dropdown"],
420
+ "Typography": ["text", "heading", "title", "label", "paragraph"],
421
+ "Media": ["image", "video", "icon", "avatar"],
422
+ };
423
+
424
+ for (const [category, patterns] of Object.entries(categoryPatterns)) {
425
+ for (const pattern of patterns) {
426
+ if (parts.some((part) => part.includes(pattern))) {
427
+ return category;
428
+ }
429
+ }
430
+ }
431
+
432
+ // Check path structure for category
433
+ const componentIdx = parts.findIndex((p) => p === "components");
434
+ if (componentIdx !== -1 && parts.length > componentIdx + 1) {
435
+ const categoryPart = parts[componentIdx + 1];
436
+ return categoryPart.charAt(0).toUpperCase() + categoryPart.slice(1);
437
+ }
438
+
439
+ return "Components";
440
+ }
441
+
442
+ /**
443
+ * Infer status from file path
444
+ */
445
+ function inferStatus(
446
+ relativePath: string
447
+ ): "stable" | "beta" | "experimental" | "deprecated" {
448
+ const lowerPath = relativePath.toLowerCase();
449
+
450
+ if (lowerPath.includes("/experimental/") || lowerPath.includes("/labs/")) {
451
+ return "experimental";
452
+ }
453
+ if (lowerPath.includes("/beta/")) {
454
+ return "beta";
455
+ }
456
+ if (lowerPath.includes("/deprecated/") || lowerPath.includes("/legacy/")) {
457
+ return "deprecated";
458
+ }
459
+
460
+ return "stable";
461
+ }
462
+
463
+ /**
464
+ * Generate component description
465
+ */
466
+ function generateDescription(
467
+ name: string,
468
+ props: Record<string, PropDefinition> | undefined,
469
+ usageAnalysis: UsageAnalysis["components"][string] | undefined
470
+ ): string {
471
+ // Convert name to readable form
472
+ const words = name
473
+ .replace(/([A-Z])/g, " $1")
474
+ .trim()
475
+ .toLowerCase();
476
+
477
+ // Check props for hints
478
+ const hasOnClick = props && ("onClick" in props || "onPress" in props);
479
+ const hasValue = props && ("value" in props || "defaultValue" in props);
480
+ const hasChildren = props && "children" in props;
481
+ const hasHref = props && "href" in props;
482
+
483
+ // Check usage contexts
484
+ const topContext = usageAnalysis?.contexts[0]?.type;
485
+
486
+ if (hasHref) {
487
+ return `Navigational ${words} for linking to other pages or resources`;
488
+ }
489
+
490
+ if (hasOnClick && !hasValue) {
491
+ return `Interactive ${words} for triggering actions`;
492
+ }
493
+
494
+ if (hasValue) {
495
+ return `Form ${words} for user input`;
496
+ }
497
+
498
+ if (topContext && topContext !== "unknown") {
499
+ return `${words.charAt(0).toUpperCase() + words.slice(1)} commonly used in ${topContext} contexts`;
500
+ }
501
+
502
+ if (hasChildren) {
503
+ return `Container ${words} for composing UI`;
504
+ }
505
+
506
+ return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;
507
+ }
508
+
509
+ /**
510
+ * Calculate confidence score based on available data
511
+ */
512
+ function calculateConfidence(
513
+ props: Record<string, PropDefinition> | undefined,
514
+ usageAnalysis: UsageAnalysis["components"][string] | undefined,
515
+ storyFile: ParsedStoryFile | undefined
516
+ ): number {
517
+ let confidence = 0;
518
+
519
+ // Props give +30 confidence
520
+ if (props && Object.keys(props).length > 0) {
521
+ confidence += 30;
522
+ }
523
+
524
+ // Usage data gives up to +40 confidence
525
+ if (usageAnalysis) {
526
+ if (usageAnalysis.totalUsages > 10) confidence += 40;
527
+ else if (usageAnalysis.totalUsages > 5) confidence += 30;
528
+ else if (usageAnalysis.totalUsages > 0) confidence += 20;
529
+ }
530
+
531
+ // Stories give +30 confidence
532
+ if (storyFile && storyFile.stories.length > 0) {
533
+ confidence += 30;
534
+ }
535
+
536
+ return Math.min(confidence, 100);
537
+ }