@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.
- package/dist/ai-client-I6MDWNYA.js +21 -0
- package/dist/bin.js +419 -410
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-HRFUSSZI.js → chunk-3SOAPJDX.js} +2 -2
- package/dist/{chunk-D5PYOXEI.js → chunk-4K7EAQ5L.js} +148 -13
- package/dist/{chunk-D5PYOXEI.js.map → chunk-4K7EAQ5L.js.map} +1 -1
- package/dist/chunk-DXX6HADE.js +443 -0
- package/dist/chunk-DXX6HADE.js.map +1 -0
- package/dist/chunk-EYXVAMEX.js +626 -0
- package/dist/chunk-EYXVAMEX.js.map +1 -0
- package/dist/{chunk-ZM4ZQZWZ.js → chunk-FO6EBJWP.js} +39 -37
- package/dist/chunk-FO6EBJWP.js.map +1 -0
- package/dist/{chunk-OQO55NKV.js → chunk-QM7SVOGF.js} +120 -12
- package/dist/chunk-QM7SVOGF.js.map +1 -0
- package/dist/{chunk-5G3VZH43.js → chunk-RF3C6LGA.js} +281 -351
- package/dist/chunk-RF3C6LGA.js.map +1 -0
- package/dist/{chunk-WXSR2II7.js → chunk-SM674YAS.js} +58 -6
- package/dist/chunk-SM674YAS.js.map +1 -0
- package/dist/chunk-SXTKFDCR.js +104 -0
- package/dist/chunk-SXTKFDCR.js.map +1 -0
- package/dist/{chunk-PW7QTQA6.js → chunk-UV5JQV3R.js} +2 -2
- package/dist/core/index.js +13 -1
- package/dist/{discovery-NEOY4MPN.js → discovery-VSGC76JN.js} +3 -3
- package/dist/{generate-FBHSXR3D.js → generate-QZXOXYFW.js} +4 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/init-XK6PRUE5.js +636 -0
- package/dist/init-XK6PRUE5.js.map +1 -0
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-CJF2DOQW.js → scan-CHQHXWVD.js} +6 -6
- package/dist/scan-generate-U3RFVDTX.js +1115 -0
- package/dist/scan-generate-U3RFVDTX.js.map +1 -0
- package/dist/{service-TQYWY65E.js → service-MMEKG4MZ.js} +3 -3
- package/dist/{snapshot-SV2JOFZH.js → snapshot-53TUR3HW.js} +2 -2
- package/dist/{static-viewer-NUBFPKWH.js → static-viewer-KKCR4KXR.js} +3 -3
- package/dist/static-viewer-KKCR4KXR.js.map +1 -0
- package/dist/{test-Z5LVO724.js → test-5UCKXYSC.js} +4 -4
- package/dist/{tokens-CE46OTMD.js → tokens-L46MK5AW.js} +5 -5
- package/dist/{viewer-DLLJIMCK.js → viewer-M2EQQSGE.js} +14 -14
- package/dist/viewer-M2EQQSGE.js.map +1 -0
- package/package.json +11 -9
- package/src/ai-client.ts +156 -0
- package/src/bin.ts +99 -2
- package/src/build.ts +95 -33
- package/src/commands/__tests__/drift-sync.test.ts +252 -0
- package/src/commands/__tests__/scan-generate.test.ts +497 -45
- package/src/commands/enhance.ts +11 -35
- package/src/commands/govern.ts +122 -0
- package/src/commands/init.ts +288 -260
- package/src/commands/scan-generate.ts +740 -139
- package/src/commands/scan.ts +37 -32
- package/src/commands/setup.ts +143 -52
- package/src/commands/sync.ts +357 -0
- package/src/commands/validate.ts +43 -1
- package/src/core/component-extractor.test.ts +282 -0
- package/src/core/component-extractor.ts +1030 -0
- package/src/core/discovery.ts +93 -7
- package/src/service/enhance/props-extractor.ts +235 -13
- package/src/validators.ts +236 -0
- package/src/viewer/vite-plugin.ts +1 -1
- package/dist/chunk-5G3VZH43.js.map +0 -1
- package/dist/chunk-OQO55NKV.js.map +0 -1
- package/dist/chunk-WXSR2II7.js.map +0 -1
- package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
- package/dist/init-UFGK5TCN.js +0 -867
- package/dist/init-UFGK5TCN.js.map +0 -1
- package/dist/scan-generate-SJAN5MVI.js +0 -691
- package/dist/scan-generate-SJAN5MVI.js.map +0 -1
- package/dist/viewer-DLLJIMCK.js.map +0 -1
- package/src/ai.ts +0 -266
- package/src/commands/init-framework.ts +0 -414
- package/src/mcp/bin.ts +0 -36
- package/src/migrate/bin.ts +0 -114
- package/src/theme/index.ts +0 -77
- package/src/viewer/bin.ts +0 -86
- package/src/viewer/cli/health.ts +0 -256
- package/src/viewer/cli/index.ts +0 -33
- package/src/viewer/cli/scan.ts +0 -124
- package/src/viewer/cli/utils.ts +0 -174
- /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
- /package/dist/{chunk-HRFUSSZI.js.map → chunk-3SOAPJDX.js.map} +0 -0
- /package/dist/{chunk-PW7QTQA6.js.map → chunk-UV5JQV3R.js.map} +0 -0
- /package/dist/{scan-CJF2DOQW.js.map → discovery-VSGC76JN.js.map} +0 -0
- /package/dist/{generate-FBHSXR3D.js.map → generate-QZXOXYFW.js.map} +0 -0
- /package/dist/{service-TQYWY65E.js.map → scan-CHQHXWVD.js.map} +0 -0
- /package/dist/{static-viewer-NUBFPKWH.js.map → service-MMEKG4MZ.js.map} +0 -0
- /package/dist/{snapshot-SV2JOFZH.js.map → snapshot-53TUR3HW.js.map} +0 -0
- /package/dist/{test-Z5LVO724.js.map → test-5UCKXYSC.js.map} +0 -0
- /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-
|
|
25
|
+
} from "./chunk-RF3C6LGA.js";
|
|
26
|
+
import {
|
|
27
|
+
createComponentExtractor
|
|
28
|
+
} from "./chunk-EYXVAMEX.js";
|
|
16
29
|
import {
|
|
17
30
|
scan
|
|
18
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-FO6EBJWP.js";
|
|
19
32
|
import {
|
|
20
33
|
loadConfig,
|
|
21
|
-
loadFragmentFile
|
|
22
|
-
|
|
34
|
+
loadFragmentFile,
|
|
35
|
+
parseFragmentFile
|
|
36
|
+
} from "./chunk-3SOAPJDX.js";
|
|
23
37
|
import {
|
|
24
38
|
discoverFragmentFiles
|
|
25
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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
|
|
68
|
+
import pc25 from "picocolors";
|
|
55
69
|
import { readFileSync } from "fs";
|
|
56
70
|
import { fileURLToPath } from "url";
|
|
57
|
-
import { dirname as
|
|
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-
|
|
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 =
|
|
4457
|
-
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
|
|
4863
|
+
function detectProvider2(options) {
|
|
4821
4864
|
if (options.contextOnly) return "none";
|
|
4822
|
-
|
|
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
|
|
4832
|
-
|
|
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/
|
|
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
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
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
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
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 (
|
|
5805
|
-
|
|
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 (
|
|
5808
|
-
|
|
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 (
|
|
5811
|
-
|
|
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
|
|
5848
|
+
return result;
|
|
5814
5849
|
}
|
|
5815
|
-
async function
|
|
5816
|
-
const
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
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
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
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
|
|
5904
|
+
return {
|
|
5905
|
+
success: errors.length === 0,
|
|
5906
|
+
updated,
|
|
5907
|
+
skipped,
|
|
5908
|
+
errors
|
|
5909
|
+
};
|
|
5853
5910
|
}
|
|
5854
|
-
|
|
5855
|
-
const
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5878
|
-
return { modified: true, message: `Added styles import to ${entryFile}` };
|
|
5963
|
+
return { updatedContent: content, changes };
|
|
5879
5964
|
}
|
|
5880
|
-
|
|
5881
|
-
const
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
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 (
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
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
|
|
5999
|
+
return -1;
|
|
5915
6000
|
}
|
|
5916
|
-
|
|
5917
|
-
const
|
|
5918
|
-
|
|
5919
|
-
|
|
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
|
-
|
|
5922
|
-
|
|
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 (
|
|
5927
|
-
|
|
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
|
-
|
|
5933
|
-
|
|
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
|
-
|
|
5951
|
-
|
|
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
|
-
|
|
5956
|
-
|
|
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
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
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
|
-
|
|
6015
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
} catch
|
|
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 =
|
|
6143
|
-
var pkg = JSON.parse(readFileSync(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
6251
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6280
6252
|
if (error instanceof Error && error.stack) {
|
|
6281
|
-
console.error(
|
|
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(
|
|
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(
|
|
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(
|
|
6341
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
6473
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6502
6474
|
if (action === "update") {
|
|
6503
|
-
console.log(
|
|
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-
|
|
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(
|
|
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(
|
|
6523
|
-
console.log(
|
|
6524
|
-
Run ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 (
|
|
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-
|
|
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(
|
|
6562
|
+
console.error(pc25.red("\nInit failed with errors:"));
|
|
6585
6563
|
for (const error of result.errors) {
|
|
6586
|
-
console.error(
|
|
6564
|
+
console.error(pc25.red(` - ${error}`));
|
|
6587
6565
|
}
|
|
6588
6566
|
process.exit(1);
|
|
6589
6567
|
}
|
|
6590
6568
|
} catch (error) {
|
|
6591
|
-
console.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-
|
|
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(
|
|
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-
|
|
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(
|
|
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-
|
|
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(
|
|
6620
|
+
console.error(pc25.red("\nGenerate completed with errors"));
|
|
6643
6621
|
process.exit(1);
|
|
6644
6622
|
}
|
|
6645
6623
|
} catch (error) {
|
|
6646
|
-
console.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(
|
|
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(
|
|
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-
|
|
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(
|
|
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(
|
|
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
|
});
|