@sudajs/cli 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +712 -0
- package/dist/index.js +157 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/templates/theme/AGENTS.md +73 -4
- package/templates/theme/package.json +2 -2
- package/templates/theme/src/layout.tsx +26 -8
- package/templates/theme/src/locales/en.json +2 -1
- package/templates/theme/src/manifest.ts +49 -1
- package/templates/theme/src/sections.tsx +4 -1
- package/templates/theme/src/styles.css +20 -20
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
|
-
import fs, { writeFile, stat, readdir,
|
|
3
|
+
import fs, { readFile, writeFile, stat, readdir, mkdir, copyFile, rm } from 'fs/promises';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import path2 from 'path';
|
|
6
6
|
import { createInterface } from 'readline/promises';
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
8
8
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
9
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
-
import { createThemeAgentManifest, createSudaPageAgentRuntime, checkThemeModule, formatThemeCheckResult, agentValidationResultSchema, agentPageSchemaOutputSchema, agentComponentOutputSchema } from '@sudajs/theme-engine';
|
|
10
|
+
import { themeDesignSystemSchema, createThemeAgentManifest, createSudaPageAgentRuntime, checkThemeModule, formatThemeCheckResult, agentValidationResultSchema, agentPageSchemaOutputSchema, agentComponentOutputSchema } from '@sudajs/theme-engine';
|
|
11
11
|
import { themeManifestSchema } from '@sudajs/theme-engine/server';
|
|
12
12
|
import { Command } from 'commander';
|
|
13
13
|
import { build } from 'esbuild';
|
|
@@ -402,6 +402,34 @@ var updateContactFormOutputSchema = z.discriminatedUnion("status", [
|
|
|
402
402
|
contactForm: contactFormViewSchema
|
|
403
403
|
})
|
|
404
404
|
]);
|
|
405
|
+
var themeSettingsViewSchema = z.object({
|
|
406
|
+
themeKey: z.string(),
|
|
407
|
+
themeVersion: z.string(),
|
|
408
|
+
designSystem: themeDesignSystemSchema,
|
|
409
|
+
rootProps: z.record(z.unknown()),
|
|
410
|
+
schema: z.unknown()
|
|
411
|
+
});
|
|
412
|
+
var themeSettingsOutputSchema = z.object({
|
|
413
|
+
themeSettings: themeSettingsViewSchema
|
|
414
|
+
});
|
|
415
|
+
var updateThemeSettingsInputSchema = z.object({
|
|
416
|
+
projectId: z.string().min(1),
|
|
417
|
+
rootProps: z.record(z.unknown()),
|
|
418
|
+
confirm: z.boolean().optional()
|
|
419
|
+
});
|
|
420
|
+
var updateThemeSettingsOutputSchema = z.discriminatedUnion("status", [
|
|
421
|
+
z.object({
|
|
422
|
+
status: z.literal("needs_confirmation"),
|
|
423
|
+
projectId: z.string(),
|
|
424
|
+
impact: z.string(),
|
|
425
|
+
draft: updateThemeSettingsInputSchema.omit({ confirm: true })
|
|
426
|
+
}),
|
|
427
|
+
z.object({
|
|
428
|
+
status: z.literal("updated"),
|
|
429
|
+
projectId: z.string(),
|
|
430
|
+
themeSettings: themeSettingsViewSchema
|
|
431
|
+
})
|
|
432
|
+
]);
|
|
405
433
|
var PROJECT_NAME_MIN_LENGTH = 1;
|
|
406
434
|
var PROJECT_NAME_MAX_LENGTH = 80;
|
|
407
435
|
var PROJECT_DESCRIPTION_MIN_LENGTH = 1;
|
|
@@ -696,6 +724,35 @@ ${issues}`);
|
|
|
696
724
|
throw new Error(formatThemeCheckResult(result));
|
|
697
725
|
}
|
|
698
726
|
}
|
|
727
|
+
function hasTailwindV4Dependency(dependencies, name) {
|
|
728
|
+
const value = dependencies?.[name];
|
|
729
|
+
return typeof value === "string" && /^[~^<>=\s]*4(?:[.\s<>=-]|$)/.test(value.trim());
|
|
730
|
+
}
|
|
731
|
+
async function validateThemeTailwindContract(root) {
|
|
732
|
+
const packageJsonPath = path2.join(root, "package.json");
|
|
733
|
+
const packageJson = await readJson(packageJsonPath);
|
|
734
|
+
const dependencyScopes = [packageJson.dependencies, packageJson.devDependencies];
|
|
735
|
+
const hasTailwind = dependencyScopes.some(
|
|
736
|
+
(dependencies) => hasTailwindV4Dependency(dependencies, "tailwindcss")
|
|
737
|
+
);
|
|
738
|
+
const hasPostcssPlugin = dependencyScopes.some(
|
|
739
|
+
(dependencies) => hasTailwindV4Dependency(dependencies, "@tailwindcss/postcss")
|
|
740
|
+
);
|
|
741
|
+
if (!hasTailwind) {
|
|
742
|
+
throw new Error("Suda themes must depend on tailwindcss v4.");
|
|
743
|
+
}
|
|
744
|
+
if (!hasPostcssPlugin) {
|
|
745
|
+
throw new Error("Suda themes must depend on @tailwindcss/postcss v4.");
|
|
746
|
+
}
|
|
747
|
+
const stylesEntry = path2.join(root, "src", "styles.css");
|
|
748
|
+
if (!await pathExists(stylesEntry)) {
|
|
749
|
+
throw new Error(`Missing ${stylesEntry}. Suda themes must define src/styles.css.`);
|
|
750
|
+
}
|
|
751
|
+
const styles = await readFile(stylesEntry, "utf8");
|
|
752
|
+
if (!styles.includes('@import "tailwindcss";') && !styles.includes("@import 'tailwindcss';")) {
|
|
753
|
+
throw new Error('src/styles.css must include `@import "tailwindcss";`.');
|
|
754
|
+
}
|
|
755
|
+
}
|
|
699
756
|
async function validateTheme(root) {
|
|
700
757
|
const packageJsonPath = path2.join(root, "package.json");
|
|
701
758
|
const serverEntryPath = path2.join(root, "dist", "index.js");
|
|
@@ -709,6 +766,7 @@ async function validateTheme(root) {
|
|
|
709
766
|
}
|
|
710
767
|
const module = await loadThemeModule(serverEntryPath);
|
|
711
768
|
validateThemeModule(module);
|
|
769
|
+
await validateThemeTailwindContract(root);
|
|
712
770
|
await validateThemeLocales(root);
|
|
713
771
|
const result = {
|
|
714
772
|
root,
|
|
@@ -1144,12 +1202,15 @@ var __testUtils = {
|
|
|
1144
1202
|
performActivateTheme,
|
|
1145
1203
|
performConfirmedPageOperation,
|
|
1146
1204
|
performUpdateContactForm,
|
|
1205
|
+
performUpdateThemeSettings,
|
|
1206
|
+
getRemoteThemeSettings,
|
|
1147
1207
|
renderDevStarterPageHtml,
|
|
1148
1208
|
resolveDevStarterSlug,
|
|
1149
1209
|
resolveScreenshotOptions,
|
|
1150
1210
|
slugifyThemeName,
|
|
1151
1211
|
validateThemeKey,
|
|
1152
1212
|
validateThemeName,
|
|
1213
|
+
validateThemeTailwindContract,
|
|
1153
1214
|
validateThemeLocales,
|
|
1154
1215
|
toViteModuleUrl,
|
|
1155
1216
|
updateRemotePageDraft
|
|
@@ -1883,6 +1944,58 @@ async function performUpdateContactForm(auth, input, fetcher = fetchJson) {
|
|
|
1883
1944
|
contactForm
|
|
1884
1945
|
};
|
|
1885
1946
|
}
|
|
1947
|
+
async function getRemoteThemeSettings(auth, projectId, fetcher = fetchJson) {
|
|
1948
|
+
const response = await fetcher(
|
|
1949
|
+
`${auth.baseUrl}/api/cli/agent/projects/${encodeURIComponent(projectId)}/theme/settings`,
|
|
1950
|
+
{ token: auth.token }
|
|
1951
|
+
);
|
|
1952
|
+
return themeSettingsViewSchema.parse(response);
|
|
1953
|
+
}
|
|
1954
|
+
async function updateRemoteThemeSettings(auth, input, fetcher = fetchJson) {
|
|
1955
|
+
const response = await fetcher(
|
|
1956
|
+
`${auth.baseUrl}/api/cli/agent/projects/${encodeURIComponent(input.projectId)}/theme/settings`,
|
|
1957
|
+
{
|
|
1958
|
+
method: "PATCH",
|
|
1959
|
+
token: auth.token,
|
|
1960
|
+
headers: { "Content-Type": "application/json" },
|
|
1961
|
+
body: JSON.stringify({ rootProps: input.rootProps })
|
|
1962
|
+
}
|
|
1963
|
+
);
|
|
1964
|
+
return themeSettingsViewSchema.parse(response);
|
|
1965
|
+
}
|
|
1966
|
+
function describeThemeSettingsUpdate(rootProps) {
|
|
1967
|
+
const keys = Object.keys(rootProps);
|
|
1968
|
+
const parts = keys.map((key) => {
|
|
1969
|
+
if (key !== "designSystem") {
|
|
1970
|
+
return key;
|
|
1971
|
+
}
|
|
1972
|
+
const value = rootProps.designSystem;
|
|
1973
|
+
if (value && typeof value === "object" && !Array.isArray(value) && typeof value.presetId === "string") {
|
|
1974
|
+
const presetId = value.presetId;
|
|
1975
|
+
return `designSystem preset "${presetId}"`;
|
|
1976
|
+
}
|
|
1977
|
+
return "designSystem";
|
|
1978
|
+
});
|
|
1979
|
+
return parts.join(", ") || "no explicit root prop changes";
|
|
1980
|
+
}
|
|
1981
|
+
async function performUpdateThemeSettings(auth, input, fetcher = fetchJson) {
|
|
1982
|
+
const draft = updateThemeSettingsInputSchema.omit({ confirm: true }).parse(input);
|
|
1983
|
+
const impact = `This will update theme layout root settings for project "${input.projectId}": ${describeThemeSettingsUpdate(input.rootProps)}. Theme settings affect the public site rendering after they are saved.`;
|
|
1984
|
+
if (input.confirm !== true) {
|
|
1985
|
+
return {
|
|
1986
|
+
status: "needs_confirmation",
|
|
1987
|
+
projectId: input.projectId,
|
|
1988
|
+
impact,
|
|
1989
|
+
draft
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
const themeSettings = await updateRemoteThemeSettings(auth, input, fetcher);
|
|
1993
|
+
return {
|
|
1994
|
+
status: "updated",
|
|
1995
|
+
projectId: input.projectId,
|
|
1996
|
+
themeSettings
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1886
1999
|
async function listAgentProjects() {
|
|
1887
2000
|
const auth = await requireCliBaseUrl();
|
|
1888
2001
|
const result = await fetchJson(
|
|
@@ -2109,6 +2222,48 @@ async function startMcpServer() {
|
|
|
2109
2222
|
);
|
|
2110
2223
|
}
|
|
2111
2224
|
);
|
|
2225
|
+
server.registerTool(
|
|
2226
|
+
"get_theme_settings",
|
|
2227
|
+
{
|
|
2228
|
+
description: 'Read project theme layout root settings. Use this before planning theme style changes. The result includes manifest.designSystem presets, current rootProps, and the layout root schema. To use a preset, set rootProps.designSystem to { presetId }. To fully customize colors/radius, set rootProps.designSystem to { presetId: "custom", tokens: { colors, radius } } using every token from designSystem.',
|
|
2229
|
+
inputSchema: {
|
|
2230
|
+
projectId: z.string()
|
|
2231
|
+
},
|
|
2232
|
+
outputSchema: themeSettingsOutputSchema.shape
|
|
2233
|
+
},
|
|
2234
|
+
async ({ projectId }) => {
|
|
2235
|
+
const auth = await requireCliBaseUrl();
|
|
2236
|
+
const structuredContent = themeSettingsOutputSchema.parse({
|
|
2237
|
+
themeSettings: await getRemoteThemeSettings(auth, projectId)
|
|
2238
|
+
});
|
|
2239
|
+
return mcpStructured("Suda theme settings.", structuredContent);
|
|
2240
|
+
}
|
|
2241
|
+
);
|
|
2242
|
+
server.registerTool(
|
|
2243
|
+
"update_theme_settings",
|
|
2244
|
+
{
|
|
2245
|
+
description: "Plan or update project theme layout root settings. Use this for choosing a designSystem preset or saving custom design tokens for colors and radius. Conversation flow: first call get_theme_settings, then propose the exact rootProps changes to the user. Call this tool without confirm or with confirm:false to return the impact summary; only call again with confirm:true after the user explicitly agrees. Do not create theme source code or introduce a second token system.",
|
|
2246
|
+
inputSchema: updateThemeSettingsInputSchema.shape,
|
|
2247
|
+
outputSchema: {
|
|
2248
|
+
status: z.enum(["needs_confirmation", "updated"]),
|
|
2249
|
+
projectId: z.string(),
|
|
2250
|
+
impact: z.string().optional(),
|
|
2251
|
+
draft: updateThemeSettingsInputSchema.omit({ confirm: true }).optional(),
|
|
2252
|
+
themeSettings: themeSettingsViewSchema.optional()
|
|
2253
|
+
}
|
|
2254
|
+
},
|
|
2255
|
+
async (input) => {
|
|
2256
|
+
const parsed = updateThemeSettingsInputSchema.parse(input);
|
|
2257
|
+
const auth = await requireCliBaseUrl();
|
|
2258
|
+
const structuredContent = updateThemeSettingsOutputSchema.parse(
|
|
2259
|
+
await performUpdateThemeSettings(auth, parsed)
|
|
2260
|
+
);
|
|
2261
|
+
return mcpStructured(
|
|
2262
|
+
structuredContent.status === "needs_confirmation" ? "Updating these theme settings requires explicit confirmation." : "Updated Suda theme settings.",
|
|
2263
|
+
structuredContent
|
|
2264
|
+
);
|
|
2265
|
+
}
|
|
2266
|
+
);
|
|
2112
2267
|
server.registerTool(
|
|
2113
2268
|
"create_page_draft",
|
|
2114
2269
|
{
|