@wp-typia/project-tools 0.19.3 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +23 -0
  2. package/dist/runtime/ability-spec.d.ts +90 -0
  3. package/dist/runtime/ability-spec.js +51 -0
  4. package/dist/runtime/ai-artifacts.d.ts +39 -0
  5. package/dist/runtime/ai-artifacts.js +68 -0
  6. package/dist/runtime/ai-feature-artifacts.d.ts +85 -0
  7. package/dist/runtime/ai-feature-artifacts.js +139 -0
  8. package/dist/runtime/ai-feature-capability.d.ts +114 -0
  9. package/dist/runtime/ai-feature-capability.js +150 -0
  10. package/dist/runtime/block-generator-service-spec.js +6 -0
  11. package/dist/runtime/cli-add-shared.d.ts +32 -1
  12. package/dist/runtime/cli-add-shared.js +44 -0
  13. package/dist/runtime/cli-add-workspace-ability.d.ts +8 -0
  14. package/dist/runtime/cli-add-workspace-ability.js +810 -0
  15. package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +22 -0
  16. package/dist/runtime/cli-add-workspace-ai-anchors.js +277 -0
  17. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +28 -0
  18. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +346 -0
  19. package/dist/runtime/cli-add-workspace-ai.d.ts +14 -0
  20. package/dist/runtime/cli-add-workspace-ai.js +484 -0
  21. package/dist/runtime/cli-add-workspace.d.ts +10 -0
  22. package/dist/runtime/cli-add-workspace.js +10 -0
  23. package/dist/runtime/cli-add.d.ts +1 -1
  24. package/dist/runtime/cli-add.js +1 -1
  25. package/dist/runtime/cli-core.d.ts +3 -1
  26. package/dist/runtime/cli-core.js +3 -1
  27. package/dist/runtime/cli-doctor-workspace.js +140 -1
  28. package/dist/runtime/cli-help.js +4 -0
  29. package/dist/runtime/index.d.ts +3 -1
  30. package/dist/runtime/index.js +2 -1
  31. package/dist/runtime/scaffold-compatibility.d.ts +65 -0
  32. package/dist/runtime/scaffold-compatibility.js +152 -0
  33. package/dist/runtime/scaffold-template-variable-groups.d.ts +2 -0
  34. package/dist/runtime/scaffold-template-variables.js +6 -0
  35. package/dist/runtime/scaffold.d.ts +3 -0
  36. package/dist/runtime/typia-llm.d.ts +213 -0
  37. package/dist/runtime/typia-llm.js +348 -0
  38. package/dist/runtime/wordpress-ai.d.ts +122 -0
  39. package/dist/runtime/wordpress-ai.js +177 -0
  40. package/dist/runtime/workspace-inventory.d.ts +51 -4
  41. package/dist/runtime/workspace-inventory.js +157 -4
  42. package/package.json +12 -2
  43. package/templates/_shared/base/{{slugKebabCase}}.php.mustache +3 -3
  44. package/templates/_shared/compound/core/{{slugKebabCase}}.php.mustache +3 -3
  45. package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +3 -3
  46. package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +3 -3
  47. package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +3 -3
  48. package/templates/_shared/persistence/core/{{slugKebabCase}}.php.mustache +3 -3
  49. package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +3 -3
  50. package/templates/query-loop/{{slugKebabCase}}.php.mustache +3 -3
@@ -0,0 +1,150 @@
1
+ /** Canonical registry of AI-related features supported by wp-typia today. */
2
+ export const AI_FEATURE_DEFINITIONS = {
3
+ wordpressAiClient: {
4
+ description: 'WordPress 7.0 AI Client surface used by AI-capable feature endpoints.',
5
+ id: 'wordpress-ai-client',
6
+ label: 'WordPress AI Client',
7
+ minimumVersions: {
8
+ wordpress: '7.0',
9
+ },
10
+ runtimeGates: [
11
+ {
12
+ kind: 'wordpress-core-feature',
13
+ value: 'WordPress AI Client',
14
+ },
15
+ ],
16
+ },
17
+ wordpressCoreAbilities: {
18
+ description: 'Client-side ability discovery surface for editor and admin flows.',
19
+ id: 'wordpress-core-abilities',
20
+ label: '@wordpress/core-abilities',
21
+ minimumVersions: {
22
+ wordpress: '7.0',
23
+ },
24
+ runtimeGates: [
25
+ {
26
+ kind: 'script-package',
27
+ value: '@wordpress/core-abilities',
28
+ },
29
+ ],
30
+ },
31
+ wordpressMcpPublicMetadata: {
32
+ description: 'Optional MCP exposure metadata consumed by a separate adapter path rather than core alone.',
33
+ id: 'wordpress-mcp-public-metadata',
34
+ label: 'MCP public metadata',
35
+ runtimeGates: [
36
+ {
37
+ kind: 'adapter',
38
+ value: 'MCP adapter',
39
+ },
40
+ ],
41
+ },
42
+ wordpressServerAbilities: {
43
+ description: 'Server-side ability registration and execution surface for WordPress-native abilities.',
44
+ id: 'wordpress-server-abilities',
45
+ label: 'WordPress Abilities API',
46
+ minimumVersions: {
47
+ wordpress: '6.9',
48
+ },
49
+ runtimeGates: [
50
+ {
51
+ kind: 'php-function',
52
+ value: 'wp_register_ability',
53
+ },
54
+ {
55
+ kind: 'php-function',
56
+ value: 'wp_register_ability_category',
57
+ },
58
+ ],
59
+ },
60
+ };
61
+ const DEFAULT_AI_FEATURE_REGISTRY = Object.values(AI_FEATURE_DEFINITIONS).reduce((accumulator, definition) => {
62
+ accumulator[definition.id] = definition;
63
+ return accumulator;
64
+ }, {});
65
+ function parseVersionFloorParts(value) {
66
+ return value.split('.').map((part, index) => {
67
+ if (!/^\d+$/.test(part)) {
68
+ throw new Error(`parseVersionFloorParts received an invalid version floor "${value}" at segment ${index + 1}.`);
69
+ }
70
+ return Number.parseInt(part, 10);
71
+ });
72
+ }
73
+ function compareVersionFloors(left, right) {
74
+ const leftParts = parseVersionFloorParts(left);
75
+ const rightParts = parseVersionFloorParts(right);
76
+ const length = Math.max(leftParts.length, rightParts.length);
77
+ for (let index = 0; index < length; index += 1) {
78
+ const leftValue = leftParts[index] ?? 0;
79
+ const rightValue = rightParts[index] ?? 0;
80
+ if (leftValue > rightValue) {
81
+ return 1;
82
+ }
83
+ if (leftValue < rightValue) {
84
+ return -1;
85
+ }
86
+ }
87
+ return 0;
88
+ }
89
+ function pickHigherVersionFloor(current, candidate) {
90
+ if (!candidate) {
91
+ return current;
92
+ }
93
+ if (!current) {
94
+ return candidate;
95
+ }
96
+ return compareVersionFloors(current, candidate) >= 0 ? current : candidate;
97
+ }
98
+ function normalizeSelections(selections) {
99
+ const normalized = new Map();
100
+ for (const selection of selections) {
101
+ const current = normalized.get(selection.featureId);
102
+ if (!current || selection.mode === 'required') {
103
+ normalized.set(selection.featureId, selection);
104
+ }
105
+ }
106
+ return [...normalized.values()];
107
+ }
108
+ /**
109
+ * Resolves a normalized AI feature capability plan from a list of selections.
110
+ *
111
+ * Required selections win when the same feature id appears multiple times, and
112
+ * the resulting hard minimum platform floor is computed from required features
113
+ * only.
114
+ *
115
+ * @param selections Desired feature selections for a scaffold or projection.
116
+ * @param registry Feature registry to resolve against. Defaults to the built-in registry.
117
+ * @returns The normalized capability plan plus required version floors.
118
+ * @throws When a selection references an unknown feature id.
119
+ */
120
+ export function resolveAiFeatureCapabilityPlan(selections, registry = DEFAULT_AI_FEATURE_REGISTRY) {
121
+ const requiredFeatures = [];
122
+ const optionalFeatures = [];
123
+ let wordpress;
124
+ let php;
125
+ for (const selection of normalizeSelections(selections)) {
126
+ const definition = registry[selection.featureId];
127
+ if (!definition) {
128
+ throw new Error(`Unknown AI feature capability "${selection.featureId}".`);
129
+ }
130
+ const resolvedDefinition = {
131
+ ...definition,
132
+ mode: selection.mode,
133
+ };
134
+ if (selection.mode === 'required') {
135
+ requiredFeatures.push(resolvedDefinition);
136
+ wordpress = pickHigherVersionFloor(wordpress, definition.minimumVersions?.wordpress);
137
+ php = pickHigherVersionFloor(php, definition.minimumVersions?.php);
138
+ continue;
139
+ }
140
+ optionalFeatures.push(resolvedDefinition);
141
+ }
142
+ return {
143
+ hardMinimums: {
144
+ ...(php ? { php } : {}),
145
+ ...(wordpress ? { wordpress } : {}),
146
+ },
147
+ optionalFeatures,
148
+ requiredFeatures,
149
+ };
150
+ }
@@ -5,6 +5,7 @@ import { DEFAULT_COMPOUND_INNER_BLOCKS_PRESET_ID, getCompoundInnerBlocksPresetDe
5
5
  import { getTemplateById, } from "./template-registry.js";
6
6
  import { toPascalCase, toSnakeCase, } from "./string-case.js";
7
7
  import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from "./scaffold-identifiers.js";
8
+ import { resolveScaffoldCompatibilityPolicy } from "./scaffold-compatibility.js";
8
9
  import { attachScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
9
10
  function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePolicy, }) {
10
11
  if (templateId === "persistence") {
@@ -122,6 +123,7 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
122
123
  ? spec.persistence.persistencePolicy
123
124
  : "authenticated";
124
125
  const queryVariationNamespace = `${namespace}/${slug}`;
126
+ const compatibility = resolveScaffoldCompatibilityPolicy([]);
125
127
  const flatVariables = {
126
128
  alternateRenderTargetsCsv: formatAlternateRenderTargets(alternateRenderTargets),
127
129
  alternateRenderTargetsJson: JSON.stringify(alternateRenderTargets),
@@ -161,6 +163,9 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
161
163
  hasAlternateRenderTargets: alternateRenderTargets.length > 0
162
164
  ? "true"
163
165
  : "false",
166
+ requiresAtLeast: compatibility.pluginHeader.requiresAtLeast,
167
+ requiresPhp: compatibility.pluginHeader.requiresPhp,
168
+ testedUpTo: compatibility.pluginHeader.testedUpTo,
164
169
  projectToolsPackageVersion,
165
170
  cssClassName,
166
171
  dashCase: slug,
@@ -211,6 +216,7 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
211
216
  author: spec.project.author,
212
217
  blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
213
218
  category: spec.metadata.category,
219
+ compatibility: compatibility.pluginHeader,
214
220
  cssClassName,
215
221
  description: spec.metadata.description,
216
222
  descriptionJson: flatVariables.descriptionJson,
@@ -5,7 +5,7 @@ export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
5
5
  /**
6
6
  * Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
7
7
  */
8
- export declare const ADD_KIND_IDS: readonly ["block", "variation", "pattern", "binding-source", "rest-resource", "hooked-block", "editor-plugin"];
8
+ export declare const ADD_KIND_IDS: readonly ["block", "variation", "pattern", "binding-source", "rest-resource", "ability", "ai-feature", "hooked-block", "editor-plugin"];
9
9
  export type AddKindId = (typeof ADD_KIND_IDS)[number];
10
10
  /**
11
11
  * Supported plugin-level REST resource methods accepted by
@@ -43,6 +43,23 @@ export interface RunAddRestResourceCommandOptions {
43
43
  namespace?: string;
44
44
  restResourceName: string;
45
45
  }
46
+ /**
47
+ * Options for `wp-typia add ability`.
48
+ *
49
+ * @property cwd Working directory used to resolve the nearest official workspace.
50
+ * Defaults to `process.cwd()`.
51
+ * @property abilityName Human-entered workflow ability name that will be
52
+ * normalized into the generated slug.
53
+ */
54
+ export interface RunAddAbilityCommandOptions {
55
+ abilityName: string;
56
+ cwd?: string;
57
+ }
58
+ export interface RunAddAiFeatureCommandOptions {
59
+ aiFeatureName: string;
60
+ cwd?: string;
61
+ namespace?: string;
62
+ }
46
63
  export interface RunAddHookedBlockCommandOptions {
47
64
  anchorBlockName: string;
48
65
  blockName: string;
@@ -140,6 +157,20 @@ export declare function assertVariationDoesNotExist(projectDir: string, blockSlu
140
157
  export declare function assertPatternDoesNotExist(projectDir: string, patternSlug: string, inventory: WorkspaceInventory): void;
141
158
  export declare function assertBindingSourceDoesNotExist(projectDir: string, bindingSourceSlug: string, inventory: WorkspaceInventory): void;
142
159
  export declare function assertRestResourceDoesNotExist(projectDir: string, restResourceSlug: string, inventory: WorkspaceInventory): void;
160
+ /**
161
+ * Ensure a workflow ability scaffold does not already exist on disk or in the
162
+ * workspace inventory.
163
+ *
164
+ * The check covers the generated `src/abilities/<slug>` directory,
165
+ * `inc/abilities/<slug>.php`, and any matching `inventory.abilities` entry.
166
+ *
167
+ * @param projectDir Workspace root directory.
168
+ * @param abilitySlug Normalized workflow ability slug.
169
+ * @param inventory Parsed workspace inventory.
170
+ * @throws {Error} When the ability directory, PHP bootstrap, or inventory entry already exists.
171
+ */
172
+ export declare function assertAbilityDoesNotExist(projectDir: string, abilitySlug: string, inventory: WorkspaceInventory): void;
173
+ export declare function assertAiFeatureDoesNotExist(projectDir: string, aiFeatureSlug: string, inventory: WorkspaceInventory): void;
143
174
  /**
144
175
  * Ensure an editor plugin scaffold does not already exist on disk or in the
145
176
  * workspace inventory.
@@ -15,6 +15,8 @@ export const ADD_KIND_IDS = [
15
15
  "pattern",
16
16
  "binding-source",
17
17
  "rest-resource",
18
+ "ability",
19
+ "ai-feature",
18
20
  "hooked-block",
19
21
  "editor-plugin",
20
22
  ];
@@ -254,6 +256,44 @@ export function assertRestResourceDoesNotExist(projectDir, restResourceSlug, inv
254
256
  throw new Error(`A REST resource inventory entry already exists for ${restResourceSlug}. Choose a different name.`);
255
257
  }
256
258
  }
259
+ /**
260
+ * Ensure a workflow ability scaffold does not already exist on disk or in the
261
+ * workspace inventory.
262
+ *
263
+ * The check covers the generated `src/abilities/<slug>` directory,
264
+ * `inc/abilities/<slug>.php`, and any matching `inventory.abilities` entry.
265
+ *
266
+ * @param projectDir Workspace root directory.
267
+ * @param abilitySlug Normalized workflow ability slug.
268
+ * @param inventory Parsed workspace inventory.
269
+ * @throws {Error} When the ability directory, PHP bootstrap, or inventory entry already exists.
270
+ */
271
+ export function assertAbilityDoesNotExist(projectDir, abilitySlug, inventory) {
272
+ const abilityDir = path.join(projectDir, "src", "abilities", abilitySlug);
273
+ const abilityPhpPath = path.join(projectDir, "inc", "abilities", `${abilitySlug}.php`);
274
+ if (fs.existsSync(abilityDir)) {
275
+ throw new Error(`An ability scaffold already exists at ${path.relative(projectDir, abilityDir)}. Choose a different name.`);
276
+ }
277
+ if (fs.existsSync(abilityPhpPath)) {
278
+ throw new Error(`An ability bootstrap already exists at ${path.relative(projectDir, abilityPhpPath)}. Choose a different name.`);
279
+ }
280
+ if (inventory.abilities.some((entry) => entry.slug === abilitySlug)) {
281
+ throw new Error(`An ability inventory entry already exists for ${abilitySlug}. Choose a different name.`);
282
+ }
283
+ }
284
+ export function assertAiFeatureDoesNotExist(projectDir, aiFeatureSlug, inventory) {
285
+ const aiFeatureDir = path.join(projectDir, "src", "ai-features", aiFeatureSlug);
286
+ const aiFeaturePhpPath = path.join(projectDir, "inc", "ai-features", `${aiFeatureSlug}.php`);
287
+ if (fs.existsSync(aiFeatureDir)) {
288
+ throw new Error(`An AI feature already exists at ${path.relative(projectDir, aiFeatureDir)}. Choose a different name.`);
289
+ }
290
+ if (fs.existsSync(aiFeaturePhpPath)) {
291
+ throw new Error(`An AI feature bootstrap already exists at ${path.relative(projectDir, aiFeaturePhpPath)}. Choose a different name.`);
292
+ }
293
+ if (inventory.aiFeatures.some((entry) => entry.slug === aiFeatureSlug)) {
294
+ throw new Error(`An AI feature inventory entry already exists for ${aiFeatureSlug}. Choose a different name.`);
295
+ }
296
+ }
257
297
  /**
258
298
  * Ensure an editor plugin scaffold does not already exist on disk or in the
259
299
  * workspace inventory.
@@ -282,6 +322,8 @@ export function formatAddHelpText() {
282
322
  wp-typia add pattern <name> [--dry-run]
283
323
  wp-typia add binding-source <name> [--dry-run]
284
324
  wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create,update,delete>] [--dry-run]
325
+ wp-typia add ability <name> [--dry-run]
326
+ wp-typia add ai-feature <name> [--namespace <vendor/v1>] [--dry-run]
285
327
  wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <${HOOKED_BLOCK_POSITION_IDS.join("|")}> [--dry-run]
286
328
  wp-typia add editor-plugin <name> [--slot <${EDITOR_PLUGIN_SLOT_IDS.join("|")}>] [--dry-run]
287
329
 
@@ -293,6 +335,8 @@ Notes:
293
335
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
294
336
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
295
337
  \`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`.
338
+ \`add ability\` scaffolds typed workflow abilities under \`src/abilities/\` and server registration under \`inc/abilities/\`.
339
+ \`add ai-feature\` scaffolds server-owned AI feature endpoints under \`src/ai-features/\` and PHP route glue under \`inc/ai-features/\`.
296
340
  \`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
297
341
  \`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins/\`.`;
298
342
  }
@@ -0,0 +1,8 @@
1
+ import { type RunAddAbilityCommandOptions } from "./cli-add-shared.js";
2
+ /**
3
+ * Add one typed workflow ability scaffold to an official workspace project.
4
+ */
5
+ export declare function runAddAbilityCommand({ abilityName, cwd, }: RunAddAbilityCommandOptions): Promise<{
6
+ abilitySlug: string;
7
+ projectDir: string;
8
+ }>;