@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.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createHash } from 'crypto';
3
- import fs, { writeFile, stat, readdir, readFile, mkdir, copyFile, rm } from 'fs/promises';
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
  {