@fragments-sdk/cli 0.11.1 → 0.12.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.
Files changed (86) hide show
  1. package/dist/ai-client-I6MDWNYA.js +21 -0
  2. package/dist/bin.js +275 -368
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
  5. package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
  6. package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
  7. package/dist/chunk-GVDSFQ4E.js.map +1 -0
  8. package/dist/chunk-JJ2VRTBU.js +626 -0
  9. package/dist/chunk-JJ2VRTBU.js.map +1 -0
  10. package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
  11. package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
  12. package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
  13. package/dist/chunk-OQKMEFOS.js.map +1 -0
  14. package/dist/chunk-SXTKFDCR.js +104 -0
  15. package/dist/chunk-SXTKFDCR.js.map +1 -0
  16. package/dist/chunk-T5OMVL7E.js +443 -0
  17. package/dist/chunk-T5OMVL7E.js.map +1 -0
  18. package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
  19. package/dist/chunk-TPWGL2XS.js.map +1 -0
  20. package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
  21. package/dist/chunk-WFS63PCW.js.map +1 -0
  22. package/dist/core/index.js +9 -1
  23. package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
  24. package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
  25. package/dist/index.js +7 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/init-ZSX3NRCZ.js +636 -0
  28. package/dist/init-ZSX3NRCZ.js.map +1 -0
  29. package/dist/mcp-bin.js +2 -2
  30. package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
  31. package/dist/scan-generate-SYU4PYZD.js +1115 -0
  32. package/dist/scan-generate-SYU4PYZD.js.map +1 -0
  33. package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
  34. package/dist/{snapshot-SV2JOFZH.js → snapshot-XOISO2IS.js} +2 -2
  35. package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
  36. package/dist/static-viewer-5GXH2MGE.js.map +1 -0
  37. package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
  38. package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
  39. package/dist/{viewer-DLLJIMCK.js → viewer-7ZEAFBVN.js} +13 -13
  40. package/package.json +4 -4
  41. package/src/ai-client.ts +156 -0
  42. package/src/bin.ts +44 -2
  43. package/src/build.ts +95 -33
  44. package/src/commands/__tests__/drift-sync.test.ts +252 -0
  45. package/src/commands/__tests__/scan-generate.test.ts +497 -45
  46. package/src/commands/enhance.ts +11 -35
  47. package/src/commands/init.ts +288 -260
  48. package/src/commands/scan-generate.ts +740 -139
  49. package/src/commands/scan.ts +37 -32
  50. package/src/commands/setup.ts +143 -52
  51. package/src/commands/sync.ts +357 -0
  52. package/src/commands/validate.ts +43 -1
  53. package/src/core/component-extractor.test.ts +282 -0
  54. package/src/core/component-extractor.ts +1030 -0
  55. package/src/core/discovery.ts +93 -7
  56. package/src/service/enhance/props-extractor.ts +235 -13
  57. package/src/validators.ts +236 -0
  58. package/dist/chunk-5G3VZH43.js.map +0 -1
  59. package/dist/chunk-OQO55NKV.js.map +0 -1
  60. package/dist/chunk-WXSR2II7.js.map +0 -1
  61. package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
  62. package/dist/init-UFGK5TCN.js +0 -867
  63. package/dist/init-UFGK5TCN.js.map +0 -1
  64. package/dist/scan-generate-SJAN5MVI.js +0 -691
  65. package/dist/scan-generate-SJAN5MVI.js.map +0 -1
  66. package/src/ai.ts +0 -266
  67. package/src/commands/init-framework.ts +0 -414
  68. package/src/mcp/bin.ts +0 -36
  69. package/src/migrate/bin.ts +0 -114
  70. package/src/theme/index.ts +0 -77
  71. package/src/viewer/bin.ts +0 -86
  72. package/src/viewer/cli/health.ts +0 -256
  73. package/src/viewer/cli/index.ts +0 -33
  74. package/src/viewer/cli/scan.ts +0 -124
  75. package/src/viewer/cli/utils.ts +0 -174
  76. /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
  77. /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
  78. /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
  79. /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
  80. /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
  81. /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
  82. /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
  83. /package/dist/{snapshot-SV2JOFZH.js.map → snapshot-XOISO2IS.js.map} +0 -0
  84. /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
  85. /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
  86. /package/dist/{viewer-DLLJIMCK.js.map → viewer-7ZEAFBVN.js.map} +0 -0
package/dist/bin.js CHANGED
@@ -1,28 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
3
+ import {
4
+ createAIClient,
5
+ detectProvider,
6
+ getApiKey
7
+ } from "./chunk-SXTKFDCR.js";
8
+ import {
9
+ setup
10
+ } from "./chunk-T5OMVL7E.js";
3
11
  import {
4
12
  buildFragments,
5
13
  buildFragmentsDir,
6
14
  measureBundleSizes,
15
+ resolveComponentSourcePath,
7
16
  runAnalyzeCommand,
8
17
  runDiffCommand,
9
18
  runScreenshotCommand,
10
19
  toPerformanceData,
11
20
  validateAll,
12
21
  validateCoverage,
22
+ validateDrift,
13
23
  validateSchema,
14
24
  validateSnippets
15
- } from "./chunk-5G3VZH43.js";
25
+ } from "./chunk-GVDSFQ4E.js";
26
+ import {
27
+ createComponentExtractor
28
+ } from "./chunk-JJ2VRTBU.js";
16
29
  import {
17
30
  scan
18
- } from "./chunk-ZM4ZQZWZ.js";
31
+ } from "./chunk-TPWGL2XS.js";
19
32
  import {
20
33
  loadConfig,
21
- loadFragmentFile
22
- } from "./chunk-HRFUSSZI.js";
34
+ loadFragmentFile,
35
+ parseFragmentFile
36
+ } from "./chunk-AM4MRTMN.js";
23
37
  import {
24
38
  discoverFragmentFiles
25
- } from "./chunk-WXSR2II7.js";
39
+ } from "./chunk-OQKMEFOS.js";
26
40
  import {
27
41
  FigmaClient,
28
42
  StorageManager,
@@ -38,7 +52,7 @@ import {
38
52
  renderAllComponentVariants,
39
53
  scanCodebase,
40
54
  shutdownSharedPool
41
- } from "./chunk-D5PYOXEI.js";
55
+ } from "./chunk-LVWFOLUZ.js";
42
56
  import "./chunk-D2CDBRNU.js";
43
57
  import {
44
58
  BRAND,
@@ -46,7 +60,7 @@ import {
46
60
  formatBytes,
47
61
  generateContext,
48
62
  resolvePerformanceConfig
49
- } from "./chunk-OQO55NKV.js";
63
+ } from "./chunk-WFS63PCW.js";
50
64
  import "./chunk-Z7EY4VHE.js";
51
65
 
52
66
  // src/bin.ts
@@ -54,7 +68,7 @@ import { Command } from "commander";
54
68
  import pc24 from "picocolors";
55
69
  import { readFileSync } from "fs";
56
70
  import { fileURLToPath } from "url";
57
- import { dirname as dirname5, join as join12 } from "path";
71
+ import { dirname as dirname4, join as join11 } from "path";
58
72
 
59
73
  // src/commands/validate.ts
60
74
  import pc from "picocolors";
@@ -78,6 +92,11 @@ ${BRAND.name} Validator
78
92
  componentStart: options.componentStart,
79
93
  componentLimit
80
94
  });
95
+ } else if (options.drift) {
96
+ console.log(pc.dim("Running drift detection...\n"));
97
+ const driftResult = await validateDrift(config, configDir, { tsconfig: options.tsconfig });
98
+ result = driftResult;
99
+ printDriftReport(driftResult);
81
100
  } else {
82
101
  console.log(pc.dim("Running all validations...\n"));
83
102
  result = await validateAll(config, configDir, {
@@ -118,6 +137,30 @@ ${BRAND.name} Validator
118
137
  }
119
138
  return result;
120
139
  }
140
+ function printDriftReport(result) {
141
+ if (result.reports.length === 0) {
142
+ console.log(pc.green("No drift detected \u2014 fragments are in sync with source.\n"));
143
+ return;
144
+ }
145
+ console.log(pc.bold(`Drift detected in ${result.reports.length} component(s):
146
+ `));
147
+ for (const report of result.reports) {
148
+ console.log(` ${pc.bold(report.component)} ${pc.dim(`(${report.file})`)}`);
149
+ for (const drift of report.drifts) {
150
+ const icon = drift.kind === "removed" ? pc.red("\u2212") : drift.kind === "added" ? pc.green("+") : pc.yellow("~");
151
+ const label = drift.kind.replace("_", " ");
152
+ console.log(` ${icon} ${drift.prop}: ${label}`);
153
+ if (drift.kind !== "added" && drift.kind !== "removed") {
154
+ console.log(pc.dim(` fragment: ${drift.fragment}`));
155
+ console.log(pc.dim(` source: ${drift.source}`));
156
+ }
157
+ }
158
+ if (report.compositionDrift) {
159
+ console.log(` ${pc.yellow("~")} composition: ${report.compositionDrift}`);
160
+ }
161
+ console.log();
162
+ }
163
+ }
121
164
 
122
165
  // src/commands/build.ts
123
166
  import pc2 from "picocolors";
@@ -1858,7 +1901,7 @@ ${BRAND.name} Dev Server
1858
1901
  }
1859
1902
  }
1860
1903
  }
1861
- const { createDevServer } = await import("./viewer-DLLJIMCK.js");
1904
+ const { createDevServer } = await import("./viewer-7ZEAFBVN.js");
1862
1905
  console.log(pc7.dim("\nStarting dev server..."));
1863
1906
  const parsedPort = typeof port === "string" ? parseInt(port, 10) : port;
1864
1907
  try {
@@ -4453,8 +4496,8 @@ async function enhance(options = {}) {
4453
4496
  yes = false,
4454
4497
  dryRun = false,
4455
4498
  format = "interactive",
4456
- provider = detectProvider(options),
4457
- apiKey = getApiKey(provider, options.apiKey),
4499
+ provider = detectProvider2(options),
4500
+ apiKey = getApiKey2(provider, options.apiKey),
4458
4501
  model = options.model || DEFAULT_MODELS[provider],
4459
4502
  root = process.cwd(),
4460
4503
  contextOnly = false,
@@ -4817,33 +4860,12 @@ For each component, provide your response in JSON format:
4817
4860
  context: fullContext
4818
4861
  };
4819
4862
  }
4820
- function detectProvider(options) {
4863
+ function detectProvider2(options) {
4821
4864
  if (options.contextOnly) return "none";
4822
- if (options.provider) return options.provider;
4823
- if (options.apiKey) {
4824
- if (options.apiKey.startsWith("sk-ant-")) return "anthropic";
4825
- if (options.apiKey.startsWith("sk-")) return "openai";
4826
- }
4827
- if (process.env.ANTHROPIC_API_KEY) return "anthropic";
4828
- if (process.env.OPENAI_API_KEY) return "openai";
4829
- return "none";
4865
+ return detectProvider({ provider: options.provider, apiKey: options.apiKey });
4830
4866
  }
4831
- function getApiKey(provider, explicitKey) {
4832
- if (explicitKey) return explicitKey;
4833
- if (provider === "anthropic") return process.env.ANTHROPIC_API_KEY;
4834
- if (provider === "openai") return process.env.OPENAI_API_KEY;
4835
- return void 0;
4836
- }
4837
- async function createAIClient(provider, apiKey) {
4838
- if (provider === "anthropic") {
4839
- const Anthropic = (await import("@anthropic-ai/sdk")).default;
4840
- return new Anthropic({ apiKey });
4841
- }
4842
- if (provider === "openai") {
4843
- const OpenAI = (await import("openai")).default;
4844
- return new OpenAI({ apiKey });
4845
- }
4846
- throw new Error(`Unknown provider: ${provider}`);
4867
+ function getApiKey2(provider, explicitKey) {
4868
+ return getApiKey(provider, explicitKey);
4847
4869
  }
4848
4870
  async function generateEnhancement(client, provider, model, context2) {
4849
4871
  const systemPrompt = generateSystemPrompt();
@@ -5785,365 +5807,228 @@ ${BRAND.name} Doctor
5785
5807
  return result;
5786
5808
  }
5787
5809
 
5788
- // src/commands/setup.ts
5789
- import { readFile as readFile9, writeFile as writeFile9, access as access4, mkdir as mkdir7 } from "fs/promises";
5790
- import { join as join11, resolve as resolve9, dirname as dirname4 } from "path";
5810
+ // src/commands/sync.ts
5791
5811
  import pc23 from "picocolors";
5792
- async function fileExists(path) {
5793
- try {
5794
- await access4(path);
5795
- return true;
5796
- } catch {
5797
- return false;
5798
- }
5799
- }
5800
- async function detectFramework(root) {
5801
- if (await fileExists(join11(root, "app/layout.tsx")) || await fileExists(join11(root, "src/app/layout.tsx"))) {
5802
- return "nextjs-app";
5803
- }
5804
- if (await fileExists(join11(root, "pages/_app.tsx")) || await fileExists(join11(root, "pages/_app.ts"))) {
5805
- return "nextjs-pages";
5806
- }
5807
- if (await fileExists(join11(root, "next.config.ts")) || await fileExists(join11(root, "next.config.js")) || await fileExists(join11(root, "next.config.mjs"))) {
5808
- return "nextjs-app";
5809
- }
5810
- if (await fileExists(join11(root, "vite.config.ts")) || await fileExists(join11(root, "vite.config.js"))) {
5811
- return "vite";
5812
- }
5813
- return "unknown";
5814
- }
5815
- async function findEntryFile(root, framework) {
5816
- const candidates = [];
5817
- switch (framework) {
5818
- case "nextjs-app":
5819
- candidates.push(
5820
- "src/app/layout.tsx",
5821
- "app/layout.tsx",
5822
- "src/app/layout.ts",
5823
- "app/layout.ts"
5824
- );
5825
- break;
5826
- case "nextjs-pages":
5827
- candidates.push("pages/_app.tsx", "pages/_app.ts");
5828
- break;
5829
- case "vite":
5830
- candidates.push(
5831
- "src/main.tsx",
5832
- "src/main.ts",
5833
- "src/index.tsx",
5834
- "src/index.ts"
5835
- );
5836
- break;
5837
- default:
5838
- candidates.push(
5839
- "src/main.tsx",
5840
- "src/main.ts",
5841
- "src/index.tsx",
5842
- "src/index.ts",
5843
- "src/App.tsx",
5844
- "src/App.ts"
5845
- );
5846
- }
5847
- for (const candidate of candidates) {
5848
- if (await fileExists(join11(root, candidate))) {
5849
- return candidate;
5850
- }
5812
+ import { readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
5813
+ async function sync(options = {}) {
5814
+ const { config, configDir } = await loadConfig(options.config);
5815
+ console.log(pc23.cyan(`
5816
+ ${BRAND.name} Sync
5817
+ `));
5818
+ if (options.dryRun) {
5819
+ console.log(pc23.dim("Dry run \u2014 no files will be modified.\n"));
5851
5820
  }
5852
- return null;
5853
- }
5854
- async function findNextConfig(root) {
5855
- const candidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
5856
- for (const candidate of candidates) {
5857
- if (await fileExists(join11(root, candidate))) {
5858
- return candidate;
5821
+ const result = await runSync(config, configDir, options);
5822
+ if (result.updated.length > 0) {
5823
+ const verb = options.dryRun ? "Would update" : "Updated";
5824
+ console.log(pc23.bold(`${verb} ${result.updated.length} fragment(s):
5825
+ `));
5826
+ for (const comp of result.updated) {
5827
+ console.log(` ${pc23.green("\u2713")} ${pc23.bold(comp.name)} ${pc23.dim(`(${comp.file})`)}`);
5828
+ for (const change of comp.changes) {
5829
+ console.log(` ${pc23.dim("\u2022")} ${change}`);
5830
+ }
5859
5831
  }
5832
+ console.log();
5860
5833
  }
5861
- return null;
5862
- }
5863
- async function addStylesImport(root, entryFile) {
5864
- const fullPath = join11(root, entryFile);
5865
- const content = await readFile9(fullPath, "utf-8");
5866
- if (content.includes("@fragments-sdk/ui/styles")) {
5867
- return { modified: false, message: `Styles already imported in ${entryFile}` };
5868
- }
5869
- const stylesImport = "import '@fragments-sdk/ui/styles';";
5870
- let newContent;
5871
- if (content.startsWith("'use client'") || content.startsWith('"use client"')) {
5872
- const directiveEnd = content.indexOf("\n") + 1;
5873
- newContent = content.slice(0, directiveEnd) + stylesImport + "\n" + content.slice(directiveEnd);
5874
- } else {
5875
- newContent = stylesImport + "\n" + content;
5834
+ if (result.skipped.length > 0) {
5835
+ console.log(pc23.dim(`Skipped ${result.skipped.length}: ${result.skipped.map((s) => s.name).join(", ")}
5836
+ `));
5876
5837
  }
5877
- await writeFile9(fullPath, newContent, "utf-8");
5878
- return { modified: true, message: `Added styles import to ${entryFile}` };
5879
- }
5880
- async function addThemeProvider(root, entryFile, framework) {
5881
- const fullPath = join11(root, entryFile);
5882
- const content = await readFile9(fullPath, "utf-8");
5883
- if (content.includes("ThemeProvider")) {
5884
- return { modified: false, message: `ThemeProvider already present in ${entryFile}` };
5885
- }
5886
- if (framework === "nextjs-app") {
5887
- const providerImport = "import { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';";
5888
- let newContent = content;
5889
- const importLines = content.split("\n");
5890
- let lastImportIdx = -1;
5891
- for (let i = 0; i < importLines.length; i++) {
5892
- if (importLines[i].startsWith("import ") || importLines[i].startsWith("import '") || importLines[i].startsWith('import "')) {
5893
- lastImportIdx = i;
5894
- }
5895
- }
5896
- if (lastImportIdx >= 0) {
5897
- importLines.splice(lastImportIdx + 1, 0, providerImport);
5898
- newContent = importLines.join("\n");
5899
- } else {
5900
- newContent = providerImport + "\n" + content;
5901
- }
5902
- if (!content.includes("suppressHydrationWarning")) {
5903
- await writeFile9(fullPath, newContent, "utf-8");
5904
- return {
5905
- modified: true,
5906
- message: `Added provider imports to ${entryFile}. Wrap your {children} with:
5907
- <ThemeProvider defaultMode="system"><TooltipProvider><ToastProvider>{children}</ToastProvider></TooltipProvider></ThemeProvider>
5908
- Add suppressHydrationWarning to your <html> tag`
5909
- };
5838
+ if (result.errors.length > 0) {
5839
+ console.log(pc23.red(pc23.bold("Errors:")));
5840
+ for (const err of result.errors) {
5841
+ console.log(` ${pc23.red("\u2717")} ${pc23.bold(err.file)}: ${err.message}`);
5910
5842
  }
5911
- await writeFile9(fullPath, newContent, "utf-8");
5912
- return { modified: true, message: `Added provider imports to ${entryFile}. Wrap {children} with ThemeProvider.` };
5913
- }
5914
- return { modified: false, message: "Manual ThemeProvider setup needed \u2014 see https://usefragments.com/getting-started#provider-setup" };
5915
- }
5916
- async function addTranspilePackages(root) {
5917
- const configFile = await findNextConfig(root);
5918
- if (!configFile) {
5919
- return { modified: false, message: "No next.config found" };
5843
+ console.log();
5920
5844
  }
5921
- const fullPath = join11(root, configFile);
5922
- const content = await readFile9(fullPath, "utf-8");
5923
- if (content.includes("transpilePackages") && content.includes("@fragments-sdk/ui")) {
5924
- return { modified: false, message: `transpilePackages already configured in ${configFile}` };
5845
+ if (result.updated.length === 0 && result.errors.length === 0) {
5846
+ console.log(pc23.green("All fragments are in sync \u2014 nothing to update.\n"));
5925
5847
  }
5926
- if (content.includes("transpilePackages")) {
5927
- return {
5928
- modified: false,
5929
- message: `transpilePackages found in ${configFile} but missing '@fragments-sdk/ui'. Please add it manually.`
5930
- };
5848
+ return result;
5849
+ }
5850
+ async function runSync(config, configDir, options) {
5851
+ const fragmentFiles = await discoverFragmentFiles(config, configDir);
5852
+ const updated = [];
5853
+ const skipped = [];
5854
+ const errors = [];
5855
+ if (fragmentFiles.length === 0) {
5856
+ return { success: true, updated, skipped, errors };
5931
5857
  }
5932
- const patterns = [
5933
- // const nextConfig = { ... }
5934
- { search: /const\s+\w+\s*=\s*\{/, replacement: (match) => `${match}
5935
- transpilePackages: ['@fragments-sdk/ui'],` },
5936
- // module.exports = { ... }
5937
- { search: /module\.exports\s*=\s*\{/, replacement: (match) => `${match}
5938
- transpilePackages: ['@fragments-sdk/ui'],` },
5939
- // export default { ... }
5940
- { search: /export\s+default\s*\{/, replacement: (match) => `${match}
5941
- transpilePackages: ['@fragments-sdk/ui'],` }
5942
- ];
5943
- for (const pattern of patterns) {
5944
- if (pattern.search.test(content)) {
5945
- const newContent = content.replace(pattern.search, pattern.replacement);
5946
- await writeFile9(fullPath, newContent, "utf-8");
5947
- return { modified: true, message: `Added transpilePackages to ${configFile}` };
5858
+ const extractor = createComponentExtractor(options.tsconfig);
5859
+ try {
5860
+ for (const file of fragmentFiles) {
5861
+ try {
5862
+ const fragment = await loadFragmentFile(file.absolutePath);
5863
+ if (!fragment?.meta?.name) continue;
5864
+ if (options.component && fragment.meta.name !== options.component) continue;
5865
+ const fileContent = await readFile9(file.absolutePath, "utf-8");
5866
+ const parsed = parseFragmentFile(fileContent, file.absolutePath);
5867
+ if (!parsed.componentImport) {
5868
+ skipped.push({ name: fragment.meta.name, reason: "No component import found" });
5869
+ continue;
5870
+ }
5871
+ const sourcePath = resolveComponentSourcePath(file.absolutePath, parsed.componentImport);
5872
+ if (!sourcePath) {
5873
+ skipped.push({ name: fragment.meta.name, reason: "Cannot resolve component source" });
5874
+ continue;
5875
+ }
5876
+ const meta = extractor.extract(sourcePath, fragment.meta.name);
5877
+ if (!meta) {
5878
+ skipped.push({ name: fragment.meta.name, reason: "Extraction returned null" });
5879
+ continue;
5880
+ }
5881
+ const patch = computePatch(fileContent, fragment, meta);
5882
+ if (patch.changes.length === 0) {
5883
+ skipped.push({ name: fragment.meta.name, reason: "Already in sync" });
5884
+ continue;
5885
+ }
5886
+ if (!options.dryRun) {
5887
+ await writeFile9(file.absolutePath, patch.updatedContent, "utf-8");
5888
+ }
5889
+ updated.push({
5890
+ name: fragment.meta.name,
5891
+ file: file.relativePath,
5892
+ changes: patch.changes
5893
+ });
5894
+ } catch (err) {
5895
+ errors.push({
5896
+ file: file.relativePath,
5897
+ message: err instanceof Error ? err.message : String(err)
5898
+ });
5899
+ }
5948
5900
  }
5901
+ } finally {
5902
+ extractor.dispose();
5949
5903
  }
5950
5904
  return {
5951
- modified: false,
5952
- message: `Could not auto-modify ${configFile}. Add transpilePackages: ['@fragments-sdk/ui'] manually.`
5905
+ success: errors.length === 0,
5906
+ updated,
5907
+ skipped,
5908
+ errors
5953
5909
  };
5954
5910
  }
5955
- async function createScssSeeds(root, brand) {
5956
- const scssLocations = [
5957
- "src/app/globals.scss",
5958
- "app/globals.scss",
5959
- "src/styles/globals.scss",
5960
- "src/globals.scss",
5961
- "styles/globals.scss"
5962
- ];
5963
- for (const loc of scssLocations) {
5964
- const fullPath2 = join11(root, loc);
5965
- if (await fileExists(fullPath2)) {
5966
- const content = await readFile9(fullPath2, "utf-8");
5967
- if (content.includes("@fragments-sdk/ui/styles")) {
5968
- return { modified: false, message: `SCSS seeds already configured in ${loc}` };
5969
- }
5970
- const seedContent = generateScssSeedImport(brand);
5971
- const newContent = seedContent + "\n" + content;
5972
- await writeFile9(fullPath2, newContent, "utf-8");
5973
- return { modified: true, message: `Added SCSS seed import to ${loc}` };
5974
- }
5975
- }
5976
- const targetDir = await fileExists(join11(root, "src/app")) ? "src/app" : await fileExists(join11(root, "src")) ? "src/styles" : "styles";
5977
- const targetPath = join11(targetDir, "globals.scss");
5978
- const fullPath = join11(root, targetPath);
5979
- await mkdir7(dirname4(fullPath), { recursive: true });
5980
- await writeFile9(fullPath, generateScssSeedImport(brand), "utf-8");
5981
- return { modified: true, message: `Created ${targetPath} with SCSS seed configuration` };
5982
- }
5983
- async function setupMcpConfig(root) {
5984
- const mcpConfigPaths = [
5985
- ".cursor/mcp.json",
5986
- ".vscode/mcp.json"
5987
- ];
5988
- for (const configPath of mcpConfigPaths) {
5989
- const fullPath = join11(root, configPath);
5990
- if (await fileExists(fullPath)) {
5991
- try {
5992
- const content = await readFile9(fullPath, "utf-8");
5993
- const config = JSON.parse(content);
5994
- const servers = config.mcpServers || {};
5995
- const hasFragments = Object.values(servers).some((server) => {
5996
- const s = server;
5997
- return s.args?.some((arg) => arg.includes("@fragments-sdk/mcp"));
5998
- });
5999
- if (hasFragments) {
6000
- return { modified: false, message: `MCP server already configured in ${configPath}` };
5911
+ function computePatch(fileContent, fragment, meta) {
5912
+ const changes = [];
5913
+ let content = fileContent;
5914
+ const localSourceProps = Object.fromEntries(
5915
+ Object.entries(meta.props).filter(([_, p]) => p.source === "local")
5916
+ );
5917
+ const addedProps = [];
5918
+ for (const name of Object.keys(localSourceProps)) {
5919
+ if (!(name in fragment.props)) {
5920
+ addedProps.push(name);
5921
+ }
5922
+ }
5923
+ const removedProps = [];
5924
+ for (const name of Object.keys(fragment.props)) {
5925
+ if (!(name in localSourceProps)) {
5926
+ removedProps.push(name);
5927
+ }
5928
+ }
5929
+ if (addedProps.length > 0) {
5930
+ const propsBlockEnd = findPropsBlockEnd(content);
5931
+ if (propsBlockEnd !== -1) {
5932
+ const newEntries = addedProps.map((name) => {
5933
+ const prop = localSourceProps[name];
5934
+ return formatPropEntry(name, prop);
5935
+ }).join("\n");
5936
+ content = content.slice(0, propsBlockEnd) + newEntries + "\n " + content.slice(propsBlockEnd);
5937
+ changes.push(`Added props: ${addedProps.join(", ")}`);
5938
+ }
5939
+ }
5940
+ if (removedProps.length > 0) {
5941
+ for (const name of removedProps) {
5942
+ const propRegex = new RegExp(`([ \\t]*)${escapeRegex(name)}:\\s*\\{`, "g");
5943
+ const match = propRegex.exec(content);
5944
+ if (match) {
5945
+ const indent = match[1];
5946
+ const startIdx = match.index;
5947
+ const endIdx = findMatchingBrace2(content, match.index + match[0].length - 1);
5948
+ if (endIdx !== -1) {
5949
+ let lineEnd = endIdx + 1;
5950
+ if (content[lineEnd] === ",") lineEnd++;
5951
+ if (content[lineEnd] === "\n") lineEnd++;
5952
+ const removedBlock = content.slice(startIdx, lineEnd);
5953
+ const commented = removedBlock.split("\n").map((line) => line ? `${indent}// [drift:removed] ${line.trimStart()}` : "").join("\n");
5954
+ content = content.slice(0, startIdx) + commented + content.slice(lineEnd);
6001
5955
  }
6002
- servers.fragments = {
6003
- command: "npx",
6004
- args: ["@fragments-sdk/mcp"]
6005
- };
6006
- config.mcpServers = servers;
6007
- await writeFile9(fullPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
6008
- return { modified: true, message: `Added Fragments MCP server to ${configPath}` };
6009
- } catch {
6010
- return { modified: false, message: `Could not parse ${configPath}` };
6011
5956
  }
6012
5957
  }
5958
+ changes.push(`Removed props: ${removedProps.join(", ")}`);
6013
5959
  }
6014
- const cursorDir = join11(root, ".cursor");
6015
- const cursorMcpPath = join11(cursorDir, "mcp.json");
6016
- await mkdir7(cursorDir, { recursive: true });
6017
- const mcpConfig = {
6018
- mcpServers: {
6019
- fragments: {
6020
- command: "npx",
6021
- args: ["@fragments-sdk/mcp"]
6022
- }
6023
- }
6024
- };
6025
- await writeFile9(cursorMcpPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
6026
- return { modified: true, message: "Created .cursor/mcp.json with Fragments MCP server" };
5960
+ if (meta.composition && !fragment.ai?.compositionPattern) {
5961
+ changes.push(`Composition: "${meta.composition.pattern}" pattern detected`);
5962
+ }
5963
+ return { updatedContent: content, changes };
6027
5964
  }
6028
- function generateScssSeedImport(brand) {
6029
- const brandColor = brand || "#0066ff";
6030
- return `@use '@fragments-sdk/ui/styles' with (
6031
- $fui-brand: ${brandColor},
6032
- $fui-neutral: "stone",
6033
- $fui-density: "default",
6034
- $fui-radius-style: "rounded"
6035
- );
6036
- `;
5965
+ function findPropsBlockEnd(content) {
5966
+ const propsStart = content.search(/\bprops:\s*\{/);
5967
+ if (propsStart === -1) return -1;
5968
+ const braceStart = content.indexOf("{", propsStart);
5969
+ return findMatchingBrace2(content, braceStart);
6037
5970
  }
6038
- async function setup(options = {}) {
6039
- const root = resolve9(options.root ?? process.cwd());
6040
- const actions = [];
6041
- const errors = [];
6042
- console.log(pc23.cyan(`
6043
- ${BRAND.name} Setup
6044
- `));
6045
- const framework = await detectFramework(root);
6046
- const frameworkLabel = framework === "nextjs-app" ? "Next.js (App Router)" : framework === "nextjs-pages" ? "Next.js (Pages Router)" : framework === "vite" ? "Vite" : "Unknown";
6047
- console.log(` ${pc23.dim("Framework:")} ${frameworkLabel}`);
6048
- const entryFile = await findEntryFile(root, framework);
6049
- if (entryFile) {
6050
- console.log(` ${pc23.dim("Entry:")} ${entryFile}`);
6051
- } else {
6052
- console.log(` ${pc23.yellow("!")} Could not detect entry file`);
6053
- }
6054
- console.log();
6055
- if (entryFile) {
6056
- try {
6057
- const result = await addStylesImport(root, entryFile);
6058
- const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
6059
- console.log(` ${icon} ${result.message}`);
6060
- if (result.modified) actions.push(result.message);
6061
- } catch (error) {
6062
- const msg = `Failed to add styles import: ${error instanceof Error ? error.message : error}`;
6063
- console.log(` ${pc23.red("\u2717")} ${msg}`);
6064
- errors.push(msg);
5971
+ function findMatchingBrace2(content, start) {
5972
+ let depth = 0;
5973
+ let inString = null;
5974
+ let escaped = false;
5975
+ for (let i = start; i < content.length; i++) {
5976
+ const ch = content[i];
5977
+ if (escaped) {
5978
+ escaped = false;
5979
+ continue;
6065
5980
  }
6066
- }
6067
- if (entryFile) {
6068
- try {
6069
- const result = await addThemeProvider(root, entryFile, framework);
6070
- const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
6071
- console.log(` ${icon} ${result.message}`);
6072
- if (result.modified) actions.push(result.message);
6073
- } catch (error) {
6074
- const msg = `Failed to add ThemeProvider: ${error instanceof Error ? error.message : error}`;
6075
- console.log(` ${pc23.red("\u2717")} ${msg}`);
6076
- errors.push(msg);
5981
+ if (ch === "\\") {
5982
+ escaped = true;
5983
+ continue;
6077
5984
  }
6078
- }
6079
- if (framework === "nextjs-app" || framework === "nextjs-pages") {
6080
- try {
6081
- const result = await addTranspilePackages(root);
6082
- const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
6083
- console.log(` ${icon} ${result.message}`);
6084
- if (result.modified) actions.push(result.message);
6085
- } catch (error) {
6086
- const msg = `Failed to update next.config: ${error instanceof Error ? error.message : error}`;
6087
- console.log(` ${pc23.red("\u2717")} ${msg}`);
6088
- errors.push(msg);
5985
+ if (inString) {
5986
+ if (ch === inString) inString = null;
5987
+ continue;
6089
5988
  }
6090
- }
6091
- if (options.scss || options.brand) {
6092
- try {
6093
- const result = await createScssSeeds(root, options.brand);
6094
- const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
6095
- console.log(` ${icon} ${result.message}`);
6096
- if (result.modified) actions.push(result.message);
6097
- } catch (error) {
6098
- const msg = `Failed to create SCSS seeds: ${error instanceof Error ? error.message : error}`;
6099
- console.log(` ${pc23.red("\u2717")} ${msg}`);
6100
- errors.push(msg);
5989
+ if (ch === "'" || ch === '"' || ch === "`") {
5990
+ inString = ch;
5991
+ continue;
6101
5992
  }
6102
- }
6103
- if (options.mcp) {
6104
- try {
6105
- const result = await setupMcpConfig(root);
6106
- const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
6107
- console.log(` ${icon} ${result.message}`);
6108
- if (result.modified) actions.push(result.message);
6109
- } catch (error) {
6110
- const msg = `Failed to configure MCP: ${error instanceof Error ? error.message : error}`;
6111
- console.log(` ${pc23.red("\u2717")} ${msg}`);
6112
- errors.push(msg);
5993
+ if (ch === "{") depth++;
5994
+ else if (ch === "}") {
5995
+ depth--;
5996
+ if (depth === 0) return i;
6113
5997
  }
6114
5998
  }
6115
- console.log();
6116
- if (errors.length > 0) {
6117
- console.log(pc23.red(` ${errors.length} error(s) occurred during setup`));
6118
- } else if (actions.length > 0) {
6119
- console.log(pc23.green(` \u2713 Setup complete \u2014 ${actions.length} file(s) modified`));
5999
+ return -1;
6000
+ }
6001
+ function formatPropEntry(name, prop) {
6002
+ const lines = [];
6003
+ lines.push(` ${name}: {`);
6004
+ lines.push(` type: '${prop.typeKind}',`);
6005
+ if (prop.description) {
6006
+ lines.push(` description: ${JSON.stringify(prop.description)},`);
6120
6007
  } else {
6121
- console.log(pc23.green(" \u2713 Already configured \u2014 no changes needed"));
6008
+ lines.push(` description: '', // TODO: add description`);
6122
6009
  }
6123
- console.log();
6124
- console.log(pc23.dim(" Next steps:"));
6125
- if (!options.scss && !options.brand) {
6126
- console.log(pc23.dim(" \u2022 Run with --scss to add build-time theme seeds"));
6010
+ if (prop.required) {
6011
+ lines.push(` required: true,`);
6127
6012
  }
6128
- if (!options.mcp) {
6129
- console.log(pc23.dim(" \u2022 Run with --mcp to configure AI tooling (MCP server)"));
6013
+ if (prop.values && prop.values.length > 0) {
6014
+ lines.push(` values: [${prop.values.map((v) => `'${v}'`).join(", ")}],`);
6130
6015
  }
6131
- console.log(pc23.dim(" \u2022 Run `fragments doctor` to verify your setup"));
6132
- console.log(pc23.dim(" \u2022 Visit https://usefragments.com/getting-started"));
6133
- console.log();
6134
- return {
6135
- success: errors.length === 0,
6136
- actions,
6137
- errors
6138
- };
6016
+ if (prop.default !== void 0) {
6017
+ lines.push(` default: '${prop.default}',`);
6018
+ }
6019
+ lines.push(` },`);
6020
+ return lines.join("\n");
6021
+ }
6022
+ function escapeRegex(str) {
6023
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6139
6024
  }
6140
6025
 
6141
6026
  // src/bin.ts
6142
- var __dirname = dirname5(fileURLToPath(import.meta.url));
6143
- var pkg = JSON.parse(readFileSync(join12(__dirname, "../package.json"), "utf-8"));
6027
+ var __dirname = dirname4(fileURLToPath(import.meta.url));
6028
+ var pkg = JSON.parse(readFileSync(join11(__dirname, "../package.json"), "utf-8"));
6144
6029
  var program = new Command();
6145
6030
  program.name(BRAND.cliCommand).description(`${BRAND.name} - Design system documentation and compliance tool`).version(pkg.version);
6146
- program.command("validate").description("Validate fragment files").option("-c, --config <path>", "Path to config file").option("--schema", "Validate fragment schema only").option("--coverage", "Validate coverage only").option("--snippets", "Validate snippet/render policy only").option("--snippet-mode <mode>", "Override snippet policy mode (warn|error)").option("--component-start <name>", "Start component name for alphabetical snippet batch validation").option("--component-limit <n>", "Component count for alphabetical snippet batch validation", (value) => Number.parseInt(value, 10)).action(async (options) => {
6031
+ program.command("validate").description("Validate fragment files").option("-c, --config <path>", "Path to config file").option("--schema", "Validate fragment schema only").option("--coverage", "Validate coverage only").option("--snippets", "Validate snippet/render policy only").option("--drift", "Detect metadata drift between source and fragments").option("--tsconfig <path>", "Path to tsconfig.json (for drift detection)").option("--snippet-mode <mode>", "Override snippet policy mode (warn|error)").option("--component-start <name>", "Start component name for alphabetical snippet batch validation").option("--component-limit <n>", "Component count for alphabetical snippet batch validation", (value) => Number.parseInt(value, 10)).action(async (options) => {
6147
6032
  try {
6148
6033
  const result = await validate(options);
6149
6034
  if (!result.valid) {
@@ -6154,6 +6039,22 @@ program.command("validate").description("Validate fragment files").option("-c, -
6154
6039
  process.exit(1);
6155
6040
  }
6156
6041
  });
6042
+ program.command("sync").description("Auto-update fragment files from component source").option("-c, --config <path>", "Path to config file").option("--tsconfig <path>", "Path to tsconfig.json").option("--dry-run", "Preview changes without writing").option("--component <name>", "Sync specific component only").action(async (options) => {
6043
+ try {
6044
+ const result = await sync({
6045
+ config: options.config,
6046
+ tsconfig: options.tsconfig,
6047
+ dryRun: options.dryRun,
6048
+ component: options.component
6049
+ });
6050
+ if (!result.success) {
6051
+ process.exit(1);
6052
+ }
6053
+ } catch (error) {
6054
+ console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6055
+ process.exit(1);
6056
+ }
6057
+ });
6157
6058
  program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAND.dataDir}/ directory`).option("-c, --config <path>", "Path to config file").option("-o, --output <path>", "Output file path").option("--registry", `Also generate ${BRAND.dataDir}/${BRAND.registryFile} and ${BRAND.contextFile}`).option("--registry-only", `Only generate ${BRAND.dataDir}/ directory (skip ${BRAND.outFile})`).option("--from-source", "Build from source code (zero-config, no fragment files needed)").option("--skip-usage", "Skip usage analysis when building from source").option("--skip-storybook", "Skip Storybook parsing when building from source").option("-v, --verbose", "Verbose output").action(async (options) => {
6158
6059
  try {
6159
6060
  const result = await build({
@@ -6508,7 +6409,7 @@ Make sure the dev server is running: ${BRAND.cliCommand} dev`));
6508
6409
  });
6509
6410
  program.command("view").description(`Generate a static HTML viewer for ${BRAND.outFile}`).option("-i, --input <path>", `Path to ${BRAND.outFile}`, BRAND.outFile).option("-o, --output <path>", "Output HTML file path", BRAND.viewerHtmlFile).option("--open", "Open in browser after generation").action(async (options) => {
6510
6411
  try {
6511
- const { generateViewerFromJson } = await import("./static-viewer-NUBFPKWH.js");
6412
+ const { generateViewerFromJson } = await import("./static-viewer-5GXH2MGE.js");
6512
6413
  const fs2 = await import("fs/promises");
6513
6414
  const path = await import("path");
6514
6415
  const inputPath = path.resolve(process.cwd(), options.input);
@@ -6571,14 +6472,20 @@ program.command("setup").description("Configure @fragments-sdk/ui in a consumer
6571
6472
  process.exit(1);
6572
6473
  }
6573
6474
  });
6574
- program.command("init").description("Initialize fragments in a project (interactive by default)").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive mode - auto-detect and use defaults").option("--scan <path>", "Scan a TypeScript component directory and generate fragment files").action(async (options) => {
6475
+ program.command("init").description("Initialize fragments in a project (zero-config by default)").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive mode (now the default)").option("--configure", "Interactive mode for theme seeds, snapshots, etc.").option("--scan <path>", "Scan a TypeScript component directory and generate fragment files").option("--enrich", "Use AI to fill knowledge fields during --scan (requires API key)").option("--dry-run", "Show what --enrich would generate without calling API").option("--provider <provider>", "AI provider for enrichment: anthropic or openai").option("--api-key <key>", "API key for AI enrichment").option("--model <model>", "Override AI model for enrichment").action(async (options) => {
6575
6476
  try {
6576
- const { init } = await import("./init-UFGK5TCN.js");
6477
+ const { init } = await import("./init-ZSX3NRCZ.js");
6577
6478
  const result = await init({
6578
6479
  projectRoot: process.cwd(),
6579
6480
  force: options.force,
6580
6481
  yes: options.scan ? true : options.yes,
6581
- scan: options.scan
6482
+ scan: options.scan,
6483
+ configure: options.configure,
6484
+ enrich: options.enrich,
6485
+ dryRun: options.dryRun,
6486
+ provider: options.provider,
6487
+ apiKey: options.apiKey,
6488
+ model: options.model
6582
6489
  });
6583
6490
  if (!result.success) {
6584
6491
  console.error(pc24.red("\nInit failed with errors:"));
@@ -6594,7 +6501,7 @@ program.command("init").description("Initialize fragments in a project (interact
6594
6501
  });
6595
6502
  program.command("snapshot").description("Run visual snapshot tests per component variant").option("-p, --port <port>", "Port of running dev server (skips starting one)").option("--update", "Update existing snapshots instead of comparing").option("--component <name>", "Filter to a specific component").option("--spec <path>", "Path to snapshot spec file").option("--ci", "CI mode - exit 1 on mismatch").action(async (options) => {
6596
6503
  try {
6597
- const { snapshot } = await import("./snapshot-SV2JOFZH.js");
6504
+ const { snapshot } = await import("./snapshot-XOISO2IS.js");
6598
6505
  const result = await snapshot({
6599
6506
  port: options.port,
6600
6507
  update: options.update,
@@ -6612,7 +6519,7 @@ program.command("snapshot").description("Run visual snapshot tests per component
6612
6519
  });
6613
6520
  program.command("tokens").description("Discover and list design tokens from CSS/SCSS files").option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--categories", "Group tokens by category").option("--theme <theme>", "Filter by theme name").option("--category <category>", "Filter by category (color, spacing, typography, etc.)").option("--verbose", "Show all tokens (no truncation)").action(async (options) => {
6614
6521
  try {
6615
- const { tokens } = await import("./tokens-CE46OTMD.js");
6522
+ const { tokens } = await import("./tokens-T6SIVUT5.js");
6616
6523
  const result = await tokens({
6617
6524
  config: options.config,
6618
6525
  json: options.json,
@@ -6631,7 +6538,7 @@ program.command("tokens").description("Discover and list design tokens from CSS/
6631
6538
  });
6632
6539
  program.command("generate").description("Generate fragment files from component source code").argument("[component]", "Specific component name to generate (optional)").option("--force", "Overwrite existing fragment files").option("--pattern <glob>", "Pattern for component files", "src/components/**/*.tsx").action(async (component, options) => {
6633
6540
  try {
6634
- const { generate } = await import("./generate-FBHSXR3D.js");
6541
+ const { generate } = await import("./generate-RJFS2JWA.js");
6635
6542
  const result = await generate({
6636
6543
  projectRoot: process.cwd(),
6637
6544
  component,
@@ -6676,7 +6583,7 @@ program.command("perf").description("Profile component bundle sizes and performa
6676
6583
  program.command("test").description("Run interaction tests for fragments with play functions").option("-c, --config <path>", "Path to config file").option("--component <name>", "Filter by component name").option("--tags <tags>", "Filter by tags (comma-separated)").option("--grep <pattern>", "Filter by variant name pattern").option("--exclude <pattern>", "Exclude tests matching pattern").option("--parallel <count>", "Number of parallel browser contexts", parseInt, 4).option("--timeout <ms>", "Timeout per test in milliseconds", parseInt, 3e4).option("--retries <count>", "Number of retries for failed tests", parseInt, 0).option("--bail", "Stop on first failure").option("--browser <name>", "Browser to use (chromium, firefox, webkit)", "chromium").option("--headed", "Run in headed mode (show browser)").option("--a11y", "Run accessibility checks with axe-core").option("--visual", "Capture screenshots for visual regression").option("--update-snapshots", "Update visual snapshots").option("--watch", "Watch mode - re-run on file changes").option("--reporters <names>", "Reporters to use (console, junit, json)", "console").option("-o, --output <dir>", "Output directory for results", "./test-results").option("--server-url <url>", "URL of running dev server (skips starting server)").option("-p, --port <port>", "Port for dev server", parseInt, 6006).option("--ci", "CI mode - non-interactive, exit with code 1 on failure").option("--list", "List available tests without running them").action(async (options) => {
6677
6584
  try {
6678
6585
  const { config, configDir } = await loadConfig(options.config);
6679
- const { runTestCommand, listTests } = await import("./test-Z5LVO724.js");
6586
+ const { runTestCommand, listTests } = await import("./test-SI4NSHQX.js");
6680
6587
  if (options.list) {
6681
6588
  await listTests(config, configDir, {
6682
6589
  component: options.component,