@fragments-sdk/cli 0.7.0 → 0.7.1
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.
- package/dist/bin.js +245 -245
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-XHUDJNN3.js → chunk-32VIEOQY.js} +18 -18
- package/dist/chunk-32VIEOQY.js.map +1 -0
- package/dist/{chunk-CVXKXVOY.js → chunk-5ITIP3ES.js} +27 -27
- package/dist/chunk-5ITIP3ES.js.map +1 -0
- package/dist/{chunk-RVRTRESS.js → chunk-DQHWLAUV.js} +29 -29
- package/dist/chunk-DQHWLAUV.js.map +1 -0
- package/dist/{chunk-TJ34N7C7.js → chunk-GCZMFLDI.js} +30 -32
- package/dist/chunk-GCZMFLDI.js.map +1 -0
- package/dist/{chunk-6JBGU74P.js → chunk-GHYYFAQN.js} +23 -23
- package/dist/chunk-GHYYFAQN.js.map +1 -0
- package/dist/{chunk-NWQ4CJOQ.js → chunk-GKX2HPZ6.js} +40 -40
- package/dist/chunk-GKX2HPZ6.js.map +1 -0
- package/dist/{chunk-7OPWMLOE.js → chunk-U6VTHBNI.js} +110 -110
- package/dist/chunk-U6VTHBNI.js.map +1 -0
- package/dist/{core-W2HYIQW6.js → core-SFHPYR5H.js} +24 -26
- package/dist/{generate-LMTISDIJ.js → generate-54GJAWUY.js} +5 -5
- package/dist/generate-54GJAWUY.js.map +1 -0
- package/dist/index.d.ts +23 -27
- package/dist/index.js +10 -10
- package/dist/{init-7CHRKQ7P.js → init-EIM5WNMP.js} +5 -5
- package/dist/{init-7CHRKQ7P.js.map → init-EIM5WNMP.js.map} +1 -1
- package/dist/mcp-bin.js +73 -73
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-KQBKUS64.js +12 -0
- package/dist/{service-T2L7VLTE.js → service-ED2LNCTU.js} +6 -6
- package/dist/{static-viewer-GBR7YNF3.js → static-viewer-Q4F4QP5M.js} +4 -4
- package/dist/{test-OJRXNDO2.js → test-6VN2DA3S.js} +19 -19
- package/dist/test-6VN2DA3S.js.map +1 -0
- package/dist/{tokens-3BWDESVM.js → tokens-P2B7ZAM3.js} +5 -5
- package/dist/{viewer-SUFOISZM.js → viewer-GM7IQPPB.js} +199 -199
- package/dist/viewer-GM7IQPPB.js.map +1 -0
- package/package.json +2 -2
- package/src/ai.ts +5 -5
- package/src/analyze.ts +11 -11
- package/src/bin.ts +1 -1
- package/src/build.ts +33 -33
- package/src/commands/a11y.ts +6 -6
- package/src/commands/add.ts +11 -11
- package/src/commands/audit.ts +4 -4
- package/src/commands/baseline.ts +3 -3
- package/src/commands/build.ts +8 -8
- package/src/commands/compare.ts +20 -20
- package/src/commands/context.ts +16 -16
- package/src/commands/enhance.ts +36 -36
- package/src/commands/generate.ts +1 -1
- package/src/commands/graph.ts +3 -3
- package/src/commands/init.ts +1 -1
- package/src/commands/link/figma.ts +82 -82
- package/src/commands/link/index.ts +3 -3
- package/src/commands/link/storybook.ts +9 -9
- package/src/commands/list.ts +2 -2
- package/src/commands/reset.ts +15 -15
- package/src/commands/scan.ts +27 -27
- package/src/commands/storygen.ts +24 -24
- package/src/commands/validate.ts +2 -2
- package/src/commands/verify.ts +8 -8
- package/src/core/auto-props.ts +4 -4
- package/src/core/composition.test.ts +36 -36
- package/src/core/composition.ts +19 -19
- package/src/core/config.ts +6 -6
- package/src/core/{defineSegment.ts → defineFragment.ts} +16 -22
- package/src/core/discovery.ts +6 -6
- package/src/core/figma.ts +2 -2
- package/src/core/graph-extractor.test.ts +77 -77
- package/src/core/graph-extractor.ts +32 -32
- package/src/core/importAnalyzer.ts +1 -1
- package/src/core/index.ts +22 -23
- package/src/core/loader.ts +22 -22
- package/src/core/node.ts +5 -5
- package/src/core/parser.ts +31 -31
- package/src/core/previewLoader.ts +1 -1
- package/src/core/schema.ts +16 -16
- package/src/core/storyAdapter.test.ts +87 -87
- package/src/core/storyAdapter.ts +16 -16
- package/src/core/types.ts +21 -26
- package/src/diff.ts +22 -22
- package/src/index.ts +2 -2
- package/src/mcp/server.ts +80 -80
- package/src/migrate/__tests__/utils/utils.test.ts +3 -3
- package/src/migrate/bin.ts +4 -4
- package/src/migrate/converter.ts +16 -16
- package/src/migrate/index.ts +3 -3
- package/src/migrate/migrate.ts +3 -3
- package/src/migrate/parser.ts +8 -8
- package/src/migrate/report.ts +2 -2
- package/src/migrate/types.ts +4 -4
- package/src/screenshot.ts +22 -22
- package/src/service/__tests__/props-extractor.test.ts +15 -15
- package/src/service/analytics.ts +39 -39
- package/src/service/enhance/codebase-scanner.ts +1 -1
- package/src/service/enhance/index.ts +1 -1
- package/src/service/enhance/props-extractor.ts +2 -2
- package/src/service/enhance/types.ts +2 -2
- package/src/service/index.ts +2 -2
- package/src/service/metrics-store.ts +1 -1
- package/src/service/patch-generator.ts +1 -1
- package/src/setup.ts +52 -52
- package/src/shared/dev-server-client.ts +7 -7
- package/src/shared/fragment-loader.ts +59 -0
- package/src/shared/index.ts +1 -1
- package/src/shared/types.ts +4 -4
- package/src/static-viewer.ts +35 -35
- package/src/test/discovery.ts +6 -6
- package/src/test/index.ts +5 -5
- package/src/test/reporters/console.ts +1 -1
- package/src/test/reporters/junit.ts +1 -1
- package/src/test/runner.ts +7 -7
- package/src/test/types.ts +3 -3
- package/src/test/watch.ts +9 -9
- package/src/validators.ts +26 -26
- package/src/viewer/__tests__/render-utils.test.ts +28 -28
- package/src/viewer/__tests__/viewer-integration.test.ts +4 -4
- package/src/viewer/cli/health.ts +26 -26
- package/src/viewer/components/App.tsx +79 -79
- package/src/viewer/components/BottomPanel.tsx +17 -17
- package/src/viewer/components/CodePanel.tsx +3 -3
- package/src/viewer/components/CommandPalette.tsx +11 -11
- package/src/viewer/components/ComponentGraph.tsx +28 -28
- package/src/viewer/components/ComponentHeader.tsx +2 -2
- package/src/viewer/components/ContractPanel.tsx +6 -6
- package/src/viewer/components/FigmaEmbed.tsx +9 -9
- package/src/viewer/components/HealthDashboard.tsx +17 -17
- package/src/viewer/components/InteractionsPanel.tsx +2 -2
- package/src/viewer/components/IsolatedPreviewFrame.tsx +6 -6
- package/src/viewer/components/IsolatedRender.tsx +10 -10
- package/src/viewer/components/LeftSidebar.tsx +28 -28
- package/src/viewer/components/MultiViewportPreview.tsx +14 -14
- package/src/viewer/components/PreviewArea.tsx +11 -11
- package/src/viewer/components/PreviewFrameHost.tsx +51 -51
- package/src/viewer/components/RightSidebar.tsx +9 -9
- package/src/viewer/components/Sidebar.tsx +17 -17
- package/src/viewer/components/StoryRenderer.tsx +2 -2
- package/src/viewer/components/TokenStylePanel.tsx +1 -1
- package/src/viewer/components/UsageSection.tsx +2 -2
- package/src/viewer/components/VariantMatrix.tsx +11 -11
- package/src/viewer/components/VariantRenderer.tsx +3 -3
- package/src/viewer/components/VariantTabs.tsx +2 -2
- package/src/viewer/components/_future/CreatePage.tsx +6 -6
- package/src/viewer/composition-renderer.ts +11 -11
- package/src/viewer/entry.tsx +40 -40
- package/src/viewer/hooks/useFigmaIntegration.ts +1 -1
- package/src/viewer/hooks/usePreviewBridge.ts +5 -5
- package/src/viewer/hooks/useUrlState.ts +6 -6
- package/src/viewer/index.ts +2 -2
- package/src/viewer/intelligence/healthReport.ts +17 -17
- package/src/viewer/intelligence/styleDrift.ts +1 -1
- package/src/viewer/intelligence/usageScanner.ts +1 -1
- package/src/viewer/render-template.html +1 -1
- package/src/viewer/render-utils.ts +21 -21
- package/src/viewer/server.ts +18 -18
- package/src/viewer/utils/detectRelationships.ts +22 -22
- package/src/viewer/vite-plugin.ts +213 -213
- package/dist/chunk-6JBGU74P.js.map +0 -1
- package/dist/chunk-7OPWMLOE.js.map +0 -1
- package/dist/chunk-CVXKXVOY.js.map +0 -1
- package/dist/chunk-NWQ4CJOQ.js.map +0 -1
- package/dist/chunk-RVRTRESS.js.map +0 -1
- package/dist/chunk-TJ34N7C7.js.map +0 -1
- package/dist/chunk-XHUDJNN3.js.map +0 -1
- package/dist/generate-LMTISDIJ.js.map +0 -1
- package/dist/scan-WY23TJCP.js +0 -12
- package/dist/test-OJRXNDO2.js.map +0 -1
- package/dist/viewer-SUFOISZM.js.map +0 -1
- package/src/shared/segment-loader.ts +0 -59
- /package/dist/{core-W2HYIQW6.js.map → core-SFHPYR5H.js.map} +0 -0
- /package/dist/{scan-WY23TJCP.js.map → scan-KQBKUS64.js.map} +0 -0
- /package/dist/{service-T2L7VLTE.js.map → service-ED2LNCTU.js.map} +0 -0
- /package/dist/{static-viewer-GBR7YNF3.js.map → static-viewer-Q4F4QP5M.js.map} +0 -0
- /package/dist/{tokens-3BWDESVM.js.map → tokens-P2B7ZAM3.js.map} +0 -0
package/src/diff.ts
CHANGED
|
@@ -2,11 +2,11 @@ import pc from 'picocolors';
|
|
|
2
2
|
import {
|
|
3
3
|
BRAND,
|
|
4
4
|
DEFAULTS,
|
|
5
|
-
type
|
|
6
|
-
type
|
|
5
|
+
type FragmentsConfig,
|
|
6
|
+
type FragmentDefinition,
|
|
7
7
|
type Theme,
|
|
8
8
|
} from './core/index.js';
|
|
9
|
-
import {
|
|
9
|
+
import { discoverFragmentFiles, loadFragmentFile } from './core/node.js';
|
|
10
10
|
import {
|
|
11
11
|
BrowserPool,
|
|
12
12
|
CaptureEngine,
|
|
@@ -68,7 +68,7 @@ export interface DiffCommandResult {
|
|
|
68
68
|
* Execute the diff command
|
|
69
69
|
*/
|
|
70
70
|
export async function runDiffCommand(
|
|
71
|
-
config:
|
|
71
|
+
config: FragmentsConfig,
|
|
72
72
|
configDir: string,
|
|
73
73
|
options: DiffCommandOptions = {}
|
|
74
74
|
): Promise<DiffCommandResult> {
|
|
@@ -86,11 +86,11 @@ export async function runDiffCommand(
|
|
|
86
86
|
const threshold = options.threshold ?? config.screenshots?.threshold ?? DEFAULTS.diffThreshold;
|
|
87
87
|
const diffEngine = new DiffEngine(threshold);
|
|
88
88
|
|
|
89
|
-
// Discover
|
|
90
|
-
const
|
|
89
|
+
// Discover fragments
|
|
90
|
+
const fragmentFiles = await discoverFragmentFiles(config, configDir);
|
|
91
91
|
|
|
92
|
-
if (
|
|
93
|
-
console.log(pc.yellow('No
|
|
92
|
+
if (fragmentFiles.length === 0) {
|
|
93
|
+
console.log(pc.yellow('No fragment files found.'));
|
|
94
94
|
return {
|
|
95
95
|
success: true,
|
|
96
96
|
total: 0,
|
|
@@ -102,14 +102,14 @@ export async function runDiffCommand(
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
// Load all
|
|
106
|
-
const
|
|
105
|
+
// Load all fragments
|
|
106
|
+
const fragments: Array<{ path: string; fragment: FragmentDefinition }> = [];
|
|
107
107
|
|
|
108
|
-
for (const file of
|
|
108
|
+
for (const file of fragmentFiles) {
|
|
109
109
|
try {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
|
|
110
|
+
const fragment = await loadFragmentFile(file.absolutePath);
|
|
111
|
+
if (fragment) {
|
|
112
|
+
fragments.push({ path: file.relativePath, fragment });
|
|
113
113
|
}
|
|
114
114
|
} catch {
|
|
115
115
|
// Skip failed loads
|
|
@@ -117,11 +117,11 @@ export async function runDiffCommand(
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// Filter by component if specified
|
|
120
|
-
const
|
|
121
|
-
?
|
|
122
|
-
:
|
|
120
|
+
const filteredFragments = options.component
|
|
121
|
+
? fragments.filter((s) => s.fragment.meta.name === options.component)
|
|
122
|
+
: fragments;
|
|
123
123
|
|
|
124
|
-
if (options.component &&
|
|
124
|
+
if (options.component && filteredFragments.length === 0) {
|
|
125
125
|
console.log(pc.yellow(`Component "${options.component}" not found.`));
|
|
126
126
|
return {
|
|
127
127
|
success: false,
|
|
@@ -140,14 +140,14 @@ export async function runDiffCommand(
|
|
|
140
140
|
variant: string;
|
|
141
141
|
}> = [];
|
|
142
142
|
|
|
143
|
-
for (const {
|
|
143
|
+
for (const { fragment } of filteredFragments) {
|
|
144
144
|
const variants = options.variant
|
|
145
|
-
?
|
|
146
|
-
:
|
|
145
|
+
? fragment.variants.filter((v) => v.name === options.variant)
|
|
146
|
+
: fragment.variants;
|
|
147
147
|
|
|
148
148
|
for (const variant of variants) {
|
|
149
149
|
variantsToDiff.push({
|
|
150
|
-
component:
|
|
150
|
+
component: fragment.meta.name,
|
|
151
151
|
variant: variant.name,
|
|
152
152
|
});
|
|
153
153
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export {
|
|
3
3
|
loadConfig,
|
|
4
4
|
findConfigFile,
|
|
5
|
-
|
|
5
|
+
discoverFragmentFiles,
|
|
6
6
|
discoverComponentFiles,
|
|
7
7
|
extractComponentName,
|
|
8
8
|
} from "./core/node.js";
|
|
@@ -17,7 +17,7 @@ export type {
|
|
|
17
17
|
} from "./validators.js";
|
|
18
18
|
|
|
19
19
|
// Build
|
|
20
|
-
export {
|
|
20
|
+
export { buildFragments } from "./build.js";
|
|
21
21
|
export type { BuildResult } from "./build.js";
|
|
22
22
|
|
|
23
23
|
// Screenshot
|
package/src/mcp/server.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
BRAND,
|
|
10
10
|
DEFAULTS,
|
|
11
11
|
generateContext,
|
|
12
|
-
type
|
|
12
|
+
type CompiledFragmentsFile,
|
|
13
13
|
type VerifyResult,
|
|
14
14
|
type Theme,
|
|
15
15
|
} from '../core/index.js';
|
|
@@ -298,9 +298,9 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
298
298
|
);
|
|
299
299
|
|
|
300
300
|
// Lazy-loaded resources
|
|
301
|
-
let
|
|
302
|
-
// Per-
|
|
303
|
-
const
|
|
301
|
+
let fragmentsData: CompiledFragmentsFile | null = null;
|
|
302
|
+
// Per-fragment package name map (fragment name → package name from its source fragments.json)
|
|
303
|
+
const fragmentPackageMap = new Map<string, string>();
|
|
304
304
|
let defaultPackageName: string | null = null;
|
|
305
305
|
|
|
306
306
|
let browserPool: any = null;
|
|
@@ -308,9 +308,9 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
308
308
|
let diffEngine: any = null;
|
|
309
309
|
let isPoolWarming = false;
|
|
310
310
|
|
|
311
|
-
async function
|
|
312
|
-
if (
|
|
313
|
-
return
|
|
311
|
+
async function loadFragments(): Promise<CompiledFragmentsFile> {
|
|
312
|
+
if (fragmentsData) {
|
|
313
|
+
return fragmentsData;
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
const paths = findFragmentsJson(config.projectRoot);
|
|
@@ -324,51 +324,51 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
324
324
|
|
|
325
325
|
// Load and merge all found fragments files
|
|
326
326
|
const content = await readFile(paths[0], 'utf-8');
|
|
327
|
-
|
|
327
|
+
fragmentsData = JSON.parse(content) as CompiledFragmentsFile;
|
|
328
328
|
|
|
329
329
|
// Normalize legacy "recipes" key to "blocks"
|
|
330
|
-
if (!
|
|
331
|
-
|
|
330
|
+
if (!fragmentsData.blocks && fragmentsData.recipes) {
|
|
331
|
+
fragmentsData.blocks = fragmentsData.recipes;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
// Track per-
|
|
335
|
-
if (
|
|
336
|
-
for (const name of Object.keys(
|
|
337
|
-
|
|
334
|
+
// Track per-fragment package names from each source file
|
|
335
|
+
if (fragmentsData.packageName) {
|
|
336
|
+
for (const name of Object.keys(fragmentsData.fragments)) {
|
|
337
|
+
fragmentPackageMap.set(name, fragmentsData.packageName);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
for (let i = 1; i < paths.length; i++) {
|
|
342
|
-
const extra = JSON.parse(await readFile(paths[i], 'utf-8')) as
|
|
343
|
-
// Track package name for each
|
|
342
|
+
const extra = JSON.parse(await readFile(paths[i], 'utf-8')) as CompiledFragmentsFile;
|
|
343
|
+
// Track package name for each fragment from this file
|
|
344
344
|
if (extra.packageName) {
|
|
345
|
-
for (const name of Object.keys(extra.
|
|
346
|
-
|
|
345
|
+
for (const name of Object.keys(extra.fragments)) {
|
|
346
|
+
fragmentPackageMap.set(name, extra.packageName);
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
|
-
Object.assign(
|
|
349
|
+
Object.assign(fragmentsData.fragments, extra.fragments);
|
|
350
350
|
// Support both "blocks" (new) and "recipes" (legacy) keys
|
|
351
351
|
const extraBlocks = extra.blocks ?? extra.recipes;
|
|
352
352
|
if (extraBlocks) {
|
|
353
|
-
|
|
353
|
+
fragmentsData.blocks = { ...fragmentsData.blocks, ...extraBlocks };
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
return
|
|
357
|
+
return fragmentsData;
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
/**
|
|
361
361
|
* Get the package name for import statements for a specific component.
|
|
362
|
-
* Uses per-
|
|
362
|
+
* Uses per-fragment tracking when multiple fragments.json files are merged,
|
|
363
363
|
* falls back to the first fragments.json packageName, then project package.json.
|
|
364
364
|
*/
|
|
365
|
-
async function getPackageName(
|
|
366
|
-
// Ensure
|
|
367
|
-
await
|
|
365
|
+
async function getPackageName(fragmentName?: string): Promise<string> {
|
|
366
|
+
// Ensure fragments are loaded (populates fragmentPackageMap)
|
|
367
|
+
await loadFragments();
|
|
368
368
|
|
|
369
|
-
// Check per-
|
|
370
|
-
if (
|
|
371
|
-
const segPkg =
|
|
369
|
+
// Check per-fragment map first (handles multi-library merges correctly)
|
|
370
|
+
if (fragmentName) {
|
|
371
|
+
const segPkg = fragmentPackageMap.get(fragmentName);
|
|
372
372
|
if (segPkg) return segPkg;
|
|
373
373
|
}
|
|
374
374
|
|
|
@@ -377,8 +377,8 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
// Prefer packageName from first fragments.json
|
|
380
|
-
if (
|
|
381
|
-
defaultPackageName =
|
|
380
|
+
if (fragmentsData?.packageName) {
|
|
381
|
+
defaultPackageName = fragmentsData.packageName;
|
|
382
382
|
return defaultPackageName;
|
|
383
383
|
}
|
|
384
384
|
|
|
@@ -479,7 +479,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
479
479
|
// DISCOVER — list, suggest, context, alternatives
|
|
480
480
|
// ================================================================
|
|
481
481
|
case TOOL_NAMES.discover: {
|
|
482
|
-
const data = await
|
|
482
|
+
const data = await loadFragments();
|
|
483
483
|
const useCase = (args?.useCase as string) ?? undefined;
|
|
484
484
|
const componentForAlts = (args?.component as string) ?? undefined;
|
|
485
485
|
const category = (args?.category as string) ?? undefined;
|
|
@@ -492,10 +492,10 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
492
492
|
|
|
493
493
|
// --- Context mode: compact or format specified with no specific query ---
|
|
494
494
|
if (compact || (args?.format && !useCase && !componentForAlts && !category && !search && !status)) {
|
|
495
|
-
const
|
|
495
|
+
const fragments = Object.values(data.fragments);
|
|
496
496
|
const allBlocks = Object.values(data.blocks ?? data.recipes ?? {});
|
|
497
497
|
|
|
498
|
-
const { content: ctxContent, tokenEstimate } = generateContext(
|
|
498
|
+
const { content: ctxContent, tokenEstimate } = generateContext(fragments, {
|
|
499
499
|
format,
|
|
500
500
|
compact,
|
|
501
501
|
include: {
|
|
@@ -543,7 +543,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
543
543
|
}
|
|
544
544
|
});
|
|
545
545
|
|
|
546
|
-
const scored = Object.values(data.
|
|
546
|
+
const scored = Object.values(data.fragments).map((s) => {
|
|
547
547
|
let score = 0;
|
|
548
548
|
const reasons: string[] = [];
|
|
549
549
|
|
|
@@ -700,17 +700,17 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
700
700
|
|
|
701
701
|
// --- Alternatives mode: component provided (no useCase) ---
|
|
702
702
|
if (componentForAlts) {
|
|
703
|
-
const
|
|
703
|
+
const fragment = Object.values(data.fragments).find(
|
|
704
704
|
(s) => s.meta.name.toLowerCase() === componentForAlts.toLowerCase()
|
|
705
705
|
);
|
|
706
706
|
|
|
707
|
-
if (!
|
|
707
|
+
if (!fragment) {
|
|
708
708
|
throw new Error(`Component "${componentForAlts}" not found. Use fragments_discover to see available components.`);
|
|
709
709
|
}
|
|
710
710
|
|
|
711
|
-
const relations =
|
|
711
|
+
const relations = fragment.relations ?? [];
|
|
712
712
|
|
|
713
|
-
const referencedBy = Object.values(data.
|
|
713
|
+
const referencedBy = Object.values(data.fragments)
|
|
714
714
|
.filter((s) =>
|
|
715
715
|
s.relations?.some((r) => r.component.toLowerCase() === componentForAlts.toLowerCase())
|
|
716
716
|
)
|
|
@@ -724,10 +724,10 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
724
724
|
)?.note,
|
|
725
725
|
}));
|
|
726
726
|
|
|
727
|
-
const sameCategory = Object.values(data.
|
|
727
|
+
const sameCategory = Object.values(data.fragments)
|
|
728
728
|
.filter(
|
|
729
729
|
(s) =>
|
|
730
|
-
s.meta.category ===
|
|
730
|
+
s.meta.category === fragment.meta.category &&
|
|
731
731
|
s.meta.name.toLowerCase() !== componentForAlts.toLowerCase()
|
|
732
732
|
)
|
|
733
733
|
.map((s) => ({
|
|
@@ -739,8 +739,8 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
739
739
|
content: [{
|
|
740
740
|
type: 'text' as const,
|
|
741
741
|
text: JSON.stringify({
|
|
742
|
-
component:
|
|
743
|
-
category:
|
|
742
|
+
component: fragment.meta.name,
|
|
743
|
+
category: fragment.meta.category,
|
|
744
744
|
directRelations: relations,
|
|
745
745
|
referencedBy,
|
|
746
746
|
sameCategory,
|
|
@@ -753,7 +753,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
753
753
|
}
|
|
754
754
|
|
|
755
755
|
// --- Default: list mode ---
|
|
756
|
-
const
|
|
756
|
+
const fragments = Object.values(data.fragments)
|
|
757
757
|
.filter((s) => {
|
|
758
758
|
if (category && s.meta.category !== category) return false;
|
|
759
759
|
if (status && (s.meta.status ?? 'stable') !== status) return false;
|
|
@@ -778,12 +778,12 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
778
778
|
content: [{
|
|
779
779
|
type: 'text' as const,
|
|
780
780
|
text: JSON.stringify({
|
|
781
|
-
total:
|
|
782
|
-
|
|
783
|
-
categories: [...new Set(
|
|
784
|
-
hint:
|
|
781
|
+
total: fragments.length,
|
|
782
|
+
fragments,
|
|
783
|
+
categories: [...new Set(fragments.map((s) => s.category))],
|
|
784
|
+
hint: fragments.length === 0
|
|
785
785
|
? 'No components found. Try broader search terms or check available categories.'
|
|
786
|
-
:
|
|
786
|
+
: fragments.length > 5
|
|
787
787
|
? 'Use fragments_discover with useCase for recommendations, or fragments_inspect for details on a specific component.'
|
|
788
788
|
: undefined,
|
|
789
789
|
}, null, 2),
|
|
@@ -795,7 +795,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
795
795
|
// INSPECT — get + guidelines + example in one call
|
|
796
796
|
// ================================================================
|
|
797
797
|
case TOOL_NAMES.inspect: {
|
|
798
|
-
const data = await
|
|
798
|
+
const data = await loadFragments();
|
|
799
799
|
const componentName = args?.component as string;
|
|
800
800
|
const fields = args?.fields as string[] | undefined;
|
|
801
801
|
const variantName = (args?.variant as string) ?? undefined;
|
|
@@ -806,19 +806,19 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
806
806
|
throw new Error('component is required');
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
-
const
|
|
809
|
+
const fragment = Object.values(data.fragments).find(
|
|
810
810
|
(s) => s.meta.name.toLowerCase() === componentName.toLowerCase()
|
|
811
811
|
);
|
|
812
812
|
|
|
813
|
-
if (!
|
|
813
|
+
if (!fragment) {
|
|
814
814
|
throw new Error(`Component "${componentName}" not found. Use fragments_discover to see available components.`);
|
|
815
815
|
}
|
|
816
816
|
|
|
817
817
|
// Build the full inspect result combining get + guidelines + example
|
|
818
|
-
const pkgName = await getPackageName(
|
|
818
|
+
const pkgName = await getPackageName(fragment.meta.name);
|
|
819
819
|
|
|
820
820
|
// Filter variants for examples — fuzzy match: exact → prefix → contains
|
|
821
|
-
let variants =
|
|
821
|
+
let variants = fragment.variants;
|
|
822
822
|
if (variantName) {
|
|
823
823
|
const query = variantName.toLowerCase();
|
|
824
824
|
// 1. Exact match
|
|
@@ -842,7 +842,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
842
842
|
} else {
|
|
843
843
|
throw new Error(
|
|
844
844
|
`Variant "${variantName}" not found for ${componentName}. ` +
|
|
845
|
-
`Available: ${
|
|
845
|
+
`Available: ${fragment.variants.map((v) => v.name).join(', ')}`
|
|
846
846
|
);
|
|
847
847
|
}
|
|
848
848
|
}
|
|
@@ -868,12 +868,12 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
868
868
|
return {
|
|
869
869
|
variant: variant.name,
|
|
870
870
|
description: variant.description,
|
|
871
|
-
code: `<${
|
|
871
|
+
code: `<${fragment.meta.name} />`,
|
|
872
872
|
note: 'No code example provided in fragment. Refer to props for customization.',
|
|
873
873
|
};
|
|
874
874
|
});
|
|
875
875
|
|
|
876
|
-
const propsReference = Object.entries(
|
|
876
|
+
const propsReference = Object.entries(fragment.props ?? {}).map(([propName, prop]) => ({
|
|
877
877
|
name: propName,
|
|
878
878
|
type: prop.type,
|
|
879
879
|
required: prop.required,
|
|
@@ -881,7 +881,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
881
881
|
description: prop.description,
|
|
882
882
|
}));
|
|
883
883
|
|
|
884
|
-
const propConstraints = Object.entries(
|
|
884
|
+
const propConstraints = Object.entries(fragment.props ?? {})
|
|
885
885
|
.filter(([, prop]) => prop.constraints && prop.constraints.length > 0)
|
|
886
886
|
.map(([pName, prop]) => ({
|
|
887
887
|
prop: pName,
|
|
@@ -890,20 +890,20 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
890
890
|
|
|
891
891
|
const fullResult = {
|
|
892
892
|
// Component data (from old "get")
|
|
893
|
-
meta:
|
|
894
|
-
props:
|
|
895
|
-
variants:
|
|
896
|
-
relations:
|
|
897
|
-
contract:
|
|
898
|
-
generated:
|
|
893
|
+
meta: fragment.meta,
|
|
894
|
+
props: fragment.props,
|
|
895
|
+
variants: fragment.variants,
|
|
896
|
+
relations: fragment.relations,
|
|
897
|
+
contract: fragment.contract,
|
|
898
|
+
generated: fragment._generated,
|
|
899
899
|
// Guidelines (from old "guidelines")
|
|
900
900
|
guidelines: {
|
|
901
|
-
when: filterPlaceholders(
|
|
902
|
-
whenNot: filterPlaceholders(
|
|
903
|
-
guidelines:
|
|
904
|
-
accessibility:
|
|
901
|
+
when: filterPlaceholders(fragment.usage?.when),
|
|
902
|
+
whenNot: filterPlaceholders(fragment.usage?.whenNot),
|
|
903
|
+
guidelines: fragment.usage?.guidelines ?? [],
|
|
904
|
+
accessibility: fragment.usage?.accessibility ?? [],
|
|
905
905
|
propConstraints,
|
|
906
|
-
alternatives:
|
|
906
|
+
alternatives: fragment.relations
|
|
907
907
|
?.filter((r) => r.relationship === 'alternative')
|
|
908
908
|
.map((r) => ({
|
|
909
909
|
component: r.component,
|
|
@@ -912,7 +912,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
912
912
|
},
|
|
913
913
|
// Examples (from old "example")
|
|
914
914
|
examples: {
|
|
915
|
-
import: `import { ${
|
|
915
|
+
import: `import { ${fragment.meta.name} } from '${pkgName}';`,
|
|
916
916
|
code: examples,
|
|
917
917
|
propsReference,
|
|
918
918
|
},
|
|
@@ -935,7 +935,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
935
935
|
// BLOCKS — composition patterns
|
|
936
936
|
// ================================================================
|
|
937
937
|
case TOOL_NAMES.blocks: {
|
|
938
|
-
const data = await
|
|
938
|
+
const data = await loadFragments();
|
|
939
939
|
const blockName = args?.name as string | undefined;
|
|
940
940
|
const search = (args?.search as string)?.toLowerCase() ?? undefined;
|
|
941
941
|
const component = (args?.component as string)?.toLowerCase() ?? undefined;
|
|
@@ -1004,7 +1004,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1004
1004
|
// TOKENS — list CSS custom properties by category
|
|
1005
1005
|
// ================================================================
|
|
1006
1006
|
case TOOL_NAMES.tokens: {
|
|
1007
|
-
const data = await
|
|
1007
|
+
const data = await loadFragments();
|
|
1008
1008
|
const category = (args?.category as string)?.toLowerCase() ?? undefined;
|
|
1009
1009
|
const search = (args?.search as string)?.toLowerCase() ?? undefined;
|
|
1010
1010
|
|
|
@@ -1082,7 +1082,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1082
1082
|
// IMPLEMENT — one-shot discover + inspect + blocks + tokens
|
|
1083
1083
|
// ================================================================
|
|
1084
1084
|
case TOOL_NAMES.implement: {
|
|
1085
|
-
const data = await
|
|
1085
|
+
const data = await loadFragments();
|
|
1086
1086
|
const useCase = args?.useCase as string;
|
|
1087
1087
|
if (!useCase) {
|
|
1088
1088
|
throw new Error('useCase is required');
|
|
@@ -1091,7 +1091,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1091
1091
|
const useCaseLower = useCase.toLowerCase();
|
|
1092
1092
|
const searchTerms = useCaseLower.split(/\s+/).filter(Boolean);
|
|
1093
1093
|
|
|
1094
|
-
// 1. Score all
|
|
1094
|
+
// 1. Score all fragments (same logic as discover suggest)
|
|
1095
1095
|
const synonymMap: Record<string, string[]> = {
|
|
1096
1096
|
'form': ['input', 'field', 'submit', 'validation'],
|
|
1097
1097
|
'input': ['form', 'field', 'text', 'entry'],
|
|
@@ -1112,7 +1112,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1112
1112
|
if (synonyms) synonyms.forEach((syn) => expandedTerms.add(syn));
|
|
1113
1113
|
});
|
|
1114
1114
|
|
|
1115
|
-
const scored = Object.values(data.
|
|
1115
|
+
const scored = Object.values(data.fragments).map((s) => {
|
|
1116
1116
|
let score = 0;
|
|
1117
1117
|
const nameLower = s.meta.name.toLowerCase();
|
|
1118
1118
|
if (searchTerms.some((t) => nameLower.includes(t))) score += 15;
|
|
@@ -1127,7 +1127,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1127
1127
|
if (s.meta.category && searchTerms.some((t) => s.meta.category!.toLowerCase().includes(t))) score += 8;
|
|
1128
1128
|
if (s.meta.status === 'stable') score += 5;
|
|
1129
1129
|
if (s.meta.status === 'deprecated') score -= 25;
|
|
1130
|
-
return {
|
|
1130
|
+
return { fragment: s, score };
|
|
1131
1131
|
});
|
|
1132
1132
|
|
|
1133
1133
|
const topMatches = scored
|
|
@@ -1137,7 +1137,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1137
1137
|
|
|
1138
1138
|
// 2. Build component details for top matches
|
|
1139
1139
|
const components = await Promise.all(
|
|
1140
|
-
topMatches.map(async ({
|
|
1140
|
+
topMatches.map(async ({ fragment: s, score }) => {
|
|
1141
1141
|
const pkgName = await getPackageName(s.meta.name);
|
|
1142
1142
|
const examples = s.variants.slice(0, 2).map((v) => ({
|
|
1143
1143
|
variant: v.name,
|
|
@@ -1166,7 +1166,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1166
1166
|
.filter((b) => {
|
|
1167
1167
|
const haystack = [b.name, b.description, ...(b.tags ?? []), ...b.components, b.category].join(' ').toLowerCase();
|
|
1168
1168
|
return searchTerms.some((t) => haystack.includes(t)) ||
|
|
1169
|
-
topMatches.some(({
|
|
1169
|
+
topMatches.some(({ fragment }) => b.components.some((c) => c.toLowerCase() === fragment.meta.name.toLowerCase()));
|
|
1170
1170
|
})
|
|
1171
1171
|
.slice(0, 2)
|
|
1172
1172
|
.map((b) => ({ name: b.name, description: b.description, components: b.components, code: b.code }));
|
|
@@ -1460,7 +1460,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1460
1460
|
// FIX — unchanged
|
|
1461
1461
|
// ================================================================
|
|
1462
1462
|
case TOOL_NAMES.fix: {
|
|
1463
|
-
const data = await
|
|
1463
|
+
const data = await loadFragments();
|
|
1464
1464
|
const componentName = args?.component as string;
|
|
1465
1465
|
const variantName = (args?.variant as string) ?? undefined;
|
|
1466
1466
|
const fixType = (args?.fixType as 'token' | 'all') ?? 'all';
|
|
@@ -1469,11 +1469,11 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
1469
1469
|
throw new Error('component is required');
|
|
1470
1470
|
}
|
|
1471
1471
|
|
|
1472
|
-
const
|
|
1472
|
+
const fragment = Object.values(data.fragments).find(
|
|
1473
1473
|
(s) => s.meta.name.toLowerCase() === componentName.toLowerCase()
|
|
1474
1474
|
);
|
|
1475
1475
|
|
|
1476
|
-
if (!
|
|
1476
|
+
if (!fragment) {
|
|
1477
1477
|
throw new Error(`Component "${componentName}" not found. Use fragments_discover to see available components.`);
|
|
1478
1478
|
}
|
|
1479
1479
|
|
|
@@ -241,11 +241,11 @@ describe("storyNameToTitle", () => {
|
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
describe("extractCategory", () => {
|
|
244
|
-
it("extracts category from two-
|
|
244
|
+
it("extracts category from two-fragment path", () => {
|
|
245
245
|
expect(extractCategory("Components/Button")).toBe("components");
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
-
it("extracts category from multi-
|
|
248
|
+
it("extracts category from multi-fragment path", () => {
|
|
249
249
|
expect(extractCategory("Design System/Forms/Input")).toBe("forms");
|
|
250
250
|
});
|
|
251
251
|
|
|
@@ -254,7 +254,7 @@ describe("extractCategory", () => {
|
|
|
254
254
|
expect(extractCategory("UI/Forms/Select")).toBe("forms");
|
|
255
255
|
});
|
|
256
256
|
|
|
257
|
-
it("returns 'components' for single
|
|
257
|
+
it("returns 'components' for single fragment", () => {
|
|
258
258
|
expect(extractCategory("Button")).toBe("components");
|
|
259
259
|
});
|
|
260
260
|
|
package/src/migrate/bin.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* CLI entry point for migration tool.
|
|
4
4
|
*
|
|
5
|
-
* Can be run standalone or integrated with the main
|
|
5
|
+
* Can be run standalone or integrated with the main fragments CLI.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -19,17 +19,17 @@ const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-
|
|
|
19
19
|
const program = new Command();
|
|
20
20
|
|
|
21
21
|
program
|
|
22
|
-
.name("
|
|
22
|
+
.name("fragments-migrate")
|
|
23
23
|
.description(`${BRAND.name} Storybook Migration Tool`)
|
|
24
24
|
.version(pkg.version);
|
|
25
25
|
|
|
26
26
|
program
|
|
27
27
|
.command("migrate")
|
|
28
|
-
.description("Migrate Storybook stories to
|
|
28
|
+
.description("Migrate Storybook stories to Fragments")
|
|
29
29
|
.argument("<source>", "Source directory or file containing .stories.tsx files")
|
|
30
30
|
.option("-o, --output <dir>", "Output directory (default: same as source)")
|
|
31
31
|
.option("--dry-run", "Show what would be generated without writing files")
|
|
32
|
-
.option("--overwrite", "Overwrite existing
|
|
32
|
+
.option("--overwrite", "Overwrite existing fragment files")
|
|
33
33
|
.option("--report", "Generate HTML migration report")
|
|
34
34
|
.option("--report-path <path>", "Path for the migration report")
|
|
35
35
|
.option("-v, --verbose", "Verbose output")
|