@wp-typia/project-tools 0.22.1 → 0.22.3

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 (36) hide show
  1. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +1 -1
  2. package/dist/runtime/built-in-block-code-templates/interactivity.js +4 -2
  3. package/dist/runtime/cli-add-shared.d.ts +49 -0
  4. package/dist/runtime/cli-add-shared.js +204 -71
  5. package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +5 -0
  6. package/dist/runtime/cli-add-workspace-ability-scaffold.js +392 -0
  7. package/dist/runtime/cli-add-workspace-ability-templates.d.ts +34 -0
  8. package/dist/runtime/cli-add-workspace-ability-templates.js +500 -0
  9. package/dist/runtime/cli-add-workspace-ability-types.d.ts +27 -0
  10. package/dist/runtime/cli-add-workspace-ability-types.js +14 -0
  11. package/dist/runtime/cli-add-workspace-ability.js +12 -852
  12. package/dist/runtime/cli-add-workspace-ai-scaffold.d.ts +21 -0
  13. package/dist/runtime/cli-add-workspace-ai-scaffold.js +91 -0
  14. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +119 -1
  15. package/dist/runtime/cli-add-workspace-ai-templates.d.ts +4 -0
  16. package/dist/runtime/cli-add-workspace-ai-templates.js +605 -0
  17. package/dist/runtime/cli-add-workspace-ai.js +15 -465
  18. package/dist/runtime/cli-add-workspace-assets.js +7 -4
  19. package/dist/runtime/cli-add-workspace.js +1 -19
  20. package/dist/runtime/cli-doctor-workspace-bindings.d.ts +11 -0
  21. package/dist/runtime/cli-doctor-workspace-bindings.js +134 -0
  22. package/dist/runtime/cli-doctor-workspace-blocks.d.ts +11 -0
  23. package/dist/runtime/cli-doctor-workspace-blocks.js +504 -0
  24. package/dist/runtime/cli-doctor-workspace-features.d.ts +11 -0
  25. package/dist/runtime/cli-doctor-workspace-features.js +383 -0
  26. package/dist/runtime/cli-doctor-workspace-package.d.ts +18 -0
  27. package/dist/runtime/cli-doctor-workspace-package.js +59 -0
  28. package/dist/runtime/cli-doctor-workspace-shared.d.ts +69 -0
  29. package/dist/runtime/cli-doctor-workspace-shared.js +87 -0
  30. package/dist/runtime/cli-doctor-workspace.js +25 -1062
  31. package/dist/runtime/scaffold-compatibility.d.ts +2 -0
  32. package/dist/runtime/scaffold-compatibility.js +2 -0
  33. package/dist/runtime/typia-llm.d.ts +37 -2
  34. package/dist/runtime/typia-llm.js +240 -3
  35. package/dist/runtime/workspace-inventory.js +24 -0
  36. package/package.json +3 -3
@@ -26,7 +26,9 @@ export interface ScaffoldCompatibilityPolicy {
26
26
  export interface ScaffoldCompatibilityConfig {
27
27
  hardMinimums: AiFeatureCompatibilityFloor;
28
28
  mode: 'baseline' | 'optional' | 'required';
29
+ optionalFeatureIds: string[];
29
30
  optionalFeatures: string[];
31
+ requiredFeatureIds: string[];
30
32
  requiredFeatures: string[];
31
33
  runtimeGates: string[];
32
34
  }
@@ -88,7 +88,9 @@ export function createScaffoldCompatibilityConfig(policy) {
88
88
  return {
89
89
  hardMinimums: capabilityPlan.hardMinimums,
90
90
  mode: getPolicyMode(capabilityPlan),
91
+ optionalFeatureIds: capabilityPlan.optionalFeatures.map((feature) => feature.id),
91
92
  optionalFeatures: capabilityPlan.optionalFeatures.map((feature) => feature.label),
93
+ requiredFeatureIds: capabilityPlan.requiredFeatures.map((feature) => feature.id),
92
94
  requiredFeatures: capabilityPlan.requiredFeatures.map((feature) => feature.label),
93
95
  runtimeGates: [
94
96
  ...capabilityPlan.requiredFeatures.flatMap(formatRuntimeGate),
@@ -1,6 +1,6 @@
1
1
  import type { ArtifactSyncExecutionOptions, EndpointManifestDefinition, EndpointManifestEndpointDefinition } from '@wp-typia/block-runtime/metadata-core';
2
2
  import type { ILlmFunction, ILlmSchema, ILlmStructuredOutput } from 'typia';
3
- import { type EndpointAuthIntent, type EndpointWordPressAuthDefinition } from './schema-core.js';
3
+ import { type EndpointAuthIntent, type EndpointWordPressAuthDefinition, type OpenApiDocument } from './schema-core.js';
4
4
  /**
5
5
  * Method-level descriptor projected from an endpoint manifest for a generated
6
6
  * `typia.llm` controller interface.
@@ -140,6 +140,16 @@ export interface ProjectedTypiaLlmStructuredOutputArtifact {
140
140
  /** Structured output parameters generated by `typia.llm`. */
141
141
  parameters: ILlmStructuredOutput['parameters'];
142
142
  }
143
+ /**
144
+ * Configures optional OpenAPI-backed constraint restoration for projected
145
+ * `typia.llm` function artifacts.
146
+ */
147
+ export interface ProjectTypiaLlmOpenApiProjectionOptions {
148
+ /** Canonical endpoint-aware OpenAPI document for the projected contracts. */
149
+ openApiDocument: OpenApiDocument;
150
+ /** Optional override for resolving one function schema to an operation id. */
151
+ resolveOperationId?: (functionSchema: TypiaLlmFunctionLike, functionArtifact: ProjectedTypiaLlmFunctionArtifact) => string;
152
+ }
143
153
  /**
144
154
  * Configures projection of a compiled `typia.llm.application(...)` result into
145
155
  * the JSON-friendly adapter artifact.
@@ -149,9 +159,23 @@ export interface ProjectTypiaLlmApplicationArtifactOptions {
149
159
  application: TypiaLlmApplicationLike;
150
160
  /** Source metadata for the generated JSON artifact. */
151
161
  generatedFrom: ProjectedTypiaLlmApplicationArtifact['generatedFrom'];
162
+ /** Optional OpenAPI projection that restores canonical schema constraints. */
163
+ openApiProjection?: ProjectTypiaLlmOpenApiProjectionOptions;
152
164
  /** Optional hook for adapter-specific function schema normalization. */
153
165
  transformFunction?: (functionArtifact: ProjectedTypiaLlmFunctionArtifact, functionSchema: TypiaLlmFunctionLike) => ProjectedTypiaLlmFunctionArtifact;
154
166
  }
167
+ /**
168
+ * Configures direct OpenAPI-backed constraint restoration for one projected
169
+ * function artifact.
170
+ */
171
+ export interface ApplyOpenApiConstraintsToTypiaLlmFunctionArtifactOptions {
172
+ /** Projected function artifact to enrich with canonical constraints. */
173
+ functionArtifact: ProjectedTypiaLlmFunctionArtifact;
174
+ /** Canonical endpoint-aware OpenAPI document. */
175
+ openApiDocument: OpenApiDocument;
176
+ /** Operation id that owns the request/response contract for this function. */
177
+ operationId: string;
178
+ }
155
179
  /**
156
180
  * Configures projection of a compiled `typia.llm.structuredOutput(...)` result
157
181
  * into the JSON-friendly adapter artifact.
@@ -197,6 +221,17 @@ export declare function syncTypiaLlmAdapterModule({ check, generatedSourceFile,
197
221
  * @returns JSON-friendly function artifact.
198
222
  */
199
223
  export declare function projectTypiaLlmApplicationFunction(functionSchema: TypiaLlmFunctionLike): ProjectedTypiaLlmFunctionArtifact;
224
+ /**
225
+ * Restores canonical request and response constraints from an endpoint-aware
226
+ * OpenAPI document onto one projected `typia.llm` function artifact.
227
+ *
228
+ * Query-only inputs merge into the root parameter object. Mixed body/query
229
+ * inputs merge into the generated `body` and `query` properties.
230
+ *
231
+ * @param options Projected artifact plus the canonical OpenAPI document.
232
+ * @returns A cloned artifact enriched with OpenAPI-backed constraints when available.
233
+ */
234
+ export declare function applyOpenApiConstraintsToTypiaLlmFunctionArtifact({ functionArtifact, openApiDocument, operationId, }: ApplyOpenApiConstraintsToTypiaLlmFunctionArtifactOptions): ProjectedTypiaLlmFunctionArtifact;
200
235
  /**
201
236
  * Projects a compiled `typia.llm.application(...)` result into a JSON-friendly
202
237
  * downstream adapter artifact.
@@ -204,7 +239,7 @@ export declare function projectTypiaLlmApplicationFunction(functionSchema: Typia
204
239
  * @param options Compiled application value and source metadata.
205
240
  * @returns JSON-friendly application artifact.
206
241
  */
207
- export declare function projectTypiaLlmApplicationArtifact({ application, generatedFrom, transformFunction, }: ProjectTypiaLlmApplicationArtifactOptions): ProjectedTypiaLlmApplicationArtifact;
242
+ export declare function projectTypiaLlmApplicationArtifact({ application, generatedFrom, openApiProjection, transformFunction, }: ProjectTypiaLlmApplicationArtifactOptions): ProjectedTypiaLlmApplicationArtifact;
208
243
  /**
209
244
  * Projects a compiled `typia.llm.structuredOutput(...)` result into a
210
245
  * JSON-friendly downstream adapter artifact.
@@ -81,9 +81,195 @@ const TYPESCRIPT_RESERVED_WORDS = new Set([
81
81
  'with',
82
82
  'yield',
83
83
  ]);
84
+ const JSON_SCHEMA_CONSTRAINT_KEYS = [
85
+ 'additionalProperties',
86
+ 'const',
87
+ 'default',
88
+ 'enum',
89
+ 'exclusiveMaximum',
90
+ 'exclusiveMinimum',
91
+ 'format',
92
+ 'maxItems',
93
+ 'maxLength',
94
+ 'maximum',
95
+ 'minItems',
96
+ 'minLength',
97
+ 'minimum',
98
+ 'multipleOf',
99
+ 'pattern',
100
+ 'type',
101
+ ];
84
102
  function cloneJsonValueIfDefined(value) {
85
103
  return value === undefined ? undefined : cloneJsonValue(value);
86
104
  }
105
+ function isJsonSchemaObject(value) {
106
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
107
+ }
108
+ function decodeJsonPointerSegment(segment) {
109
+ return segment.replace(/~1/g, '/').replace(/~0/g, '~');
110
+ }
111
+ function resolveOpenApiReferenceTarget(document, reference) {
112
+ if (!reference.startsWith('#/')) {
113
+ throw new Error(`Unsupported OpenAPI schema reference "${reference}".`);
114
+ }
115
+ let current = document;
116
+ for (const rawSegment of reference.slice(2).split('/')) {
117
+ const segment = decodeJsonPointerSegment(rawSegment);
118
+ if (Array.isArray(current)) {
119
+ const index = Number.parseInt(segment, 10);
120
+ current = Number.isInteger(index) ? current[index] : undefined;
121
+ continue;
122
+ }
123
+ if (!isJsonSchemaObject(current)) {
124
+ current = undefined;
125
+ break;
126
+ }
127
+ current = current[segment];
128
+ }
129
+ if (!isJsonSchemaObject(current)) {
130
+ throw new Error(`Unable to resolve OpenAPI schema reference "${reference}".`);
131
+ }
132
+ return current;
133
+ }
134
+ function resolveOpenApiSchemaObject(document, schema, seenReferences = new Set()) {
135
+ if (!isJsonSchemaObject(schema)) {
136
+ return {};
137
+ }
138
+ const reference = schema.$ref;
139
+ if (typeof reference !== 'string') {
140
+ return schema;
141
+ }
142
+ if (seenReferences.has(reference)) {
143
+ return schema;
144
+ }
145
+ return resolveOpenApiSchemaObject(document, resolveOpenApiReferenceTarget(document, reference), new Set([...seenReferences, reference]));
146
+ }
147
+ function collectOpenApiSeenReferences(schema, seenReferences) {
148
+ const reference = schema.$ref;
149
+ if (typeof reference !== 'string' || seenReferences.has(reference)) {
150
+ return seenReferences;
151
+ }
152
+ return new Set([...seenReferences, reference]);
153
+ }
154
+ function mergeJsonSchemaConstraintProperties(document, target, source, seenReferences = new Set()) {
155
+ const nextSeenReferences = collectOpenApiSeenReferences(source, seenReferences);
156
+ const resolvedSource = resolveOpenApiSchemaObject(document, source, seenReferences);
157
+ const merged = target;
158
+ for (const key of JSON_SCHEMA_CONSTRAINT_KEYS) {
159
+ if (resolvedSource[key] !== undefined) {
160
+ merged[key] = cloneJsonValue(resolvedSource[key]);
161
+ }
162
+ }
163
+ if (Array.isArray(resolvedSource.required)) {
164
+ merged.required = resolvedSource.required.filter((value) => typeof value === 'string');
165
+ }
166
+ if (Array.isArray(resolvedSource.items)) {
167
+ merged.items = cloneJsonValue(resolvedSource.items);
168
+ }
169
+ else if (isJsonSchemaObject(resolvedSource.items)) {
170
+ const nextItems = isJsonSchemaObject(merged.items) ? merged.items : {};
171
+ merged.items = mergeJsonSchemaConstraintProperties(document, nextItems, resolvedSource.items, nextSeenReferences);
172
+ }
173
+ if (isJsonSchemaObject(resolvedSource.properties)) {
174
+ const targetProperties = isJsonSchemaObject(merged.properties)
175
+ ? merged.properties
176
+ : {};
177
+ for (const [propertyName, propertySchema] of Object.entries(resolvedSource.properties)) {
178
+ if (!isJsonSchemaObject(propertySchema)) {
179
+ continue;
180
+ }
181
+ const nextProperty = isJsonSchemaObject(targetProperties[propertyName])
182
+ ? targetProperties[propertyName]
183
+ : {};
184
+ targetProperties[propertyName] = mergeJsonSchemaConstraintProperties(document, nextProperty, propertySchema, nextSeenReferences);
185
+ }
186
+ merged.properties = targetProperties;
187
+ }
188
+ return merged;
189
+ }
190
+ function findOpenApiOperationById(document, operationId) {
191
+ for (const pathItem of Object.values(document.paths)) {
192
+ if (!isJsonSchemaObject(pathItem)) {
193
+ continue;
194
+ }
195
+ for (const method of ['delete', 'get', 'patch', 'post', 'put']) {
196
+ const operation = resolveOpenApiSchemaObject(document, pathItem[method]);
197
+ if (!isJsonSchemaObject(operation) ||
198
+ typeof operation.operationId !== 'string') {
199
+ continue;
200
+ }
201
+ if (operation.operationId === operationId) {
202
+ return operation;
203
+ }
204
+ }
205
+ }
206
+ return null;
207
+ }
208
+ function resolveOpenApiRequestBodySchema(operation, document) {
209
+ const requestBody = resolveOpenApiSchemaObject(document, operation.requestBody);
210
+ const content = isJsonSchemaObject(requestBody.content)
211
+ ? requestBody.content
212
+ : null;
213
+ const jsonMediaType = content && isJsonSchemaObject(content['application/json'])
214
+ ? content['application/json']
215
+ : null;
216
+ const schema = jsonMediaType?.schema;
217
+ if (!isJsonSchemaObject(schema)) {
218
+ return null;
219
+ }
220
+ return resolveOpenApiSchemaObject(document, schema);
221
+ }
222
+ function resolveOpenApiSuccessResponseSchema(operation, document) {
223
+ for (const [statusCode, response] of Object.entries(operation.responses)) {
224
+ const resolvedResponse = resolveOpenApiSchemaObject(document, response);
225
+ if (!/^2(?:\d\d|XX)$/u.test(statusCode) ||
226
+ !isJsonSchemaObject(resolvedResponse)) {
227
+ continue;
228
+ }
229
+ const content = isJsonSchemaObject(resolvedResponse.content)
230
+ ? resolvedResponse.content
231
+ : null;
232
+ const jsonMediaType = content && isJsonSchemaObject(content['application/json'])
233
+ ? content['application/json']
234
+ : null;
235
+ const schema = jsonMediaType?.schema;
236
+ if (!isJsonSchemaObject(schema)) {
237
+ continue;
238
+ }
239
+ return resolveOpenApiSchemaObject(document, schema);
240
+ }
241
+ return null;
242
+ }
243
+ function getOrCreateObjectProperty(target, propertyName) {
244
+ const targetProperties = isJsonSchemaObject(target.properties)
245
+ ? target.properties
246
+ : {};
247
+ const nextProperty = isJsonSchemaObject(targetProperties[propertyName])
248
+ ? targetProperties[propertyName]
249
+ : {};
250
+ targetProperties[propertyName] = nextProperty;
251
+ target.properties = targetProperties;
252
+ return nextProperty;
253
+ }
254
+ function applyOpenApiQueryParameterConstraints(target, operation, document) {
255
+ for (const parameter of operation.parameters ?? []) {
256
+ const resolvedParameter = resolveOpenApiSchemaObject(document, parameter);
257
+ if (!isJsonSchemaObject(resolvedParameter) ||
258
+ resolvedParameter.in !== 'query' ||
259
+ typeof resolvedParameter.name !== 'string') {
260
+ continue;
261
+ }
262
+ const propertyTarget = getOrCreateObjectProperty(target, resolvedParameter.name);
263
+ mergeJsonSchemaConstraintProperties(document, propertyTarget, resolveOpenApiSchemaObject(document, resolvedParameter.schema));
264
+ if (resolvedParameter.required === true) {
265
+ const required = new Set(Array.isArray(target.required)
266
+ ? target.required.filter((value) => typeof value === 'string')
267
+ : []);
268
+ required.add(resolvedParameter.name);
269
+ target.required = [...required];
270
+ }
271
+ }
272
+ }
87
273
  function getContractSourceTypeName(manifest, endpoint, contractName) {
88
274
  const contract = manifest.contracts[contractName];
89
275
  if (!contract) {
@@ -342,6 +528,50 @@ function cloneProjectedTypiaLlmFunctionArtifact(functionArtifact) {
342
528
  ...(functionArtifact.tags ? { tags: [...functionArtifact.tags] } : {}),
343
529
  };
344
530
  }
531
+ /**
532
+ * Restores canonical request and response constraints from an endpoint-aware
533
+ * OpenAPI document onto one projected `typia.llm` function artifact.
534
+ *
535
+ * Query-only inputs merge into the root parameter object. Mixed body/query
536
+ * inputs merge into the generated `body` and `query` properties.
537
+ *
538
+ * @param options Projected artifact plus the canonical OpenAPI document.
539
+ * @returns A cloned artifact enriched with OpenAPI-backed constraints when available.
540
+ */
541
+ export function applyOpenApiConstraintsToTypiaLlmFunctionArtifact({ functionArtifact, openApiDocument, operationId, }) {
542
+ const constrainedArtifact = cloneProjectedTypiaLlmFunctionArtifact(functionArtifact);
543
+ const operation = findOpenApiOperationById(openApiDocument, operationId);
544
+ if (!operation) {
545
+ return constrainedArtifact;
546
+ }
547
+ const hasQueryParameters = (operation.parameters ?? []).some((parameter) => {
548
+ const resolvedParameter = resolveOpenApiSchemaObject(openApiDocument, parameter);
549
+ return (isJsonSchemaObject(resolvedParameter) &&
550
+ resolvedParameter.in === 'query' &&
551
+ typeof resolvedParameter.name === 'string');
552
+ });
553
+ const requestBodySchema = resolveOpenApiRequestBodySchema(operation, openApiDocument);
554
+ if (requestBodySchema) {
555
+ if (hasQueryParameters) {
556
+ mergeJsonSchemaConstraintProperties(openApiDocument, getOrCreateObjectProperty(constrainedArtifact.parameters, 'body'), requestBodySchema);
557
+ }
558
+ else {
559
+ mergeJsonSchemaConstraintProperties(openApiDocument, constrainedArtifact.parameters, requestBodySchema);
560
+ }
561
+ }
562
+ if (hasQueryParameters) {
563
+ applyOpenApiQueryParameterConstraints(requestBodySchema
564
+ ? getOrCreateObjectProperty(constrainedArtifact.parameters, 'query')
565
+ : constrainedArtifact.parameters, operation, openApiDocument);
566
+ }
567
+ if (constrainedArtifact.output) {
568
+ const responseSchema = resolveOpenApiSuccessResponseSchema(operation, openApiDocument);
569
+ if (responseSchema) {
570
+ mergeJsonSchemaConstraintProperties(openApiDocument, constrainedArtifact.output, responseSchema);
571
+ }
572
+ }
573
+ return constrainedArtifact;
574
+ }
345
575
  /**
346
576
  * Projects a compiled `typia.llm.application(...)` result into a JSON-friendly
347
577
  * downstream adapter artifact.
@@ -349,13 +579,20 @@ function cloneProjectedTypiaLlmFunctionArtifact(functionArtifact) {
349
579
  * @param options Compiled application value and source metadata.
350
580
  * @returns JSON-friendly application artifact.
351
581
  */
352
- export function projectTypiaLlmApplicationArtifact({ application, generatedFrom, transformFunction, }) {
582
+ export function projectTypiaLlmApplicationArtifact({ application, generatedFrom, openApiProjection, transformFunction, }) {
353
583
  return {
354
584
  functions: application.functions.map((functionSchema) => {
355
585
  const functionArtifact = projectTypiaLlmApplicationFunction(functionSchema);
356
- const transformedArtifact = transformFunction
357
- ? transformFunction(functionArtifact, functionSchema)
586
+ const openApiAlignedArtifact = openApiProjection
587
+ ? applyOpenApiConstraintsToTypiaLlmFunctionArtifact({
588
+ functionArtifact,
589
+ openApiDocument: openApiProjection.openApiDocument,
590
+ operationId: openApiProjection.resolveOperationId?.(functionSchema, functionArtifact) ?? functionSchema.name,
591
+ })
358
592
  : functionArtifact;
593
+ const transformedArtifact = transformFunction
594
+ ? transformFunction(openApiAlignedArtifact, functionSchema)
595
+ : openApiAlignedArtifact;
359
596
  return cloneProjectedTypiaLlmFunctionArtifact(transformedArtifact);
360
597
  }),
361
598
  generatedFrom: cloneJsonValue(generatedFrom),
@@ -121,7 +121,9 @@ const WORKSPACE_COMPATIBILITY_CONFIG_FIELD = `\tcompatibility?: {
121
121
  \t\t\twordpress?: string;
122
122
  \t\t};
123
123
  \t\tmode: 'baseline' | 'optional' | 'required';
124
+ \t\toptionalFeatureIds: string[];
124
125
  \t\toptionalFeatures: string[];
126
+ \t\trequiredFeatureIds: string[];
125
127
  \t\trequiredFeatures: string[];
126
128
  \t\truntimeGates: string[];
127
129
  \t};
@@ -670,6 +672,26 @@ function ensureInterfaceField(source, interfaceName, fieldName, fieldSource) {
670
672
  return `${start}${body}${body.length > 0 && !body.endsWith(lineEnding) ? lineEnding : ""}${formattedFieldSource}${end}`;
671
673
  });
672
674
  }
675
+ function normalizeInterfaceFieldBlock(source, interfaceName, fieldName, fieldSource, requiredFragments) {
676
+ const interfacePattern = new RegExp(`(export\\s+interface\\s+${escapeRegex(interfaceName)}\\s*\\{\\r?\\n)([\\s\\S]*?)(\\r?\\n\\})`, "u");
677
+ return source.replace(interfacePattern, (match, start, body, end) => {
678
+ const fieldPattern = new RegExp(`(^([ \\t]*)${escapeRegex(fieldName)}\\??:\\s*\\{[ \\t]*\\r?\\n)([\\s\\S]*?)(^\\2\\};\\r?\\n?)`, "mu");
679
+ const fieldMatch = fieldPattern.exec(body);
680
+ if (!fieldMatch) {
681
+ return match;
682
+ }
683
+ const existingFieldSource = fieldMatch[0];
684
+ if (requiredFragments.every((fragment) => existingFieldSource.includes(fragment))) {
685
+ return match;
686
+ }
687
+ const lineEnding = start.endsWith("\r\n") ? "\r\n" : "\n";
688
+ const formattedFieldSource = `${fieldSource
689
+ .replace(/\r?\n$/u, "")
690
+ .split("\n")
691
+ .join(lineEnding)}${lineEnding}`;
692
+ return `${start}${body.slice(0, fieldMatch.index)}${formattedFieldSource}${body.slice(fieldMatch.index + existingFieldSource.length)}${end}`;
693
+ });
694
+ }
673
695
  /**
674
696
  * Update `scripts/block-config.ts` source text with additional inventory entries.
675
697
  *
@@ -701,7 +723,9 @@ export function updateWorkspaceInventorySource(source, { blockEntries = [], bloc
701
723
  nextSource = ensureInterfaceField(nextSource, "WorkspaceBindingSourceConfig", "attribute", "\tattribute?: string;");
702
724
  nextSource = ensureInterfaceField(nextSource, "WorkspaceBindingSourceConfig", "block", "\tblock?: string;");
703
725
  nextSource = ensureInterfaceField(nextSource, "WorkspaceAbilityConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD);
726
+ nextSource = normalizeInterfaceFieldBlock(nextSource, "WorkspaceAbilityConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD, ["optionalFeatureIds: string[];", "requiredFeatureIds: string[];"]);
704
727
  nextSource = ensureInterfaceField(nextSource, "WorkspaceAiFeatureConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD);
728
+ nextSource = normalizeInterfaceFieldBlock(nextSource, "WorkspaceAiFeatureConfig", "compatibility", WORKSPACE_COMPATIBILITY_CONFIG_FIELD, ["optionalFeatureIds: string[];", "requiredFeatureIds: string[];"]);
705
729
  nextSource = appendEntriesAtMarker(nextSource, EDITOR_PLUGIN_CONFIG_ENTRY_MARKER, editorPluginEntries);
706
730
  return nextSource;
707
731
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-typia/project-tools",
3
- "version": "0.22.1",
3
+ "version": "0.22.3",
4
4
  "description": "Project orchestration and programmatic tooling for wp-typia",
5
5
  "packageManager": "bun@1.3.11",
6
6
  "type": "module",
@@ -105,9 +105,9 @@
105
105
  "prepack": "bun run build && node ./scripts/publish-manifest.mjs prepare",
106
106
  "postpack": "node ./scripts/publish-manifest.mjs restore",
107
107
  "test": "bun run build && bun test tests/*.test.ts",
108
- "test:quick": "bun run build && bun test tests/workspace-add.test.ts tests/workspace-doctor.test.ts tests/scaffold-compound.test.ts",
108
+ "test:quick": "bun run build && bun test tests/workspace-add.test.ts tests/cli-add-workspace-ability.test.ts tests/cli-add-workspace-ai.test.ts tests/workspace-doctor.test.ts tests/scaffold-compound.test.ts",
109
109
  "test:scaffold-core": "bun run build && bun test tests/block-generator-service.test.ts tests/built-in-block-artifacts.test.ts tests/scaffold-basic.test.ts tests/scaffold-persistence.test.ts tests/template-source.test.ts tests/init-command.test.ts tests/package-versions.test.ts tests/cli-entry.test.ts tests/cli-prompt.test.ts tests/import-policy.test.ts tests/wordpress-ai-spec.test.ts tests/typia-llm.test.ts",
110
- "test:workspace": "bun run build && bun test tests/workspace-add.test.ts tests/workspace-doctor.test.ts",
110
+ "test:workspace": "bun run build && bun test tests/workspace-add.test.ts tests/cli-add-workspace-ability.test.ts tests/cli-add-workspace-ai.test.ts tests/workspace-doctor.test.ts",
111
111
  "test:compound": "bun run build && bun test tests/scaffold-compound.test.ts",
112
112
  "test:migration-planning": "bun run build && bun test tests/migration-init.test.ts tests/migration-config.test.ts tests/migration-plan-wizard.test.ts",
113
113
  "test:migration-execution": "bun run build && bun test tests/migration-scaffold-diff.test.ts tests/migration-doctor.test.ts tests/migration-fixtures-fuzz.test.ts",