@wp-typia/project-tools 0.24.6 → 0.24.7

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.
Files changed (27) hide show
  1. package/dist/runtime/cli/cli-core.d.ts +1 -1
  2. package/dist/runtime/cli/cli-help.js +10 -9
  3. package/dist/runtime/doctor/cli-doctor-wordpress-version.d.ts +15 -0
  4. package/dist/runtime/doctor/cli-doctor-wordpress-version.js +233 -0
  5. package/dist/runtime/doctor/cli-doctor-workspace.d.ts +10 -3
  6. package/dist/runtime/doctor/cli-doctor-workspace.js +9 -3
  7. package/dist/runtime/doctor/cli-doctor.d.ts +10 -1
  8. package/dist/runtime/doctor/cli-doctor.js +10 -3
  9. package/dist/runtime/index.d.ts +1 -1
  10. package/dist/runtime/templates/block-generator-service-core.d.ts +1 -1
  11. package/dist/runtime/templates/block-generator-service-core.js +2 -1
  12. package/dist/runtime/templates/block-generator-service-spec.d.ts +4 -1
  13. package/dist/runtime/templates/block-generator-service-spec.js +6 -3
  14. package/dist/runtime/templates/cli-scaffold-emission.d.ts +4 -0
  15. package/dist/runtime/templates/cli-scaffold.d.ts +2 -1
  16. package/dist/runtime/templates/cli-scaffold.js +4 -1
  17. package/dist/runtime/templates/scaffold-compatibility.d.ts +29 -0
  18. package/dist/runtime/templates/scaffold-compatibility.js +43 -0
  19. package/dist/runtime/templates/scaffold-template-variables.d.ts +12 -1
  20. package/dist/runtime/templates/scaffold-template-variables.js +7 -3
  21. package/dist/runtime/templates/scaffold.d.ts +3 -1
  22. package/dist/runtime/templates/scaffold.js +4 -1
  23. package/dist/runtime/workspace/workspace-inventory-parser-entries.js +92 -3
  24. package/dist/runtime/workspace/workspace-inventory-parser-validation.d.ts +3 -2
  25. package/dist/runtime/workspace/workspace-inventory-section-descriptors.js +2 -0
  26. package/dist/runtime/workspace/workspace-inventory-types.d.ts +3 -0
  27. package/package.json +2 -2
@@ -30,7 +30,7 @@
30
30
  * `getTemplateSelectOptions`, `listTemplates`, and `isBuiltInTemplateId` for
31
31
  * template inspection flows.
32
32
  */
33
- export { createDoctorRunSummary, getDoctorChecks, getDoctorExitFailureChecks, getDoctorExitFailureDetailLines, runDoctor, type DoctorCheck, type DoctorCheckScope, type DoctorExitPolicy, type DoctorFailureSummary, type DoctorRunSummary, } from "../doctor/cli-doctor.js";
33
+ export { createDoctorRunSummary, getDoctorChecks, getDoctorExitFailureChecks, getDoctorExitFailureDetailLines, runDoctor, type DoctorCheck, type DoctorCheckScope, type DoctorExitPolicy, type DoctorFailureSummary, type DoctorRunSummary, type GetDoctorChecksOptions, } from "../doctor/cli-doctor.js";
34
34
  export { createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODE_METADATA, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getCliDiagnosticCodeMetadata, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
35
35
  export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, } from "./cli-diagnostics.js";
36
36
  export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, PATTERN_CATALOG_SCOPE_IDS, runAddAdminViewCommand, runAddAbilityCommand, runAddBindingSourceCommand, runAddAiFeatureCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddContractCommand, runAddCoreVariationCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddPostMetaCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "../add/cli-add.js";
@@ -10,13 +10,13 @@ import { TEMPLATE_IDS } from "../templates/template-registry.js";
10
10
  */
11
11
  export function formatHelpText() {
12
12
  return `Usage:
13
- wp-typia create <project-dir> [--template <basic|interactivity>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
14
- wp-typia create <project-dir> [--template workspace] [--profile <plugin-qa>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
15
- wp-typia create <project-dir> [--template query-loop] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--query-post-type <post-type>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
16
- wp-typia create <project-dir> [--template <./path|github:owner/repo/path[#ref]>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
17
- wp-typia create <project-dir> [--template <npm-package>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
18
- wp-typia create <project-dir> [--template persistence] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
19
- wp-typia create <project-dir> [--template compound] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
13
+ wp-typia create <project-dir> [--template <basic|interactivity>] [--wp-version <6.9|7.0>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
14
+ wp-typia create <project-dir> [--template workspace] [--wp-version <6.9|7.0>] [--profile <plugin-qa>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
15
+ wp-typia create <project-dir> [--template query-loop] [--wp-version <6.9|7.0>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--query-post-type <post-type>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
16
+ wp-typia create <project-dir> [--template <./path|github:owner/repo/path[#ref]>] [--wp-version <6.9|7.0>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
17
+ wp-typia create <project-dir> [--template <npm-package>] [--wp-version <6.9|7.0>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
18
+ wp-typia create <project-dir> [--template persistence] [--wp-version <6.9|7.0>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
19
+ wp-typia create <project-dir> [--template compound] [--wp-version <6.9|7.0>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
20
20
  wp-typia <project-dir> [create flags...]
21
21
  wp-typia init [project-dir] [--apply] [--package-manager <id>]
22
22
  wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>]
@@ -39,7 +39,7 @@ export function formatHelpText() {
39
39
  wp-typia migrate <init|snapshot|diff|scaffold|verify|doctor|fixtures|fuzz> [...]
40
40
  wp-typia templates list
41
41
  wp-typia templates inspect <id>
42
- wp-typia doctor
42
+ wp-typia doctor [--wp-version-check]
43
43
  wp-typia mcp <list|sync>
44
44
  wp-typia skills <list|sync>
45
45
  wp-typia completions <bash|zsh|fish|powershell>
@@ -55,6 +55,7 @@ Notes:
55
55
  \`wp-typia init\` previews the minimum retrofit sync surface by default; rerun with \`--apply\` to write package.json updates and generated helper scripts.
56
56
  Use \`--template workspace\` as shorthand for \`@wp-typia/create-workspace-template\`, the official empty workspace scaffold behind \`wp-typia add ...\`.
57
57
  Use \`--profile plugin-qa\` with \`--template workspace\` when a plugin needs local wp-env smoke checks, \`.env.example\`, and release zip scripts from day one; omit it for the minimal workspace shell.
58
+ Use \`--wp-version 6.9\` to keep legacy \`Tested up to: 6.9\` headers; new scaffolds default to WordPress 7.0 while keeping \`Requires at least\` at the selected feature floor.
58
59
  Interactive add flows let you choose a template when \`--template\` is omitted; non-interactive runs default to \`basic\`.
59
60
  Unknown \`core/*\` targets for \`add core-variation\` warn instead of failing so newer WordPress core blocks remain scaffoldable; third-party namespaces are allowed without local discovery.
60
61
  \`add admin-view\` scaffolds an opt-in DataViews-powered WordPress admin screen under \`src/admin-views/\`.
@@ -77,7 +78,7 @@ Notes:
77
78
  \`add ai-feature\` scaffolds server-owned AI feature endpoints under \`src/ai-features/\` and PHP route glue under \`inc/ai-features/\`.
78
79
  \`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins/\`; legacy aliases \`PluginSidebar\` and \`PluginDocumentSettingPanel\` resolve to \`sidebar\` and \`document-setting-panel\`.
79
80
  \`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
80
- \`wp-typia doctor\` always checks environment readiness and reports when it only ran environment-level diagnostics; official workspace roots also get inventory, source-tree drift, shared convention checks, and iframe/API v3 compatibility checks.
81
+ \`wp-typia doctor\` always checks environment readiness and reports when it only ran environment-level diagnostics; official workspace roots also get inventory, source-tree drift, shared convention checks, and iframe/API v3 compatibility checks. Pass \`--wp-version-check\` to compare generated feature floors such as block supports, blockHooks, abilities, and AI feature metadata with plugin bootstrap compatibility headers.
81
82
  \`wp-typia init\` previews or applies the minimum sync/doctor/migration adoption layer for supported existing layouts; pass \`--package-manager <id>\` to pin emitted scripts and next steps.
82
83
  \`wp-typia migrate doctor --all\` checks migration target alignment, snapshots, fixtures, and generated migration artifacts.
83
84
  \`migrate\` is the canonical migration command; \`migrations\` is no longer supported.`;
@@ -0,0 +1,15 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ import type { WorkspaceInventory } from "../workspace/workspace-inventory.js";
3
+ import type { WorkspaceProject } from "../workspace/workspace-project.js";
4
+ /** Options for opt-in WordPress version compatibility doctor checks. */
5
+ export interface WorkspaceWordPressVersionDoctorCheckOptions {
6
+ /** WordPress target used for `Tested up to` warnings. Defaults to the scaffold target. */
7
+ targetVersion?: string;
8
+ }
9
+ /**
10
+ * Collect opt-in WordPress feature floor checks for an official workspace.
11
+ *
12
+ * These checks compare generated feature metadata against plugin bootstrap
13
+ * headers without changing the default doctor output shape.
14
+ */
15
+ export declare function getWorkspaceWordPressVersionDoctorChecks(workspace: WorkspaceProject, inventory: WorkspaceInventory, options?: WorkspaceWordPressVersionDoctorCheckOptions): DoctorCheck[];
@@ -0,0 +1,233 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
4
+ import ts from "typescript";
5
+ import { createDoctorCheck, resolveWorkspaceBootstrapPath, } from "./cli-doctor-workspace-shared.js";
6
+ import { readJsonFileSync } from "../shared/json-utils.js";
7
+ import { getPropertyNameText } from "../shared/ts-property-names.js";
8
+ import { compareVersionFloors, pickHigherVersionFloor, } from "../shared/version-floor.js";
9
+ import { DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION } from "../templates/scaffold-compatibility.js";
10
+ const WORDPRESS_VERSION_CHECK_CODES = {
11
+ featureMinimum: "wp-typia.workspace.wordpress.feature-minimum",
12
+ testedTarget: "wp-typia.workspace.wordpress.tested-target",
13
+ };
14
+ const BLOCK_METADATA_WORDPRESS_FLOORS = {
15
+ blockHooks: "6.4",
16
+ supportsInteractivity: "6.5",
17
+ supportsSplitting: "6.5",
18
+ };
19
+ function isEnabledMetadataValue(value) {
20
+ return value !== undefined && value !== false && value !== null;
21
+ }
22
+ function getBootstrapHeaderValue(source, headerName) {
23
+ const escapedHeaderName = headerName.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
24
+ const pattern = new RegExp(`^\\s*\\*\\s*${escapedHeaderName}:\\s*([^\\r\\n]*)`, "mu");
25
+ const match = pattern.exec(source);
26
+ return match?.[1]?.trim();
27
+ }
28
+ function readBootstrapHeaderVersions(workspace) {
29
+ const bootstrapPath = resolveWorkspaceBootstrapPath(workspace.projectDir, workspace.packageName);
30
+ if (!fs.existsSync(bootstrapPath)) {
31
+ return {};
32
+ }
33
+ const source = fs.readFileSync(bootstrapPath, "utf8");
34
+ return {
35
+ requiresAtLeast: getBootstrapHeaderValue(source, "Requires at least"),
36
+ testedUpTo: getBootstrapHeaderValue(source, "Tested up to"),
37
+ };
38
+ }
39
+ function pushRequirement(requirements, label, version) {
40
+ requirements.push({
41
+ label,
42
+ version,
43
+ });
44
+ }
45
+ function collectBlockMetadataRequirements(workspace, inventory) {
46
+ const issues = [];
47
+ const requirements = [];
48
+ for (const block of inventory.blocks) {
49
+ const blockJsonRelativePath = path.join("src", "blocks", block.slug, "block.json");
50
+ const blockJsonPath = path.join(workspace.projectDir, blockJsonRelativePath);
51
+ if (!fs.existsSync(blockJsonPath)) {
52
+ continue;
53
+ }
54
+ let blockJson;
55
+ try {
56
+ blockJson = parseScaffoldBlockMetadata(readJsonFileSync(blockJsonPath, {
57
+ context: "workspace block metadata",
58
+ }));
59
+ }
60
+ catch (error) {
61
+ issues.push(`${blockJsonRelativePath}: ${error instanceof Error ? error.message : String(error)}`);
62
+ continue;
63
+ }
64
+ if (isEnabledMetadataValue(blockJson.supports?.interactivity)) {
65
+ pushRequirement(requirements, `Block ${block.slug} supports.interactivity`, BLOCK_METADATA_WORDPRESS_FLOORS.supportsInteractivity);
66
+ }
67
+ if (isEnabledMetadataValue(blockJson.supports?.splitting)) {
68
+ pushRequirement(requirements, `Block ${block.slug} supports.splitting`, BLOCK_METADATA_WORDPRESS_FLOORS.supportsSplitting);
69
+ }
70
+ if (blockJson.blockHooks !== undefined) {
71
+ pushRequirement(requirements, `Block ${block.slug} blockHooks`, BLOCK_METADATA_WORDPRESS_FLOORS.blockHooks);
72
+ }
73
+ }
74
+ return {
75
+ issues,
76
+ requirements,
77
+ };
78
+ }
79
+ function findExportedArrayLiteral(sourceFile, exportName) {
80
+ for (const statement of sourceFile.statements) {
81
+ if (!ts.isVariableStatement(statement) ||
82
+ !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
83
+ continue;
84
+ }
85
+ for (const declaration of statement.declarationList.declarations) {
86
+ if (ts.isIdentifier(declaration.name) &&
87
+ declaration.name.text === exportName &&
88
+ declaration.initializer &&
89
+ ts.isArrayLiteralExpression(declaration.initializer)) {
90
+ return declaration.initializer;
91
+ }
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ function getObjectProperty(objectLiteral, key) {
97
+ for (const property of objectLiteral.properties) {
98
+ if (!ts.isPropertyAssignment(property)) {
99
+ continue;
100
+ }
101
+ if (getPropertyNameText(property.name) === key) {
102
+ return property.initializer;
103
+ }
104
+ }
105
+ return undefined;
106
+ }
107
+ function getObjectLiteralProperty(objectLiteral, key) {
108
+ const property = objectLiteral ? getObjectProperty(objectLiteral, key) : undefined;
109
+ return property && ts.isObjectLiteralExpression(property) ? property : undefined;
110
+ }
111
+ function getStringLiteralProperty(objectLiteral, key) {
112
+ const property = getObjectProperty(objectLiteral, key);
113
+ return property && ts.isStringLiteralLike(property) ? property.text : undefined;
114
+ }
115
+ function collectInventoryCompatibilityRequirements(inventory) {
116
+ const issues = [];
117
+ const requirements = [];
118
+ const sourceFile = ts.createSourceFile("scripts/block-config.ts", inventory.source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
119
+ for (const section of [
120
+ { exportName: "ABILITIES", label: "Ability" },
121
+ { exportName: "AI_FEATURES", label: "AI feature" },
122
+ ]) {
123
+ const arrayLiteral = findExportedArrayLiteral(sourceFile, section.exportName);
124
+ if (!arrayLiteral) {
125
+ continue;
126
+ }
127
+ arrayLiteral.elements.forEach((element, elementIndex) => {
128
+ if (!ts.isObjectLiteralExpression(element)) {
129
+ return;
130
+ }
131
+ const slug = getStringLiteralProperty(element, "slug") ?? `entry ${elementIndex + 1}`;
132
+ const compatibility = getObjectLiteralProperty(element, "compatibility");
133
+ const hardMinimums = getObjectLiteralProperty(compatibility, "hardMinimums");
134
+ const wordpressMinimum = hardMinimums
135
+ ? getObjectProperty(hardMinimums, "wordpress")
136
+ : undefined;
137
+ if (wordpressMinimum === undefined) {
138
+ return;
139
+ }
140
+ if (!ts.isStringLiteralLike(wordpressMinimum)) {
141
+ issues.push(`${section.exportName}[${elementIndex}].compatibility.hardMinimums.wordpress must be a string literal.`);
142
+ return;
143
+ }
144
+ requirements.push({
145
+ label: `${section.label} ${slug} compatibility metadata`,
146
+ version: wordpressMinimum.text,
147
+ });
148
+ });
149
+ }
150
+ return {
151
+ issues,
152
+ requirements,
153
+ };
154
+ }
155
+ function collectWordPressVersionRequirements(workspace, inventory) {
156
+ const blockRequirements = collectBlockMetadataRequirements(workspace, inventory);
157
+ const inventoryRequirements = collectInventoryCompatibilityRequirements(inventory);
158
+ return {
159
+ issues: [...blockRequirements.issues, ...inventoryRequirements.issues],
160
+ requirements: [
161
+ ...blockRequirements.requirements,
162
+ ...inventoryRequirements.requirements,
163
+ ],
164
+ };
165
+ }
166
+ function pickHighestRequirementFloor(requirements, issues) {
167
+ let highest;
168
+ for (const requirement of requirements) {
169
+ try {
170
+ highest = pickHigherVersionFloor(highest, requirement.version);
171
+ }
172
+ catch {
173
+ issues.push(`${requirement.label} declares invalid WordPress version floor "${requirement.version}".`);
174
+ }
175
+ }
176
+ return highest;
177
+ }
178
+ function formatRequirementSummary(requirements, floor) {
179
+ const labels = requirements
180
+ .filter((requirement) => requirement.version === floor)
181
+ .map((requirement) => requirement.label);
182
+ return labels.length > 0 ? labels.join(", ") : "generated workspace features";
183
+ }
184
+ function createFeatureMinimumCheck(workspace, inventory, headers) {
185
+ const { issues, requirements } = collectWordPressVersionRequirements(workspace, inventory);
186
+ const highestFloor = pickHighestRequirementFloor(requirements, issues);
187
+ if (issues.length > 0) {
188
+ return createDoctorCheck("WordPress feature minimum", "fail", issues.join("; "), WORDPRESS_VERSION_CHECK_CODES.featureMinimum);
189
+ }
190
+ if (!highestFloor) {
191
+ return createDoctorCheck("WordPress feature minimum", "pass", "No generated workspace features declare an additional WordPress hard floor.", WORDPRESS_VERSION_CHECK_CODES.featureMinimum);
192
+ }
193
+ if (!headers.requiresAtLeast) {
194
+ return createDoctorCheck("WordPress feature minimum", "fail", `Plugin bootstrap is missing a Requires at least header but generated features require WordPress ${highestFloor}.`, WORDPRESS_VERSION_CHECK_CODES.featureMinimum);
195
+ }
196
+ try {
197
+ if (compareVersionFloors(headers.requiresAtLeast, highestFloor) < 0) {
198
+ return createDoctorCheck("WordPress feature minimum", "fail", `Requires at least ${headers.requiresAtLeast} is below generated feature floor ${highestFloor} (${formatRequirementSummary(requirements, highestFloor)}).`, WORDPRESS_VERSION_CHECK_CODES.featureMinimum);
199
+ }
200
+ }
201
+ catch {
202
+ return createDoctorCheck("WordPress feature minimum", "fail", `Plugin bootstrap Requires at least header "${headers.requiresAtLeast}" is not a dotted numeric version.`, WORDPRESS_VERSION_CHECK_CODES.featureMinimum);
203
+ }
204
+ return createDoctorCheck("WordPress feature minimum", "pass", `Requires at least ${headers.requiresAtLeast} covers generated feature floor ${highestFloor} (${formatRequirementSummary(requirements, highestFloor)}).`, WORDPRESS_VERSION_CHECK_CODES.featureMinimum);
205
+ }
206
+ function createTestedTargetCheck(headers, targetVersion) {
207
+ if (!headers.testedUpTo) {
208
+ return createDoctorCheck("WordPress tested target", "fail", `Plugin bootstrap is missing a Tested up to header; expected ${targetVersion} or newer.`, WORDPRESS_VERSION_CHECK_CODES.testedTarget);
209
+ }
210
+ try {
211
+ if (compareVersionFloors(headers.testedUpTo, targetVersion) < 0) {
212
+ return createDoctorCheck("WordPress tested target", "warn", `Tested up to ${headers.testedUpTo} is below the selected WordPress target ${targetVersion}.`, WORDPRESS_VERSION_CHECK_CODES.testedTarget);
213
+ }
214
+ }
215
+ catch {
216
+ return createDoctorCheck("WordPress tested target", "fail", `Plugin bootstrap Tested up to header "${headers.testedUpTo}" is not a dotted numeric version.`, WORDPRESS_VERSION_CHECK_CODES.testedTarget);
217
+ }
218
+ return createDoctorCheck("WordPress tested target", "pass", `Tested up to ${headers.testedUpTo} covers the selected WordPress target ${targetVersion}.`, WORDPRESS_VERSION_CHECK_CODES.testedTarget);
219
+ }
220
+ /**
221
+ * Collect opt-in WordPress feature floor checks for an official workspace.
222
+ *
223
+ * These checks compare generated feature metadata against plugin bootstrap
224
+ * headers without changing the default doctor output shape.
225
+ */
226
+ export function getWorkspaceWordPressVersionDoctorChecks(workspace, inventory, options = {}) {
227
+ const targetVersion = options.targetVersion ?? DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION;
228
+ const headers = readBootstrapHeaderVersions(workspace);
229
+ return [
230
+ createFeatureMinimumCheck(workspace, inventory, headers),
231
+ createTestedTargetCheck(headers, targetVersion),
232
+ ];
233
+ }
@@ -1,4 +1,9 @@
1
1
  import type { DoctorCheck } from "./cli-doctor.js";
2
+ /** Options used when collecting workspace-scoped doctor rows. */
3
+ export interface WorkspaceDoctorChecksOptions {
4
+ /** Include opt-in WordPress feature floor and plugin header checks. */
5
+ wordpressVersionCheck?: boolean;
6
+ }
2
7
  /**
3
8
  * Collect workspace-scoped doctor checks for the given working directory.
4
9
  *
@@ -9,10 +14,12 @@ import type { DoctorCheck } from "./cli-doctor.js";
9
14
  * failing rows are returned early and the remaining checks are skipped.
10
15
  * When an official workspace is detected, a passing "Doctor scope" row is
11
16
  * emitted first so the remaining package metadata, inventory, source-tree
12
- * drift, and optional migration hint rows are clearly framed as workspace
13
- * diagnostics for that run.
17
+ * drift, optional WordPress version checks, and optional migration hint rows
18
+ * are clearly framed as workspace diagnostics for that run.
14
19
  *
15
20
  * @param cwd Working directory expected to host an official workspace.
21
+ * @param options Optional feature gates for additional workspace doctor rows.
22
+ * @param options.wordpressVersionCheck Include WordPress feature floor and plugin header checks.
16
23
  * @returns Ordered workspace check rows ready for CLI rendering.
17
24
  */
18
- export declare function getWorkspaceDoctorChecks(cwd: string): Promise<DoctorCheck[]>;
25
+ export declare function getWorkspaceDoctorChecks(cwd: string, options?: WorkspaceDoctorChecksOptions): Promise<DoctorCheck[]>;
@@ -1,6 +1,7 @@
1
1
  import { getWorkspaceBindingDoctorChecks, } from "./cli-doctor-workspace-bindings.js";
2
2
  import { getWorkspaceBlockDoctorChecks, } from "./cli-doctor-workspace-blocks.js";
3
3
  import { getWorkspaceFeatureDoctorChecks, } from "./cli-doctor-workspace-features.js";
4
+ import { getWorkspaceWordPressVersionDoctorChecks, } from "./cli-doctor-wordpress-version.js";
4
5
  import { getMigrationWorkspaceHintCheck, getWorkspacePackageMetadataCheck, prepareWorkspacePackageDoctorSnapshot, } from "./cli-doctor-workspace-package.js";
5
6
  import { createDoctorCheck, createDoctorScopeCheck, } from "./cli-doctor-workspace-shared.js";
6
7
  import { readWorkspaceInventoryAsync, } from "../workspace/workspace-inventory.js";
@@ -31,13 +32,15 @@ function formatWorkspaceInventorySummary(inventory) {
31
32
  * failing rows are returned early and the remaining checks are skipped.
32
33
  * When an official workspace is detected, a passing "Doctor scope" row is
33
34
  * emitted first so the remaining package metadata, inventory, source-tree
34
- * drift, and optional migration hint rows are clearly framed as workspace
35
- * diagnostics for that run.
35
+ * drift, optional WordPress version checks, and optional migration hint rows
36
+ * are clearly framed as workspace diagnostics for that run.
36
37
  *
37
38
  * @param cwd Working directory expected to host an official workspace.
39
+ * @param options Optional feature gates for additional workspace doctor rows.
40
+ * @param options.wordpressVersionCheck Include WordPress feature floor and plugin header checks.
38
41
  * @returns Ordered workspace check rows ready for CLI rendering.
39
42
  */
40
- export async function getWorkspaceDoctorChecks(cwd) {
43
+ export async function getWorkspaceDoctorChecks(cwd, options = {}) {
41
44
  const checks = [];
42
45
  let workspace = null;
43
46
  let invalidWorkspaceReason = null;
@@ -85,6 +88,9 @@ export async function getWorkspaceDoctorChecks(cwd) {
85
88
  checks.push(...getWorkspaceBlockDoctorChecks(workspace, inventory));
86
89
  checks.push(...getWorkspaceBindingDoctorChecks(workspace, inventory));
87
90
  checks.push(...getWorkspaceFeatureDoctorChecks(workspace, inventory));
91
+ if (options.wordpressVersionCheck) {
92
+ checks.push(...getWorkspaceWordPressVersionDoctorChecks(workspace, inventory));
93
+ }
88
94
  const migrationWorkspaceCheck = getMigrationWorkspaceHintCheck(workspacePackageJson, packageDoctorSnapshot);
89
95
  if (migrationWorkspaceCheck) {
90
96
  checks.push(migrationWorkspaceCheck);
@@ -41,10 +41,16 @@ interface RunDoctorOptions {
41
41
  exitPolicy?: DoctorExitPolicy;
42
42
  renderLine?: (check: DoctorCheck) => void;
43
43
  renderSummaryLine?: (summaryLine: string) => void;
44
+ wordpressVersionCheck?: boolean;
44
45
  }
45
46
  interface DoctorSummaryOptions {
46
47
  exitPolicy?: DoctorExitPolicy;
47
48
  }
49
+ /** Options used when collecting doctor checks before rendering. */
50
+ export interface GetDoctorChecksOptions {
51
+ /** Include opt-in WordPress feature floor and plugin header checks. */
52
+ wordpressVersionCheck?: boolean;
53
+ }
48
54
  /**
49
55
  * Collect all runtime doctor checks for the current environment.
50
56
  *
@@ -54,9 +60,11 @@ interface DoctorSummaryOptions {
54
60
  * patterns, bindings, and optional migration hints) in display order.
55
61
  *
56
62
  * @param cwd Working directory to validate for writability.
63
+ * @param options Optional feature gates for additional doctor rows.
64
+ * @param options.wordpressVersionCheck Include WordPress feature floor and plugin header checks.
57
65
  * @returns Ordered doctor check rows ready for CLI rendering.
58
66
  */
59
- export declare function getDoctorChecks(cwd: string): Promise<DoctorCheck[]>;
67
+ export declare function getDoctorChecks(cwd: string, options?: GetDoctorChecksOptions): Promise<DoctorCheck[]>;
60
68
  /**
61
69
  * Return failed rows that contribute to the process exit code for one policy.
62
70
  */
@@ -83,6 +91,7 @@ export declare function createDoctorRunSummary(checks: readonly DoctorCheck[], o
83
91
  * @param options.exitPolicy Policy deciding which failed checks contribute to the process exit code.
84
92
  * @param options.renderLine Optional renderer for each check row. Defaults to the stdout line printer.
85
93
  * @param options.renderSummaryLine Optional renderer for the summary row. Defaults to the stdout line printer unless a custom `renderLine` suppresses implicit summary output.
94
+ * @param options.wordpressVersionCheck Include WordPress feature floor and plugin header checks.
86
95
  * @returns The completed list of doctor checks.
87
96
  * @throws {Error} When one or more failed checks contribute to the exit code under the active policy.
88
97
  */
@@ -46,12 +46,16 @@ function toFailureSummary(check, severity) {
46
46
  * patterns, bindings, and optional migration hints) in display order.
47
47
  *
48
48
  * @param cwd Working directory to validate for writability.
49
+ * @param options Optional feature gates for additional doctor rows.
50
+ * @param options.wordpressVersionCheck Include WordPress feature floor and plugin header checks.
49
51
  * @returns Ordered doctor check rows ready for CLI rendering.
50
52
  */
51
- export async function getDoctorChecks(cwd) {
53
+ export async function getDoctorChecks(cwd, options = {}) {
52
54
  return [
53
55
  ...annotateDoctorChecks(await getEnvironmentDoctorChecks(cwd), "environment"),
54
- ...annotateDoctorChecks(await getWorkspaceDoctorChecks(cwd), "workspace"),
56
+ ...annotateDoctorChecks(await getWorkspaceDoctorChecks(cwd, {
57
+ wordpressVersionCheck: options.wordpressVersionCheck,
58
+ }), "workspace"),
55
59
  ];
56
60
  }
57
61
  /**
@@ -103,6 +107,7 @@ export function createDoctorRunSummary(checks, options = {}) {
103
107
  * @param options.exitPolicy Policy deciding which failed checks contribute to the process exit code.
104
108
  * @param options.renderLine Optional renderer for each check row. Defaults to the stdout line printer.
105
109
  * @param options.renderSummaryLine Optional renderer for the summary row. Defaults to the stdout line printer unless a custom `renderLine` suppresses implicit summary output.
110
+ * @param options.wordpressVersionCheck Include WordPress feature floor and plugin header checks.
106
111
  * @returns The completed list of doctor checks.
107
112
  * @throws {Error} When one or more failed checks contribute to the exit code under the active policy.
108
113
  */
@@ -111,7 +116,9 @@ export async function runDoctor(cwd, options = {}) {
111
116
  const renderLine = options.renderLine ?? renderDefaultDoctorCheckLine;
112
117
  const renderSummaryLine = options.renderSummaryLine ??
113
118
  (options.renderLine ? () => undefined : renderDefaultDoctorSummaryLine);
114
- const checks = await getDoctorChecks(cwd);
119
+ const checks = await getDoctorChecks(cwd, {
120
+ wordpressVersionCheck: options.wordpressVersionCheck,
121
+ });
115
122
  for (const check of checks) {
116
123
  renderLine(check);
117
124
  }
@@ -41,5 +41,5 @@ export type { ExternalTemplateCachePruneOptions, ExternalTemplateCachePruneResul
41
41
  export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
42
42
  export { createReadlinePrompt, createDoctorRunSummary, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorExitFailureChecks, getDoctorExitFailureDetailLines, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddCoreVariationCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddPostMetaCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
43
43
  export { extractPatternSectionRoleMatches, extractPatternSectionRolesFromAttributes, formatPatternCatalogDiagnostics, PATTERN_CATALOG_SCOPE_IDS, PATTERN_SECTION_ROLE_PATTERN, PATTERN_TAG_PATTERN, resolvePatternCatalogContentFile, validatePatternCatalog, } from "./pattern-catalog.js";
44
- export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, DoctorCheck, DoctorCheckScope, DoctorExitPolicy, DoctorFailureSummary, DoctorRunSummary, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, WorkspaceBlockSelectOption, } from "./cli-core.js";
44
+ export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, DoctorCheck, DoctorCheckScope, DoctorExitPolicy, DoctorFailureSummary, DoctorRunSummary, GetDoctorChecksOptions, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, WorkspaceBlockSelectOption, } from "./cli-core.js";
45
45
  export type { PatternCatalogDiagnostic, PatternCatalogDiagnosticCode, PatternCatalogDiagnosticSeverity, PatternCatalogEntry, PatternCatalogScope, PatternCatalogSectionRoleConvention, PatternCatalogSectionRoleMatch, PatternCatalogValidationOptions, PatternCatalogValidationResult, } from "./pattern-catalog.js";
@@ -1,7 +1,7 @@
1
1
  import { type ApplyBlockInput, type PlanBlockInput, type PlanBlockResult, type RenderBlockInput, type RenderBlockResult, type ValidateBlockInput, type ValidateBlockResult } from "./block-generator-service-spec.js";
2
2
  import type { ScaffoldProjectResult } from "./scaffold.js";
3
3
  export declare class BlockGeneratorService {
4
- plan({ allowExistingDir, alternateRenderTargets, answers, cwd, dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi, withTestPreset, withWpEnv, }: PlanBlockInput): Promise<PlanBlockResult>;
4
+ plan({ allowExistingDir, alternateRenderTargets, answers, cwd, dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi, withTestPreset, withWpEnv, wpVersion, }: PlanBlockInput): Promise<PlanBlockResult>;
5
5
  validate({ plan }: ValidateBlockInput): Promise<ValidateBlockResult>;
6
6
  render({ validated }: RenderBlockInput): Promise<RenderBlockResult>;
7
7
  apply({ rendered, installDependencies, onProgress, }: ApplyBlockInput): Promise<ScaffoldProjectResult>;
@@ -83,7 +83,7 @@ async function runCleanupGroup(label, cleanups) {
83
83
  }
84
84
  }
85
85
  export class BlockGeneratorService {
86
- async plan({ allowExistingDir = false, alternateRenderTargets, answers, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall = false, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
86
+ async plan({ allowExistingDir = false, alternateRenderTargets, answers, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall = false, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, wpVersion, }) {
87
87
  return {
88
88
  spec: createBuiltInBlockSpec({
89
89
  alternateRenderTargets,
@@ -94,6 +94,7 @@ export class BlockGeneratorService {
94
94
  withMigrationUi,
95
95
  withTestPreset,
96
96
  withWpEnv,
97
+ wpVersion,
97
98
  }),
98
99
  target: {
99
100
  allowExistingDir,
@@ -3,6 +3,7 @@ import { type AlternateRenderTargetId } from "../add/alternate-render-targets.js
3
3
  import { type CompoundInnerBlocksPresetId } from "../add/compound-inner-blocks.js";
4
4
  import { type BuiltInTemplateId } from "./template-registry.js";
5
5
  import type { InstallDependenciesOptions } from "./scaffold-apply-utils.js";
6
+ import { type ScaffoldWordPressTargetVersion } from "./scaffold-compatibility.js";
6
7
  import type { DataStorageMode, PersistencePolicy, ScaffoldAnswers, ScaffoldProgressEvent, ScaffoldTemplateVariables } from "./scaffold.js";
7
8
  export interface BlockSpec {
8
9
  alternateRenderTargets: readonly AlternateRenderTargetId[];
@@ -42,6 +43,7 @@ export interface BlockSpec {
42
43
  withMigrationUi: boolean;
43
44
  withTestPreset: boolean;
44
45
  withWpEnv: boolean;
46
+ wpVersion: ScaffoldWordPressTargetVersion;
45
47
  };
46
48
  template: {
47
49
  description: string;
@@ -81,6 +83,7 @@ export interface PlanBlockInput {
81
83
  queryPostType?: string;
82
84
  withTestPreset?: boolean;
83
85
  withWpEnv?: boolean;
86
+ wpVersion?: ScaffoldWordPressTargetVersion;
84
87
  }
85
88
  export interface PlanBlockResult {
86
89
  spec: BlockSpec;
@@ -114,5 +117,5 @@ export interface ApplyBlockInput {
114
117
  installDependencies?: ((options: InstallDependenciesOptions) => Promise<void>) | undefined;
115
118
  onProgress?: ((event: ScaffoldProgressEvent) => void | Promise<void>) | undefined;
116
119
  }
117
- export declare function createBuiltInBlockSpec({ alternateRenderTargets, answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi, withTestPreset, withWpEnv, }: Omit<PlanBlockInput, "allowExistingDir" | "cwd" | "noInstall" | "packageManager" | "projectDir" | "variant">): BlockSpec;
120
+ export declare function createBuiltInBlockSpec({ alternateRenderTargets, answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi, withTestPreset, withWpEnv, wpVersion, }: Omit<PlanBlockInput, "allowExistingDir" | "cwd" | "noInstall" | "packageManager" | "projectDir" | "variant">): BlockSpec;
118
121
  export declare function buildTemplateVariablesFromBlockSpec(spec: BlockSpec): ScaffoldTemplateVariables;
@@ -5,7 +5,7 @@ import { DEFAULT_COMPOUND_INNER_BLOCKS_PRESET_ID, getCompoundInnerBlocksPresetDe
5
5
  import { getTemplateById, } from "./template-registry.js";
6
6
  import { toPascalCase, toSnakeCase, } from "../shared/string-case.js";
7
7
  import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from "./scaffold-identifiers.js";
8
- import { resolveScaffoldCompatibilityPolicy } from "./scaffold-compatibility.js";
8
+ import { createScaffoldCompatibilityBaseline, DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION, resolveScaffoldCompatibilityPolicy, } from "./scaffold-compatibility.js";
9
9
  import { attachScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
10
10
  function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePolicy, }) {
11
11
  if (templateId === "persistence") {
@@ -37,7 +37,7 @@ const DEFAULT_QUERY_LOOP_ALLOWED_CONTROLS = [
37
37
  "author",
38
38
  "search",
39
39
  ];
40
- export function createBuiltInBlockSpec({ alternateRenderTargets, answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
40
+ export function createBuiltInBlockSpec({ alternateRenderTargets, answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi = false, withTestPreset = false, withWpEnv = false, wpVersion, }) {
41
41
  const template = getTemplateById(templateId);
42
42
  const metadataDefaults = getBuiltInTemplateMetadataDefaults(templateId);
43
43
  const identifiers = resolveScaffoldIdentifiers({
@@ -89,6 +89,7 @@ export function createBuiltInBlockSpec({ alternateRenderTargets, answers, dataSt
89
89
  withMigrationUi,
90
90
  withTestPreset,
91
91
  withWpEnv,
92
+ wpVersion: wpVersion ?? DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION,
92
93
  },
93
94
  template: {
94
95
  description: template.description,
@@ -123,7 +124,9 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
123
124
  ? spec.persistence.persistencePolicy
124
125
  : "authenticated";
125
126
  const queryVariationNamespace = `${namespace}/${slug}`;
126
- const compatibility = resolveScaffoldCompatibilityPolicy([]);
127
+ const compatibility = resolveScaffoldCompatibilityPolicy([], {
128
+ baseline: createScaffoldCompatibilityBaseline(spec.runtime.wpVersion),
129
+ });
127
130
  const flatVariables = {
128
131
  alternateRenderTargetsCsv: formatAlternateRenderTargets(alternateRenderTargets),
129
132
  alternateRenderTargetsJson: JSON.stringify(alternateRenderTargets),
@@ -103,6 +103,10 @@ export interface ScaffoldEmissionOptions {
103
103
  * Whether local wp-env support should be emitted.
104
104
  */
105
105
  withWpEnv: boolean;
106
+ /**
107
+ * WordPress version target used for generated plugin Tested up to headers.
108
+ */
109
+ wpVersion?: ScaffoldProjectOptions["wpVersion"];
106
110
  }
107
111
  /**
108
112
  * Emit scaffold files into the target project directory.
@@ -42,6 +42,7 @@ interface RunScaffoldFlowOptions {
42
42
  withMigrationUi?: boolean;
43
43
  withTestPreset?: boolean;
44
44
  withWpEnv?: boolean;
45
+ wpVersion?: string;
45
46
  yes?: boolean;
46
47
  }
47
48
  /**
@@ -51,7 +52,7 @@ interface RunScaffoldFlowOptions {
51
52
  * project.
52
53
  * @returns The scaffold result together with next-step guidance.
53
54
  */
54
- export declare function runScaffoldFlow({ projectInput, cwd, templateId, alternateRenderTargets, dataStorageMode, dryRun, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, packageManager, namespace, profile, textDomain, phpPrefix, queryPostType, yes, noInstall, onProgress, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }: RunScaffoldFlowOptions): Promise<{
55
+ export declare function runScaffoldFlow({ projectInput, cwd, templateId, alternateRenderTargets, dataStorageMode, dryRun, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, packageManager, namespace, profile, textDomain, phpPrefix, queryPostType, yes, noInstall, onProgress, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, wpVersion, }: RunScaffoldFlowOptions): Promise<{
55
56
  dryRun: boolean;
56
57
  optionalOnboarding: import("./cli-scaffold-output.js").OptionalOnboardingGuidance;
57
58
  plan: import("./cli-scaffold-emission.js").ScaffoldDryRunPlan | undefined;
@@ -9,6 +9,7 @@ import { collectProjectDirectoryWarnings, collectTemplateCapabilityWarnings, res
9
9
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from "./template-registry.js";
10
10
  import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
11
11
  import { resolveLocalCliPathOption, normalizeOptionalCliString, } from "../cli/cli-validation.js";
12
+ import { resolveScaffoldWordPressTargetVersion, } from "./scaffold-compatibility.js";
12
13
  export { getNextSteps, getOptionalOnboarding } from "./cli-scaffold-output.js";
13
14
  /**
14
15
  * Resolve scaffold options, prompts, and follow-up steps for one CLI run.
@@ -17,7 +18,7 @@ export { getNextSteps, getOptionalOnboarding } from "./cli-scaffold-output.js";
17
18
  * project.
18
19
  * @returns The scaffold result together with next-step guidance.
19
20
  */
20
- export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templateId, alternateRenderTargets, dataStorageMode, dryRun = false, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, packageManager, namespace, profile, textDomain, phpPrefix, queryPostType, yes = false, noInstall = false, onProgress, isInteractive = false, allowExistingDir = false, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies = undefined, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }) {
21
+ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templateId, alternateRenderTargets, dataStorageMode, dryRun = false, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, packageManager, namespace, profile, textDomain, phpPrefix, queryPostType, yes = false, noInstall = false, onProgress, isInteractive = false, allowExistingDir = false, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies = undefined, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, wpVersion, }) {
21
22
  const normalizedExternalLayerId = normalizeOptionalCliString(externalLayerId);
22
23
  const normalizedExternalLayerSource = resolveLocalCliPathOption({
23
24
  cwd,
@@ -85,6 +86,7 @@ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templ
85
86
  isInteractive,
86
87
  selectPackageManager,
87
88
  });
89
+ const resolvedWpVersion = resolveScaffoldWordPressTargetVersion(wpVersion);
88
90
  const resolvedWithWpEnv = resolvedProfile === "plugin-qa"
89
91
  ? true
90
92
  : await resolveOptionalBooleanFlag({
@@ -145,6 +147,7 @@ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templ
145
147
  withMigrationUi: resolvedWithMigrationUi,
146
148
  withTestPreset: resolvedWithTestPreset,
147
149
  withWpEnv: resolvedWithWpEnv,
150
+ wpVersion: resolvedWpVersion,
148
151
  };
149
152
  const resolvedResult = dryRun
150
153
  ? await buildScaffoldDryRunPlan(emissionOptions)
@@ -20,6 +20,18 @@ export interface ScaffoldCompatibilityPolicy {
20
20
  capabilityPlan: ResolvedAiFeatureCapabilityPlan;
21
21
  pluginHeader: ScaffoldPluginHeaderCompatibility;
22
22
  }
23
+ /**
24
+ * Supported WordPress target versions for generated scaffold plugin headers.
25
+ */
26
+ export declare const SCAFFOLD_WORDPRESS_TARGET_VERSIONS: readonly ["6.9", "7.0"];
27
+ /**
28
+ * String literal union for scaffold WordPress targets, currently `6.9` or `7.0`.
29
+ */
30
+ export type ScaffoldWordPressTargetVersion = (typeof SCAFFOLD_WORDPRESS_TARGET_VERSIONS)[number];
31
+ /**
32
+ * Default WordPress target version used when create callers omit `--wp-version`.
33
+ */
34
+ export declare const DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION: ScaffoldWordPressTargetVersion;
23
35
  /**
24
36
  * Serializable compatibility metadata stored in generated workspace inventory.
25
37
  */
@@ -46,6 +58,23 @@ export interface UpdatePluginHeaderCompatibilityOptions {
46
58
  * Baseline headers used by scaffold output before optional features are added.
47
59
  */
48
60
  export declare const DEFAULT_SCAFFOLD_COMPATIBILITY: ScaffoldPluginHeaderCompatibility;
61
+ /**
62
+ * Return whether a string is one of the supported scaffold WordPress targets.
63
+ */
64
+ export declare function isScaffoldWordPressTargetVersion(value: string): value is ScaffoldWordPressTargetVersion;
65
+ /**
66
+ * Resolve CLI input into a supported scaffold WordPress target version.
67
+ *
68
+ * Empty input falls back to the default `7.0` target.
69
+ */
70
+ export declare function resolveScaffoldWordPressTargetVersion(value?: string): ScaffoldWordPressTargetVersion;
71
+ /**
72
+ * Build scaffold header compatibility for the selected WordPress target.
73
+ *
74
+ * The selected target controls `Tested up to`; hard minimums remain tied to the
75
+ * compatibility policy floor.
76
+ */
77
+ export declare function createScaffoldCompatibilityBaseline(wpVersion?: ScaffoldWordPressTargetVersion): ScaffoldPluginHeaderCompatibility;
49
78
  /**
50
79
  * Optional WordPress AI Client surface used by server-only AI feature scaffold.
51
80
  */
@@ -5,7 +5,16 @@
5
5
  * metadata aligned when optional or required AI-capable features are added.
6
6
  */
7
7
  import { AI_FEATURE_DEFINITIONS, resolveAiFeatureCapabilityPlan, } from '../add/ai-feature-capability.js';
8
+ import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from '../cli/cli-diagnostics.js';
8
9
  import { parseVersionFloorParts, pickHigherVersionFloor, } from '../shared/version-floor.js';
10
+ /**
11
+ * Supported WordPress target versions for generated scaffold plugin headers.
12
+ */
13
+ export const SCAFFOLD_WORDPRESS_TARGET_VERSIONS = ['6.9', '7.0'];
14
+ /**
15
+ * Default WordPress target version used when create callers omit `--wp-version`.
16
+ */
17
+ export const DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION = '7.0';
9
18
  /**
10
19
  * Baseline headers used by scaffold output before optional features are added.
11
20
  */
@@ -14,6 +23,40 @@ export const DEFAULT_SCAFFOLD_COMPATIBILITY = {
14
23
  requiresPhp: '8.0',
15
24
  testedUpTo: '6.9',
16
25
  };
26
+ /**
27
+ * Return whether a string is one of the supported scaffold WordPress targets.
28
+ */
29
+ export function isScaffoldWordPressTargetVersion(value) {
30
+ return SCAFFOLD_WORDPRESS_TARGET_VERSIONS.includes(value);
31
+ }
32
+ /**
33
+ * Resolve CLI input into a supported scaffold WordPress target version.
34
+ *
35
+ * Empty input falls back to the default `7.0` target.
36
+ */
37
+ export function resolveScaffoldWordPressTargetVersion(value) {
38
+ if (value === undefined || value.trim().length === 0) {
39
+ return DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION;
40
+ }
41
+ const normalized = value.trim();
42
+ if (isScaffoldWordPressTargetVersion(normalized)) {
43
+ return normalized;
44
+ }
45
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, `Unsupported --wp-version "${value}". Expected one of: ${SCAFFOLD_WORDPRESS_TARGET_VERSIONS.join(', ')}.`);
46
+ }
47
+ /**
48
+ * Build scaffold header compatibility for the selected WordPress target.
49
+ *
50
+ * The selected target controls `Tested up to`; hard minimums remain tied to the
51
+ * compatibility policy floor.
52
+ */
53
+ export function createScaffoldCompatibilityBaseline(wpVersion) {
54
+ const testedUpTo = wpVersion ?? DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION;
55
+ return {
56
+ ...DEFAULT_SCAFFOLD_COMPATIBILITY,
57
+ testedUpTo,
58
+ };
59
+ }
17
60
  /**
18
61
  * Optional WordPress AI Client surface used by server-only AI feature scaffold.
19
62
  */
@@ -1,9 +1,20 @@
1
1
  import type { ScaffoldAnswers, ScaffoldTemplateVariables } from './scaffold.js';
2
+ import { type ScaffoldWordPressTargetVersion } from "./scaffold-compatibility.js";
3
+ /**
4
+ * Optional overrides for scaffold template variable generation.
5
+ */
6
+ export interface GetTemplateVariablesOptions {
7
+ /**
8
+ * WordPress target version used for generated plugin `Tested up to` headers.
9
+ */
10
+ wpVersion?: ScaffoldWordPressTargetVersion;
11
+ }
2
12
  /**
3
13
  * Build the normalized template variables used by scaffold rendering.
4
14
  *
5
15
  * @param templateId Selected scaffold template identifier.
6
16
  * @param answers Normalized scaffold answers collected from defaults, flags, and prompts.
17
+ * @param options Optional compatibility overrides used while building variables.
7
18
  * @returns Template variables ready for file interpolation and generated artifacts.
8
19
  */
9
- export declare function getTemplateVariables(templateId: string, answers: ScaffoldAnswers): ScaffoldTemplateVariables;
20
+ export declare function getTemplateVariables(templateId: string, answers: ScaffoldAnswers, options?: GetTemplateVariablesOptions): ScaffoldTemplateVariables;
@@ -6,21 +6,23 @@ import { DEFAULT_COMPOUND_INNER_BLOCKS_PRESET_ID, getCompoundInnerBlocksPresetDe
6
6
  import { getTemplateById, isBuiltInTemplateId, } from './template-registry.js';
7
7
  import { toPascalCase, toSnakeCase, } from '../shared/string-case.js';
8
8
  import { attachScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
9
- import { resolveScaffoldCompatibilityPolicy } from "./scaffold-compatibility.js";
9
+ import { createScaffoldCompatibilityBaseline, resolveScaffoldCompatibilityPolicy, } from "./scaffold-compatibility.js";
10
10
  /**
11
11
  * Build the normalized template variables used by scaffold rendering.
12
12
  *
13
13
  * @param templateId Selected scaffold template identifier.
14
14
  * @param answers Normalized scaffold answers collected from defaults, flags, and prompts.
15
+ * @param options Optional compatibility overrides used while building variables.
15
16
  * @returns Template variables ready for file interpolation and generated artifacts.
16
17
  */
17
- export function getTemplateVariables(templateId, answers) {
18
+ export function getTemplateVariables(templateId, answers, options = {}) {
18
19
  if (isBuiltInTemplateId(templateId)) {
19
20
  return buildTemplateVariablesFromBlockSpec(createBuiltInBlockSpec({
20
21
  answers,
21
22
  dataStorageMode: answers.dataStorageMode,
22
23
  persistencePolicy: answers.persistencePolicy,
23
24
  templateId,
25
+ wpVersion: options.wpVersion,
24
26
  }));
25
27
  }
26
28
  const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, wpTypiaPackageVersion, } = getPackageVersions();
@@ -59,7 +61,9 @@ export function getTemplateVariables(templateId, answers) {
59
61
  const persistencePolicy = templateId === 'persistence' || compoundPersistenceEnabled
60
62
  ? answers.persistencePolicy ?? 'authenticated'
61
63
  : 'authenticated';
62
- const compatibility = resolveScaffoldCompatibilityPolicy([]);
64
+ const compatibility = resolveScaffoldCompatibilityPolicy([], {
65
+ baseline: createScaffoldCompatibilityBaseline(options.wpVersion),
66
+ });
63
67
  const flatVariables = {
64
68
  alternateRenderTargetsCsv: '',
65
69
  alternateRenderTargetsJson: '[]',
@@ -1,4 +1,5 @@
1
1
  import type { PackageManagerId } from "../shared/package-managers.js";
2
+ import type { ScaffoldWordPressTargetVersion } from "./scaffold-compatibility.js";
2
3
  import type { ScaffoldTemplateVariableGroupsCarrier } from "./scaffold-template-variable-groups.js";
3
4
  /**
4
5
  * User-facing scaffold answers before template rendering.
@@ -176,6 +177,7 @@ interface ScaffoldProjectOptions {
176
177
  withMigrationUi?: boolean;
177
178
  withTestPreset?: boolean;
178
179
  withWpEnv?: boolean;
180
+ wpVersion?: ScaffoldWordPressTargetVersion;
179
181
  }
180
182
  export interface ScaffoldProjectResult {
181
183
  packageManager: PackageManagerId;
@@ -202,4 +204,4 @@ export declare function isCreateProfileId(value: string): value is CreateProfile
202
204
  * profile list for CLI diagnostics.
203
205
  */
204
206
  export declare function resolveCreateProfileId(profile?: string): CreateProfileId | undefined;
205
- export declare function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, profile, repositoryReference, cwd, allowExistingDir, noInstall, installDependencies, onProgress, variant, withMigrationUi, withTestPreset, withWpEnv, }: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
207
+ export declare function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, profile, repositoryReference, cwd, allowExistingDir, noInstall, installDependencies, onProgress, variant, withMigrationUi, withTestPreset, withWpEnv, wpVersion, }: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
@@ -77,7 +77,7 @@ async function applyCreateProfile({ profile, projectDir, }) {
77
77
  async function reportScaffoldProgress(onProgress, event) {
78
78
  await onProgress?.(event);
79
79
  }
80
- export async function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, profile, repositoryReference, cwd = process.cwd(), allowExistingDir = false, noInstall = false, installDependencies = undefined, onProgress = undefined, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
80
+ export async function scaffoldProject({ projectDir, templateId, answers, alternateRenderTargets, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, profile, repositoryReference, cwd = process.cwd(), allowExistingDir = false, noInstall = false, installDependencies = undefined, onProgress = undefined, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, wpVersion, }) {
81
81
  const resolvedTemplateId = normalizeTemplateSelection(templateId);
82
82
  const resolvedPackageManager = getPackageManager(packageManager).id;
83
83
  const isBuiltInTemplate = isBuiltInTemplateId(resolvedTemplateId);
@@ -115,6 +115,7 @@ export async function scaffoldProject({ projectDir, templateId, answers, alterna
115
115
  withMigrationUi,
116
116
  withTestPreset,
117
117
  withWpEnv,
118
+ wpVersion,
118
119
  });
119
120
  const validated = await blockGeneratorService.validate({ plan });
120
121
  const rendered = await blockGeneratorService.render({ validated });
@@ -131,6 +132,8 @@ export async function scaffoldProject({ projectDir, templateId, answers, alterna
131
132
  ...answers,
132
133
  dataStorageMode: dataStorageMode ?? answers.dataStorageMode,
133
134
  persistencePolicy: persistencePolicy ?? answers.persistencePolicy,
135
+ }, {
136
+ wpVersion,
134
137
  });
135
138
  await reportScaffoldProgress(onProgress, {
136
139
  detail: "Loading template files, variants, and external package metadata when needed.",
@@ -88,6 +88,93 @@ function getOptionalBooleanProperty(entryName, elementIndex, objectLiteral, key)
88
88
  }
89
89
  return undefined;
90
90
  }
91
+ function findObjectPropertyExpression(objectLiteral, key) {
92
+ for (const property of objectLiteral.properties) {
93
+ if (!ts.isPropertyAssignment(property)) {
94
+ continue;
95
+ }
96
+ if (getPropertyNameText(property.name) === key) {
97
+ return property.initializer;
98
+ }
99
+ }
100
+ return undefined;
101
+ }
102
+ function getRequiredObjectLiteralProperty(objectLiteral, key, context) {
103
+ const expression = findObjectPropertyExpression(objectLiteral, key);
104
+ if (!expression) {
105
+ throw new Error(`${context}.${key} is required in scripts/block-config.ts.`);
106
+ }
107
+ if (!ts.isObjectLiteralExpression(expression)) {
108
+ throw new Error(`${context}.${key} must be an object literal in scripts/block-config.ts.`);
109
+ }
110
+ return expression;
111
+ }
112
+ function getRequiredStringProperty(objectLiteral, key, context) {
113
+ const expression = findObjectPropertyExpression(objectLiteral, key);
114
+ if (!expression) {
115
+ throw new Error(`${context}.${key} is required in scripts/block-config.ts.`);
116
+ }
117
+ if (!ts.isStringLiteralLike(expression)) {
118
+ throw new Error(`${context}.${key} must be a string literal in scripts/block-config.ts.`);
119
+ }
120
+ return expression.text;
121
+ }
122
+ function getOptionalNestedStringProperty(objectLiteral, key, context) {
123
+ const expression = findObjectPropertyExpression(objectLiteral, key);
124
+ if (!expression) {
125
+ return undefined;
126
+ }
127
+ if (!ts.isStringLiteralLike(expression)) {
128
+ throw new Error(`${context}.${key} must be a string literal in scripts/block-config.ts.`);
129
+ }
130
+ return expression.text;
131
+ }
132
+ function getRequiredStringArrayProperty(objectLiteral, key, context) {
133
+ const expression = findObjectPropertyExpression(objectLiteral, key);
134
+ if (!expression) {
135
+ throw new Error(`${context}.${key} is required in scripts/block-config.ts.`);
136
+ }
137
+ if (!ts.isArrayLiteralExpression(expression)) {
138
+ throw new Error(`${context}.${key} must be an array literal in scripts/block-config.ts.`);
139
+ }
140
+ return expression.elements.map((element, itemIndex) => {
141
+ if (!ts.isStringLiteralLike(element)) {
142
+ throw new Error(`${context}.${key}[${itemIndex}] must be a string literal in scripts/block-config.ts.`);
143
+ }
144
+ return element.text;
145
+ });
146
+ }
147
+ function parseCompatibilityConfigLiteral(objectLiteral, context) {
148
+ const hardMinimumsObject = getRequiredObjectLiteralProperty(objectLiteral, "hardMinimums", context);
149
+ const mode = getRequiredStringProperty(objectLiteral, "mode", context);
150
+ if (mode !== "baseline" && mode !== "optional" && mode !== "required") {
151
+ throw new Error(`${context}.mode must be baseline, optional, or required in scripts/block-config.ts.`);
152
+ }
153
+ const php = getOptionalNestedStringProperty(hardMinimumsObject, "php", `${context}.hardMinimums`);
154
+ const wordpress = getOptionalNestedStringProperty(hardMinimumsObject, "wordpress", `${context}.hardMinimums`);
155
+ return {
156
+ hardMinimums: {
157
+ ...(php ? { php } : {}),
158
+ ...(wordpress ? { wordpress } : {}),
159
+ },
160
+ mode,
161
+ optionalFeatureIds: getRequiredStringArrayProperty(objectLiteral, "optionalFeatureIds", context),
162
+ optionalFeatures: getRequiredStringArrayProperty(objectLiteral, "optionalFeatures", context),
163
+ requiredFeatureIds: getRequiredStringArrayProperty(objectLiteral, "requiredFeatureIds", context),
164
+ requiredFeatures: getRequiredStringArrayProperty(objectLiteral, "requiredFeatures", context),
165
+ runtimeGates: getRequiredStringArrayProperty(objectLiteral, "runtimeGates", context),
166
+ };
167
+ }
168
+ function getOptionalCompatibilityConfigProperty(entryName, elementIndex, objectLiteral, key) {
169
+ const expression = findObjectPropertyExpression(objectLiteral, key);
170
+ if (!expression) {
171
+ return undefined;
172
+ }
173
+ if (!ts.isObjectLiteralExpression(expression)) {
174
+ throw new Error(`${entryName}[${elementIndex}].${key} must be an object literal in scripts/block-config.ts.`);
175
+ }
176
+ return parseCompatibilityConfigLiteral(expression, `${entryName}[${elementIndex}].${key}`);
177
+ }
91
178
  function parseInventoryEntries(arrayLiteral, descriptor) {
92
179
  return arrayLiteral.elements.map((element, elementIndex) => {
93
180
  if (!ts.isObjectLiteralExpression(element)) {
@@ -98,9 +185,11 @@ function parseInventoryEntries(arrayLiteral, descriptor) {
98
185
  const kind = field.kind ?? "string";
99
186
  const value = kind === "stringArray"
100
187
  ? getOptionalStringArrayProperty(descriptor.entryName, elementIndex, element, field.key)
101
- : kind === "boolean"
102
- ? getOptionalBooleanProperty(descriptor.entryName, elementIndex, element, field.key)
103
- : getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
188
+ : kind === "compatibilityConfig"
189
+ ? getOptionalCompatibilityConfigProperty(descriptor.entryName, elementIndex, element, field.key)
190
+ : kind === "boolean"
191
+ ? getOptionalBooleanProperty(descriptor.entryName, elementIndex, element, field.key)
192
+ : getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
104
193
  field.validate?.(value, {
105
194
  elementIndex,
106
195
  entryName: descriptor.entryName,
@@ -1,8 +1,9 @@
1
1
  import type { WorkspaceInventoryAppendOptionKey, WorkspaceInventoryEntriesKey, WorkspaceInventorySectionFlagKey } from "./workspace-inventory-types.js";
2
+ import type { ScaffoldCompatibilityConfig } from "../templates/scaffold-compatibility.js";
2
3
  /**
3
4
  * Literal value shape accepted by descriptor-driven inventory entry fields.
4
5
  */
5
- export type InventoryEntryFieldValue = string | string[] | boolean | undefined;
6
+ export type InventoryEntryFieldValue = string | string[] | boolean | ScaffoldCompatibilityConfig | undefined;
6
7
  /**
7
8
  * Context passed to custom field validators while parsing one inventory entry.
8
9
  */
@@ -13,7 +14,7 @@ export type InventoryEntryFieldValidationContext = {
13
14
  };
14
15
  type InventoryEntryFieldDescriptor = {
15
16
  key: string;
16
- kind?: "boolean" | "string" | "stringArray";
17
+ kind?: "boolean" | "compatibilityConfig" | "string" | "stringArray";
17
18
  required?: boolean;
18
19
  validate?: (value: InventoryEntryFieldValue, context: InventoryEntryFieldValidationContext) => void;
19
20
  };
@@ -336,6 +336,7 @@ export const INVENTORY_SECTIONS = [
336
336
  entryName: "ABILITIES",
337
337
  fields: [
338
338
  { key: "clientFile", required: true },
339
+ { key: "compatibility", kind: "compatibilityConfig" },
339
340
  { key: "configFile", required: true },
340
341
  { key: "dataFile", required: true },
341
342
  { key: "inputSchemaFile", required: true },
@@ -371,6 +372,7 @@ export const INVENTORY_SECTIONS = [
371
372
  { key: "aiSchemaFile", required: true },
372
373
  { key: "apiFile", required: true },
373
374
  { key: "clientFile", required: true },
375
+ { key: "compatibility", kind: "compatibilityConfig" },
374
376
  { key: "dataFile", required: true },
375
377
  { key: "namespace", required: true },
376
378
  { key: "openApiFile", required: true },
@@ -1,3 +1,4 @@
1
+ import type { ScaffoldCompatibilityConfig } from "../templates/scaffold-compatibility.js";
1
2
  export interface WorkspaceBlockInventoryEntry {
2
3
  apiTypesFile?: string;
3
4
  attributeTypeName?: string;
@@ -113,6 +114,7 @@ export interface WorkspacePostMetaInventoryEntry {
113
114
  */
114
115
  export interface WorkspaceAbilityInventoryEntry {
115
116
  clientFile: string;
117
+ compatibility?: ScaffoldCompatibilityConfig;
116
118
  configFile: string;
117
119
  dataFile: string;
118
120
  inputSchemaFile: string;
@@ -134,6 +136,7 @@ export interface WorkspaceAiFeatureInventoryEntry {
134
136
  aiSchemaFile: string;
135
137
  apiFile: string;
136
138
  clientFile: string;
139
+ compatibility?: ScaffoldCompatibilityConfig;
137
140
  dataFile: string;
138
141
  namespace: string;
139
142
  openApiFile: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-typia/project-tools",
3
- "version": "0.24.6",
3
+ "version": "0.24.7",
4
4
  "description": "Project orchestration and programmatic tooling for wp-typia",
5
5
  "packageManager": "bun@1.3.11",
6
6
  "type": "module",
@@ -168,7 +168,7 @@
168
168
  "@wp-typia/api-client": "^0.4.6",
169
169
  "@wp-typia/block-runtime": "^0.7.1",
170
170
  "@wp-typia/rest": "^0.3.14",
171
- "@wp-typia/block-types": "^0.3.3",
171
+ "@wp-typia/block-types": "^0.3.4",
172
172
  "mustache": "^4.2.0",
173
173
  "npm-package-arg": "^13.0.0",
174
174
  "semver": "^7.7.3",