@sudajs/cli 0.10.4 → 0.12.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/index.d.ts +712 -0
- package/dist/index.js +199 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/templates/theme/AGENTS.md +358 -21
- package/templates/theme/package.json +2 -2
- package/templates/theme/src/index.tsx +3 -2
- package/templates/theme/src/layout.tsx +26 -8
- package/templates/theme/src/locales/en.json +10 -1
- package/templates/theme/src/manifest.ts +49 -1
- package/templates/theme/src/sections.tsx +73 -2
- package/templates/theme/src/styles.css +20 -20
- package/templates/theme/src/templates.ts +151 -6
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,
|
|
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;
|
|
@@ -691,6 +719,39 @@ ${issues}`);
|
|
|
691
719
|
if (!Array.isArray(module.starterPages)) {
|
|
692
720
|
throw new Error("starterPages must be an array.");
|
|
693
721
|
}
|
|
722
|
+
const result = checkThemeModule(module);
|
|
723
|
+
if (!result.ok) {
|
|
724
|
+
throw new Error(formatThemeCheckResult(result));
|
|
725
|
+
}
|
|
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
|
+
}
|
|
694
755
|
}
|
|
695
756
|
async function validateTheme(root) {
|
|
696
757
|
const packageJsonPath = path2.join(root, "package.json");
|
|
@@ -705,6 +766,7 @@ async function validateTheme(root) {
|
|
|
705
766
|
}
|
|
706
767
|
const module = await loadThemeModule(serverEntryPath);
|
|
707
768
|
validateThemeModule(module);
|
|
769
|
+
await validateThemeTailwindContract(root);
|
|
708
770
|
await validateThemeLocales(root);
|
|
709
771
|
const result = {
|
|
710
772
|
root,
|
|
@@ -1140,12 +1202,15 @@ var __testUtils = {
|
|
|
1140
1202
|
performActivateTheme,
|
|
1141
1203
|
performConfirmedPageOperation,
|
|
1142
1204
|
performUpdateContactForm,
|
|
1205
|
+
performUpdateThemeSettings,
|
|
1206
|
+
getRemoteThemeSettings,
|
|
1143
1207
|
renderDevStarterPageHtml,
|
|
1144
1208
|
resolveDevStarterSlug,
|
|
1145
1209
|
resolveScreenshotOptions,
|
|
1146
1210
|
slugifyThemeName,
|
|
1147
1211
|
validateThemeKey,
|
|
1148
1212
|
validateThemeName,
|
|
1213
|
+
validateThemeTailwindContract,
|
|
1149
1214
|
validateThemeLocales,
|
|
1150
1215
|
toViteModuleUrl,
|
|
1151
1216
|
updateRemotePageDraft
|
|
@@ -1686,16 +1751,16 @@ function printJson(value) {
|
|
|
1686
1751
|
console.log(JSON.stringify(value, null, 2));
|
|
1687
1752
|
}
|
|
1688
1753
|
function createSectionSchema(manifest, sectionType) {
|
|
1689
|
-
const section =
|
|
1690
|
-
if (
|
|
1754
|
+
const section = createSudaPageAgentRuntime(manifest).getSectionSchemaOutput(sectionType);
|
|
1755
|
+
if (section === null) {
|
|
1691
1756
|
throw new Error(`Unknown section type "${sectionType}".`);
|
|
1692
1757
|
}
|
|
1693
|
-
return
|
|
1758
|
+
return section;
|
|
1694
1759
|
}
|
|
1695
1760
|
async function validateAgentPage(themeKey, input, options) {
|
|
1696
1761
|
const manifest = await fetchAgentManifest(themeKey, options);
|
|
1697
1762
|
const pageContent = await readJson(path2.resolve(input));
|
|
1698
|
-
const result =
|
|
1763
|
+
const result = createSudaPageAgentRuntime(manifest).validatePageContent(pageContent);
|
|
1699
1764
|
printJson(agentValidationResultSchema.parse(result));
|
|
1700
1765
|
if (!result.valid) {
|
|
1701
1766
|
process.exitCode = 1;
|
|
@@ -1707,9 +1772,9 @@ async function createAgentPage(options) {
|
|
|
1707
1772
|
}
|
|
1708
1773
|
const pageContent = await readJson(path2.resolve(options.input));
|
|
1709
1774
|
const manifest = await fetchAgentManifest(options.theme, options);
|
|
1710
|
-
const
|
|
1711
|
-
if (!validation.valid) {
|
|
1712
|
-
printJson(agentValidationResultSchema.parse(validation));
|
|
1775
|
+
const prepared = createSudaPageAgentRuntime(manifest).preparePageData(pageContent);
|
|
1776
|
+
if (!prepared.validation.valid) {
|
|
1777
|
+
printJson(agentValidationResultSchema.parse(prepared.validation));
|
|
1713
1778
|
process.exitCode = 1;
|
|
1714
1779
|
return;
|
|
1715
1780
|
}
|
|
@@ -1879,6 +1944,58 @@ async function performUpdateContactForm(auth, input, fetcher = fetchJson) {
|
|
|
1879
1944
|
contactForm
|
|
1880
1945
|
};
|
|
1881
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
|
+
}
|
|
1882
1999
|
async function listAgentProjects() {
|
|
1883
2000
|
const auth = await requireCliBaseUrl();
|
|
1884
2001
|
const result = await fetchJson(
|
|
@@ -2005,7 +2122,7 @@ async function startMcpServer() {
|
|
|
2005
2122
|
server.registerTool(
|
|
2006
2123
|
"get_page_schema",
|
|
2007
2124
|
{
|
|
2008
|
-
description: "Return the
|
|
2125
|
+
description: "Return the schema for configuring existing Suda theme sections as page content. Use only the returned section types, fields, defaultProps, and slot allowedComponents; never create theme source code or new component types. If the theme has a contact section, generate only the section placement/content props; public form fields and delivery integrations are project contact form settings exposed to themes as metadata.contactForm. Use get_contact_form_settings and update_contact_form_settings for user-approved contact form changes.",
|
|
2009
2126
|
inputSchema: {
|
|
2010
2127
|
theme: z.string(),
|
|
2011
2128
|
version: z.string().optional(),
|
|
@@ -2017,16 +2134,16 @@ async function startMcpServer() {
|
|
|
2017
2134
|
},
|
|
2018
2135
|
async ({ theme, version, projectId, themeRoot, includeExample }) => {
|
|
2019
2136
|
const manifest = await fetchAgentManifest(theme, { version, projectId, themeRoot });
|
|
2020
|
-
const structuredContent =
|
|
2137
|
+
const structuredContent = createSudaPageAgentRuntime(manifest).getPageSchemaOutput({
|
|
2021
2138
|
includeExample: includeExample === true
|
|
2022
2139
|
});
|
|
2023
|
-
return mcpStructured("
|
|
2140
|
+
return mcpStructured("Suda page content configuration schema.", structuredContent);
|
|
2024
2141
|
}
|
|
2025
2142
|
);
|
|
2026
2143
|
server.registerTool(
|
|
2027
2144
|
"get_section_schema",
|
|
2028
2145
|
{
|
|
2029
|
-
description: "Return the
|
|
2146
|
+
description: "Return the schema for configuring one existing section in a theme. Use the returned fields, defaultProps, and slot allowedComponents to set props; do not design or add theme components. For contact sections, use the schema for presentation props only and do not hardcode form field metadata, notification channels, webhook URLs, or recipients into page content. Use contact form tools for project-level contact settings.",
|
|
2030
2147
|
inputSchema: {
|
|
2031
2148
|
theme: z.string(),
|
|
2032
2149
|
section: z.string(),
|
|
@@ -2039,13 +2156,13 @@ async function startMcpServer() {
|
|
|
2039
2156
|
async ({ theme, section, version, projectId, themeRoot }) => {
|
|
2040
2157
|
const manifest = await fetchAgentManifest(theme, { version, projectId, themeRoot });
|
|
2041
2158
|
const structuredContent = createSectionSchema(manifest, section);
|
|
2042
|
-
return mcpStructured("
|
|
2159
|
+
return mcpStructured("Suda section configuration schema.", structuredContent);
|
|
2043
2160
|
}
|
|
2044
2161
|
);
|
|
2045
2162
|
server.registerTool(
|
|
2046
2163
|
"validate_page_config",
|
|
2047
2164
|
{
|
|
2048
|
-
description: "Validate
|
|
2165
|
+
description: "Validate Suda page content JSON against an existing theme schema.",
|
|
2049
2166
|
inputSchema: {
|
|
2050
2167
|
theme: z.string(),
|
|
2051
2168
|
data: z.unknown(),
|
|
@@ -2058,7 +2175,7 @@ async function startMcpServer() {
|
|
|
2058
2175
|
async ({ theme, data, version, projectId, themeRoot }) => {
|
|
2059
2176
|
const manifest = await fetchAgentManifest(theme, { version, projectId, themeRoot });
|
|
2060
2177
|
const structuredContent = agentValidationResultSchema.parse(
|
|
2061
|
-
|
|
2178
|
+
createSudaPageAgentRuntime(manifest).validatePageContent(data)
|
|
2062
2179
|
);
|
|
2063
2180
|
return mcpStructured("Suda page content validation result.", structuredContent);
|
|
2064
2181
|
}
|
|
@@ -2105,10 +2222,52 @@ async function startMcpServer() {
|
|
|
2105
2222
|
);
|
|
2106
2223
|
}
|
|
2107
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
|
+
);
|
|
2108
2267
|
server.registerTool(
|
|
2109
2268
|
"create_page_draft",
|
|
2110
2269
|
{
|
|
2111
|
-
description: "Create a new workspace page draft from
|
|
2270
|
+
description: "Create a new workspace page draft from Suda page content JSON that configures existing theme sections. When creating a complete website, mark exactly one primary landing page with isHome: true so it becomes the public homepage at `/`. After this tool succeeds, tell the user the page is only a draft, explain that they can review it in the SudaCloud workspace by navigating to Pages, opening the created page, previewing it, then clicking Publish, and ask whether they want you to publish it now. Do not call publish_page until the user explicitly confirms.",
|
|
2112
2271
|
inputSchema: {
|
|
2113
2272
|
projectId: z.string(),
|
|
2114
2273
|
title: z.string(),
|
|
@@ -2122,14 +2281,14 @@ async function startMcpServer() {
|
|
|
2122
2281
|
},
|
|
2123
2282
|
async ({ projectId, title, slug, isHome, theme, data, version }) => {
|
|
2124
2283
|
const manifest = await fetchAgentManifest(theme, { version, projectId });
|
|
2125
|
-
const
|
|
2126
|
-
if (!validation.valid) {
|
|
2284
|
+
const prepared = createSudaPageAgentRuntime(manifest).preparePageData(data);
|
|
2285
|
+
if (!prepared.validation.valid) {
|
|
2127
2286
|
return mcpStructured(
|
|
2128
2287
|
"Suda page content validation failed. Fix issues before creating a draft.",
|
|
2129
2288
|
createPageDraftOutputSchema.parse({
|
|
2130
2289
|
status: "invalid",
|
|
2131
2290
|
valid: false,
|
|
2132
|
-
issues: validation.issues
|
|
2291
|
+
issues: prepared.validation.issues
|
|
2133
2292
|
})
|
|
2134
2293
|
);
|
|
2135
2294
|
}
|
|
@@ -2152,7 +2311,7 @@ async function startMcpServer() {
|
|
|
2152
2311
|
server.registerTool(
|
|
2153
2312
|
"update_page_draft",
|
|
2154
2313
|
{
|
|
2155
|
-
description: "Update an existing workspace page draft by slug from
|
|
2314
|
+
description: "Update an existing workspace page draft by slug from Suda page content JSON that configures existing theme sections.",
|
|
2156
2315
|
inputSchema: {
|
|
2157
2316
|
projectId: z.string(),
|
|
2158
2317
|
slug: z.string(),
|
|
@@ -2164,14 +2323,14 @@ async function startMcpServer() {
|
|
|
2164
2323
|
},
|
|
2165
2324
|
async ({ projectId, slug, theme, data, version }) => {
|
|
2166
2325
|
const manifest = await fetchAgentManifest(theme, { version, projectId });
|
|
2167
|
-
const
|
|
2168
|
-
if (!validation.valid) {
|
|
2326
|
+
const prepared = createSudaPageAgentRuntime(manifest).preparePageData(data);
|
|
2327
|
+
if (!prepared.validation.valid) {
|
|
2169
2328
|
return mcpStructured(
|
|
2170
2329
|
"Suda page content validation failed. Fix issues before updating a draft.",
|
|
2171
2330
|
updatePageDraftOutputSchema.parse({
|
|
2172
2331
|
status: "invalid",
|
|
2173
2332
|
valid: false,
|
|
2174
|
-
issues: validation.issues
|
|
2333
|
+
issues: prepared.validation.issues
|
|
2175
2334
|
})
|
|
2176
2335
|
);
|
|
2177
2336
|
}
|
|
@@ -2282,6 +2441,11 @@ async function writeThemeArtifacts(theme) {
|
|
|
2282
2441
|
await writeFile(
|
|
2283
2442
|
path2.join(dist, "starter-pages.json"),
|
|
2284
2443
|
`${JSON.stringify(theme.module.starterPages, null, 2)}
|
|
2444
|
+
`
|
|
2445
|
+
);
|
|
2446
|
+
await writeFile(
|
|
2447
|
+
path2.join(dist, "cms-templates.json"),
|
|
2448
|
+
`${JSON.stringify(theme.module.cmsTemplates, null, 2)}
|
|
2285
2449
|
`
|
|
2286
2450
|
);
|
|
2287
2451
|
await writeFile(
|
|
@@ -2800,20 +2964,24 @@ function buildProgram() {
|
|
|
2800
2964
|
printJson({ themes });
|
|
2801
2965
|
});
|
|
2802
2966
|
const agentTheme = agent.command("theme").description("Inspect a single theme.");
|
|
2803
|
-
agentTheme.command("describe").description("Describe a theme's
|
|
2967
|
+
agentTheme.command("describe").description("Describe a theme's page content configuration schema.").argument("<theme>", "Theme key.").option("--version <version>", "Theme version.").option("--project-id <projectId>", "Project scope.").option("--theme-root <path>", "Read a local theme.").option("--example", "Include example page content.").action(async (themeKey, options) => {
|
|
2804
2968
|
const manifest = await fetchAgentManifest(themeKey, options);
|
|
2805
2969
|
printJson(
|
|
2806
|
-
|
|
2970
|
+
createSudaPageAgentRuntime(manifest).getPageSchemaOutput({
|
|
2971
|
+
includeExample: options.example === true
|
|
2972
|
+
})
|
|
2807
2973
|
);
|
|
2808
2974
|
});
|
|
2809
|
-
const agentPage = agent.command("page").description("
|
|
2810
|
-
agentPage.command("schema").description("Print the
|
|
2975
|
+
const agentPage = agent.command("page").description("Page content configuration tooling.");
|
|
2976
|
+
agentPage.command("schema").description("Print the page content configuration schema for a theme.").argument("<theme>", "Theme key.").option("--version <version>", "Theme version.").option("--project-id <projectId>", "Project scope.").option("--theme-root <path>", "Read a local theme.").option("--example", "Include example page content.").action(async (themeKey, options) => {
|
|
2811
2977
|
const manifest = await fetchAgentManifest(themeKey, options);
|
|
2812
2978
|
printJson(
|
|
2813
|
-
|
|
2979
|
+
createSudaPageAgentRuntime(manifest).getPageSchemaOutput({
|
|
2980
|
+
includeExample: options.example === true
|
|
2981
|
+
})
|
|
2814
2982
|
);
|
|
2815
2983
|
});
|
|
2816
|
-
agentPage.command("validate").description("Validate
|
|
2984
|
+
agentPage.command("validate").description("Validate a Suda page content JSON document.").requiredOption("--theme <theme>", "Theme key.").requiredOption("--input <file>", "JSON file containing Suda page content.").option("--version <version>", "Theme version.").option("--project-id <projectId>", "Project scope.").option("--theme-root <path>", "Read a local theme.").action(async (options) => {
|
|
2817
2985
|
await validateAgentPage(options.theme, options.input, options);
|
|
2818
2986
|
});
|
|
2819
2987
|
agentPage.command("create").description(
|
|
@@ -2822,7 +2990,7 @@ function buildProgram() {
|
|
|
2822
2990
|
await createAgentPage(options);
|
|
2823
2991
|
});
|
|
2824
2992
|
const agentSection = agent.command("section").description("Inspect a single section.");
|
|
2825
|
-
agentSection.command("schema").description("Print one
|
|
2993
|
+
agentSection.command("schema").description("Print one section configuration schema for a theme.").requiredOption("--theme <theme>", "Theme key.").requiredOption("--section <section>", "Section type.").option("--version <version>", "Theme version.").option("--project-id <projectId>", "Project scope.").option("--theme-root <path>", "Read a local theme.").action(async (options) => {
|
|
2826
2994
|
const manifest = await fetchAgentManifest(options.theme, options);
|
|
2827
2995
|
printJson(createSectionSchema(manifest, options.section));
|
|
2828
2996
|
});
|