wp-typia 0.20.2 → 0.20.4

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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./cli-tesygdnr.js";
5
5
  import {
6
6
  seedProjectMigrations
7
- } from "./cli-qpt5dt0x.js";
7
+ } from "./cli-2rev5hqm.js";
8
8
  import {
9
9
  ensureMigrationDirectories,
10
10
  isPlainObject,
@@ -59,7 +59,7 @@ import {
59
59
  toTitleCase,
60
60
  validateBlockSlug,
61
61
  validateNamespace
62
- } from "./cli-rg481yks.js";
62
+ } from "./cli-3w3qxq9w.js";
63
63
  import {
64
64
  createManagedTempRoot
65
65
  } from "./cli-t73q5aqz.js";
@@ -11427,6 +11427,268 @@ async function resolveTemplateSource(templateId, cwd, variables, variant) {
11427
11427
  }
11428
11428
  }
11429
11429
 
11430
+ // ../wp-typia-project-tools/src/runtime/ai-feature-capability.ts
11431
+ var AI_FEATURE_DEFINITIONS = {
11432
+ wordpressAiClient: {
11433
+ description: "WordPress 7.0 AI Client surface used by AI-capable feature endpoints.",
11434
+ id: "wordpress-ai-client",
11435
+ label: "WordPress AI Client",
11436
+ minimumVersions: {
11437
+ wordpress: "7.0"
11438
+ },
11439
+ runtimeGates: [
11440
+ {
11441
+ kind: "wordpress-core-feature",
11442
+ value: "WordPress AI Client"
11443
+ }
11444
+ ]
11445
+ },
11446
+ wordpressCoreAbilities: {
11447
+ description: "Client-side ability discovery surface for editor and admin flows.",
11448
+ id: "wordpress-core-abilities",
11449
+ label: "@wordpress/core-abilities",
11450
+ minimumVersions: {
11451
+ wordpress: "7.0"
11452
+ },
11453
+ runtimeGates: [
11454
+ {
11455
+ kind: "script-package",
11456
+ value: "@wordpress/core-abilities"
11457
+ }
11458
+ ]
11459
+ },
11460
+ wordpressMcpPublicMetadata: {
11461
+ description: "Optional MCP exposure metadata consumed by a separate adapter path rather than core alone.",
11462
+ id: "wordpress-mcp-public-metadata",
11463
+ label: "MCP public metadata",
11464
+ runtimeGates: [
11465
+ {
11466
+ kind: "adapter",
11467
+ value: "MCP adapter"
11468
+ }
11469
+ ]
11470
+ },
11471
+ wordpressServerAbilities: {
11472
+ description: "Server-side ability registration and execution surface for WordPress-native abilities.",
11473
+ id: "wordpress-server-abilities",
11474
+ label: "WordPress Abilities API",
11475
+ minimumVersions: {
11476
+ wordpress: "6.9"
11477
+ },
11478
+ runtimeGates: [
11479
+ {
11480
+ kind: "php-function",
11481
+ value: "wp_register_ability"
11482
+ },
11483
+ {
11484
+ kind: "php-function",
11485
+ value: "wp_register_ability_category"
11486
+ }
11487
+ ]
11488
+ }
11489
+ };
11490
+ var DEFAULT_AI_FEATURE_REGISTRY = Object.values(AI_FEATURE_DEFINITIONS).reduce((accumulator, definition) => {
11491
+ accumulator[definition.id] = definition;
11492
+ return accumulator;
11493
+ }, {});
11494
+ function parseVersionFloorParts(value) {
11495
+ return value.split(".").map((part, index) => {
11496
+ if (!/^\d+$/.test(part)) {
11497
+ throw new Error(`parseVersionFloorParts received an invalid version floor "${value}" at segment ${index + 1}.`);
11498
+ }
11499
+ return Number.parseInt(part, 10);
11500
+ });
11501
+ }
11502
+ function compareVersionFloors(left, right) {
11503
+ const leftParts = parseVersionFloorParts(left);
11504
+ const rightParts = parseVersionFloorParts(right);
11505
+ const length = Math.max(leftParts.length, rightParts.length);
11506
+ for (let index = 0;index < length; index += 1) {
11507
+ const leftValue = leftParts[index] ?? 0;
11508
+ const rightValue = rightParts[index] ?? 0;
11509
+ if (leftValue > rightValue) {
11510
+ return 1;
11511
+ }
11512
+ if (leftValue < rightValue) {
11513
+ return -1;
11514
+ }
11515
+ }
11516
+ return 0;
11517
+ }
11518
+ function pickHigherVersionFloor(current, candidate) {
11519
+ if (!candidate) {
11520
+ return current;
11521
+ }
11522
+ if (!current) {
11523
+ return candidate;
11524
+ }
11525
+ return compareVersionFloors(current, candidate) >= 0 ? current : candidate;
11526
+ }
11527
+ function normalizeSelections(selections) {
11528
+ const normalized = new Map;
11529
+ for (const selection of selections) {
11530
+ const current = normalized.get(selection.featureId);
11531
+ if (!current || selection.mode === "required") {
11532
+ normalized.set(selection.featureId, selection);
11533
+ }
11534
+ }
11535
+ return [...normalized.values()];
11536
+ }
11537
+ function resolveAiFeatureCapabilityPlan(selections, registry = DEFAULT_AI_FEATURE_REGISTRY) {
11538
+ const requiredFeatures = [];
11539
+ const optionalFeatures = [];
11540
+ let wordpress;
11541
+ let php;
11542
+ for (const selection of normalizeSelections(selections)) {
11543
+ const definition = registry[selection.featureId];
11544
+ if (!definition) {
11545
+ throw new Error(`Unknown AI feature capability "${selection.featureId}".`);
11546
+ }
11547
+ const resolvedDefinition = {
11548
+ ...definition,
11549
+ mode: selection.mode
11550
+ };
11551
+ if (selection.mode === "required") {
11552
+ requiredFeatures.push(resolvedDefinition);
11553
+ wordpress = pickHigherVersionFloor(wordpress, definition.minimumVersions?.wordpress);
11554
+ php = pickHigherVersionFloor(php, definition.minimumVersions?.php);
11555
+ continue;
11556
+ }
11557
+ optionalFeatures.push(resolvedDefinition);
11558
+ }
11559
+ return {
11560
+ hardMinimums: {
11561
+ ...php ? { php } : {},
11562
+ ...wordpress ? { wordpress } : {}
11563
+ },
11564
+ optionalFeatures,
11565
+ requiredFeatures
11566
+ };
11567
+ }
11568
+
11569
+ // ../wp-typia-project-tools/src/runtime/scaffold-compatibility.ts
11570
+ var DEFAULT_SCAFFOLD_COMPATIBILITY = {
11571
+ requiresAtLeast: "6.7",
11572
+ requiresPhp: "8.0",
11573
+ testedUpTo: "6.9"
11574
+ };
11575
+ var OPTIONAL_WORDPRESS_AI_CLIENT_COMPATIBILITY = [
11576
+ {
11577
+ featureId: AI_FEATURE_DEFINITIONS.wordpressAiClient.id,
11578
+ mode: "optional"
11579
+ }
11580
+ ];
11581
+ var REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY = [
11582
+ {
11583
+ featureId: AI_FEATURE_DEFINITIONS.wordpressServerAbilities.id,
11584
+ mode: "required"
11585
+ },
11586
+ {
11587
+ featureId: AI_FEATURE_DEFINITIONS.wordpressCoreAbilities.id,
11588
+ mode: "required"
11589
+ }
11590
+ ];
11591
+ function parseVersionFloorParts2(value) {
11592
+ return value.split(".").map((part, index) => {
11593
+ if (!/^\d+$/u.test(part)) {
11594
+ throw new Error(`parseVersionFloorParts received an invalid version floor "${value}" at segment ${index + 1}.`);
11595
+ }
11596
+ return Number.parseInt(part, 10);
11597
+ });
11598
+ }
11599
+ function compareVersionFloors2(left, right) {
11600
+ const leftParts = parseVersionFloorParts2(left);
11601
+ const rightParts = parseVersionFloorParts2(right);
11602
+ const length = Math.max(leftParts.length, rightParts.length);
11603
+ for (let index = 0;index < length; index += 1) {
11604
+ const leftValue = leftParts[index] ?? 0;
11605
+ const rightValue = rightParts[index] ?? 0;
11606
+ if (leftValue > rightValue) {
11607
+ return 1;
11608
+ }
11609
+ if (leftValue < rightValue) {
11610
+ return -1;
11611
+ }
11612
+ }
11613
+ return 0;
11614
+ }
11615
+ function pickHigherVersionFloor2(current, candidate) {
11616
+ if (!candidate) {
11617
+ return current;
11618
+ }
11619
+ return compareVersionFloors2(current, candidate) >= 0 ? current : candidate;
11620
+ }
11621
+ function pickHigherHeaderVersionFloor(policyValue, currentValue) {
11622
+ const normalizedCurrentValue = currentValue.trim();
11623
+ if (!normalizedCurrentValue) {
11624
+ return policyValue;
11625
+ }
11626
+ try {
11627
+ return pickHigherVersionFloor2(policyValue, normalizedCurrentValue);
11628
+ } catch {
11629
+ return policyValue;
11630
+ }
11631
+ }
11632
+ function formatRuntimeGate(feature) {
11633
+ return (feature.runtimeGates ?? []).map((gate) => `${feature.label}: ${gate.kind} ${gate.value}`);
11634
+ }
11635
+ function getPolicyMode(capabilityPlan) {
11636
+ if (capabilityPlan.requiredFeatures.length > 0) {
11637
+ return "required";
11638
+ }
11639
+ if (capabilityPlan.optionalFeatures.length > 0) {
11640
+ return "optional";
11641
+ }
11642
+ return "baseline";
11643
+ }
11644
+ function resolveScaffoldCompatibilityPolicy(selections, {
11645
+ baseline = DEFAULT_SCAFFOLD_COMPATIBILITY
11646
+ } = {}) {
11647
+ const capabilityPlan = resolveAiFeatureCapabilityPlan(selections);
11648
+ const requiresAtLeast = pickHigherVersionFloor2(baseline.requiresAtLeast, capabilityPlan.hardMinimums.wordpress);
11649
+ const requiresPhp = pickHigherVersionFloor2(baseline.requiresPhp, capabilityPlan.hardMinimums.php);
11650
+ const testedUpTo = pickHigherVersionFloor2(baseline.testedUpTo, requiresAtLeast);
11651
+ return {
11652
+ capabilityPlan,
11653
+ pluginHeader: {
11654
+ requiresAtLeast,
11655
+ requiresPhp,
11656
+ testedUpTo
11657
+ }
11658
+ };
11659
+ }
11660
+ function createScaffoldCompatibilityConfig(policy) {
11661
+ const { capabilityPlan } = policy;
11662
+ return {
11663
+ hardMinimums: capabilityPlan.hardMinimums,
11664
+ mode: getPolicyMode(capabilityPlan),
11665
+ optionalFeatures: capabilityPlan.optionalFeatures.map((feature) => feature.label),
11666
+ requiredFeatures: capabilityPlan.requiredFeatures.map((feature) => feature.label),
11667
+ runtimeGates: [
11668
+ ...capabilityPlan.requiredFeatures.flatMap(formatRuntimeGate),
11669
+ ...capabilityPlan.optionalFeatures.flatMap(formatRuntimeGate)
11670
+ ]
11671
+ };
11672
+ }
11673
+ function renderScaffoldCompatibilityConfig(policy, indent = "\t\t") {
11674
+ const config = createScaffoldCompatibilityConfig(policy);
11675
+ return JSON.stringify(config, null, "\t").split(`
11676
+ `).map((line, index) => index === 0 ? line : `${indent}${line}`).join(`
11677
+ `);
11678
+ }
11679
+ function replacePluginHeaderVersionFloor(source, pattern, policyValue) {
11680
+ return source.replace(pattern, (_match, prefix, currentValue, lineEnding) => {
11681
+ const versionPrefix = prefix.endsWith(":") ? `${prefix} ` : prefix;
11682
+ return `${versionPrefix}${pickHigherHeaderVersionFloor(policyValue, currentValue)}${lineEnding}`;
11683
+ });
11684
+ }
11685
+ function updatePluginHeaderCompatibility(source, policy) {
11686
+ const { pluginHeader } = policy;
11687
+ const nextSource = replacePluginHeaderVersionFloor(source, /(\* Requires at least:[^\S\r\n]*)([^\r\n]*)(\r?)/u, pluginHeader.requiresAtLeast);
11688
+ const nextSourceWithTestedUpTo = replacePluginHeaderVersionFloor(nextSource, /(\* Tested up to:[^\S\r\n]*)([^\r\n]*)(\r?)/u, pluginHeader.testedUpTo);
11689
+ return replacePluginHeaderVersionFloor(nextSourceWithTestedUpTo, /(\* Requires PHP:[^\S\r\n]*)([^\r\n]*)(\r?)/u, pluginHeader.requiresPhp);
11690
+ }
11691
+
11430
11692
  // ../wp-typia-project-tools/src/runtime/block-generator-service-spec.ts
11431
11693
  function getBuiltInPersistenceSpec({
11432
11694
  templateId,
@@ -11556,6 +11818,7 @@ function buildTemplateVariablesFromBlockSpec(spec) {
11556
11818
  const dataStorageMode = persistenceEnabled ? spec.persistence.dataStorageMode : "custom-table";
11557
11819
  const persistencePolicy = persistenceEnabled ? spec.persistence.persistencePolicy : "authenticated";
11558
11820
  const queryVariationNamespace = `${namespace}/${slug}`;
11821
+ const compatibility = resolveScaffoldCompatibilityPolicy([]);
11559
11822
  const flatVariables = {
11560
11823
  alternateRenderTargetsCsv: formatAlternateRenderTargets(alternateRenderTargets),
11561
11824
  alternateRenderTargetsJson: JSON.stringify(alternateRenderTargets),
@@ -11583,6 +11846,9 @@ function buildTemplateVariablesFromBlockSpec(spec) {
11583
11846
  hasAlternateMjmlRenderTarget: hasAlternateMjmlRenderTarget ? "true" : "false",
11584
11847
  hasAlternatePlainTextRenderTarget: hasAlternatePlainTextRenderTarget ? "true" : "false",
11585
11848
  hasAlternateRenderTargets: alternateRenderTargets.length > 0 ? "true" : "false",
11849
+ requiresAtLeast: compatibility.pluginHeader.requiresAtLeast,
11850
+ requiresPhp: compatibility.pluginHeader.requiresPhp,
11851
+ testedUpTo: compatibility.pluginHeader.testedUpTo,
11586
11852
  projectToolsPackageVersion,
11587
11853
  cssClassName,
11588
11854
  dashCase: slug,
@@ -11626,6 +11892,7 @@ function buildTemplateVariablesFromBlockSpec(spec) {
11626
11892
  author: spec.project.author,
11627
11893
  blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
11628
11894
  category: spec.metadata.category,
11895
+ compatibility: compatibility.pluginHeader,
11629
11896
  cssClassName,
11630
11897
  description: spec.metadata.description,
11631
11898
  descriptionJson: flatVariables.descriptionJson,
@@ -15924,6 +16191,7 @@ function getTemplateVariables(templateId, answers) {
15924
16191
  const compoundPersistenceEnabled = templateId === "persistence" ? true : templateId === "compound" ? Boolean(answers.dataStorageMode || answers.persistencePolicy) : false;
15925
16192
  const dataStorageMode = templateId === "persistence" || compoundPersistenceEnabled ? answers.dataStorageMode ?? "custom-table" : "custom-table";
15926
16193
  const persistencePolicy = templateId === "persistence" || compoundPersistenceEnabled ? answers.persistencePolicy ?? "authenticated" : "authenticated";
16194
+ const compatibility = resolveScaffoldCompatibilityPolicy([]);
15927
16195
  const flatVariables = {
15928
16196
  alternateRenderTargetsCsv: "",
15929
16197
  alternateRenderTargetsJson: "[]",
@@ -15952,6 +16220,8 @@ function getTemplateVariables(templateId, answers) {
15952
16220
  hasAlternatePlainTextRenderTarget: "false",
15953
16221
  hasAlternateRenderTargets: "false",
15954
16222
  projectToolsPackageVersion,
16223
+ requiresAtLeast: compatibility.pluginHeader.requiresAtLeast,
16224
+ requiresPhp: compatibility.pluginHeader.requiresPhp,
15955
16225
  cssClassName,
15956
16226
  dataStorageMode,
15957
16227
  dashCase: slug,
@@ -15975,6 +16245,7 @@ function getTemplateVariables(templateId, answers) {
15975
16245
  phpPrefix,
15976
16246
  phpPrefixUpper,
15977
16247
  restPackageVersion,
16248
+ testedUpTo: compatibility.pluginHeader.testedUpTo,
15978
16249
  publicWriteRequestIdDeclaration: persistencePolicy === "public" ? "publicWriteRequestId: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;" : "publicWriteRequestId?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
15979
16250
  restWriteAuthIntent: persistencePolicy === "public" ? "public-write-protected" : "authenticated",
15980
16251
  restWriteAuthMechanism: persistencePolicy === "public" ? "public-signed-token" : "rest-nonce",
@@ -16015,6 +16286,7 @@ function getTemplateVariables(templateId, answers) {
16015
16286
  author: answers.author.trim(),
16016
16287
  blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
16017
16288
  category: metadataDefaults?.category ?? template?.defaultCategory ?? "widgets",
16289
+ compatibility: compatibility.pluginHeader,
16018
16290
  cssClassName,
16019
16291
  description,
16020
16292
  descriptionJson: JSON.stringify(description),
@@ -16285,6 +16557,6 @@ async function resolveOptionalInteractiveExternalLayerId({
16285
16557
  }
16286
16558
  }
16287
16559
 
16288
- export { syncPersistenceRestArtifacts, copyInterpolatedDirectory, listInterpolatedDirectoryOutputs, getPrimaryDevelopmentScript, getOptionalOnboardingSteps, getOptionalOnboardingNote, getOptionalOnboardingShortNote, formatNonEmptyTargetDirectoryError, parseTemplateLocator, resolveExternalTemplateLayers, resolveTemplateSeed, normalizeOptionalCliString, resolveLocalCliPathOption, assertExternalLayerCompositionOptions, assertBuiltInTemplateVariantAllowed, parseAlternateRenderTargets, parseCompoundInnerBlocksPreset, getDefaultAnswers, resolveTemplateId, resolvePackageManagerId, collectScaffoldAnswers, DATA_STORAGE_MODES, PERSISTENCE_POLICIES, isDataStorageMode, isPersistencePolicy, scaffoldProject, resolveOptionalInteractiveExternalLayerId };
16560
+ export { syncPersistenceRestArtifacts, copyInterpolatedDirectory, listInterpolatedDirectoryOutputs, getPrimaryDevelopmentScript, getOptionalOnboardingSteps, getOptionalOnboardingNote, getOptionalOnboardingShortNote, formatNonEmptyTargetDirectoryError, require_semver2 as require_semver, parseTemplateLocator, resolveExternalTemplateLayers, resolveTemplateSeed, normalizeOptionalCliString, resolveLocalCliPathOption, assertExternalLayerCompositionOptions, assertBuiltInTemplateVariantAllowed, parseAlternateRenderTargets, parseCompoundInnerBlocksPreset, OPTIONAL_WORDPRESS_AI_CLIENT_COMPATIBILITY, REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY, resolveScaffoldCompatibilityPolicy, renderScaffoldCompatibilityConfig, updatePluginHeaderCompatibility, getDefaultAnswers, resolveTemplateId, resolvePackageManagerId, collectScaffoldAnswers, DATA_STORAGE_MODES, PERSISTENCE_POLICIES, isDataStorageMode, isPersistencePolicy, scaffoldProject, resolveOptionalInteractiveExternalLayerId };
16289
16561
 
16290
- //# debugId=C62257CB4F3F0AF464756E2164756E21
16562
+ //# debugId=45408AA53A2AA42964756E2164756E21
@@ -20,7 +20,7 @@ import {
20
20
  REST_RESOURCE_METHOD_IDS,
21
21
  REST_RESOURCE_NAMESPACE_PATTERN,
22
22
  readWorkspaceInventory
23
- } from "./cli-rg481yks.js";
23
+ } from "./cli-3w3qxq9w.js";
24
24
  import"./cli-t73q5aqz.js";
25
25
  import {
26
26
  WORKSPACE_TEMPLATE_PACKAGE,
@@ -136,6 +136,10 @@ var WORKSPACE_BINDING_SERVER_GLOB = "/src/bindings/*/server.php";
136
136
  var WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js";
137
137
  var WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
138
138
  var WORKSPACE_REST_RESOURCE_GLOB = "/inc/rest/*.php";
139
+ var WORKSPACE_ABILITY_GLOB = "/inc/abilities/*.php";
140
+ var WORKSPACE_ABILITY_EDITOR_SCRIPT = "build/abilities/index.js";
141
+ var WORKSPACE_ABILITY_EDITOR_ASSET = "build/abilities/index.asset.php";
142
+ var WORKSPACE_AI_FEATURE_GLOB = "/inc/ai-features/*.php";
139
143
  var WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT = "build/editor-plugins/index.js";
140
144
  var WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET = "build/editor-plugins/index.asset.php";
141
145
  var WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE = "build/editor-plugins/style-index.css";
@@ -342,6 +346,102 @@ function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix)
342
346
  const hasRegisterHook = source.includes(registerHook);
343
347
  return createDoctorCheck2("REST resource bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "REST resource PHP loader hook is present" : "Missing REST resource PHP require glob or init hook");
344
348
  }
349
+ function getWorkspaceAbilityRequiredFiles(ability) {
350
+ return Array.from(new Set([
351
+ ability.clientFile,
352
+ ability.configFile,
353
+ ability.dataFile,
354
+ ability.inputSchemaFile,
355
+ ability.outputSchemaFile,
356
+ ability.phpFile,
357
+ ability.typesFile
358
+ ]));
359
+ }
360
+ function checkWorkspaceAbilityConfig(projectDir, ability) {
361
+ const configPath = path2.join(projectDir, ability.configFile);
362
+ if (!fs2.existsSync(configPath)) {
363
+ return createDoctorCheck2(`Ability config ${ability.slug}`, "fail", `Missing ${ability.configFile}`);
364
+ }
365
+ try {
366
+ const config = JSON.parse(fs2.readFileSync(configPath, "utf8"));
367
+ const abilityId = typeof config.abilityId === "string" ? config.abilityId.trim() : "";
368
+ const categorySlug = typeof config.category?.slug === "string" ? config.category.slug.trim() : "";
369
+ const hasValidAbilityId = /^[a-z0-9-]+\/[a-z0-9-]+$/u.test(abilityId);
370
+ const hasValidCategorySlug = /^[a-z0-9-]+$/u.test(categorySlug);
371
+ return createDoctorCheck2(`Ability config ${ability.slug}`, hasValidAbilityId && hasValidCategorySlug ? "pass" : "fail", hasValidAbilityId && hasValidCategorySlug ? `Ability id ${abilityId} in category ${categorySlug} is valid` : "Ability config must define a valid abilityId (`namespace/ability-name`) and category.slug.");
372
+ } catch (error) {
373
+ return createDoctorCheck2(`Ability config ${ability.slug}`, "fail", error instanceof Error ? error.message : String(error));
374
+ }
375
+ }
376
+ function checkWorkspaceAbilityBootstrap(projectDir, packageName, phpPrefix) {
377
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
378
+ const bootstrapPath = path2.join(projectDir, `${packageBaseName}.php`);
379
+ if (!fs2.existsSync(bootstrapPath)) {
380
+ return createDoctorCheck2("Ability bootstrap", "fail", `Missing ${path2.basename(bootstrapPath)}`);
381
+ }
382
+ const source = fs2.readFileSync(bootstrapPath, "utf8");
383
+ const loadFunctionName = `${phpPrefix}_load_workflow_abilities`;
384
+ const enqueueFunctionName = `${phpPrefix}_enqueue_workflow_abilities`;
385
+ const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
386
+ const adminEnqueueHook = `add_action( 'admin_enqueue_scripts', '${enqueueFunctionName}' );`;
387
+ const editorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
388
+ const hasLoaderHook = source.includes(loadHook);
389
+ const hasAdminEnqueueHook = source.includes(adminEnqueueHook);
390
+ const hasEditorEnqueueHook = source.includes(editorEnqueueHook);
391
+ const hasServerGlob = source.includes(WORKSPACE_ABILITY_GLOB);
392
+ const hasEditorScript = source.includes(WORKSPACE_ABILITY_EDITOR_SCRIPT);
393
+ const hasEditorAsset = source.includes(WORKSPACE_ABILITY_EDITOR_ASSET);
394
+ const hasScriptModuleEnqueue = source.includes("wp_enqueue_script_module");
395
+ return createDoctorCheck2("Ability bootstrap", hasLoaderHook && hasAdminEnqueueHook && hasEditorEnqueueHook && hasServerGlob && hasEditorScript && hasEditorAsset && hasScriptModuleEnqueue ? "pass" : "fail", hasLoaderHook && hasAdminEnqueueHook && hasEditorEnqueueHook && hasServerGlob && hasEditorScript && hasEditorAsset && hasScriptModuleEnqueue ? "Ability loader and admin/editor script-module bootstrap hooks are present" : "Missing ability loader hook, script-module enqueue, or build/abilities asset references");
396
+ }
397
+ function checkWorkspaceAbilityIndex(projectDir, abilities) {
398
+ const indexRelativePath = [
399
+ path2.join("src", "abilities", "index.ts"),
400
+ path2.join("src", "abilities", "index.js")
401
+ ].find((relativePath) => fs2.existsSync(path2.join(projectDir, relativePath)));
402
+ if (!indexRelativePath) {
403
+ return createDoctorCheck2("Abilities index", "fail", "Missing src/abilities/index.ts or src/abilities/index.js");
404
+ }
405
+ const indexPath = path2.join(projectDir, indexRelativePath);
406
+ const source = fs2.readFileSync(indexPath, "utf8");
407
+ const missingExports = abilities.filter((ability) => {
408
+ const exportPattern = new RegExp(`^\\s*export\\s+(?:\\*\\s+from|\\{[^}]+\\}\\s+from)\\s+['"\`]\\./${escapeRegex(ability.slug)}\\/client['"\`]`, "mu");
409
+ return !exportPattern.test(source);
410
+ });
411
+ return createDoctorCheck2("Abilities index", missingExports.length === 0 ? "pass" : "fail", missingExports.length === 0 ? "Ability client helpers are aggregated" : `Missing ability exports for: ${missingExports.map((entry) => entry.slug).join(", ")}`);
412
+ }
413
+ function getWorkspaceAiFeatureRequiredFiles(aiFeature) {
414
+ return Array.from(new Set([
415
+ aiFeature.aiSchemaFile,
416
+ aiFeature.apiFile,
417
+ path2.join(path2.dirname(aiFeature.typesFile), "api-schemas", "feature-request.schema.json"),
418
+ path2.join(path2.dirname(aiFeature.typesFile), "api-schemas", "feature-response.schema.json"),
419
+ path2.join(path2.dirname(aiFeature.typesFile), "api-schemas", "feature-result.schema.json"),
420
+ aiFeature.clientFile,
421
+ aiFeature.dataFile,
422
+ aiFeature.openApiFile,
423
+ aiFeature.phpFile,
424
+ aiFeature.typesFile,
425
+ aiFeature.validatorsFile
426
+ ]));
427
+ }
428
+ function checkWorkspaceAiFeatureConfig(aiFeature) {
429
+ const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(aiFeature.namespace);
430
+ return createDoctorCheck2(`AI feature config ${aiFeature.slug}`, hasNamespace ? "pass" : "fail", hasNamespace ? `AI feature namespace ${aiFeature.namespace} is valid` : "AI feature namespace is invalid");
431
+ }
432
+ function checkWorkspaceAiFeatureBootstrap(projectDir, packageName, phpPrefix) {
433
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
434
+ const bootstrapPath = path2.join(projectDir, `${packageBaseName}.php`);
435
+ if (!fs2.existsSync(bootstrapPath)) {
436
+ return createDoctorCheck2("AI feature bootstrap", "fail", `Missing ${path2.basename(bootstrapPath)}`);
437
+ }
438
+ const source = fs2.readFileSync(bootstrapPath, "utf8");
439
+ const registerFunctionName = `${phpPrefix}_register_ai_features`;
440
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
441
+ const hasServerGlob = source.includes(WORKSPACE_AI_FEATURE_GLOB);
442
+ const hasRegisterHook = source.includes(registerHook);
443
+ return createDoctorCheck2("AI feature bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "AI feature PHP loader hook is present" : "Missing AI feature PHP require glob or init hook");
444
+ }
345
445
  function getWorkspaceEditorPluginRequiredFiles(editorPlugin) {
346
446
  const editorPluginDir = path2.join("src", "editor-plugins", editorPlugin.slug);
347
447
  return Array.from(new Set([
@@ -439,7 +539,7 @@ function getWorkspaceDoctorChecks(cwd) {
439
539
  checks.push(checkWorkspacePackageMetadata(workspace, workspacePackageJson));
440
540
  try {
441
541
  const inventory = readWorkspaceInventory(workspace.projectDir);
442
- checks.push(createDoctorCheck2("Workspace inventory", "pass", `${inventory.blocks.length} block(s), ${inventory.variations.length} variation(s), ${inventory.patterns.length} pattern(s), ${inventory.bindingSources.length} binding source(s), ${inventory.restResources.length} REST resource(s), ${inventory.editorPlugins.length} editor plugin(s)`));
542
+ checks.push(createDoctorCheck2("Workspace inventory", "pass", `${inventory.blocks.length} block(s), ${inventory.variations.length} variation(s), ${inventory.patterns.length} pattern(s), ${inventory.bindingSources.length} binding source(s), ${inventory.restResources.length} REST resource(s), ${inventory.abilities.length} ability scaffold(s), ${inventory.aiFeatures.length} AI feature(s), ${inventory.editorPlugins.length} editor plugin(s)`));
443
543
  for (const block of inventory.blocks) {
444
544
  checks.push(checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, getWorkspaceBlockRequiredFiles(block)));
445
545
  checks.push(checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block));
@@ -483,6 +583,21 @@ function getWorkspaceDoctorChecks(cwd) {
483
583
  checks.push(checkWorkspaceRestResourceConfig(restResource));
484
584
  checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
485
585
  }
586
+ if (inventory.abilities.length > 0) {
587
+ checks.push(checkWorkspaceAbilityBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
588
+ checks.push(checkWorkspaceAbilityIndex(workspace.projectDir, inventory.abilities));
589
+ }
590
+ for (const ability of inventory.abilities) {
591
+ checks.push(checkWorkspaceAbilityConfig(workspace.projectDir, ability));
592
+ checks.push(checkExistingFiles(workspace.projectDir, `Ability ${ability.slug}`, getWorkspaceAbilityRequiredFiles(ability)));
593
+ }
594
+ if (inventory.aiFeatures.length > 0) {
595
+ checks.push(checkWorkspaceAiFeatureBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
596
+ }
597
+ for (const aiFeature of inventory.aiFeatures) {
598
+ checks.push(checkWorkspaceAiFeatureConfig(aiFeature));
599
+ checks.push(checkExistingFiles(workspace.projectDir, `AI feature ${aiFeature.slug}`, getWorkspaceAiFeatureRequiredFiles(aiFeature)));
600
+ }
486
601
  if (inventory.editorPlugins.length > 0) {
487
602
  checks.push(checkWorkspaceEditorPluginBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
488
603
  checks.push(checkWorkspaceEditorPluginIndex(workspace.projectDir, inventory.editorPlugins));
@@ -533,4 +648,4 @@ export {
533
648
  getDoctorChecks
534
649
  };
535
650
 
536
- //# debugId=BACE4BB4FC84F0EB64756E2164756E21
651
+ //# debugId=DA7EC2F06C52A22464756E2164756E21
@@ -19,9 +19,9 @@ import {
19
19
  resolvePackageManagerId,
20
20
  resolveTemplateId,
21
21
  scaffoldProject
22
- } from "./cli-32rf304y.js";
22
+ } from "./cli-c5021kqy.js";
23
23
  import"./cli-tesygdnr.js";
24
- import"./cli-qpt5dt0x.js";
24
+ import"./cli-2rev5hqm.js";
25
25
  import"./cli-gcbre1zs.js";
26
26
  import"./cli-bq2v559b.js";
27
27
  import {
@@ -33,7 +33,7 @@ import {
33
33
  OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE,
34
34
  isBuiltInTemplateId
35
35
  } from "./cli-tke8twkn.js";
36
- import"./cli-rg481yks.js";
36
+ import"./cli-3w3qxq9w.js";
37
37
  import {
38
38
  createManagedTempRoot
39
39
  } from "./cli-t73q5aqz.js";
package/dist-bunli/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  mergeWpTypiaUserConfig,
15
15
  package_default,
16
16
  writeStructuredCliDiagnosticError
17
- } from "./cli-n4m6yqz1.js";
17
+ } from "./cli-68145vb5.js";
18
18
  import"./cli-03j0axbt.js";
19
19
  import {
20
20
  GLOBAL_FLAGS,
@@ -2539,7 +2539,7 @@ async function formatCliError(error) {
2539
2539
  }
2540
2540
  async function createWpTypiaCli(options = {}) {
2541
2541
  applyStandaloneSupportLayoutEnv();
2542
- const { wpTypiaCommands } = await import("./command-list-5xp9pjyy.js");
2542
+ const { wpTypiaCommands } = await import("./command-list-kx7q3f18.js");
2543
2543
  const cli = await createCLI({
2544
2544
  ...bunliConfig,
2545
2545
  description: package_default.description,