@fragments-sdk/cli 0.11.1 → 0.13.0

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 (89) hide show
  1. package/dist/ai-client-I6MDWNYA.js +21 -0
  2. package/dist/bin.js +419 -410
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-HRFUSSZI.js → chunk-3SOAPJDX.js} +2 -2
  5. package/dist/{chunk-D5PYOXEI.js → chunk-4K7EAQ5L.js} +148 -13
  6. package/dist/{chunk-D5PYOXEI.js.map → chunk-4K7EAQ5L.js.map} +1 -1
  7. package/dist/chunk-DXX6HADE.js +443 -0
  8. package/dist/chunk-DXX6HADE.js.map +1 -0
  9. package/dist/chunk-EYXVAMEX.js +626 -0
  10. package/dist/chunk-EYXVAMEX.js.map +1 -0
  11. package/dist/{chunk-ZM4ZQZWZ.js → chunk-FO6EBJWP.js} +39 -37
  12. package/dist/chunk-FO6EBJWP.js.map +1 -0
  13. package/dist/{chunk-OQO55NKV.js → chunk-QM7SVOGF.js} +120 -12
  14. package/dist/chunk-QM7SVOGF.js.map +1 -0
  15. package/dist/{chunk-5G3VZH43.js → chunk-RF3C6LGA.js} +281 -351
  16. package/dist/chunk-RF3C6LGA.js.map +1 -0
  17. package/dist/{chunk-WXSR2II7.js → chunk-SM674YAS.js} +58 -6
  18. package/dist/chunk-SM674YAS.js.map +1 -0
  19. package/dist/chunk-SXTKFDCR.js +104 -0
  20. package/dist/chunk-SXTKFDCR.js.map +1 -0
  21. package/dist/{chunk-PW7QTQA6.js → chunk-UV5JQV3R.js} +2 -2
  22. package/dist/core/index.js +13 -1
  23. package/dist/{discovery-NEOY4MPN.js → discovery-VSGC76JN.js} +3 -3
  24. package/dist/{generate-FBHSXR3D.js → generate-QZXOXYFW.js} +4 -4
  25. package/dist/index.js +7 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/init-XK6PRUE5.js +636 -0
  28. package/dist/init-XK6PRUE5.js.map +1 -0
  29. package/dist/mcp-bin.js +2 -2
  30. package/dist/{scan-CJF2DOQW.js → scan-CHQHXWVD.js} +6 -6
  31. package/dist/scan-generate-U3RFVDTX.js +1115 -0
  32. package/dist/scan-generate-U3RFVDTX.js.map +1 -0
  33. package/dist/{service-TQYWY65E.js → service-MMEKG4MZ.js} +3 -3
  34. package/dist/{snapshot-SV2JOFZH.js → snapshot-53TUR3HW.js} +2 -2
  35. package/dist/{static-viewer-NUBFPKWH.js → static-viewer-KKCR4KXR.js} +3 -3
  36. package/dist/static-viewer-KKCR4KXR.js.map +1 -0
  37. package/dist/{test-Z5LVO724.js → test-5UCKXYSC.js} +4 -4
  38. package/dist/{tokens-CE46OTMD.js → tokens-L46MK5AW.js} +5 -5
  39. package/dist/{viewer-DLLJIMCK.js → viewer-M2EQQSGE.js} +14 -14
  40. package/dist/viewer-M2EQQSGE.js.map +1 -0
  41. package/package.json +11 -9
  42. package/src/ai-client.ts +156 -0
  43. package/src/bin.ts +99 -2
  44. package/src/build.ts +95 -33
  45. package/src/commands/__tests__/drift-sync.test.ts +252 -0
  46. package/src/commands/__tests__/scan-generate.test.ts +497 -45
  47. package/src/commands/enhance.ts +11 -35
  48. package/src/commands/govern.ts +122 -0
  49. package/src/commands/init.ts +288 -260
  50. package/src/commands/scan-generate.ts +740 -139
  51. package/src/commands/scan.ts +37 -32
  52. package/src/commands/setup.ts +143 -52
  53. package/src/commands/sync.ts +357 -0
  54. package/src/commands/validate.ts +43 -1
  55. package/src/core/component-extractor.test.ts +282 -0
  56. package/src/core/component-extractor.ts +1030 -0
  57. package/src/core/discovery.ts +93 -7
  58. package/src/service/enhance/props-extractor.ts +235 -13
  59. package/src/validators.ts +236 -0
  60. package/src/viewer/vite-plugin.ts +1 -1
  61. package/dist/chunk-5G3VZH43.js.map +0 -1
  62. package/dist/chunk-OQO55NKV.js.map +0 -1
  63. package/dist/chunk-WXSR2II7.js.map +0 -1
  64. package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
  65. package/dist/init-UFGK5TCN.js +0 -867
  66. package/dist/init-UFGK5TCN.js.map +0 -1
  67. package/dist/scan-generate-SJAN5MVI.js +0 -691
  68. package/dist/scan-generate-SJAN5MVI.js.map +0 -1
  69. package/dist/viewer-DLLJIMCK.js.map +0 -1
  70. package/src/ai.ts +0 -266
  71. package/src/commands/init-framework.ts +0 -414
  72. package/src/mcp/bin.ts +0 -36
  73. package/src/migrate/bin.ts +0 -114
  74. package/src/theme/index.ts +0 -77
  75. package/src/viewer/bin.ts +0 -86
  76. package/src/viewer/cli/health.ts +0 -256
  77. package/src/viewer/cli/index.ts +0 -33
  78. package/src/viewer/cli/scan.ts +0 -124
  79. package/src/viewer/cli/utils.ts +0 -174
  80. /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
  81. /package/dist/{chunk-HRFUSSZI.js.map → chunk-3SOAPJDX.js.map} +0 -0
  82. /package/dist/{chunk-PW7QTQA6.js.map → chunk-UV5JQV3R.js.map} +0 -0
  83. /package/dist/{scan-CJF2DOQW.js.map → discovery-VSGC76JN.js.map} +0 -0
  84. /package/dist/{generate-FBHSXR3D.js.map → generate-QZXOXYFW.js.map} +0 -0
  85. /package/dist/{service-TQYWY65E.js.map → scan-CHQHXWVD.js.map} +0 -0
  86. /package/dist/{static-viewer-NUBFPKWH.js.map → service-MMEKG4MZ.js.map} +0 -0
  87. /package/dist/{snapshot-SV2JOFZH.js.map → snapshot-53TUR3HW.js.map} +0 -0
  88. /package/dist/{test-Z5LVO724.js.map → test-5UCKXYSC.js.map} +0 -0
  89. /package/dist/{tokens-CE46OTMD.js.map → tokens-L46MK5AW.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-DXX6HADE.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-RF3C6LGA.js";
26
+ import {
27
+ createComponentExtractor
28
+ } from "./chunk-EYXVAMEX.js";
16
29
  import {
17
30
  scan
18
- } from "./chunk-ZM4ZQZWZ.js";
31
+ } from "./chunk-FO6EBJWP.js";
19
32
  import {
20
33
  loadConfig,
21
- loadFragmentFile
22
- } from "./chunk-HRFUSSZI.js";
34
+ loadFragmentFile,
35
+ parseFragmentFile
36
+ } from "./chunk-3SOAPJDX.js";
23
37
  import {
24
38
  discoverFragmentFiles
25
- } from "./chunk-WXSR2II7.js";
39
+ } from "./chunk-SM674YAS.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-4K7EAQ5L.js";
42
56
  import "./chunk-D2CDBRNU.js";
43
57
  import {
44
58
  BRAND,
@@ -46,15 +60,15 @@ import {
46
60
  formatBytes,
47
61
  generateContext,
48
62
  resolvePerformanceConfig
49
- } from "./chunk-OQO55NKV.js";
63
+ } from "./chunk-QM7SVOGF.js";
50
64
  import "./chunk-Z7EY4VHE.js";
51
65
 
52
66
  // src/bin.ts
53
67
  import { Command } from "commander";
54
- import pc24 from "picocolors";
68
+ import pc25 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-M2EQQSGE.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,372 +5807,322 @@ ${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;
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"));
5798
5820
  }
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";
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
+ }
5831
+ }
5832
+ console.log();
5803
5833
  }
5804
- if (await fileExists(join11(root, "pages/_app.tsx")) || await fileExists(join11(root, "pages/_app.ts"))) {
5805
- return "nextjs-pages";
5834
+ if (result.skipped.length > 0) {
5835
+ console.log(pc23.dim(`Skipped ${result.skipped.length}: ${result.skipped.map((s) => s.name).join(", ")}
5836
+ `));
5806
5837
  }
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";
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}`);
5842
+ }
5843
+ console.log();
5809
5844
  }
5810
- if (await fileExists(join11(root, "vite.config.ts")) || await fileExists(join11(root, "vite.config.js"))) {
5811
- return "vite";
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"));
5812
5847
  }
5813
- return "unknown";
5848
+ return result;
5814
5849
  }
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
- );
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 };
5846
5857
  }
5847
- for (const candidate of candidates) {
5848
- if (await fileExists(join11(root, candidate))) {
5849
- return candidate;
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
+ }
5850
5900
  }
5901
+ } finally {
5902
+ extractor.dispose();
5851
5903
  }
5852
- return null;
5904
+ return {
5905
+ success: errors.length === 0,
5906
+ updated,
5907
+ skipped,
5908
+ errors
5909
+ };
5853
5910
  }
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;
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);
5955
+ }
5956
+ }
5859
5957
  }
5958
+ changes.push(`Removed props: ${removedProps.join(", ")}`);
5860
5959
  }
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;
5960
+ if (meta.composition && !fragment.ai?.compositionPattern) {
5961
+ changes.push(`Composition: "${meta.composition.pattern}" pattern detected`);
5876
5962
  }
5877
- await writeFile9(fullPath, newContent, "utf-8");
5878
- return { modified: true, message: `Added styles import to ${entryFile}` };
5963
+ return { updatedContent: content, changes };
5879
5964
  }
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;
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);
5970
+ }
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;
5901
5980
  }
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
- };
5981
+ if (ch === "\\") {
5982
+ escaped = true;
5983
+ continue;
5984
+ }
5985
+ if (inString) {
5986
+ if (ch === inString) inString = null;
5987
+ continue;
5988
+ }
5989
+ if (ch === "'" || ch === '"' || ch === "`") {
5990
+ inString = ch;
5991
+ continue;
5992
+ }
5993
+ if (ch === "{") depth++;
5994
+ else if (ch === "}") {
5995
+ depth--;
5996
+ if (depth === 0) return i;
5910
5997
  }
5911
- await writeFile9(fullPath, newContent, "utf-8");
5912
- return { modified: true, message: `Added provider imports to ${entryFile}. Wrap {children} with ThemeProvider.` };
5913
5998
  }
5914
- return { modified: false, message: "Manual ThemeProvider setup needed \u2014 see https://usefragments.com/getting-started#provider-setup" };
5999
+ return -1;
5915
6000
  }
5916
- async function addTranspilePackages(root) {
5917
- const configFile = await findNextConfig(root);
5918
- if (!configFile) {
5919
- return { modified: false, message: "No next.config found" };
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)},`);
6007
+ } else {
6008
+ lines.push(` description: '', // TODO: add description`);
5920
6009
  }
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}` };
6010
+ if (prop.required) {
6011
+ lines.push(` required: true,`);
5925
6012
  }
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
- };
6013
+ if (prop.values && prop.values.length > 0) {
6014
+ lines.push(` values: [${prop.values.map((v) => `'${v}'`).join(", ")}],`);
5931
6015
  }
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}` };
5948
- }
6016
+ if (prop.default !== void 0) {
6017
+ lines.push(` default: '${prop.default}',`);
5949
6018
  }
5950
- return {
5951
- modified: false,
5952
- message: `Could not auto-modify ${configFile}. Add transpilePackages: ['@fragments-sdk/ui'] manually.`
5953
- };
6019
+ lines.push(` },`);
6020
+ return lines.join("\n");
5954
6021
  }
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` };
6022
+ function escapeRegex(str) {
6023
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5982
6024
  }
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}` };
6001
- }
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
- }
6012
- }
6025
+
6026
+ // src/commands/govern.ts
6027
+ import pc24 from "picocolors";
6028
+ async function governCheck(options = {}) {
6029
+ console.log(pc24.cyan(`
6030
+ ${BRAND.name} Governance Check
6031
+ `));
6032
+ const { cliCheck, formatVerdict } = await import("@fragments-sdk/govern");
6033
+ const result = await cliCheck(options);
6034
+ const output = formatVerdict(result.verdict, options.format ?? "summary");
6035
+ if (!options.quiet) {
6036
+ console.log(output);
6037
+ console.log();
6013
6038
  }
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" };
6027
- }
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
- `;
6037
- }
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
6039
+ if (result.verdict.passed) {
6040
+ console.log(pc24.green(`\u2713 Governance check passed (score: ${result.verdict.score}/100)
6044
6041
  `));
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
6042
  } 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);
6065
- }
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);
6077
- }
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);
6089
- }
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);
6101
- }
6043
+ console.log(pc24.red(`\u2717 Governance check failed (score: ${result.verdict.score}/100)
6044
+ `));
6102
6045
  }
6103
- if (options.mcp) {
6046
+ return { exitCode: result.exitCode };
6047
+ }
6048
+ async function governInit(options = {}) {
6049
+ const { writeFile: writeFile10 } = await import("fs/promises");
6050
+ const { resolve: resolve9 } = await import("path");
6051
+ const { generateConfigTemplate } = await import("@fragments-sdk/govern");
6052
+ const outputPath = resolve9(options.output ?? "govern.config.ts");
6053
+ const template = generateConfigTemplate();
6054
+ await writeFile10(outputPath, template, "utf-8");
6055
+ console.log(pc24.green(`\u2713 Created ${outputPath}
6056
+ `));
6057
+ }
6058
+ async function governReport() {
6059
+ const { readFile: readFile10 } = await import("fs/promises");
6060
+ console.log(pc24.cyan(`
6061
+ ${BRAND.name} Governance Report
6062
+ `));
6063
+ let raw;
6064
+ try {
6065
+ raw = await readFile10(".govern-audit.jsonl", "utf-8");
6066
+ } catch {
6067
+ console.log(pc24.yellow("No audit log found (.govern-audit.jsonl)\n"));
6068
+ console.log(pc24.dim("Run `fragments govern check` to generate audit data.\n"));
6069
+ return;
6070
+ }
6071
+ const lines = raw.trim().split("\n").filter(Boolean);
6072
+ if (lines.length === 0) {
6073
+ console.log(pc24.yellow("Audit log is empty.\n"));
6074
+ return;
6075
+ }
6076
+ let totalScore = 0;
6077
+ let passCount = 0;
6078
+ let totalViolations = 0;
6079
+ for (const line of lines) {
6104
6080
  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);
6081
+ const entry = JSON.parse(line);
6082
+ totalScore += entry.score ?? 0;
6083
+ if (entry.passed) passCount++;
6084
+ totalViolations += entry.violationCount ?? 0;
6085
+ } catch {
6113
6086
  }
6114
6087
  }
6088
+ const avgScore = Math.round(totalScore / lines.length);
6089
+ const passRate = Math.round(passCount / lines.length * 100);
6090
+ console.log(` Checks: ${lines.length}`);
6091
+ console.log(` Pass rate: ${passRate}%`);
6092
+ console.log(` Avg score: ${avgScore}/100`);
6093
+ console.log(` Violations: ${totalViolations} total`);
6115
6094
  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`));
6120
- } else {
6121
- console.log(pc23.green(" \u2713 Already configured \u2014 no changes needed"));
6122
- }
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"));
6127
- }
6128
- if (!options.mcp) {
6129
- console.log(pc23.dim(" \u2022 Run with --mcp to configure AI tooling (MCP server)"));
6130
- }
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
- };
6139
6095
  }
6140
6096
 
6141
6097
  // src/bin.ts
6142
- var __dirname = dirname5(fileURLToPath(import.meta.url));
6143
- var pkg = JSON.parse(readFileSync(join12(__dirname, "../package.json"), "utf-8"));
6098
+ var __dirname = dirname4(fileURLToPath(import.meta.url));
6099
+ var pkg = JSON.parse(readFileSync(join11(__dirname, "../package.json"), "utf-8"));
6144
6100
  var program = new Command();
6145
6101
  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) => {
6102
+ 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
6103
  try {
6148
6104
  const result = await validate(options);
6149
6105
  if (!result.valid) {
6150
6106
  process.exit(1);
6151
6107
  }
6152
6108
  } catch (error) {
6153
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6109
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6110
+ process.exit(1);
6111
+ }
6112
+ });
6113
+ 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) => {
6114
+ try {
6115
+ const result = await sync({
6116
+ config: options.config,
6117
+ tsconfig: options.tsconfig,
6118
+ dryRun: options.dryRun,
6119
+ component: options.component
6120
+ });
6121
+ if (!result.success) {
6122
+ process.exit(1);
6123
+ }
6124
+ } catch (error) {
6125
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6154
6126
  process.exit(1);
6155
6127
  }
6156
6128
  });
@@ -6170,7 +6142,7 @@ program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAN
6170
6142
  process.exit(1);
6171
6143
  }
6172
6144
  } catch (error) {
6173
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6145
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6174
6146
  process.exit(1);
6175
6147
  }
6176
6148
  });
@@ -6189,7 +6161,7 @@ program.command("context").description("Generate AI-ready context for your desig
6189
6161
  process.exit(1);
6190
6162
  }
6191
6163
  } catch (error) {
6192
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6164
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6193
6165
  process.exit(1);
6194
6166
  }
6195
6167
  });
@@ -6216,7 +6188,7 @@ program.command("ai").description("Generate context optimized for AI assistants
6216
6188
  }
6217
6189
  }
6218
6190
  } catch (error) {
6219
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6191
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6220
6192
  process.exit(1);
6221
6193
  }
6222
6194
  });
@@ -6224,7 +6196,7 @@ program.command("list").description("List all discovered fragment files").option
6224
6196
  try {
6225
6197
  await list(options);
6226
6198
  } catch (error) {
6227
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6199
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6228
6200
  process.exit(1);
6229
6201
  }
6230
6202
  });
@@ -6232,7 +6204,7 @@ program.command("reset").description("Reset to initial state (delete all generat
6232
6204
  try {
6233
6205
  await reset(options);
6234
6206
  } catch (error) {
6235
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6207
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6236
6208
  process.exit(1);
6237
6209
  }
6238
6210
  });
@@ -6246,7 +6218,7 @@ linkCommand.command("figma").argument("[figma-url]", "Figma file URL to link com
6246
6218
  variants: options.variants
6247
6219
  });
6248
6220
  } catch (error) {
6249
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6221
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6250
6222
  process.exit(1);
6251
6223
  }
6252
6224
  });
@@ -6261,7 +6233,7 @@ linkCommand.command("storybook").description("Bootstrap fragments from existing
6261
6233
  exclude: options.exclude
6262
6234
  });
6263
6235
  } catch (error) {
6264
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6236
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6265
6237
  process.exit(1);
6266
6238
  }
6267
6239
  });
@@ -6276,9 +6248,9 @@ program.command("dev").description("Start the development server with live compo
6276
6248
  skipBuild: options.skipBuild
6277
6249
  });
6278
6250
  } catch (error) {
6279
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6251
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6280
6252
  if (error instanceof Error && error.stack) {
6281
- console.error(pc24.dim(error.stack));
6253
+ console.error(pc25.dim(error.stack));
6282
6254
  }
6283
6255
  process.exit(1);
6284
6256
  }
@@ -6299,7 +6271,7 @@ program.command("screenshot").description("Capture screenshots of component vari
6299
6271
  process.exit(1);
6300
6272
  }
6301
6273
  } catch (error) {
6302
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6274
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6303
6275
  process.exit(1);
6304
6276
  }
6305
6277
  });
@@ -6318,7 +6290,7 @@ program.command("diff").argument("[component]", "Component name to diff (optiona
6318
6290
  process.exit(1);
6319
6291
  }
6320
6292
  } catch (error) {
6321
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6293
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6322
6294
  process.exit(1);
6323
6295
  }
6324
6296
  });
@@ -6337,8 +6309,8 @@ program.command("compare").argument("[component]", "Component name to compare").
6337
6309
  process.exit(1);
6338
6310
  }
6339
6311
  } catch (error) {
6340
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6341
- console.log(pc24.dim(`
6312
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6313
+ console.log(pc25.dim(`
6342
6314
  Make sure the dev server is running: ${BRAND.cliCommand} dev`));
6343
6315
  process.exit(1);
6344
6316
  }
@@ -6357,7 +6329,7 @@ program.command("analyze").description("Analyze design system and generate repor
6357
6329
  process.exit(1);
6358
6330
  }
6359
6331
  } catch (error) {
6360
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6332
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6361
6333
  process.exit(1);
6362
6334
  }
6363
6335
  });
@@ -6376,7 +6348,7 @@ program.command("verify").argument("[component]", "Component name to verify (opt
6376
6348
  if (options.ci) {
6377
6349
  console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Verification failed" }));
6378
6350
  } else {
6379
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6351
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6380
6352
  }
6381
6353
  process.exit(1);
6382
6354
  }
@@ -6393,7 +6365,7 @@ program.command("audit").description("Scan all fragments and show compliance met
6393
6365
  if (options.json) {
6394
6366
  console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Audit failed" }));
6395
6367
  } else {
6396
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6368
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6397
6369
  }
6398
6370
  process.exit(1);
6399
6371
  }
@@ -6415,7 +6387,7 @@ program.command("a11y").description("Run accessibility checks on all component v
6415
6387
  if (options.json) {
6416
6388
  console.log(JSON.stringify({ error: error instanceof Error ? error.message : "A11y check failed" }));
6417
6389
  } else {
6418
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6390
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6419
6391
  }
6420
6392
  process.exit(1);
6421
6393
  }
@@ -6438,7 +6410,7 @@ program.command("enhance").description("AI-powered documentation generation from
6438
6410
  if (options.format === "json") {
6439
6411
  console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Enhance failed" }));
6440
6412
  } else {
6441
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6413
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6442
6414
  }
6443
6415
  process.exit(1);
6444
6416
  }
@@ -6459,7 +6431,7 @@ program.command("scan").description(`Zero-config ${BRAND.outFile} generation fro
6459
6431
  process.exit(1);
6460
6432
  }
6461
6433
  } catch (error) {
6462
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6434
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6463
6435
  process.exit(1);
6464
6436
  }
6465
6437
  });
@@ -6472,7 +6444,7 @@ program.command("storygen").description("Generate Storybook stories from fragmen
6472
6444
  format: options.format
6473
6445
  });
6474
6446
  } catch (error) {
6475
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6447
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6476
6448
  process.exit(1);
6477
6449
  }
6478
6450
  });
@@ -6484,7 +6456,7 @@ program.command("metrics").argument("[component]", "Component name (optional, sh
6484
6456
  json: options.json
6485
6457
  });
6486
6458
  } catch (error) {
6487
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6459
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6488
6460
  process.exit(1);
6489
6461
  }
6490
6462
  });
@@ -6498,9 +6470,9 @@ program.command("baseline").description("Manage visual regression baselines").ar
6498
6470
  port: options.port
6499
6471
  });
6500
6472
  } catch (error) {
6501
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6473
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6502
6474
  if (action === "update") {
6503
- console.log(pc24.dim(`
6475
+ console.log(pc25.dim(`
6504
6476
  Make sure the dev server is running: ${BRAND.cliCommand} dev`));
6505
6477
  }
6506
6478
  process.exit(1);
@@ -6508,27 +6480,27 @@ Make sure the dev server is running: ${BRAND.cliCommand} dev`));
6508
6480
  });
6509
6481
  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
6482
  try {
6511
- const { generateViewerFromJson } = await import("./static-viewer-NUBFPKWH.js");
6483
+ const { generateViewerFromJson } = await import("./static-viewer-KKCR4KXR.js");
6512
6484
  const fs2 = await import("fs/promises");
6513
6485
  const path = await import("path");
6514
6486
  const inputPath = path.resolve(process.cwd(), options.input);
6515
6487
  const outputPath = path.resolve(process.cwd(), options.output);
6516
- console.log(pc24.cyan(`
6488
+ console.log(pc25.cyan(`
6517
6489
  ${BRAND.name} Viewer Generator
6518
6490
  `));
6519
6491
  try {
6520
6492
  await fs2.access(inputPath);
6521
6493
  } catch {
6522
- console.log(pc24.red(`Error: ${options.input} not found.`));
6523
- console.log(pc24.dim(`
6524
- Run ${pc24.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
6494
+ console.log(pc25.red(`Error: ${options.input} not found.`));
6495
+ console.log(pc25.dim(`
6496
+ Run ${pc25.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
6525
6497
  `));
6526
6498
  process.exit(1);
6527
6499
  }
6528
- console.log(pc24.dim(`Reading: ${options.input}`));
6500
+ console.log(pc25.dim(`Reading: ${options.input}`));
6529
6501
  const html = await generateViewerFromJson(inputPath);
6530
6502
  await fs2.writeFile(outputPath, html);
6531
- console.log(pc24.green(`
6503
+ console.log(pc25.green(`
6532
6504
  \u2713 Generated: ${options.output}
6533
6505
  `));
6534
6506
  if (options.open) {
@@ -6537,7 +6509,7 @@ Run ${pc24.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
6537
6509
  exec(`${openCmd} "${outputPath}"`);
6538
6510
  }
6539
6511
  } catch (error) {
6540
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6512
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6541
6513
  process.exit(1);
6542
6514
  }
6543
6515
  });
@@ -6550,7 +6522,7 @@ program.command("add").argument("[name]", 'Component name (e.g., "Button", "Text
6550
6522
  component: options.component
6551
6523
  });
6552
6524
  } catch (error) {
6553
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6525
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6554
6526
  process.exit(1);
6555
6527
  }
6556
6528
  });
@@ -6567,34 +6539,40 @@ program.command("setup").description("Configure @fragments-sdk/ui in a consumer
6567
6539
  process.exit(1);
6568
6540
  }
6569
6541
  } catch (error) {
6570
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6542
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6571
6543
  process.exit(1);
6572
6544
  }
6573
6545
  });
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) => {
6546
+ 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
6547
  try {
6576
- const { init } = await import("./init-UFGK5TCN.js");
6548
+ const { init } = await import("./init-XK6PRUE5.js");
6577
6549
  const result = await init({
6578
6550
  projectRoot: process.cwd(),
6579
6551
  force: options.force,
6580
6552
  yes: options.scan ? true : options.yes,
6581
- scan: options.scan
6553
+ scan: options.scan,
6554
+ configure: options.configure,
6555
+ enrich: options.enrich,
6556
+ dryRun: options.dryRun,
6557
+ provider: options.provider,
6558
+ apiKey: options.apiKey,
6559
+ model: options.model
6582
6560
  });
6583
6561
  if (!result.success) {
6584
- console.error(pc24.red("\nInit failed with errors:"));
6562
+ console.error(pc25.red("\nInit failed with errors:"));
6585
6563
  for (const error of result.errors) {
6586
- console.error(pc24.red(` - ${error}`));
6564
+ console.error(pc25.red(` - ${error}`));
6587
6565
  }
6588
6566
  process.exit(1);
6589
6567
  }
6590
6568
  } catch (error) {
6591
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6569
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6592
6570
  process.exit(1);
6593
6571
  }
6594
6572
  });
6595
6573
  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
6574
  try {
6597
- const { snapshot } = await import("./snapshot-SV2JOFZH.js");
6575
+ const { snapshot } = await import("./snapshot-53TUR3HW.js");
6598
6576
  const result = await snapshot({
6599
6577
  port: options.port,
6600
6578
  update: options.update,
@@ -6606,13 +6584,13 @@ program.command("snapshot").description("Run visual snapshot tests per component
6606
6584
  process.exit(1);
6607
6585
  }
6608
6586
  } catch (error) {
6609
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6587
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6610
6588
  process.exit(1);
6611
6589
  }
6612
6590
  });
6613
6591
  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
6592
  try {
6615
- const { tokens } = await import("./tokens-CE46OTMD.js");
6593
+ const { tokens } = await import("./tokens-L46MK5AW.js");
6616
6594
  const result = await tokens({
6617
6595
  config: options.config,
6618
6596
  json: options.json,
@@ -6625,13 +6603,13 @@ program.command("tokens").description("Discover and list design tokens from CSS/
6625
6603
  process.exit(1);
6626
6604
  }
6627
6605
  } catch (error) {
6628
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6606
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6629
6607
  process.exit(1);
6630
6608
  }
6631
6609
  });
6632
6610
  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
6611
  try {
6634
- const { generate } = await import("./generate-FBHSXR3D.js");
6612
+ const { generate } = await import("./generate-QZXOXYFW.js");
6635
6613
  const result = await generate({
6636
6614
  projectRoot: process.cwd(),
6637
6615
  component,
@@ -6639,11 +6617,11 @@ program.command("generate").description("Generate fragment files from component
6639
6617
  componentPattern: options.pattern
6640
6618
  });
6641
6619
  if (!result.success) {
6642
- console.error(pc24.red("\nGenerate completed with errors"));
6620
+ console.error(pc25.red("\nGenerate completed with errors"));
6643
6621
  process.exit(1);
6644
6622
  }
6645
6623
  } catch (error) {
6646
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6624
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6647
6625
  process.exit(1);
6648
6626
  }
6649
6627
  });
@@ -6651,7 +6629,7 @@ program.command("graph").description("Query the component relationship graph").a
6651
6629
  try {
6652
6630
  await graph(component, options);
6653
6631
  } catch (error) {
6654
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6632
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6655
6633
  process.exit(1);
6656
6634
  }
6657
6635
  });
@@ -6669,14 +6647,14 @@ program.command("perf").description("Profile component bundle sizes and performa
6669
6647
  process.exit(1);
6670
6648
  }
6671
6649
  } catch (error) {
6672
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6650
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6673
6651
  process.exit(1);
6674
6652
  }
6675
6653
  });
6676
6654
  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
6655
  try {
6678
6656
  const { config, configDir } = await loadConfig(options.config);
6679
- const { runTestCommand, listTests } = await import("./test-Z5LVO724.js");
6657
+ const { runTestCommand, listTests } = await import("./test-5UCKXYSC.js");
6680
6658
  if (options.list) {
6681
6659
  await listTests(config, configDir, {
6682
6660
  component: options.component,
@@ -6709,7 +6687,7 @@ program.command("test").description("Run interaction tests for fragments with pl
6709
6687
  });
6710
6688
  process.exit(exitCode);
6711
6689
  } catch (error) {
6712
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6690
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6713
6691
  process.exit(1);
6714
6692
  }
6715
6693
  });
@@ -6724,7 +6702,38 @@ program.command("doctor").description("Diagnose design system configuration issu
6724
6702
  process.exit(1);
6725
6703
  }
6726
6704
  } catch (error) {
6727
- console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
6705
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6706
+ process.exit(1);
6707
+ }
6708
+ });
6709
+ var governCmd = program.command("govern").description("AI UI governance checks");
6710
+ governCmd.command("check").description("Validate a UISpec against governance policies").option("-i, --input <path>", "Path to UISpec JSON file (or - for stdin)").option("-c, --config <path>", "Path to govern.config.ts").option("-f, --format <format>", "Output format: summary, json, sarif", "summary").option("-q, --quiet", "Suppress non-error output").action(async (options) => {
6711
+ try {
6712
+ const { exitCode } = await governCheck({
6713
+ input: options.input,
6714
+ config: options.config,
6715
+ format: options.format,
6716
+ quiet: options.quiet
6717
+ });
6718
+ process.exit(exitCode);
6719
+ } catch (error) {
6720
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6721
+ process.exit(1);
6722
+ }
6723
+ });
6724
+ governCmd.command("init").description("Generate a govern.config.ts template").option("-o, --output <path>", "Output path", "govern.config.ts").action(async (options) => {
6725
+ try {
6726
+ await governInit({ output: options.output });
6727
+ } catch (error) {
6728
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6729
+ process.exit(1);
6730
+ }
6731
+ });
6732
+ governCmd.command("report").description("Summarize governance audit log").action(async () => {
6733
+ try {
6734
+ await governReport();
6735
+ } catch (error) {
6736
+ console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
6728
6737
  process.exit(1);
6729
6738
  }
6730
6739
  });