@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.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, createAgentPageSchemaOutput, checkThemeModule, formatThemeCheckResult, validateAgentPageContentWithManifest, agentValidationResultSchema, findAgentSection, createAgentComponentSchemaOutput, 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;
@@ -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 = findAgentSection(manifest, sectionType);
1690
- if (!section) {
1754
+ const section = createSudaPageAgentRuntime(manifest).getSectionSchemaOutput(sectionType);
1755
+ if (section === null) {
1691
1756
  throw new Error(`Unknown section type "${sectionType}".`);
1692
1757
  }
1693
- return createAgentComponentSchemaOutput(section, "section");
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 = validateAgentPageContentWithManifest(pageContent, manifest);
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 validation = validateAgentPageContentWithManifest(pageContent, manifest);
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 AI-first output schema for generating Suda page content, including all section schemas. Field schemas include dynamic visibility metadata for Suda visibleIf fields. 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.",
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 = createAgentPageSchemaOutput(manifest, {
2137
+ const structuredContent = createSudaPageAgentRuntime(manifest).getPageSchemaOutput({
2021
2138
  includeExample: includeExample === true
2022
2139
  });
2023
- return mcpStructured("AI-first Suda page content output schema.", structuredContent);
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 AI-first output schema for one section component in a theme. Field schemas include dynamic visibility metadata for Suda visibleIf fields. 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.",
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("AI-first Suda section output schema.", structuredContent);
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 AI-generated Suda page content against a theme.",
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
- validateAgentPageContentWithManifest(data, manifest)
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 AI-generated Suda page content. 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.",
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 validation = validateAgentPageContentWithManifest(data, manifest);
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 AI-generated Suda page content.",
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 validation = validateAgentPageContentWithManifest(data, manifest);
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 AI-first page 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) => {
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
- createAgentPageSchemaOutput(manifest, { includeExample: options.example === true })
2970
+ createSudaPageAgentRuntime(manifest).getPageSchemaOutput({
2971
+ includeExample: options.example === true
2972
+ })
2807
2973
  );
2808
2974
  });
2809
- const agentPage = agent.command("page").description("AI-first page content tooling.");
2810
- agentPage.command("schema").description("Print the AI-first page content output 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) => {
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
- createAgentPageSchemaOutput(manifest, { includeExample: options.example === true })
2979
+ createSudaPageAgentRuntime(manifest).getPageSchemaOutput({
2980
+ includeExample: options.example === true
2981
+ })
2814
2982
  );
2815
2983
  });
2816
- agentPage.command("validate").description("Validate an AI-generated 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) => {
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 AI-first section output 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) => {
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
  });