@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/src/ai.ts
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import type { CompiledFragment, CompiledFragmentsFile } from "./core/index.js";
|
|
2
|
-
import { generateContext } from "./core/index.js";
|
|
3
|
-
|
|
4
|
-
export interface AiSuggestOptions {
|
|
5
|
-
/** The user's prompt describing what they want to build */
|
|
6
|
-
prompt: string;
|
|
7
|
-
/** Optional additional context */
|
|
8
|
-
context?: string;
|
|
9
|
-
/** Compiled fragments to use */
|
|
10
|
-
fragments: CompiledFragment[];
|
|
11
|
-
/** Whether to stream output */
|
|
12
|
-
stream?: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AiSuggestResult {
|
|
16
|
-
success: boolean;
|
|
17
|
-
response?: string;
|
|
18
|
-
error?: string;
|
|
19
|
-
tokensUsed?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Call AI API to get component suggestions based on design system context.
|
|
24
|
-
* Supports both Anthropic and OpenAI APIs via environment variables.
|
|
25
|
-
*/
|
|
26
|
-
export async function aiSuggest(options: AiSuggestOptions): Promise<AiSuggestResult> {
|
|
27
|
-
const { prompt, context, fragments, stream = true } = options;
|
|
28
|
-
|
|
29
|
-
// Check for API keys
|
|
30
|
-
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
31
|
-
const openaiKey = process.env.OPENAI_API_KEY;
|
|
32
|
-
|
|
33
|
-
if (!anthropicKey && !openaiKey) {
|
|
34
|
-
return {
|
|
35
|
-
success: false,
|
|
36
|
-
error: "No API key found. Set ANTHROPIC_API_KEY or OPENAI_API_KEY environment variable.",
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Generate design system context
|
|
41
|
-
const { content: systemContext } = generateContext(fragments, {
|
|
42
|
-
format: "markdown",
|
|
43
|
-
compact: false,
|
|
44
|
-
include: {
|
|
45
|
-
props: true,
|
|
46
|
-
variants: true,
|
|
47
|
-
usage: true,
|
|
48
|
-
code: true,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Build the system prompt
|
|
53
|
-
const systemPrompt = `You are a UI developer assistant helping to build interfaces using a specific design system.
|
|
54
|
-
|
|
55
|
-
You have access to the following design system components:
|
|
56
|
-
|
|
57
|
-
${systemContext}
|
|
58
|
-
|
|
59
|
-
When suggesting components:
|
|
60
|
-
1. ONLY use components from this design system - never suggest generic HTML or components not listed above
|
|
61
|
-
2. Explain WHY each component is appropriate for the use case
|
|
62
|
-
3. Show complete, ready-to-use code examples with correct props
|
|
63
|
-
4. Follow the "when to use" and "when NOT to use" guidelines for each component
|
|
64
|
-
5. Consider accessibility and best practices
|
|
65
|
-
6. If multiple approaches are valid, recommend the simplest one first
|
|
66
|
-
|
|
67
|
-
Format your response as:
|
|
68
|
-
1. A brief explanation of the approach
|
|
69
|
-
2. Code example(s) with comments
|
|
70
|
-
3. Any important notes about props or customization`;
|
|
71
|
-
|
|
72
|
-
// Build the user prompt
|
|
73
|
-
const userPrompt = context
|
|
74
|
-
? `${prompt}\n\nAdditional context: ${context}`
|
|
75
|
-
: prompt;
|
|
76
|
-
|
|
77
|
-
// Prefer Anthropic, fallback to OpenAI
|
|
78
|
-
if (anthropicKey) {
|
|
79
|
-
return callAnthropic(anthropicKey, systemPrompt, userPrompt, stream);
|
|
80
|
-
} else {
|
|
81
|
-
return callOpenAI(openaiKey!, systemPrompt, userPrompt, stream);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Call Anthropic's Claude API
|
|
87
|
-
*/
|
|
88
|
-
async function callAnthropic(
|
|
89
|
-
apiKey: string,
|
|
90
|
-
systemPrompt: string,
|
|
91
|
-
userPrompt: string,
|
|
92
|
-
stream: boolean
|
|
93
|
-
): Promise<AiSuggestResult> {
|
|
94
|
-
try {
|
|
95
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
96
|
-
method: "POST",
|
|
97
|
-
headers: {
|
|
98
|
-
"Content-Type": "application/json",
|
|
99
|
-
"x-api-key": apiKey,
|
|
100
|
-
"anthropic-version": "2023-06-01",
|
|
101
|
-
},
|
|
102
|
-
body: JSON.stringify({
|
|
103
|
-
model: "claude-sonnet-4-20250514",
|
|
104
|
-
max_tokens: 4096,
|
|
105
|
-
system: systemPrompt,
|
|
106
|
-
messages: [
|
|
107
|
-
{
|
|
108
|
-
role: "user",
|
|
109
|
-
content: userPrompt,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
stream: stream,
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
if (!response.ok) {
|
|
117
|
-
const error = await response.text();
|
|
118
|
-
return {
|
|
119
|
-
success: false,
|
|
120
|
-
error: `Anthropic API error: ${response.status} - ${error}`,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (stream && response.body) {
|
|
125
|
-
// Stream the response
|
|
126
|
-
const reader = response.body.getReader();
|
|
127
|
-
const decoder = new TextDecoder();
|
|
128
|
-
let fullResponse = "";
|
|
129
|
-
|
|
130
|
-
while (true) {
|
|
131
|
-
const { done, value } = await reader.read();
|
|
132
|
-
if (done) break;
|
|
133
|
-
|
|
134
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
135
|
-
const lines = chunk.split("\n");
|
|
136
|
-
|
|
137
|
-
for (const line of lines) {
|
|
138
|
-
if (line.startsWith("data: ")) {
|
|
139
|
-
const data = line.slice(6);
|
|
140
|
-
if (data === "[DONE]") continue;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const parsed = JSON.parse(data);
|
|
144
|
-
if (parsed.type === "content_block_delta" && parsed.delta?.text) {
|
|
145
|
-
process.stdout.write(parsed.delta.text);
|
|
146
|
-
fullResponse += parsed.delta.text;
|
|
147
|
-
}
|
|
148
|
-
} catch {
|
|
149
|
-
// Skip malformed JSON
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
console.log(); // Final newline
|
|
156
|
-
return {
|
|
157
|
-
success: true,
|
|
158
|
-
response: fullResponse,
|
|
159
|
-
};
|
|
160
|
-
} else {
|
|
161
|
-
const data = await response.json();
|
|
162
|
-
const text = data.content?.[0]?.text ?? "";
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
success: true,
|
|
166
|
-
response: text,
|
|
167
|
-
tokensUsed: data.usage?.input_tokens + data.usage?.output_tokens,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
} catch (error) {
|
|
171
|
-
return {
|
|
172
|
-
success: false,
|
|
173
|
-
error: `Failed to call Anthropic API: ${error instanceof Error ? error.message : error}`,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Call OpenAI's API
|
|
180
|
-
*/
|
|
181
|
-
async function callOpenAI(
|
|
182
|
-
apiKey: string,
|
|
183
|
-
systemPrompt: string,
|
|
184
|
-
userPrompt: string,
|
|
185
|
-
stream: boolean
|
|
186
|
-
): Promise<AiSuggestResult> {
|
|
187
|
-
try {
|
|
188
|
-
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
189
|
-
method: "POST",
|
|
190
|
-
headers: {
|
|
191
|
-
"Content-Type": "application/json",
|
|
192
|
-
Authorization: `Bearer ${apiKey}`,
|
|
193
|
-
},
|
|
194
|
-
body: JSON.stringify({
|
|
195
|
-
model: "gpt-4o",
|
|
196
|
-
messages: [
|
|
197
|
-
{ role: "system", content: systemPrompt },
|
|
198
|
-
{ role: "user", content: userPrompt },
|
|
199
|
-
],
|
|
200
|
-
max_tokens: 4096,
|
|
201
|
-
stream: stream,
|
|
202
|
-
}),
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
if (!response.ok) {
|
|
206
|
-
const error = await response.text();
|
|
207
|
-
return {
|
|
208
|
-
success: false,
|
|
209
|
-
error: `OpenAI API error: ${response.status} - ${error}`,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (stream && response.body) {
|
|
214
|
-
// Stream the response
|
|
215
|
-
const reader = response.body.getReader();
|
|
216
|
-
const decoder = new TextDecoder();
|
|
217
|
-
let fullResponse = "";
|
|
218
|
-
|
|
219
|
-
while (true) {
|
|
220
|
-
const { done, value } = await reader.read();
|
|
221
|
-
if (done) break;
|
|
222
|
-
|
|
223
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
224
|
-
const lines = chunk.split("\n");
|
|
225
|
-
|
|
226
|
-
for (const line of lines) {
|
|
227
|
-
if (line.startsWith("data: ")) {
|
|
228
|
-
const data = line.slice(6);
|
|
229
|
-
if (data === "[DONE]") continue;
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
const parsed = JSON.parse(data);
|
|
233
|
-
const content = parsed.choices?.[0]?.delta?.content;
|
|
234
|
-
if (content) {
|
|
235
|
-
process.stdout.write(content);
|
|
236
|
-
fullResponse += content;
|
|
237
|
-
}
|
|
238
|
-
} catch {
|
|
239
|
-
// Skip malformed JSON
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
console.log(); // Final newline
|
|
246
|
-
return {
|
|
247
|
-
success: true,
|
|
248
|
-
response: fullResponse,
|
|
249
|
-
};
|
|
250
|
-
} else {
|
|
251
|
-
const data = await response.json();
|
|
252
|
-
const text = data.choices?.[0]?.message?.content ?? "";
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
success: true,
|
|
256
|
-
response: text,
|
|
257
|
-
tokensUsed: data.usage?.total_tokens,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
} catch (error) {
|
|
261
|
-
return {
|
|
262
|
-
success: false,
|
|
263
|
-
error: `Failed to call OpenAI API: ${error instanceof Error ? error.message : error}`,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
}
|
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Framework detection and auto-configuration for fragments init.
|
|
3
|
-
*
|
|
4
|
-
* Detects the consumer's framework (Next.js, Vite, Remix, Astro)
|
|
5
|
-
* and generates appropriate configuration files.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readFile, writeFile, access } from "node:fs/promises";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import pc from "picocolors";
|
|
11
|
-
|
|
12
|
-
// ============================================
|
|
13
|
-
// Types
|
|
14
|
-
// ============================================
|
|
15
|
-
|
|
16
|
-
export type Framework = "nextjs" | "vite" | "remix" | "astro" | "unknown";
|
|
17
|
-
|
|
18
|
-
export interface FrameworkDetection {
|
|
19
|
-
framework: Framework;
|
|
20
|
-
/** Package that triggered the detection */
|
|
21
|
-
detectedBy: string | null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface FrameworkSetupOptions {
|
|
25
|
-
/** Project root directory */
|
|
26
|
-
projectRoot: string;
|
|
27
|
-
/** Override auto-detected framework */
|
|
28
|
-
framework?: Framework;
|
|
29
|
-
/** Seed overrides for globals.scss generation */
|
|
30
|
-
seeds?: {
|
|
31
|
-
brand?: string;
|
|
32
|
-
neutral?: string;
|
|
33
|
-
density?: string;
|
|
34
|
-
radiusStyle?: string;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface FrameworkSetupResult {
|
|
39
|
-
framework: Framework;
|
|
40
|
-
filesCreated: string[];
|
|
41
|
-
packagesToInstall: string[];
|
|
42
|
-
configModified: string[];
|
|
43
|
-
warnings: string[];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ============================================
|
|
47
|
-
// Framework Detection
|
|
48
|
-
// ============================================
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Detect framework from package.json dependencies
|
|
52
|
-
*/
|
|
53
|
-
export async function detectFramework(
|
|
54
|
-
projectRoot: string
|
|
55
|
-
): Promise<FrameworkDetection> {
|
|
56
|
-
try {
|
|
57
|
-
const pkgPath = join(projectRoot, "package.json");
|
|
58
|
-
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
59
|
-
const pkg = JSON.parse(pkgContent);
|
|
60
|
-
|
|
61
|
-
const allDeps = {
|
|
62
|
-
...pkg.dependencies,
|
|
63
|
-
...pkg.devDependencies,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Check in order of specificity
|
|
67
|
-
if (allDeps["next"]) {
|
|
68
|
-
return { framework: "nextjs", detectedBy: "next" };
|
|
69
|
-
}
|
|
70
|
-
if (allDeps["@remix-run/react"]) {
|
|
71
|
-
return { framework: "remix", detectedBy: "@remix-run/react" };
|
|
72
|
-
}
|
|
73
|
-
if (allDeps["astro"]) {
|
|
74
|
-
return { framework: "astro", detectedBy: "astro" };
|
|
75
|
-
}
|
|
76
|
-
if (allDeps["vite"]) {
|
|
77
|
-
return { framework: "vite", detectedBy: "vite" };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return { framework: "unknown", detectedBy: null };
|
|
81
|
-
} catch {
|
|
82
|
-
return { framework: "unknown", detectedBy: null };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ============================================
|
|
87
|
-
// Globals SCSS Generation
|
|
88
|
-
// ============================================
|
|
89
|
-
|
|
90
|
-
function generateGlobalsSCSS(seeds?: FrameworkSetupOptions["seeds"]): string {
|
|
91
|
-
const withClauses: string[] = [];
|
|
92
|
-
|
|
93
|
-
if (seeds?.brand) {
|
|
94
|
-
withClauses.push(` $fui-brand: ${seeds.brand}`);
|
|
95
|
-
}
|
|
96
|
-
if (seeds?.neutral) {
|
|
97
|
-
withClauses.push(` $fui-neutral: "${seeds.neutral}"`);
|
|
98
|
-
}
|
|
99
|
-
if (seeds?.density) {
|
|
100
|
-
withClauses.push(` $fui-density: "${seeds.density}"`);
|
|
101
|
-
}
|
|
102
|
-
if (seeds?.radiusStyle) {
|
|
103
|
-
withClauses.push(` $fui-radius-style: "${seeds.radiusStyle}"`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const useStatement =
|
|
107
|
-
withClauses.length > 0
|
|
108
|
-
? `@use '@fragments-sdk/ui/styles' with (\n${withClauses.join(",\n")}\n);`
|
|
109
|
-
: `@use '@fragments-sdk/ui/styles';`;
|
|
110
|
-
|
|
111
|
-
return `// Fragments SDK Global Styles
|
|
112
|
-
// Customize seed values to theme the entire design system.
|
|
113
|
-
// See: https://usefragments.com/docs/theming
|
|
114
|
-
${useStatement}
|
|
115
|
-
`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ============================================
|
|
119
|
-
// Providers Component Generation
|
|
120
|
-
// ============================================
|
|
121
|
-
|
|
122
|
-
function generateProviders(): string {
|
|
123
|
-
return `'use client';
|
|
124
|
-
|
|
125
|
-
import { ThemeProvider } from '@fragments-sdk/ui';
|
|
126
|
-
|
|
127
|
-
export function Providers({ children }: { children: React.ReactNode }) {
|
|
128
|
-
return (
|
|
129
|
-
<ThemeProvider defaultMode="system" attribute="data-theme">
|
|
130
|
-
{children}
|
|
131
|
-
</ThemeProvider>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// ============================================
|
|
138
|
-
// Per-Framework Configuration
|
|
139
|
-
// ============================================
|
|
140
|
-
|
|
141
|
-
async function setupNextJS(
|
|
142
|
-
projectRoot: string,
|
|
143
|
-
options: FrameworkSetupOptions
|
|
144
|
-
): Promise<FrameworkSetupResult> {
|
|
145
|
-
const result: FrameworkSetupResult = {
|
|
146
|
-
framework: "nextjs",
|
|
147
|
-
filesCreated: [],
|
|
148
|
-
packagesToInstall: [],
|
|
149
|
-
configModified: [],
|
|
150
|
-
warnings: [],
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// Check if sass is installed
|
|
154
|
-
try {
|
|
155
|
-
const pkgContent = await readFile(
|
|
156
|
-
join(projectRoot, "package.json"),
|
|
157
|
-
"utf-8"
|
|
158
|
-
);
|
|
159
|
-
const pkg = JSON.parse(pkgContent);
|
|
160
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
161
|
-
if (!allDeps["sass"]) {
|
|
162
|
-
result.packagesToInstall.push("sass");
|
|
163
|
-
}
|
|
164
|
-
} catch {
|
|
165
|
-
// Proceed without checking
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Update next.config if transpilePackages is needed
|
|
169
|
-
const nextConfigPaths = [
|
|
170
|
-
"next.config.ts",
|
|
171
|
-
"next.config.mjs",
|
|
172
|
-
"next.config.js",
|
|
173
|
-
];
|
|
174
|
-
|
|
175
|
-
for (const configName of nextConfigPaths) {
|
|
176
|
-
const configPath = join(projectRoot, configName);
|
|
177
|
-
try {
|
|
178
|
-
await access(configPath);
|
|
179
|
-
const content = await readFile(configPath, "utf-8");
|
|
180
|
-
|
|
181
|
-
if (!content.includes("transpilePackages")) {
|
|
182
|
-
// Add transpilePackages to the config
|
|
183
|
-
if (content.includes("const nextConfig")) {
|
|
184
|
-
const updated = content.replace(
|
|
185
|
-
/const nextConfig\s*=\s*\{/,
|
|
186
|
-
`const nextConfig = {\n transpilePackages: ['@fragments-sdk/ui'],`
|
|
187
|
-
);
|
|
188
|
-
await writeFile(configPath, updated, "utf-8");
|
|
189
|
-
result.configModified.push(configName);
|
|
190
|
-
} else {
|
|
191
|
-
result.warnings.push(
|
|
192
|
-
`Could not auto-modify ${configName}. Add transpilePackages: ['@fragments-sdk/ui'] manually.`
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
break;
|
|
197
|
-
} catch {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Generate globals.scss
|
|
203
|
-
const globalsPath = join(projectRoot, "src", "styles", "globals.scss");
|
|
204
|
-
try {
|
|
205
|
-
await access(globalsPath);
|
|
206
|
-
result.warnings.push(
|
|
207
|
-
"src/styles/globals.scss already exists. Skipped generation."
|
|
208
|
-
);
|
|
209
|
-
} catch {
|
|
210
|
-
await writeFile(globalsPath, generateGlobalsSCSS(options.seeds), "utf-8");
|
|
211
|
-
result.filesCreated.push("src/styles/globals.scss");
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Generate providers.tsx
|
|
215
|
-
const providersPath = join(projectRoot, "src", "providers.tsx");
|
|
216
|
-
try {
|
|
217
|
-
await access(providersPath);
|
|
218
|
-
result.warnings.push("src/providers.tsx already exists. Skipped.");
|
|
219
|
-
} catch {
|
|
220
|
-
await writeFile(providersPath, generateProviders(), "utf-8");
|
|
221
|
-
result.filesCreated.push("src/providers.tsx");
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return result;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async function setupVite(
|
|
228
|
-
projectRoot: string,
|
|
229
|
-
options: FrameworkSetupOptions
|
|
230
|
-
): Promise<FrameworkSetupResult> {
|
|
231
|
-
const result: FrameworkSetupResult = {
|
|
232
|
-
framework: "vite",
|
|
233
|
-
filesCreated: [],
|
|
234
|
-
packagesToInstall: [],
|
|
235
|
-
configModified: [],
|
|
236
|
-
warnings: [],
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// Check if sass is installed
|
|
240
|
-
try {
|
|
241
|
-
const pkgContent = await readFile(
|
|
242
|
-
join(projectRoot, "package.json"),
|
|
243
|
-
"utf-8"
|
|
244
|
-
);
|
|
245
|
-
const pkg = JSON.parse(pkgContent);
|
|
246
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
247
|
-
if (!allDeps["sass"]) {
|
|
248
|
-
result.packagesToInstall.push("sass");
|
|
249
|
-
}
|
|
250
|
-
} catch {
|
|
251
|
-
// Proceed
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Generate globals.scss
|
|
255
|
-
const globalsPath = join(projectRoot, "src", "styles", "globals.scss");
|
|
256
|
-
try {
|
|
257
|
-
await access(globalsPath);
|
|
258
|
-
result.warnings.push(
|
|
259
|
-
"src/styles/globals.scss already exists. Skipped generation."
|
|
260
|
-
);
|
|
261
|
-
} catch {
|
|
262
|
-
await writeFile(globalsPath, generateGlobalsSCSS(options.seeds), "utf-8");
|
|
263
|
-
result.filesCreated.push("src/styles/globals.scss");
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Generate providers.tsx
|
|
267
|
-
const providersPath = join(projectRoot, "src", "providers.tsx");
|
|
268
|
-
try {
|
|
269
|
-
await access(providersPath);
|
|
270
|
-
result.warnings.push("src/providers.tsx already exists. Skipped.");
|
|
271
|
-
} catch {
|
|
272
|
-
await writeFile(providersPath, generateProviders(), "utf-8");
|
|
273
|
-
result.filesCreated.push("src/providers.tsx");
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return result;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
async function setupRemix(
|
|
280
|
-
projectRoot: string,
|
|
281
|
-
options: FrameworkSetupOptions
|
|
282
|
-
): Promise<FrameworkSetupResult> {
|
|
283
|
-
const result: FrameworkSetupResult = {
|
|
284
|
-
framework: "remix",
|
|
285
|
-
filesCreated: [],
|
|
286
|
-
packagesToInstall: [],
|
|
287
|
-
configModified: [],
|
|
288
|
-
warnings: [],
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
result.packagesToInstall.push("sass");
|
|
292
|
-
|
|
293
|
-
// Generate globals.scss in app/styles
|
|
294
|
-
const globalsPath = join(projectRoot, "app", "styles", "globals.scss");
|
|
295
|
-
try {
|
|
296
|
-
await access(globalsPath);
|
|
297
|
-
result.warnings.push(
|
|
298
|
-
"app/styles/globals.scss already exists. Skipped generation."
|
|
299
|
-
);
|
|
300
|
-
} catch {
|
|
301
|
-
await writeFile(globalsPath, generateGlobalsSCSS(options.seeds), "utf-8");
|
|
302
|
-
result.filesCreated.push("app/styles/globals.scss");
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Generate providers.tsx
|
|
306
|
-
const providersPath = join(projectRoot, "app", "providers.tsx");
|
|
307
|
-
try {
|
|
308
|
-
await access(providersPath);
|
|
309
|
-
result.warnings.push("app/providers.tsx already exists. Skipped.");
|
|
310
|
-
} catch {
|
|
311
|
-
await writeFile(providersPath, generateProviders(), "utf-8");
|
|
312
|
-
result.filesCreated.push("app/providers.tsx");
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
result.warnings.push(
|
|
316
|
-
'Add @fragments-sdk/ui to serverDependenciesToBundle in remix.config if using source imports.'
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async function setupAstro(
|
|
323
|
-
projectRoot: string,
|
|
324
|
-
options: FrameworkSetupOptions
|
|
325
|
-
): Promise<FrameworkSetupResult> {
|
|
326
|
-
const result: FrameworkSetupResult = {
|
|
327
|
-
framework: "astro",
|
|
328
|
-
filesCreated: [],
|
|
329
|
-
packagesToInstall: [],
|
|
330
|
-
configModified: [],
|
|
331
|
-
warnings: [],
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
result.packagesToInstall.push("sass");
|
|
335
|
-
|
|
336
|
-
// Generate globals.scss
|
|
337
|
-
const globalsPath = join(projectRoot, "src", "styles", "globals.scss");
|
|
338
|
-
try {
|
|
339
|
-
await access(globalsPath);
|
|
340
|
-
result.warnings.push(
|
|
341
|
-
"src/styles/globals.scss already exists. Skipped generation."
|
|
342
|
-
);
|
|
343
|
-
} catch {
|
|
344
|
-
await writeFile(globalsPath, generateGlobalsSCSS(options.seeds), "utf-8");
|
|
345
|
-
result.filesCreated.push("src/styles/globals.scss");
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// ============================================
|
|
352
|
-
// Main Setup Function
|
|
353
|
-
// ============================================
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Set up framework-specific configuration for @fragments-sdk/ui
|
|
357
|
-
*/
|
|
358
|
-
export async function setupFramework(
|
|
359
|
-
options: FrameworkSetupOptions
|
|
360
|
-
): Promise<FrameworkSetupResult> {
|
|
361
|
-
const { projectRoot } = options;
|
|
362
|
-
|
|
363
|
-
// Detect or use provided framework
|
|
364
|
-
let framework = options.framework;
|
|
365
|
-
if (!framework || framework === "unknown") {
|
|
366
|
-
const detection = await detectFramework(projectRoot);
|
|
367
|
-
framework = detection.framework;
|
|
368
|
-
|
|
369
|
-
if (detection.detectedBy) {
|
|
370
|
-
console.log(
|
|
371
|
-
pc.green(` Detected ${frameworkLabel(framework)}`) +
|
|
372
|
-
pc.dim(` (via ${detection.detectedBy})`)
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
} else {
|
|
376
|
-
console.log(pc.green(` Framework: ${frameworkLabel(framework)}`));
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
switch (framework) {
|
|
380
|
-
case "nextjs":
|
|
381
|
-
return setupNextJS(projectRoot, options);
|
|
382
|
-
case "vite":
|
|
383
|
-
return setupVite(projectRoot, options);
|
|
384
|
-
case "remix":
|
|
385
|
-
return setupRemix(projectRoot, options);
|
|
386
|
-
case "astro":
|
|
387
|
-
return setupAstro(projectRoot, options);
|
|
388
|
-
default:
|
|
389
|
-
return {
|
|
390
|
-
framework: "unknown",
|
|
391
|
-
filesCreated: [],
|
|
392
|
-
packagesToInstall: ["sass"],
|
|
393
|
-
configModified: [],
|
|
394
|
-
warnings: [
|
|
395
|
-
"Could not detect framework. Install sass and import @fragments-sdk/ui/styles manually.",
|
|
396
|
-
],
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function frameworkLabel(framework: Framework): string {
|
|
402
|
-
switch (framework) {
|
|
403
|
-
case "nextjs":
|
|
404
|
-
return "Next.js";
|
|
405
|
-
case "vite":
|
|
406
|
-
return "Vite";
|
|
407
|
-
case "remix":
|
|
408
|
-
return "Remix";
|
|
409
|
-
case "astro":
|
|
410
|
-
return "Astro";
|
|
411
|
-
default:
|
|
412
|
-
return "Unknown";
|
|
413
|
-
}
|
|
414
|
-
}
|
package/src/mcp/bin.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { startMcpServer } from './server.js';
|
|
3
|
-
|
|
4
|
-
// Parse command line arguments
|
|
5
|
-
const args = process.argv.slice(2);
|
|
6
|
-
let projectRoot = process.cwd();
|
|
7
|
-
let viewerUrl: string | undefined;
|
|
8
|
-
|
|
9
|
-
for (let i = 0; i < args.length; i++) {
|
|
10
|
-
const arg = args[i];
|
|
11
|
-
|
|
12
|
-
if (arg === '--project-root' || arg === '-p') {
|
|
13
|
-
projectRoot = args[++i] ?? projectRoot;
|
|
14
|
-
} else if (arg === '--viewer-url' || arg === '-u') {
|
|
15
|
-
viewerUrl = args[++i];
|
|
16
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
17
|
-
console.log(`
|
|
18
|
-
Usage: fragments-mcp [options]
|
|
19
|
-
|
|
20
|
-
Options:
|
|
21
|
-
-p, --project-root <path> Project root directory (default: cwd)
|
|
22
|
-
-u, --viewer-url <url> Viewer URL (default: http://localhost:6006)
|
|
23
|
-
-h, --help Show this help message
|
|
24
|
-
`);
|
|
25
|
-
process.exit(0);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Start server
|
|
30
|
-
startMcpServer({
|
|
31
|
-
projectRoot,
|
|
32
|
-
viewerUrl,
|
|
33
|
-
}).catch((error) => {
|
|
34
|
-
console.error('Failed to start MCP server:', error);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
});
|