@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,267 @@
1
+ /**
2
+ * fragments link storybook - Bootstrap segments from Storybook stories
3
+ */
4
+
5
+ import { writeFile, mkdir } from 'node:fs/promises';
6
+ import { join, dirname, relative } from 'node:path';
7
+ import pc from 'picocolors';
8
+ import { BRAND } from '../../core/index.js';
9
+ import {
10
+ detectStorybookConfig,
11
+ discoverStoryFiles as discoverStorybookFiles,
12
+ parseStoryFile,
13
+ convertToSegment,
14
+ } from '../../migrate/index.js';
15
+
16
+ /**
17
+ * Options for link storybook command
18
+ */
19
+ export interface LinkStorybookOptions {
20
+ /** Path to .storybook/main.* config */
21
+ config?: string;
22
+ /** Output directory for segment files */
23
+ out?: string;
24
+ /** Skip confirmation prompts */
25
+ yes?: boolean;
26
+ /** Preview what would be generated without writing files */
27
+ dryRun?: boolean;
28
+ /** Only process stories matching glob */
29
+ include?: string;
30
+ /** Skip stories matching glob */
31
+ exclude?: string;
32
+ /** Show verbose output */
33
+ verbose?: boolean;
34
+ }
35
+
36
+ /**
37
+ * Result of link storybook command
38
+ */
39
+ export interface LinkStorybookResult {
40
+ success: boolean;
41
+ generated: number;
42
+ }
43
+
44
+ /**
45
+ * Preview item for stories
46
+ */
47
+ interface PreviewItem {
48
+ componentName: string;
49
+ sourceFile: string;
50
+ outputFile: string;
51
+ variantCount: number;
52
+ propCount: number;
53
+ confidence: number;
54
+ warnings: string[];
55
+ }
56
+
57
+ /**
58
+ * Run the link storybook command
59
+ */
60
+ export async function linkStorybook(
61
+ options: LinkStorybookOptions = {}
62
+ ): Promise<LinkStorybookResult> {
63
+ const { out, yes = false, dryRun = false, verbose = false } = options;
64
+
65
+ console.log(pc.cyan(`\n${BRAND.name} Storybook Link\n`));
66
+
67
+ // Detect Storybook config
68
+ const projectRoot = process.cwd();
69
+ console.log(pc.dim('Detecting Storybook configuration...'));
70
+
71
+ const sbConfig = await detectStorybookConfig(projectRoot);
72
+ if (!sbConfig) {
73
+ console.log(pc.yellow('\nNo Storybook configuration found.'));
74
+ console.log(pc.dim('Looking for: .storybook/main.ts, .storybook/main.js, etc.'));
75
+ console.log(pc.dim('\nUse --config to specify a custom path.'));
76
+ process.exit(1);
77
+ }
78
+
79
+ console.log(pc.green(`āœ“ Found: ${sbConfig.configPath}`));
80
+ if (sbConfig.framework) {
81
+ console.log(pc.dim(` Framework: ${sbConfig.framework}`));
82
+ }
83
+ if (sbConfig.errors?.length) {
84
+ for (const err of sbConfig.errors) {
85
+ console.log(pc.yellow(` Warning: ${err}`));
86
+ }
87
+ }
88
+
89
+ // Discover story files
90
+ console.log(pc.dim('\nDiscovering story files...'));
91
+ const storyFiles = await discoverStorybookFiles(projectRoot, sbConfig.storyPatterns);
92
+
93
+ if (storyFiles.length === 0) {
94
+ console.log(pc.yellow('\nNo story files found.'));
95
+ console.log(pc.dim(`Patterns: ${sbConfig.storyPatterns.join(', ')}`));
96
+ process.exit(1);
97
+ }
98
+
99
+ console.log(pc.green(`āœ“ Found ${storyFiles.length} story file(s)\n`));
100
+
101
+ // Parse and convert each story
102
+ const previews: PreviewItem[] = [];
103
+ let parseErrors = 0;
104
+ const total = storyFiles.length;
105
+
106
+ console.log(pc.dim(`Analyzing ${total} stories...\n`));
107
+
108
+ for (let i = 0; i < storyFiles.length; i++) {
109
+ const storyFile = storyFiles[i];
110
+ const relativePath = relative(projectRoot, storyFile);
111
+
112
+ // Show progress
113
+ process.stdout.write(`\r ${pc.dim(`[${i + 1}/${total}]`)} ${relativePath.slice(0, 60).padEnd(60)}`);
114
+
115
+ try {
116
+ const parsed = await parseStoryFile(storyFile);
117
+ const result = convertToSegment(parsed);
118
+
119
+ // Determine output path
120
+ const outputFile = out
121
+ ? join(out, relativePath.replace(/\.stories\.(tsx?|jsx?)$/, BRAND.fileExtension))
122
+ : result.outputFile;
123
+
124
+ previews.push({
125
+ componentName: result.componentName,
126
+ sourceFile: relativePath,
127
+ outputFile: relative(projectRoot, outputFile),
128
+ variantCount: result.variantCount,
129
+ propCount: result.propCount,
130
+ confidence: result.confidence,
131
+ warnings: result.warnings,
132
+ });
133
+ } catch (error) {
134
+ parseErrors++;
135
+ if (verbose) {
136
+ process.stdout.write('\n');
137
+ console.log(pc.red(`āœ— ${relativePath}: ${error instanceof Error ? error.message : 'Parse error'}`));
138
+ }
139
+ }
140
+ }
141
+
142
+ // Clear progress line
143
+ process.stdout.write('\r' + ' '.repeat(80) + '\r');
144
+
145
+ // Display preview table
146
+ console.log(pc.bold('Preview:\n'));
147
+ console.log(pc.dim('ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”'));
148
+ console.log(pc.dim('│') + ' Component'.padEnd(20) + pc.dim('│') + ' Stories'.padEnd(10) + pc.dim('│') + ' Props'.padEnd(8) + pc.dim('│') + ' Confidence'.padEnd(13) + pc.dim('│'));
149
+ console.log(pc.dim('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤'));
150
+
151
+ for (const item of previews) {
152
+ const confColor = item.confidence >= 0.8 ? pc.green : item.confidence >= 0.5 ? pc.yellow : pc.red;
153
+ const confLabel = item.confidence >= 0.8 ? 'high' : item.confidence >= 0.5 ? 'medium' : 'low';
154
+ console.log(
155
+ pc.dim('│') +
156
+ ` ${item.componentName}`.padEnd(20).slice(0, 20) +
157
+ pc.dim('│') +
158
+ ` ${item.variantCount}`.padEnd(10) +
159
+ pc.dim('│') +
160
+ ` ${item.propCount}`.padEnd(8) +
161
+ pc.dim('│') +
162
+ ` ${confColor(confLabel)}`.padEnd(13 + (confColor === pc.green ? 10 : confColor === pc.yellow ? 11 : 9)) +
163
+ pc.dim('│')
164
+ );
165
+ }
166
+
167
+ console.log(pc.dim('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜'));
168
+
169
+ if (parseErrors > 0) {
170
+ console.log(pc.yellow(`\n${parseErrors} file(s) could not be parsed (use --verbose for details)`));
171
+ }
172
+
173
+ if (previews.length === 0) {
174
+ console.log(pc.yellow('\nNo stories could be parsed successfully.'));
175
+ return { success: true, generated: 0 };
176
+ }
177
+
178
+ // Dry run stops here
179
+ if (dryRun) {
180
+ console.log(pc.dim(`\n${previews.length} segment file(s) would be created`));
181
+ console.log(pc.yellow('\n[Dry run - no files were written]'));
182
+ return { success: true, generated: 0 };
183
+ }
184
+
185
+ // Interactive selection or auto mode (--yes)
186
+ let selectedPreviews = previews;
187
+
188
+ if (yes) {
189
+ console.log(pc.dim(`\n${previews.length} segment file(s) will be created`));
190
+ } else {
191
+ const { checkbox } = await import('@inquirer/prompts');
192
+
193
+ console.log(pc.dim('\nUse ↑/↓ to navigate, Space to toggle, Enter to confirm\n'));
194
+
195
+ const choices = previews.map((item) => {
196
+ const confColor = item.confidence >= 0.8 ? pc.green : item.confidence >= 0.5 ? pc.yellow : pc.red;
197
+ const confLabel = item.confidence >= 0.8 ? 'high' : item.confidence >= 0.5 ? 'medium' : 'low';
198
+ return {
199
+ name: `${pc.bold(item.componentName)} ${pc.dim(`(${item.variantCount} stories, ${confLabel} confidence)`)}`,
200
+ value: item,
201
+ checked: true,
202
+ };
203
+ });
204
+
205
+ try {
206
+ selectedPreviews = await checkbox({
207
+ message: 'Select stories to convert:',
208
+ choices,
209
+ pageSize: 15,
210
+ });
211
+ } catch {
212
+ // User cancelled (Ctrl+C)
213
+ console.log(pc.dim('\nNo changes made.'));
214
+ return { success: true, generated: 0 };
215
+ }
216
+
217
+ if (selectedPreviews.length === 0) {
218
+ console.log(pc.dim('\nNo stories selected. No changes made.'));
219
+ return { success: true, generated: 0 };
220
+ }
221
+ }
222
+
223
+ // Generate files
224
+ const genTotal = selectedPreviews.length;
225
+ console.log(pc.dim(`\nGenerating ${genTotal} segment file(s)...\n`));
226
+
227
+ let generated = 0;
228
+ let genErrors = 0;
229
+ for (let i = 0; i < selectedPreviews.length; i++) {
230
+ const preview = selectedPreviews[i];
231
+ const storyFile = join(projectRoot, preview.sourceFile);
232
+ try {
233
+ const parsed = await parseStoryFile(storyFile);
234
+ const result = convertToSegment(parsed);
235
+
236
+ // Determine output path
237
+ const outputFile = out
238
+ ? join(projectRoot, out, preview.sourceFile.replace(/\.stories\.(tsx?|jsx?)$/, BRAND.fileExtension))
239
+ : result.outputFile;
240
+
241
+ // Create directory if needed
242
+ await mkdir(dirname(outputFile), { recursive: true });
243
+
244
+ // Write file
245
+ await writeFile(outputFile, result.code);
246
+ generated++;
247
+
248
+ console.log(`${pc.dim(`[${i + 1}/${genTotal}]`)} ${pc.green('āœ“')} ${result.componentName}`);
249
+ } catch (error) {
250
+ genErrors++;
251
+ console.log(`${pc.dim(`[${i + 1}/${genTotal}]`)} ${pc.red('āœ—')} ${preview.componentName}`);
252
+ }
253
+ }
254
+
255
+ console.log(pc.green(`\nāœ“ Generated ${generated} segment file(s)\n`));
256
+
257
+ // Next steps
258
+ console.log(pc.dim('───────────────────────────────────────'));
259
+ console.log(pc.bold('\nNext steps:'));
260
+ console.log(` 1. Review generated ${BRAND.fileExtension} files`);
261
+ console.log(` 2. Fill in usage.when and usage.whenNot fields`);
262
+ console.log(` 3. Run ${pc.cyan(`${BRAND.cliCommand} build`)} to compile`);
263
+ console.log(` 4. Run ${pc.cyan(`${BRAND.cliCommand} dev`)} to view your design system`);
264
+ console.log();
265
+
266
+ return { success: true, generated };
267
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * fragments list - List all discovered fragment files
3
+ */
4
+
5
+ import pc from 'picocolors';
6
+ import { BRAND } from '../core/index.js';
7
+ import { loadConfig, discoverSegmentFiles } from '../core/node.js';
8
+
9
+ /**
10
+ * Options for list command
11
+ */
12
+ export interface ListOptions {
13
+ /** Path to config file */
14
+ config?: string;
15
+ }
16
+
17
+ /**
18
+ * Result of list command
19
+ */
20
+ export interface ListResult {
21
+ success: boolean;
22
+ files: Array<{
23
+ absolutePath: string;
24
+ relativePath: string;
25
+ }>;
26
+ }
27
+
28
+ /**
29
+ * Run the list command
30
+ */
31
+ export async function list(options: ListOptions = {}): Promise<ListResult> {
32
+ const { config, configDir } = await loadConfig(options.config);
33
+ const files = await discoverSegmentFiles(config, configDir);
34
+
35
+ console.log(pc.cyan(`\n${BRAND.name} - Discovered Fragments\n`));
36
+
37
+ if (files.length === 0) {
38
+ console.log(pc.yellow('No fragment files found.\n'));
39
+ console.log(pc.dim(`Looking for: ${config.include.join(', ')}`));
40
+ return { success: true, files: [] };
41
+ }
42
+
43
+ for (const file of files) {
44
+ console.log(` ${pc.dim('•')} ${file.relativePath}`);
45
+ }
46
+ console.log(pc.dim(`\n ${files.length} fragment(s) found\n`));
47
+
48
+ return { success: true, files };
49
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * fragments metrics - View compliance trends over time
3
+ */
4
+
5
+ import pc from 'picocolors';
6
+ import { BRAND } from '../core/index.js';
7
+ import { loadConfig } from '../core/node.js';
8
+ import { createMetricsStore } from '../service/index.js';
9
+
10
+ /**
11
+ * Options for metrics command
12
+ */
13
+ export interface MetricsOptions {
14
+ /** Path to config file */
15
+ config?: string;
16
+ /** Number of days to look back */
17
+ days?: number;
18
+ /** Output JSON format */
19
+ json?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Trend data point
24
+ */
25
+ export interface TrendDataPoint {
26
+ date: string;
27
+ compliance: number;
28
+ violations: number;
29
+ }
30
+
31
+ /**
32
+ * Trend result
33
+ */
34
+ export interface TrendResult {
35
+ dataPoints: TrendDataPoint[];
36
+ averageCompliance: number;
37
+ trend: 'improving' | 'declining' | 'stable';
38
+ }
39
+
40
+ /**
41
+ * Result of metrics command
42
+ */
43
+ export interface MetricsResult {
44
+ success: boolean;
45
+ trend?: TrendResult;
46
+ }
47
+
48
+ /**
49
+ * Run the metrics command
50
+ */
51
+ export async function metrics(
52
+ component: string | undefined,
53
+ options: MetricsOptions = {}
54
+ ): Promise<MetricsResult> {
55
+ const { config: configPath, days = 30, json = false } = options;
56
+
57
+ const { configDir } = await loadConfig(configPath);
58
+ const store = createMetricsStore(configDir);
59
+
60
+ console.log(pc.cyan(`\n${BRAND.name} Compliance Metrics\n`));
61
+
62
+ const trend = await store.getTrend(component || 'all', {
63
+ days,
64
+ groupBy: days > 14 ? 'week' : 'day',
65
+ });
66
+
67
+ if (json) {
68
+ console.log(JSON.stringify(trend, null, 2));
69
+ return { success: true, trend };
70
+ }
71
+
72
+ // Header
73
+ const title = component ? `Component: ${component}` : 'System-wide';
74
+ console.log(pc.bold(title));
75
+ console.log(pc.dim(`Last ${days} days\n`));
76
+
77
+ // Check if we have data
78
+ if (trend.dataPoints.length === 0) {
79
+ console.log(pc.yellow('No metrics data found.\n'));
80
+ console.log(pc.dim('Metrics are recorded automatically when running verification commands.'));
81
+ console.log(pc.dim(`Try running: ${pc.cyan(`${BRAND.cliCommand} verify --ci`)}\n`));
82
+ return { success: true, trend };
83
+ }
84
+
85
+ // Sparkline
86
+ const sparkline = store.generateSparkline(trend.dataPoints);
87
+ console.log(pc.bold('Trend: ') + sparkline);
88
+ console.log();
89
+
90
+ // Stats
91
+ const trendColor = trend.trend === 'improving' ? pc.green :
92
+ trend.trend === 'declining' ? pc.red : pc.dim;
93
+ const trendIcon = trend.trend === 'improving' ? '↑' :
94
+ trend.trend === 'declining' ? '↓' : '→';
95
+
96
+ console.log(` Average compliance: ${pc.bold(`${trend.averageCompliance}%`)}`);
97
+ console.log(` Direction: ${trendColor(`${trendIcon} ${trend.trend}`)}`);
98
+ console.log(` Data points: ${trend.dataPoints.length}`);
99
+ console.log();
100
+
101
+ // Recent data points
102
+ const recent = trend.dataPoints.slice(-5);
103
+ if (recent.length > 0) {
104
+ console.log(pc.dim('Recent data:'));
105
+ for (const point of recent) {
106
+ const complianceColor = point.compliance >= 90 ? pc.green :
107
+ point.compliance >= 70 ? pc.yellow : pc.red;
108
+ console.log(` ${pc.dim(point.date)} ${complianceColor(`${point.compliance}%`)} ${pc.dim(`(${point.violations} violations)`)}`);
109
+ }
110
+ console.log();
111
+ }
112
+
113
+ return { success: true, trend };
114
+ }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * fragments reset - Reset to initial state (delete all generated files)
3
+ */
4
+
5
+ import { stat, rm, unlink, readdir } from 'node:fs/promises';
6
+ import { join, relative } from 'node:path';
7
+ import pc from 'picocolors';
8
+ import fg from 'fast-glob';
9
+ import { BRAND } from '../core/index.js';
10
+ import { loadConfig } from '../core/node.js';
11
+
12
+ /**
13
+ * Options for reset command
14
+ */
15
+ export interface ResetOptions {
16
+ /** Skip confirmation prompt */
17
+ yes?: boolean;
18
+ /** Show what would be deleted without deleting */
19
+ dryRun?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Result of reset command
24
+ */
25
+ export interface ResetResult {
26
+ success: boolean;
27
+ deletedFiles: number;
28
+ deletedDirs: number;
29
+ }
30
+
31
+ /**
32
+ * Run the reset command
33
+ */
34
+ export async function reset(options: ResetOptions = {}): Promise<ResetResult> {
35
+ const { yes = false, dryRun = false } = options;
36
+
37
+ console.log(pc.cyan(`\n${BRAND.name} Reset\n`));
38
+
39
+ const projectRoot = process.cwd();
40
+
41
+ // Items to delete
42
+ const filesToDelete: string[] = [];
43
+ const dirsToDelete: string[] = [];
44
+
45
+ // Check data directory (.segments/)
46
+ const dataDir = join(projectRoot, BRAND.dataDir);
47
+ try {
48
+ const dataDirStat = await stat(dataDir);
49
+ if (dataDirStat.isDirectory()) {
50
+ dirsToDelete.push(dataDir);
51
+ }
52
+ } catch {
53
+ // Directory doesn't exist
54
+ }
55
+
56
+ // Check for segments.json (default output file)
57
+ const defaultOutFile = join(projectRoot, 'segments.json');
58
+ try {
59
+ const fileStat = await stat(defaultOutFile);
60
+ if (fileStat.isFile()) {
61
+ filesToDelete.push(defaultOutFile);
62
+ }
63
+ } catch {
64
+ // File doesn't exist
65
+ }
66
+
67
+ // Try to load config for custom outFile and include patterns
68
+ let segmentPatterns = [`**/*${BRAND.fileExtension}`];
69
+ try {
70
+ const { config } = await loadConfig();
71
+ if (config.outFile && config.outFile !== 'segments.json') {
72
+ const customOutFile = join(projectRoot, config.outFile);
73
+ try {
74
+ const fileStat = await stat(customOutFile);
75
+ if (fileStat.isFile()) {
76
+ filesToDelete.push(customOutFile);
77
+ }
78
+ } catch {
79
+ // Custom outfile doesn't exist
80
+ }
81
+ }
82
+ // Use config include patterns if available
83
+ if (config.include && config.include.length > 0) {
84
+ segmentPatterns = config.include;
85
+ }
86
+ } catch {
87
+ // No config file, use defaults
88
+ }
89
+
90
+ // Find all segment files (*.segment.tsx)
91
+ console.log(pc.dim('Scanning for generated files...\n'));
92
+ for (const pattern of segmentPatterns) {
93
+ const matches = await fg(pattern, {
94
+ cwd: projectRoot,
95
+ ignore: ['**/node_modules/**'],
96
+ absolute: true,
97
+ });
98
+ for (const match of matches) {
99
+ if (!filesToDelete.includes(match)) {
100
+ filesToDelete.push(match);
101
+ }
102
+ }
103
+ }
104
+
105
+ // Find all Documentation.mdx files (generated by link storybook)
106
+ const mdxFiles = await fg('**/Documentation.mdx', {
107
+ cwd: projectRoot,
108
+ ignore: ['**/node_modules/**'],
109
+ absolute: true,
110
+ });
111
+ for (const mdxFile of mdxFiles) {
112
+ if (!filesToDelete.includes(mdxFile)) {
113
+ filesToDelete.push(mdxFile);
114
+ }
115
+ }
116
+
117
+ // Check if nothing to delete
118
+ if (filesToDelete.length === 0 && dirsToDelete.length === 0) {
119
+ console.log(pc.yellow('Nothing to reset. No generated files found.\n'));
120
+ return { success: true, deletedFiles: 0, deletedDirs: 0 };
121
+ }
122
+
123
+ // Show summary
124
+ console.log(pc.dim('The following will be deleted:\n'));
125
+
126
+ // Show directories
127
+ for (const dir of dirsToDelete) {
128
+ const relativePath = relative(projectRoot, dir);
129
+ console.log(` šŸ“ ${relativePath}/`);
130
+ }
131
+
132
+ // Group files by type for cleaner output
133
+ const segmentFiles = filesToDelete.filter((f) => f.endsWith(BRAND.fileExtension));
134
+ const mdxFilesFound = filesToDelete.filter((f) => f.endsWith('.mdx'));
135
+ const otherFiles = filesToDelete.filter(
136
+ (f) => !f.endsWith(BRAND.fileExtension) && !f.endsWith('.mdx')
137
+ );
138
+
139
+ if (segmentFiles.length > 0) {
140
+ console.log(` šŸ“„ ${segmentFiles.length} segment file(s) (*${BRAND.fileExtension})`);
141
+ if (segmentFiles.length <= 5) {
142
+ for (const f of segmentFiles) {
143
+ console.log(pc.dim(` ${relative(projectRoot, f)}`));
144
+ }
145
+ } else {
146
+ for (const f of segmentFiles.slice(0, 3)) {
147
+ console.log(pc.dim(` ${relative(projectRoot, f)}`));
148
+ }
149
+ console.log(pc.dim(` ... and ${segmentFiles.length - 3} more`));
150
+ }
151
+ }
152
+
153
+ if (mdxFilesFound.length > 0) {
154
+ console.log(` šŸ“„ ${mdxFilesFound.length} documentation file(s) (*.mdx)`);
155
+ if (mdxFilesFound.length <= 5) {
156
+ for (const f of mdxFilesFound) {
157
+ console.log(pc.dim(` ${relative(projectRoot, f)}`));
158
+ }
159
+ } else {
160
+ for (const f of mdxFilesFound.slice(0, 3)) {
161
+ console.log(pc.dim(` ${relative(projectRoot, f)}`));
162
+ }
163
+ console.log(pc.dim(` ... and ${mdxFilesFound.length - 3} more`));
164
+ }
165
+ }
166
+
167
+ for (const f of otherFiles) {
168
+ console.log(` šŸ“„ ${relative(projectRoot, f)}`);
169
+ }
170
+
171
+ const totalCount = filesToDelete.length + dirsToDelete.length;
172
+ console.log(pc.dim(`\n Total: ${totalCount} item(s)\n`));
173
+
174
+ // Dry run exits here
175
+ if (dryRun) {
176
+ console.log(pc.yellow('[Dry run - no files were deleted]\n'));
177
+ return { success: true, deletedFiles: 0, deletedDirs: 0 };
178
+ }
179
+
180
+ // Confirm unless --yes
181
+ let proceed = yes;
182
+ if (!proceed) {
183
+ const { confirm } = await import('@inquirer/prompts');
184
+ try {
185
+ proceed = await confirm({
186
+ message: `Delete ${totalCount} item(s)?`,
187
+ default: false,
188
+ });
189
+ } catch {
190
+ // User cancelled (Ctrl+C)
191
+ proceed = false;
192
+ }
193
+ }
194
+
195
+ if (!proceed) {
196
+ console.log(pc.dim('\nNo changes made.\n'));
197
+ return { success: true, deletedFiles: 0, deletedDirs: 0 };
198
+ }
199
+
200
+ // Delete directories first
201
+ console.log();
202
+ let deletedDirs = 0;
203
+ for (const dir of dirsToDelete) {
204
+ try {
205
+ const relativePath = relative(projectRoot, dir);
206
+ await rm(dir, { recursive: true, force: true });
207
+ console.log(` ${pc.green('āœ“')} Deleted ${relativePath}/`);
208
+ deletedDirs++;
209
+ } catch {
210
+ console.log(` ${pc.red('āœ—')} Failed: ${relative(projectRoot, dir)}`);
211
+ }
212
+ }
213
+
214
+ // Delete files
215
+ let deletedCount = 0;
216
+ let failedCount = 0;
217
+ for (const file of filesToDelete) {
218
+ try {
219
+ await unlink(file);
220
+ deletedCount++;
221
+ } catch {
222
+ failedCount++;
223
+ }
224
+ }
225
+
226
+ if (deletedCount > 0) {
227
+ console.log(` ${pc.green('āœ“')} Deleted ${deletedCount} file(s)`);
228
+ }
229
+ if (failedCount > 0) {
230
+ console.log(` ${pc.red('āœ—')} Failed to delete ${failedCount} file(s)`);
231
+ }
232
+
233
+ console.log(pc.green(`\nāœ“ Reset complete\n`));
234
+ console.log(pc.dim(`Config file retained: ${BRAND.configFile}`));
235
+ console.log(pc.dim(`Run ${pc.cyan(`${BRAND.cliCommand} init`)} to start fresh\n`));
236
+
237
+ return {
238
+ success: true,
239
+ deletedFiles: deletedCount,
240
+ deletedDirs,
241
+ };
242
+ }