@fragments-sdk/cli 0.14.3 → 0.15.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/README.md +0 -3
- package/dist/{ai-client-I6MDWNYA.js → ai-client-LSLQGOMM.js} +1 -2
- package/dist/bin.js +4745 -3817
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
- package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
- package/dist/chunk-32LIWN2P.js.map +1 -0
- package/dist/chunk-5JF26E55.js +1255 -0
- package/dist/chunk-5JF26E55.js.map +1 -0
- package/dist/{chunk-APTQIBS5.js → chunk-6SQPP47U.js} +153 -1342
- package/dist/chunk-6SQPP47U.js.map +1 -0
- package/dist/chunk-7DZC4YEV.js +294 -0
- package/dist/chunk-7DZC4YEV.js.map +1 -0
- package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
- package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
- package/dist/{chunk-55KERLWL.js → chunk-HQ6A6DTV.js} +1587 -1073
- package/dist/chunk-HQ6A6DTV.js.map +1 -0
- package/dist/chunk-MHIBEEW4.js +511 -0
- package/dist/chunk-MHIBEEW4.js.map +1 -0
- package/dist/{chunk-5A6X2Y73.js → chunk-ONUP6Z4W.js} +25 -13
- package/dist/chunk-ONUP6Z4W.js.map +1 -0
- package/dist/chunk-QCN35LJU.js +630 -0
- package/dist/chunk-QCN35LJU.js.map +1 -0
- package/dist/chunk-T47OLCSF.js +36 -0
- package/dist/chunk-T47OLCSF.js.map +1 -0
- package/dist/codebase-scanner-MQHUZC2G.js +21 -0
- package/dist/converter-7XM3Y6NJ.js +33 -0
- package/dist/converter-7XM3Y6NJ.js.map +1 -0
- package/dist/core/index.js +43 -2
- package/dist/create-IH4R45GE.js +806 -0
- package/dist/create-IH4R45GE.js.map +1 -0
- package/dist/{generate-RYWIPDN2.js → generate-PVOLUAAC.js} +4 -6
- package/dist/{generate-RYWIPDN2.js.map → generate-PVOLUAAC.js.map} +1 -1
- package/dist/govern-scan-OYFZYOQW.js +413 -0
- package/dist/govern-scan-OYFZYOQW.js.map +1 -0
- package/dist/index.d.ts +4 -23
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/{init-WRUSW7R5.js → init-SSGUSP7Z.js} +131 -129
- package/dist/init-SSGUSP7Z.js.map +1 -0
- package/dist/{init-cloud-REQ3XLHO.js → init-cloud-3DNKPWFB.js} +30 -5
- package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-3DNKPWFB.js.map} +1 -1
- package/dist/mcp-bin.js +5 -37
- package/dist/mcp-bin.js.map +1 -1
- package/dist/node-37AUE74M.js +65 -0
- package/dist/push-contracts-WY32TFP6.js +84 -0
- package/dist/push-contracts-WY32TFP6.js.map +1 -0
- package/dist/scan-PKSYSTRR.js +15 -0
- package/dist/{scan-generate-TFZVL3BT.js → scan-generate-VY27PIOX.js} +340 -52
- package/dist/scan-generate-VY27PIOX.js.map +1 -0
- package/dist/scanner-4KZNOXAK.js +12 -0
- package/dist/{service-HKJ6B7P7.js → service-QJGWUIVL.js} +41 -30
- package/dist/{snapshot-C5DYIGIV.js → snapshot-WIJMEIFT.js} +2 -3
- package/dist/{snapshot-C5DYIGIV.js.map → snapshot-WIJMEIFT.js.map} +1 -1
- package/dist/{static-viewer-DUVC4UIM.js → static-viewer-7QIBQZRC.js} +3 -4
- package/dist/static-viewer-7QIBQZRC.js.map +1 -0
- package/dist/{test-JW7JIDFG.js → test-64Z5BKBA.js} +4 -7
- package/dist/{test-JW7JIDFG.js.map → test-64Z5BKBA.js.map} +1 -1
- package/dist/token-normalizer-TEPOVBPV.js +312 -0
- package/dist/token-normalizer-TEPOVBPV.js.map +1 -0
- package/dist/token-parser-32KOIOFN.js +22 -0
- package/dist/token-parser-32KOIOFN.js.map +1 -0
- package/dist/{tokens-KE73G5JC.js → tokens-NZWFQIAB.js} +10 -9
- package/dist/{tokens-KE73G5JC.js.map → tokens-NZWFQIAB.js.map} +1 -1
- package/dist/tokens-generate-5JQSJ27E.js +85 -0
- package/dist/tokens-generate-5JQSJ27E.js.map +1 -0
- package/dist/tokens-push-HY3KO36V.js +148 -0
- package/dist/tokens-push-HY3KO36V.js.map +1 -0
- package/package.json +8 -6
- package/src/bin.ts +300 -48
- package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
- package/src/commands/__tests__/build-freshness.test.ts +231 -0
- package/src/commands/__tests__/create.test.ts +71 -0
- package/src/commands/__tests__/drift-sync.test.ts +1 -1
- package/src/commands/__tests__/govern.test.ts +258 -0
- package/src/commands/__tests__/init.test.ts +113 -0
- package/src/commands/__tests__/scan-generate.test.ts +189 -70
- package/src/commands/__tests__/verify.test.ts +91 -0
- package/src/commands/build.ts +54 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/create.ts +536 -0
- package/src/commands/discover.ts +151 -0
- package/src/commands/doctor.ts +3 -2
- package/src/commands/enhance.ts +3 -1
- package/src/commands/govern-scan.ts +565 -0
- package/src/commands/govern.ts +67 -4
- package/src/commands/init-cloud.ts +32 -4
- package/src/commands/init.ts +152 -28
- package/src/commands/inspect.ts +290 -0
- package/src/commands/migrate-contract.ts +85 -0
- package/src/commands/push-contracts.ts +112 -0
- package/src/commands/scan-generate.ts +439 -51
- package/src/commands/scan.ts +14 -0
- package/src/commands/setup.ts +27 -50
- package/src/commands/sync.ts +2 -2
- package/src/commands/tokens-generate.ts +113 -0
- package/src/commands/tokens-push.ts +199 -0
- package/src/commands/verify.ts +195 -1
- package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
- package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
- package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
- package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
- package/src/core/__tests__/contract-parity.test.ts +316 -0
- package/src/core/__tests__/token-resolver.test.ts +1 -1
- package/src/core/component-extractor.test.ts +40 -1
- package/src/core/config.ts +2 -1
- package/src/core/discovery.ts +13 -2
- package/src/core/drift-verifier.ts +123 -0
- package/src/core/extractor-adapter.ts +80 -0
- package/src/index.ts +3 -3
- package/src/mcp/__tests__/projectFields.test.ts +1 -1
- package/src/mcp/utils.ts +1 -50
- package/src/migrate/converter.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +253 -0
- package/src/migrate/report.ts +1 -1
- package/src/scripts/token-benchmark.ts +121 -0
- package/src/service/__tests__/props-extractor.test.ts +94 -0
- package/src/service/__tests__/token-normalizer.test.ts +690 -0
- package/src/service/ast-utils.ts +4 -23
- package/src/service/babel-config.ts +23 -0
- package/src/service/enhance/converter.ts +61 -0
- package/src/service/enhance/props-extractor.ts +25 -8
- package/src/service/enhance/scanner.ts +5 -24
- package/src/service/index.ts +8 -0
- package/src/service/snippet-validation.ts +9 -3
- package/src/service/tailwind-v4-parser.ts +314 -0
- package/src/service/token-normalizer.ts +510 -0
- package/src/service/token-parser.ts +56 -0
- package/src/setup.ts +10 -39
- package/src/shared/index.ts +1 -0
- package/src/shared/project-fields.ts +46 -0
- package/src/theme/__tests__/component-contrast.test.ts +2 -2
- package/src/theme/__tests__/serializer.test.ts +1 -1
- package/src/theme/generator.ts +16 -1
- package/src/theme/schema.ts +8 -0
- package/src/theme/serializer.ts +13 -9
- package/src/theme/types.ts +8 -0
- package/src/validators.ts +1 -2
- package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
- package/src/viewer/style-utils.ts +27 -412
- package/src/viewer/vite-plugin.ts +2 -2
- package/dist/chunk-55KERLWL.js.map +0 -1
- package/dist/chunk-5A6X2Y73.js.map +0 -1
- package/dist/chunk-APTQIBS5.js.map +0 -1
- package/dist/chunk-EYXVAMEX.js +0 -626
- package/dist/chunk-EYXVAMEX.js.map +0 -1
- package/dist/chunk-I34BC3CU.js.map +0 -1
- package/dist/chunk-LOYS64QS.js +0 -2453
- package/dist/chunk-LOYS64QS.js.map +0 -1
- package/dist/chunk-Z7EY4VHE.js +0 -50
- package/dist/chunk-ZKTFKHWN.js +0 -324
- package/dist/chunk-ZKTFKHWN.js.map +0 -1
- package/dist/discovery-VDANZAJ2.js +0 -28
- package/dist/init-WRUSW7R5.js.map +0 -1
- package/dist/sass.node-4XJK6YBF.js +0 -130708
- package/dist/sass.node-4XJK6YBF.js.map +0 -1
- package/dist/scan-YJHQIRKG.js +0 -14
- package/dist/scan-generate-TFZVL3BT.js.map +0 -1
- package/dist/viewer-2TZS3NDL.js +0 -2730
- package/dist/viewer-2TZS3NDL.js.map +0 -1
- package/src/build.ts +0 -612
- package/src/commands/dev.ts +0 -107
- package/src/core/auto-props.ts +0 -464
- package/src/core/component-extractor.ts +0 -1030
- package/src/core/token-resolver.ts +0 -155
- /package/dist/{ai-client-I6MDWNYA.js.map → ai-client-LSLQGOMM.js.map} +0 -0
- /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
- /package/dist/{chunk-Z7EY4VHE.js.map → codebase-scanner-MQHUZC2G.js.map} +0 -0
- /package/dist/{discovery-VDANZAJ2.js.map → node-37AUE74M.js.map} +0 -0
- /package/dist/{scan-YJHQIRKG.js.map → scan-PKSYSTRR.js.map} +0 -0
- /package/dist/{service-HKJ6B7P7.js.map → scanner-4KZNOXAK.js.map} +0 -0
- /package/dist/{static-viewer-DUVC4UIM.js.map → service-QJGWUIVL.js.map} +0 -0
package/src/bin.ts
CHANGED
|
@@ -17,13 +17,14 @@ import { loadConfig } from './core/node.js';
|
|
|
17
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8')) as { version: string };
|
|
19
19
|
|
|
20
|
+
const EXPERIMENTAL = process.env.FRAGMENTS_EXPERIMENTAL === '1';
|
|
21
|
+
|
|
20
22
|
// Import command implementations
|
|
21
23
|
import { validate } from './commands/validate.js';
|
|
22
24
|
import { build } from './commands/build.js';
|
|
23
25
|
import { context } from './commands/context.js';
|
|
24
26
|
import { list } from './commands/list.js';
|
|
25
27
|
import { reset } from './commands/reset.js';
|
|
26
|
-
import { dev } from './commands/dev.js';
|
|
27
28
|
import { compare } from './commands/compare.js';
|
|
28
29
|
import { verify } from './commands/verify.js';
|
|
29
30
|
import { audit } from './commands/audit.js';
|
|
@@ -36,11 +37,14 @@ import { linkFigma, linkStorybook } from './commands/link/index.js';
|
|
|
36
37
|
import { enhance } from './commands/enhance.js';
|
|
37
38
|
import { scan } from './commands/scan.js';
|
|
38
39
|
import { graph } from './commands/graph.js';
|
|
40
|
+
import { inspect } from './commands/inspect.js';
|
|
41
|
+
import { discover } from './commands/discover.js';
|
|
39
42
|
import { perf } from './commands/perf.js';
|
|
40
43
|
import { doctor } from './commands/doctor.js';
|
|
41
44
|
import { setup } from './commands/setup.js';
|
|
42
45
|
import { sync } from './commands/sync.js';
|
|
43
46
|
import { governCheck, governInit, governReport, governConnect } from './commands/govern.js';
|
|
47
|
+
import { migrateContract } from './commands/migrate-contract.js';
|
|
44
48
|
|
|
45
49
|
// Import existing commands that were already extracted
|
|
46
50
|
import { runScreenshotCommand } from './screenshot.js';
|
|
@@ -119,6 +123,8 @@ program
|
|
|
119
123
|
.option('--registry', `Also generate ${BRAND.dataDir}/${BRAND.registryFile} and ${BRAND.contextFile}`)
|
|
120
124
|
.option('--registry-only', `Only generate ${BRAND.dataDir}/ directory (skip ${BRAND.outFile})`)
|
|
121
125
|
.option('--from-source', 'Build from source code (zero-config, no fragment files needed)')
|
|
126
|
+
.option('--if-needed', `Skip rebuilding when ${BRAND.outFile} is already fresh`)
|
|
127
|
+
.option('--check', `Check whether ${BRAND.outFile} is fresh and exit non-zero if it is stale`)
|
|
122
128
|
.option('--skip-usage', 'Skip usage analysis when building from source')
|
|
123
129
|
.option('--skip-storybook', 'Skip Storybook parsing when building from source')
|
|
124
130
|
.option('-v, --verbose', 'Verbose output')
|
|
@@ -130,6 +136,8 @@ program
|
|
|
130
136
|
registry: options.registry,
|
|
131
137
|
registryOnly: options.registryOnly,
|
|
132
138
|
fromSource: options.fromSource,
|
|
139
|
+
ifNeeded: options.ifNeeded,
|
|
140
|
+
check: options.check,
|
|
133
141
|
skipUsage: options.skipUsage,
|
|
134
142
|
skipStorybook: options.skipStorybook,
|
|
135
143
|
verbose: options.verbose,
|
|
@@ -304,37 +312,6 @@ linkCommand
|
|
|
304
312
|
}
|
|
305
313
|
});
|
|
306
314
|
|
|
307
|
-
// ============================================================================
|
|
308
|
-
// DEV COMMAND
|
|
309
|
-
// ============================================================================
|
|
310
|
-
program
|
|
311
|
-
.command('dev')
|
|
312
|
-
.description('Start the development server with live component rendering')
|
|
313
|
-
.option('-p, --port <port>', 'Port to run on', '6006')
|
|
314
|
-
.option('--no-open', 'Do not open browser')
|
|
315
|
-
.option('--skip-setup', 'Skip auto-setup (Storybook import, build, Figma link)')
|
|
316
|
-
.option('--skip-storybook', 'Skip auto-importing from Storybook')
|
|
317
|
-
.option('--skip-figma', 'Skip Figma link check')
|
|
318
|
-
.option('--skip-build', `Skip auto-building ${BRAND.outFile}`)
|
|
319
|
-
.action(async (options) => {
|
|
320
|
-
try {
|
|
321
|
-
await dev({
|
|
322
|
-
port: options.port,
|
|
323
|
-
open: options.open,
|
|
324
|
-
skipSetup: options.skipSetup,
|
|
325
|
-
skipStorybook: options.skipStorybook,
|
|
326
|
-
skipFigma: options.skipFigma,
|
|
327
|
-
skipBuild: options.skipBuild,
|
|
328
|
-
});
|
|
329
|
-
} catch (error) {
|
|
330
|
-
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
331
|
-
if (error instanceof Error && error.stack) {
|
|
332
|
-
console.error(pc.dim(error.stack));
|
|
333
|
-
}
|
|
334
|
-
process.exit(1);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
|
|
338
315
|
// ============================================================================
|
|
339
316
|
// SCREENSHOT COMMAND
|
|
340
317
|
// ============================================================================
|
|
@@ -433,7 +410,7 @@ program
|
|
|
433
410
|
}
|
|
434
411
|
} catch (error) {
|
|
435
412
|
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
436
|
-
console.log(pc.dim(`\nMake sure
|
|
413
|
+
console.log(pc.dim(`\nMake sure a dev server is running on the expected port.`));
|
|
437
414
|
process.exit(1);
|
|
438
415
|
}
|
|
439
416
|
});
|
|
@@ -642,6 +619,33 @@ program
|
|
|
642
619
|
}
|
|
643
620
|
});
|
|
644
621
|
|
|
622
|
+
// ============================================================================
|
|
623
|
+
// MIGRATE-CONTRACT COMMAND
|
|
624
|
+
// ============================================================================
|
|
625
|
+
program
|
|
626
|
+
.command('migrate-contract')
|
|
627
|
+
.description('Migrate .fragment.tsx files to .contract.json format')
|
|
628
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
629
|
+
.option('--glob <pattern>', 'Glob pattern for fragment files', 'src/**/*.fragment.tsx')
|
|
630
|
+
.option('--dry-run', 'Preview migration without writing files')
|
|
631
|
+
.option('--tsconfig <path>', 'Path to tsconfig.json')
|
|
632
|
+
.action(async (options) => {
|
|
633
|
+
try {
|
|
634
|
+
const result = await migrateContract({
|
|
635
|
+
config: options.config,
|
|
636
|
+
glob: options.glob,
|
|
637
|
+
dryRun: options.dryRun,
|
|
638
|
+
tsconfig: options.tsconfig,
|
|
639
|
+
});
|
|
640
|
+
if (result.failed > 0) {
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
} catch (error) {
|
|
644
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
645
|
+
process.exit(1);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
645
649
|
// ============================================================================
|
|
646
650
|
// STORYGEN COMMAND
|
|
647
651
|
// ============================================================================
|
|
@@ -714,7 +718,7 @@ program
|
|
|
714
718
|
} catch (error) {
|
|
715
719
|
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
716
720
|
if (action === 'update') {
|
|
717
|
-
console.log(pc.dim(`\nMake sure
|
|
721
|
+
console.log(pc.dim(`\nMake sure a dev server is running on the expected port.`));
|
|
718
722
|
}
|
|
719
723
|
process.exit(1);
|
|
720
724
|
}
|
|
@@ -791,6 +795,47 @@ program
|
|
|
791
795
|
}
|
|
792
796
|
});
|
|
793
797
|
|
|
798
|
+
// ============================================================================
|
|
799
|
+
// CREATE COMMAND
|
|
800
|
+
// ============================================================================
|
|
801
|
+
program
|
|
802
|
+
.command('create')
|
|
803
|
+
.argument('[name]', 'Project name')
|
|
804
|
+
.description('Create a new project with Fragments UI and your custom theme')
|
|
805
|
+
.option('-t, --template <template>', 'Framework template (nextjs, vite)', 'nextjs')
|
|
806
|
+
.option('--pm <manager>', 'Package manager (npm, pnpm, yarn, bun)')
|
|
807
|
+
.option('--theme <encoded>', 'Encoded theme string')
|
|
808
|
+
.option('--preset <id>', 'Theme preset ID from usefragments.com/create')
|
|
809
|
+
.option('--brand <color>', 'Brand color hex (e.g., #6366f1)')
|
|
810
|
+
.option('--scss', 'Use SCSS output (installs sass)')
|
|
811
|
+
.option('--mcp', 'Configure MCP server for AI tooling')
|
|
812
|
+
.option('-y, --yes', 'Skip interactive prompts')
|
|
813
|
+
.option('--no-git', 'Skip git initialization')
|
|
814
|
+
.action(async (name, options) => {
|
|
815
|
+
try {
|
|
816
|
+
const { create } = await import('./commands/create.js');
|
|
817
|
+
const result = await create({
|
|
818
|
+
name,
|
|
819
|
+
template: options.template,
|
|
820
|
+
packageManager: options.pm,
|
|
821
|
+
theme: options.theme,
|
|
822
|
+
preset: options.preset,
|
|
823
|
+
brand: options.brand,
|
|
824
|
+
scss: options.scss,
|
|
825
|
+
mcp: options.mcp,
|
|
826
|
+
yes: options.yes,
|
|
827
|
+
noGit: !options.git,
|
|
828
|
+
});
|
|
829
|
+
if (!result.success) {
|
|
830
|
+
if (result.error) console.error(pc.red(`Error: ${result.error}`));
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
833
|
+
} catch (error) {
|
|
834
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
835
|
+
process.exit(1);
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
|
|
794
839
|
// ============================================================================
|
|
795
840
|
// SETUP COMMAND
|
|
796
841
|
// ============================================================================
|
|
@@ -823,27 +868,40 @@ program
|
|
|
823
868
|
// ============================================================================
|
|
824
869
|
// INIT COMMAND
|
|
825
870
|
// ============================================================================
|
|
826
|
-
program
|
|
871
|
+
const initCmd = program
|
|
827
872
|
.command('init')
|
|
828
873
|
.description('Initialize fragments in a project (zero-config by default)')
|
|
829
874
|
.option('--force', 'Overwrite existing config')
|
|
830
875
|
.option('-y, --yes', 'Non-interactive mode (now the default)')
|
|
831
|
-
.option('--cloud', 'Set up Fragments Cloud governance (zero-config browser auth)')
|
|
832
|
-
.option('--cloud-url <url>', 'Cloud dashboard URL (default: https://app.usefragments.com)')
|
|
833
|
-
.option('--port <port>', 'Localhost port for auth callback (default: 9876)')
|
|
834
|
-
.option('--auth-only', 'Only authenticate, skip project setup')
|
|
835
|
-
.option('--skip-check', 'Skip running the first governance check')
|
|
836
876
|
.option('--configure', 'Interactive mode for theme seeds, snapshots, etc.')
|
|
877
|
+
.option('--metadata-only', 'Generate config and metadata without modifying runtime app setup')
|
|
878
|
+
.option('--govern', 'Alias for --metadata-only')
|
|
837
879
|
.option('--scan <path>', 'Scan a TypeScript component directory and generate fragment files')
|
|
838
880
|
.option('--enrich', 'Use AI to fill knowledge fields during --scan (requires API key)')
|
|
839
881
|
.option('--dry-run', 'Show what --enrich would generate without calling API')
|
|
840
882
|
.option('--provider <provider>', 'AI provider for enrichment: anthropic or openai')
|
|
841
883
|
.option('--api-key <key>', 'API key for AI enrichment')
|
|
842
|
-
.option('--model <model>', 'Override AI model for enrichment')
|
|
843
|
-
|
|
884
|
+
.option('--model <model>', 'Override AI model for enrichment');
|
|
885
|
+
|
|
886
|
+
// Cloud governance flags — only visible with FRAGMENTS_EXPERIMENTAL=1
|
|
887
|
+
if (EXPERIMENTAL) {
|
|
888
|
+
initCmd
|
|
889
|
+
.option('--cloud', 'Set up Fragments Cloud governance (zero-config browser auth)')
|
|
890
|
+
.option('--cloud-url <url>', 'Cloud dashboard URL (default: https://app.usefragments.com)')
|
|
891
|
+
.option('--port <port>', 'Localhost port for auth callback (default: 9876)')
|
|
892
|
+
.option('--auth-only', 'Only authenticate, skip project setup')
|
|
893
|
+
.option('--skip-check', 'Skip running the first governance check');
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
initCmd.action(async (options) => {
|
|
844
897
|
try {
|
|
845
|
-
// Cloud init —
|
|
898
|
+
// Cloud init — experimental, requires FRAGMENTS_EXPERIMENTAL=1
|
|
846
899
|
if (options.cloud) {
|
|
900
|
+
if (!EXPERIMENTAL) {
|
|
901
|
+
console.log(pc.yellow(`\n Fragments Cloud is not yet publicly available.`));
|
|
902
|
+
console.log(pc.dim(` Set FRAGMENTS_EXPERIMENTAL=1 to enable preview features.\n`));
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
847
905
|
const { initCloud } = await import('./commands/init-cloud.js');
|
|
848
906
|
await initCloud({
|
|
849
907
|
url: options.cloudUrl,
|
|
@@ -866,6 +924,8 @@ program
|
|
|
866
924
|
provider: options.provider,
|
|
867
925
|
apiKey: options.apiKey,
|
|
868
926
|
model: options.model,
|
|
927
|
+
metadataOnly: options.metadataOnly,
|
|
928
|
+
govern: options.govern,
|
|
869
929
|
});
|
|
870
930
|
|
|
871
931
|
if (!result.success) {
|
|
@@ -912,11 +972,15 @@ program
|
|
|
912
972
|
});
|
|
913
973
|
|
|
914
974
|
// ============================================================================
|
|
915
|
-
// TOKENS COMMAND
|
|
975
|
+
// TOKENS COMMAND GROUP
|
|
916
976
|
// ============================================================================
|
|
917
|
-
program
|
|
977
|
+
const tokensCmd = program
|
|
918
978
|
.command('tokens')
|
|
919
|
-
.description('
|
|
979
|
+
.description('Design token discovery, listing, and generation');
|
|
980
|
+
|
|
981
|
+
tokensCmd
|
|
982
|
+
.command('list', { isDefault: true })
|
|
983
|
+
.description('Discover and list design tokens from CSS/SCSS/DTCG files')
|
|
920
984
|
.option('-c, --config <path>', 'Path to config file')
|
|
921
985
|
.option('--json', 'Output as JSON')
|
|
922
986
|
.option('--categories', 'Group tokens by category')
|
|
@@ -944,6 +1008,54 @@ program
|
|
|
944
1008
|
}
|
|
945
1009
|
});
|
|
946
1010
|
|
|
1011
|
+
tokensCmd
|
|
1012
|
+
.command('generate')
|
|
1013
|
+
.description('Generate CSS, SCSS, Tailwind, or Figma output from a DTCG .tokens.json file')
|
|
1014
|
+
.requiredOption('--from <path>', 'Path to DTCG .tokens.json source file')
|
|
1015
|
+
.requiredOption('--format <formats>', 'Output formats (comma-separated: css, scss, tailwind, figma)')
|
|
1016
|
+
.option('--out <dir>', 'Output directory (default: same directory as source)')
|
|
1017
|
+
.option('--prefix <prefix>', 'Token name prefix')
|
|
1018
|
+
.option('--selector <selector>', 'CSS selector for custom properties (default: :root)')
|
|
1019
|
+
.option('--verbose', 'Verbose output')
|
|
1020
|
+
.action(async (options) => {
|
|
1021
|
+
try {
|
|
1022
|
+
const { tokensGenerate } = await import('./commands/tokens-generate.js');
|
|
1023
|
+
await tokensGenerate({
|
|
1024
|
+
from: options.from,
|
|
1025
|
+
format: options.format,
|
|
1026
|
+
out: options.out,
|
|
1027
|
+
prefix: options.prefix,
|
|
1028
|
+
selector: options.selector,
|
|
1029
|
+
verbose: options.verbose,
|
|
1030
|
+
});
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1033
|
+
process.exit(1);
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
tokensCmd
|
|
1038
|
+
.command('push')
|
|
1039
|
+
.description('Push code tokens to Fragments Cloud for drift comparison')
|
|
1040
|
+
.option('-c, --config <path>', 'Path to fragments config file')
|
|
1041
|
+
.option('--tailwind-v4 <path>', 'Path to Tailwind v4 CSS file with @theme block')
|
|
1042
|
+
.option('--dry-run', 'Parse and display tokens without pushing')
|
|
1043
|
+
.option('--verbose', 'Show detailed output')
|
|
1044
|
+
.action(async (options) => {
|
|
1045
|
+
try {
|
|
1046
|
+
const { tokensPush } = await import('./commands/tokens-push.js');
|
|
1047
|
+
await tokensPush({
|
|
1048
|
+
config: options.config,
|
|
1049
|
+
tailwindV4: options.tailwindV4,
|
|
1050
|
+
dryRun: options.dryRun,
|
|
1051
|
+
verbose: options.verbose,
|
|
1052
|
+
});
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1055
|
+
process.exit(1);
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
|
|
947
1059
|
// ============================================================================
|
|
948
1060
|
// GENERATE COMMAND
|
|
949
1061
|
// ============================================================================
|
|
@@ -995,6 +1107,71 @@ program
|
|
|
995
1107
|
}
|
|
996
1108
|
});
|
|
997
1109
|
|
|
1110
|
+
// ============================================================================
|
|
1111
|
+
// INSPECT COMMAND
|
|
1112
|
+
// ============================================================================
|
|
1113
|
+
program
|
|
1114
|
+
.command('inspect')
|
|
1115
|
+
.description('Inspect a single component from compiled fragments data')
|
|
1116
|
+
.argument('<component>', 'Component name to inspect')
|
|
1117
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
1118
|
+
.option('--fields <fields>', 'Comma-separated fields to include (dot notation: meta, props, examples)')
|
|
1119
|
+
.option('--variant <name>', 'Filter to a specific variant')
|
|
1120
|
+
.option('--verbosity <level>', 'Output verbosity: compact, standard, full', 'standard')
|
|
1121
|
+
.option('--maxExamples <n>', 'Max variant examples to include', (v) => Number.parseInt(v, 10))
|
|
1122
|
+
.option('--json', 'Output as JSON')
|
|
1123
|
+
.action(async (component, options) => {
|
|
1124
|
+
try {
|
|
1125
|
+
await inspect(component, {
|
|
1126
|
+
config: options.config,
|
|
1127
|
+
fields: options.fields,
|
|
1128
|
+
variant: options.variant,
|
|
1129
|
+
verbosity: options.verbosity,
|
|
1130
|
+
maxExamples: options.maxExamples,
|
|
1131
|
+
json: options.json,
|
|
1132
|
+
});
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
if (options.json) {
|
|
1135
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Inspect failed' }));
|
|
1136
|
+
} else {
|
|
1137
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1138
|
+
}
|
|
1139
|
+
process.exit(1);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
// ============================================================================
|
|
1144
|
+
// DISCOVER COMMAND
|
|
1145
|
+
// ============================================================================
|
|
1146
|
+
program
|
|
1147
|
+
.command('discover')
|
|
1148
|
+
.description('List and filter components from compiled fragments data')
|
|
1149
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
1150
|
+
.option('--search <term>', 'Search by name, description, or tags')
|
|
1151
|
+
.option('--category <category>', 'Filter by category')
|
|
1152
|
+
.option('--status <status>', 'Filter by status: stable, beta, deprecated, experimental')
|
|
1153
|
+
.option('--compact', 'Output component names only')
|
|
1154
|
+
.option('--json', 'Output as JSON')
|
|
1155
|
+
.action(async (options) => {
|
|
1156
|
+
try {
|
|
1157
|
+
await discover({
|
|
1158
|
+
config: options.config,
|
|
1159
|
+
search: options.search,
|
|
1160
|
+
category: options.category,
|
|
1161
|
+
status: options.status,
|
|
1162
|
+
compact: options.compact,
|
|
1163
|
+
json: options.json,
|
|
1164
|
+
});
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
if (options.json) {
|
|
1167
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Discover failed' }));
|
|
1168
|
+
} else {
|
|
1169
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1170
|
+
}
|
|
1171
|
+
process.exit(1);
|
|
1172
|
+
}
|
|
1173
|
+
});
|
|
1174
|
+
|
|
998
1175
|
// ============================================================================
|
|
999
1176
|
// PERF COMMAND
|
|
1000
1177
|
// ============================================================================
|
|
@@ -1134,11 +1311,18 @@ program
|
|
|
1134
1311
|
});
|
|
1135
1312
|
|
|
1136
1313
|
// ============================================================================
|
|
1137
|
-
// GOVERN COMMAND
|
|
1314
|
+
// GOVERN COMMAND (experimental — FRAGMENTS_EXPERIMENTAL=1)
|
|
1138
1315
|
// ============================================================================
|
|
1139
1316
|
const governCmd = program
|
|
1140
1317
|
.command('govern')
|
|
1141
|
-
.description('AI UI governance checks')
|
|
1318
|
+
.description(EXPERIMENTAL ? 'AI UI governance checks' : 'AI UI governance checks (preview — set FRAGMENTS_EXPERIMENTAL=1)')
|
|
1319
|
+
.hook('preAction', () => {
|
|
1320
|
+
if (!EXPERIMENTAL) {
|
|
1321
|
+
console.log(pc.yellow(`\n Fragments governance is not yet publicly available.`));
|
|
1322
|
+
console.log(pc.dim(` Set FRAGMENTS_EXPERIMENTAL=1 to enable preview features.\n`));
|
|
1323
|
+
process.exit(1);
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1142
1326
|
|
|
1143
1327
|
governCmd
|
|
1144
1328
|
.command('check')
|
|
@@ -1199,5 +1383,73 @@ governCmd
|
|
|
1199
1383
|
}
|
|
1200
1384
|
});
|
|
1201
1385
|
|
|
1386
|
+
governCmd
|
|
1387
|
+
.command('scan')
|
|
1388
|
+
.description('Scan JSX/TSX codebase for governance violations')
|
|
1389
|
+
.option('-d, --dir <path>', 'Root directory (default: auto-detect)')
|
|
1390
|
+
.option('-c, --config <path>', 'Path to govern.config.ts')
|
|
1391
|
+
.option('-f, --format <format>', 'Output format: summary, json, sarif', 'summary')
|
|
1392
|
+
.option('-q, --quiet', 'Suppress non-error output')
|
|
1393
|
+
.action(async (options) => {
|
|
1394
|
+
try {
|
|
1395
|
+
const { governScan } = await import('./commands/govern-scan.js');
|
|
1396
|
+
const { exitCode } = await governScan({
|
|
1397
|
+
dir: options.dir,
|
|
1398
|
+
config: options.config,
|
|
1399
|
+
format: options.format,
|
|
1400
|
+
quiet: options.quiet,
|
|
1401
|
+
});
|
|
1402
|
+
process.exit(exitCode);
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1405
|
+
process.exit(1);
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
governCmd
|
|
1410
|
+
.command('watch')
|
|
1411
|
+
.description('Watch JSX/TSX files and re-check on changes')
|
|
1412
|
+
.option('-d, --dir <path>', 'Root directory (default: auto-detect)')
|
|
1413
|
+
.option('-c, --config <path>', 'Path to govern.config.ts')
|
|
1414
|
+
.option('-q, --quiet', 'Suppress non-error output')
|
|
1415
|
+
.option('--debounce <ms>', 'Debounce interval in ms', '300')
|
|
1416
|
+
.action(async (options) => {
|
|
1417
|
+
try {
|
|
1418
|
+
const { governWatch } = await import('./commands/govern-scan.js');
|
|
1419
|
+
await governWatch({
|
|
1420
|
+
dir: options.dir,
|
|
1421
|
+
config: options.config,
|
|
1422
|
+
quiet: options.quiet,
|
|
1423
|
+
debounce: parseInt(options.debounce, 10),
|
|
1424
|
+
});
|
|
1425
|
+
} catch (error) {
|
|
1426
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1427
|
+
process.exit(1);
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
governCmd
|
|
1432
|
+
.command('push-contracts')
|
|
1433
|
+
.description('Push component contracts to Fragments Cloud')
|
|
1434
|
+
.option('-i, --input <path>', 'Path to fragments.json (default: ./fragments.json)')
|
|
1435
|
+
.option('--url <url>', 'Fragments Cloud URL')
|
|
1436
|
+
.option('--api-key <key>', 'API key (default: FRAGMENTS_API_KEY env var)')
|
|
1437
|
+
.option('-q, --quiet', 'Suppress non-error output')
|
|
1438
|
+
.action(async (options) => {
|
|
1439
|
+
try {
|
|
1440
|
+
const { pushContracts } = await import('./commands/push-contracts.js');
|
|
1441
|
+
const { exitCode } = await pushContracts({
|
|
1442
|
+
input: options.input,
|
|
1443
|
+
url: options.url,
|
|
1444
|
+
apiKey: options.apiKey,
|
|
1445
|
+
quiet: options.quiet,
|
|
1446
|
+
});
|
|
1447
|
+
process.exit(exitCode);
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1450
|
+
process.exit(1);
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1202
1454
|
// Parse command line arguments
|
|
1203
1455
|
program.parse();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://usefragments.com/schemas/contract.v1.json",
|
|
3
|
+
"name": "Label",
|
|
4
|
+
"description": "Label component",
|
|
5
|
+
"category": "Typography",
|
|
6
|
+
"sourcePath": "label.tsx",
|
|
7
|
+
"exportName": "Label",
|
|
8
|
+
"propsSummary": [
|
|
9
|
+
"className: string",
|
|
10
|
+
"htmlFor: string",
|
|
11
|
+
"form: string",
|
|
12
|
+
"children: node"
|
|
13
|
+
],
|
|
14
|
+
"props": {
|
|
15
|
+
"className": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": ""
|
|
18
|
+
},
|
|
19
|
+
"htmlFor": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": ""
|
|
22
|
+
},
|
|
23
|
+
"form": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": ""
|
|
26
|
+
},
|
|
27
|
+
"children": {
|
|
28
|
+
"type": "node",
|
|
29
|
+
"description": ""
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"usage": {
|
|
33
|
+
"when": [],
|
|
34
|
+
"whenNot": []
|
|
35
|
+
},
|
|
36
|
+
"provenance": {
|
|
37
|
+
"source": "extracted",
|
|
38
|
+
"verified": false,
|
|
39
|
+
"frameworkSupport": "native",
|
|
40
|
+
"extractedAt": "2026-03-22T23:45:29.606Z"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Primitive } from "./primitive";
|
|
3
|
+
|
|
4
|
+
function Label({
|
|
5
|
+
className,
|
|
6
|
+
...props
|
|
7
|
+
}: React.ComponentProps<typeof Primitive.Root>) {
|
|
8
|
+
return <Primitive.Root data-class={className} {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { Label };
|
package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://usefragments.com/schemas/contract.v1.json",
|
|
3
|
+
"name": "Primitive",
|
|
4
|
+
"description": "Primitive component",
|
|
5
|
+
"category": "Components",
|
|
6
|
+
"sourcePath": "primitive.tsx",
|
|
7
|
+
"exportName": "Primitive",
|
|
8
|
+
"propsSummary": [],
|
|
9
|
+
"props": {},
|
|
10
|
+
"usage": {
|
|
11
|
+
"when": [],
|
|
12
|
+
"whenNot": []
|
|
13
|
+
},
|
|
14
|
+
"provenance": {
|
|
15
|
+
"source": "extracted",
|
|
16
|
+
"verified": false,
|
|
17
|
+
"frameworkSupport": "native",
|
|
18
|
+
"extractedAt": "2026-03-22T23:45:29.607Z"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export interface PrimitiveLabelProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
htmlFor?: string;
|
|
6
|
+
form?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function Root(props: PrimitiveLabelProps) {
|
|
11
|
+
return <label {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Primitive = { Root };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"types": [
|
|
12
|
+
"react",
|
|
13
|
+
"react-dom"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"src/**/*.tsx"
|
|
18
|
+
],
|
|
19
|
+
"exclude": [
|
|
20
|
+
"**/*.fragment.tsx",
|
|
21
|
+
"**/*.contract.json"
|
|
22
|
+
]
|
|
23
|
+
}
|