@wp-typia/project-tools 0.16.12 → 0.16.13

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.
@@ -1,389 +1,2 @@
1
- import { getPackageVersions } from "./package-versions.js";
2
- import { BUILTIN_BLOCK_METADATA_VERSION, COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS, getBuiltInTemplateMetadataDefaults, } from "./template-defaults.js";
3
- import { getTemplateById, } from "./template-registry.js";
4
- import { resolveBuiltInTemplateSource } from "./template-builtins.js";
5
- import { toPascalCase, toSnakeCase, } from "./string-case.js";
6
- import { applyBuiltInScaffoldProjectFiles, buildGitignore, buildReadme, } from "./scaffold-apply-utils.js";
7
- import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from "./scaffold-identifiers.js";
8
- import { buildBuiltInBlockArtifacts, } from "./built-in-block-artifacts.js";
9
- import { buildBuiltInCodeArtifacts, } from "./built-in-block-code-artifacts.js";
10
- import { stableJsonStringify } from "./object-utils.js";
11
- import { getStarterManifestFiles } from "./starter-manifests.js";
12
- import { resolveTemplateSeed, parseTemplateLocator } from "./template-source.js";
13
- import { assertExternalTemplateLayersDoNotWriteProtectedOutputs, resolveExternalTemplateLayers, } from "./template-layers.js";
14
- import { getBuiltInTemplateOverlayDir, getBuiltInTemplateSharedLayerDirs, resolveBuiltInTemplateSourceFromLayerDirs, } from "./template-builtins.js";
15
- const renderedArtifactCache = new WeakMap();
16
- function createVariablesFingerprint(variables) {
17
- return stableJsonStringify(variables);
18
- }
19
- function buildProtectedTemplateOutputPaths({ codeArtifacts, spec, variables, artifacts, }) {
20
- const protectedOutputs = new Set([
21
- ".gitignore",
22
- "package.json",
23
- "scripts/add-compound-child.ts",
24
- "scripts/block-config.ts",
25
- "scripts/sync-project.ts",
26
- "scripts/sync-rest-contracts.ts",
27
- "scripts/sync-types-to-block-json.ts",
28
- "tsconfig.json",
29
- "webpack.config.js",
30
- `${variables.slugKebabCase}.php`,
31
- ]);
32
- for (const artifact of codeArtifacts) {
33
- protectedOutputs.add(artifact.relativePath);
34
- }
35
- for (const artifact of artifacts) {
36
- protectedOutputs.add(`${artifact.relativeDir}/block.json`);
37
- protectedOutputs.add(`${artifact.relativeDir}/types.ts`);
38
- }
39
- for (const manifest of getStarterManifestFiles(spec.template.family, variables)) {
40
- protectedOutputs.add(manifest.relativePath);
41
- }
42
- return protectedOutputs;
43
- }
44
- function buildCombinedTemplateLayerDirs({ baseLayerDirs, externalEntries, templateId, }) {
45
- const orderedLayerDirs = [];
46
- const seenLayerDirs = new Set();
47
- for (const layerDir of baseLayerDirs) {
48
- if (seenLayerDirs.has(layerDir)) {
49
- continue;
50
- }
51
- orderedLayerDirs.push(layerDir);
52
- seenLayerDirs.add(layerDir);
53
- }
54
- for (const entry of externalEntries) {
55
- if (seenLayerDirs.has(entry.dir)) {
56
- continue;
57
- }
58
- orderedLayerDirs.push(entry.dir);
59
- seenLayerDirs.add(entry.dir);
60
- }
61
- const overlayDir = getBuiltInTemplateOverlayDir(templateId);
62
- if (!seenLayerDirs.has(overlayDir)) {
63
- orderedLayerDirs.push(overlayDir);
64
- }
65
- return orderedLayerDirs;
66
- }
67
- function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePolicy, }) {
68
- if (templateId === "persistence") {
69
- return {
70
- dataStorageMode: dataStorageMode ?? "custom-table",
71
- enabled: true,
72
- persistencePolicy: persistencePolicy ?? "authenticated",
73
- scope: "single",
74
- };
75
- }
76
- if (templateId === "compound" && (dataStorageMode || persistencePolicy)) {
77
- return {
78
- dataStorageMode: dataStorageMode ?? "custom-table",
79
- enabled: true,
80
- persistencePolicy: persistencePolicy ?? "authenticated",
81
- scope: "compound-parent",
82
- };
83
- }
84
- return {
85
- enabled: false,
86
- };
87
- }
88
- export function createBuiltInBlockSpec({ answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
89
- const template = getTemplateById(templateId);
90
- const metadataDefaults = getBuiltInTemplateMetadataDefaults(templateId);
91
- const identifiers = resolveScaffoldIdentifiers({
92
- namespace: answers.namespace,
93
- phpPrefix: answers.phpPrefix,
94
- slug: answers.slug,
95
- textDomain: answers.textDomain,
96
- });
97
- const resolvedDataStorageMode = dataStorageMode ?? answers.dataStorageMode;
98
- const resolvedPersistencePolicy = persistencePolicy ?? answers.persistencePolicy;
99
- return {
100
- block: identifiers,
101
- metadata: {
102
- category: metadataDefaults.category,
103
- description: answers.description.trim(),
104
- icon: metadataDefaults.icon,
105
- keyword: identifiers.slug.replace(/-/g, " "),
106
- title: answers.title.trim(),
107
- },
108
- persistence: getBuiltInPersistenceSpec({
109
- dataStorageMode: resolvedDataStorageMode,
110
- persistencePolicy: resolvedPersistencePolicy,
111
- templateId,
112
- }),
113
- project: {
114
- author: answers.author.trim(),
115
- },
116
- runtime: {
117
- withMigrationUi,
118
- withTestPreset,
119
- withWpEnv,
120
- },
121
- template: {
122
- description: template.description,
123
- family: templateId,
124
- features: [...template.features],
125
- },
126
- };
127
- }
128
- export function buildTemplateVariablesFromBlockSpec(spec) {
129
- const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
130
- const slug = spec.block.slug;
131
- const slugSnakeCase = toSnakeCase(slug);
132
- const pascalCase = toPascalCase(slug);
133
- const title = spec.metadata.title;
134
- const namespace = spec.block.namespace;
135
- const textDomain = spec.block.textDomain;
136
- const phpPrefix = spec.block.phpPrefix;
137
- const phpPrefixUpper = phpPrefix.toUpperCase();
138
- const compoundChildTitle = `${title} Item`;
139
- const cssClassName = buildBlockCssClassName(namespace, slug);
140
- const compoundChildCssClassName = buildBlockCssClassName(namespace, `${slug}-item`);
141
- const persistenceEnabled = spec.persistence.enabled;
142
- const dataStorageMode = persistenceEnabled ? spec.persistence.dataStorageMode : "custom-table";
143
- const persistencePolicy = persistenceEnabled
144
- ? spec.persistence.persistencePolicy
145
- : "authenticated";
146
- return {
147
- apiClientPackageVersion,
148
- author: spec.project.author,
149
- blockRuntimePackageVersion,
150
- blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
151
- blockTypesPackageVersion,
152
- category: spec.metadata.category,
153
- icon: spec.metadata.icon,
154
- compoundChildTitle,
155
- compoundChildCategory: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.category,
156
- compoundChildCssClassName,
157
- compoundChildIcon: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.icon,
158
- compoundChildTitleJson: JSON.stringify(compoundChildTitle),
159
- compoundPersistenceEnabled: spec.template.family === "compound" && persistenceEnabled ? "true" : "false",
160
- projectToolsPackageVersion,
161
- cssClassName,
162
- dashCase: slug,
163
- dataStorageMode,
164
- description: spec.metadata.description,
165
- frontendCssClassName: buildFrontendCssClassName(cssClassName),
166
- isAuthenticatedPersistencePolicy: persistencePolicy === "authenticated" ? "true" : "false",
167
- isPublicPersistencePolicy: persistencePolicy === "public" ? "true" : "false",
168
- bootstrapCredentialDeclarations: persistencePolicy === "public"
169
- ? "publicWriteExpiresAt?: number & tags.Type< 'uint32' >;\n\tpublicWriteToken?: string & tags.MinLength< 1 > & tags.MaxLength< 512 >;"
170
- : "restNonce?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
171
- persistencePolicyDescriptionJson: JSON.stringify(persistencePolicy === "authenticated"
172
- ? "Writes require a logged-in user and a valid REST nonce."
173
- : "Anonymous writes use signed short-lived public tokens, per-request ids, and coarse rate limiting."),
174
- keyword: spec.metadata.keyword,
175
- namespace,
176
- needsMigration: "{{needsMigration}}",
177
- pascalCase,
178
- phpPrefix,
179
- phpPrefixUpper,
180
- restPackageVersion,
181
- publicWriteRequestIdDeclaration: persistencePolicy === "public"
182
- ? "publicWriteRequestId: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;"
183
- : "publicWriteRequestId?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
184
- restWriteAuthIntent: persistencePolicy === "public"
185
- ? "public-write-protected"
186
- : "authenticated",
187
- restWriteAuthMechanism: persistencePolicy === "public" ? "public-signed-token" : "rest-nonce",
188
- restWriteAuthMode: persistencePolicy === "public" ? "public-signed-token" : "authenticated-rest-nonce",
189
- slug,
190
- slugCamelCase: pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1),
191
- slugKebabCase: slug,
192
- slugSnakeCase,
193
- textDomain,
194
- textdomain: textDomain,
195
- title,
196
- titleJson: JSON.stringify(title),
197
- titleCase: pascalCase,
198
- persistencePolicy,
199
- };
200
- }
201
- export class BlockGeneratorService {
202
- async plan({ allowExistingDir = false, answers, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall = false, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
203
- return {
204
- spec: createBuiltInBlockSpec({
205
- answers,
206
- dataStorageMode,
207
- persistencePolicy,
208
- templateId,
209
- withMigrationUi,
210
- withTestPreset,
211
- withWpEnv,
212
- }),
213
- target: {
214
- allowExistingDir,
215
- cwd,
216
- externalLayerId,
217
- externalLayerSource,
218
- externalLayerSourceLabel,
219
- noInstall,
220
- packageManager,
221
- projectDir,
222
- repositoryReference,
223
- variant,
224
- },
225
- };
226
- }
227
- async validate({ plan }) {
228
- if (plan.target.externalLayerId && !plan.target.externalLayerSource) {
229
- throw new Error("externalLayerId requires externalLayerSource when composing built-in template layers.");
230
- }
231
- if (plan.target.variant) {
232
- throw new Error(`--variant is only supported for official external template configs. Received variant "${plan.target.variant}" for built-in template "${plan.spec.template.family}".`);
233
- }
234
- return plan;
235
- }
236
- async render({ validated }) {
237
- const variables = buildTemplateVariablesFromBlockSpec(validated.spec);
238
- const persistenceEnabled = validated.spec.persistence.enabled;
239
- const artifacts = buildBuiltInBlockArtifacts({
240
- templateId: validated.spec.template.family,
241
- variables,
242
- });
243
- const codeArtifacts = buildBuiltInCodeArtifacts({
244
- templateId: validated.spec.template.family,
245
- variables,
246
- });
247
- const templateVariantOptions = {
248
- persistenceEnabled,
249
- persistencePolicy: validated.spec.persistence.enabled &&
250
- validated.spec.persistence.persistencePolicy === "public"
251
- ? "public"
252
- : "authenticated",
253
- };
254
- let templateSource = await resolveBuiltInTemplateSource(validated.spec.template.family, templateVariantOptions);
255
- const warnings = [...(templateSource.warnings ?? [])];
256
- if (validated.target.externalLayerSource) {
257
- let layerSeed;
258
- try {
259
- layerSeed = await resolveTemplateSeed(parseTemplateLocator(validated.target.externalLayerSource), validated.target.cwd);
260
- const resolvedLayers = await resolveExternalTemplateLayers({
261
- externalLayerId: validated.target.externalLayerId,
262
- sourceRoot: layerSeed.rootDir,
263
- });
264
- const baseLayerDirs = getBuiltInTemplateSharedLayerDirs(validated.spec.template.family, templateVariantOptions);
265
- await assertExternalTemplateLayersDoNotWriteProtectedOutputs({
266
- externalEntries: resolvedLayers.entries,
267
- protectedOutputPaths: buildProtectedTemplateOutputPaths({
268
- artifacts,
269
- codeArtifacts,
270
- spec: validated.spec,
271
- variables,
272
- }),
273
- view: variables,
274
- });
275
- await templateSource.cleanup?.();
276
- templateSource = await resolveBuiltInTemplateSourceFromLayerDirs(validated.spec.template.family, buildCombinedTemplateLayerDirs({
277
- baseLayerDirs,
278
- externalEntries: resolvedLayers.entries,
279
- templateId: validated.spec.template.family,
280
- }));
281
- const layerSourceCleanup = layerSeed.cleanup;
282
- const templateCleanup = templateSource.cleanup;
283
- templateSource.cleanup = async () => {
284
- const cleanupErrors = [];
285
- try {
286
- await templateCleanup?.();
287
- }
288
- catch (error) {
289
- cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
290
- }
291
- try {
292
- await layerSourceCleanup?.();
293
- }
294
- catch (error) {
295
- cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
296
- }
297
- if (cleanupErrors.length > 0) {
298
- throw new Error([
299
- "Failed to cleanup composed template sources.",
300
- ...cleanupErrors.map((error) => `- ${error.message}`),
301
- ].join("\n"));
302
- }
303
- };
304
- warnings.push(`Applied external layer "${resolvedLayers.selectedLayerId}" from "${validated.target.externalLayerSourceLabel ?? validated.target.externalLayerSource}".`);
305
- }
306
- catch (error) {
307
- await templateSource.cleanup?.();
308
- await layerSeed?.cleanup?.();
309
- throw error;
310
- }
311
- }
312
- const rendered = {
313
- ...validated,
314
- cleanup: templateSource.cleanup,
315
- gitignoreContent: buildGitignore(),
316
- postRender: {
317
- applyLocalDevPresets: true,
318
- applyMigrationUiCapability: validated.spec.runtime.withMigrationUi,
319
- seedPersistenceArtifacts: validated.spec.template.family === "persistence" ||
320
- (validated.spec.template.family === "compound" && persistenceEnabled),
321
- seedStarterManifestFiles: true,
322
- },
323
- readmeContent: buildReadme(validated.spec.template.family, variables, validated.target.packageManager, {
324
- withMigrationUi: validated.spec.runtime.withMigrationUi,
325
- withTestPreset: validated.spec.runtime.withTestPreset,
326
- withWpEnv: validated.spec.runtime.withWpEnv,
327
- }),
328
- selectedVariant: null,
329
- templateDir: templateSource.templateDir,
330
- variables,
331
- warnings,
332
- };
333
- renderedArtifactCache.set(rendered, {
334
- artifacts,
335
- codeArtifacts,
336
- variablesFingerprint: createVariablesFingerprint(variables),
337
- });
338
- return rendered;
339
- }
340
- async apply({ rendered, installDependencies, }) {
341
- const cachedArtifacts = renderedArtifactCache.get(rendered);
342
- const currentVariablesFingerprint = createVariablesFingerprint(rendered.variables);
343
- const artifacts = cachedArtifacts &&
344
- cachedArtifacts.variablesFingerprint === currentVariablesFingerprint
345
- ? cachedArtifacts.artifacts
346
- : buildBuiltInBlockArtifacts({
347
- templateId: rendered.spec.template.family,
348
- variables: rendered.variables,
349
- });
350
- const codeArtifacts = cachedArtifacts &&
351
- cachedArtifacts.variablesFingerprint === currentVariablesFingerprint
352
- ? cachedArtifacts.codeArtifacts
353
- : buildBuiltInCodeArtifacts({
354
- templateId: rendered.spec.template.family,
355
- variables: rendered.variables,
356
- });
357
- try {
358
- await applyBuiltInScaffoldProjectFiles({
359
- allowExistingDir: rendered.target.allowExistingDir,
360
- artifacts,
361
- codeArtifacts,
362
- installDependencies,
363
- noInstall: rendered.target.noInstall,
364
- packageManager: rendered.target.packageManager,
365
- projectDir: rendered.target.projectDir,
366
- repositoryReference: rendered.target.repositoryReference,
367
- gitignoreContent: rendered.gitignoreContent,
368
- readmeContent: rendered.readmeContent,
369
- templateDir: rendered.templateDir,
370
- templateId: rendered.spec.template.family,
371
- variables: rendered.variables,
372
- withMigrationUi: rendered.spec.runtime.withMigrationUi,
373
- withTestPreset: rendered.spec.runtime.withTestPreset,
374
- withWpEnv: rendered.spec.runtime.withWpEnv,
375
- });
376
- }
377
- finally {
378
- await rendered.cleanup?.();
379
- }
380
- return {
381
- packageManager: rendered.target.packageManager,
382
- projectDir: rendered.target.projectDir,
383
- selectedVariant: rendered.selectedVariant,
384
- templateId: rendered.spec.template.family,
385
- variables: rendered.variables,
386
- warnings: rendered.warnings,
387
- };
388
- }
389
- }
1
+ export * from "./block-generator-service-spec.js";
2
+ export * from "./block-generator-service-core.js";
@@ -4,12 +4,84 @@ const DEFAULT_CLI_FAILURE_SUMMARIES = {
4
4
  doctor: "One or more doctor checks failed.",
5
5
  migrate: "Unable to complete the requested migration command.",
6
6
  };
7
+ const MIN_CLI_WRAP_COLUMNS = 32;
8
+ function parseCliColumns(value) {
9
+ if (typeof value !== "string" || value.trim().length === 0) {
10
+ return null;
11
+ }
12
+ const parsed = Number.parseInt(value, 10);
13
+ return Number.isFinite(parsed) && parsed >= MIN_CLI_WRAP_COLUMNS ? parsed : null;
14
+ }
15
+ function resolveCliWrapColumns(streamColumns) {
16
+ return parseCliColumns(process.env.COLUMNS) ??
17
+ (typeof streamColumns === "number" && streamColumns >= MIN_CLI_WRAP_COLUMNS
18
+ ? streamColumns
19
+ : null);
20
+ }
21
+ function wrapCliText(text, maxWidth) {
22
+ const words = text.trim().split(/\s+/u).filter((word) => word.length > 0);
23
+ if (words.length === 0) {
24
+ return [""];
25
+ }
26
+ const lines = [];
27
+ let currentLine = words[0] ?? "";
28
+ for (const word of words.slice(1)) {
29
+ const nextLine = `${currentLine} ${word}`;
30
+ if (nextLine.length <= maxWidth) {
31
+ currentLine = nextLine;
32
+ continue;
33
+ }
34
+ lines.push(currentLine);
35
+ currentLine = word;
36
+ }
37
+ lines.push(currentLine);
38
+ return lines;
39
+ }
40
+ function formatWrappedPrefixedLine(prefix, text, columns, continuationIndent = " ") {
41
+ const singleLine = `${prefix}${text}`;
42
+ if (columns === null || singleLine.length <= columns) {
43
+ return [singleLine];
44
+ }
45
+ const words = text.trim().split(/\s+/u).filter((word) => word.length > 0);
46
+ if (words.length === 0) {
47
+ return [prefix.trimEnd()];
48
+ }
49
+ const continuationWidth = Math.max(1, columns - continuationIndent.length);
50
+ const firstLineWidth = columns - prefix.length;
51
+ if (firstLineWidth <= 0 || (words[0]?.length ?? 0) > firstLineWidth) {
52
+ return [
53
+ prefix.trimEnd(),
54
+ ...wrapCliText(text, continuationWidth).map((line) => `${continuationIndent}${line}`),
55
+ ];
56
+ }
57
+ const lines = [];
58
+ let currentPrefix = prefix;
59
+ let currentWidth = Math.max(1, columns - currentPrefix.length);
60
+ let currentLine = words[0] ?? "";
61
+ for (const word of words.slice(1)) {
62
+ const nextLine = `${currentLine} ${word}`;
63
+ if (nextLine.length <= currentWidth) {
64
+ currentLine = nextLine;
65
+ continue;
66
+ }
67
+ lines.push(`${currentPrefix}${currentLine}`);
68
+ currentPrefix = continuationIndent;
69
+ currentWidth = continuationWidth;
70
+ currentLine = word;
71
+ }
72
+ lines.push(`${currentPrefix}${currentLine}`);
73
+ return lines;
74
+ }
7
75
  function formatCliDiagnosticBlock(message) {
8
- const lines = [`wp-typia ${message.command} failed`, `Summary: ${message.summary}`];
76
+ const columns = resolveCliWrapColumns(process.stderr.columns);
77
+ const lines = [
78
+ `wp-typia ${message.command} failed`,
79
+ ...formatWrappedPrefixedLine("Summary: ", message.summary, columns),
80
+ ];
9
81
  if (message.detailLines.length > 0) {
10
82
  lines.push("Details:");
11
83
  for (const detailLine of message.detailLines) {
12
- lines.push(`- ${detailLine}`);
84
+ lines.push(...formatWrappedPrefixedLine("- ", detailLine, columns));
13
85
  }
14
86
  }
15
87
  return lines.join("\n");
@@ -77,7 +149,7 @@ export function formatCliDiagnosticError(error) {
77
149
  * Format one human-readable doctor check row.
78
150
  */
79
151
  export function formatDoctorCheckLine(check) {
80
- return `${check.status === "pass" ? "PASS" : "FAIL"} ${check.label}: ${check.detail}`;
152
+ return formatWrappedPrefixedLine(`${check.status === "pass" ? "PASS" : "FAIL"} ${check.label}: `, check.detail, resolveCliWrapColumns(process.stdout.columns)).join("\n");
81
153
  }
82
154
  /**
83
155
  * Return the failing doctor checks from one doctor run.
@@ -90,7 +162,7 @@ export function getFailingDoctorChecks(checks) {
90
162
  */
91
163
  export function formatDoctorSummaryLine(checks) {
92
164
  const failedChecks = getFailingDoctorChecks(checks);
93
- return `${failedChecks.length === 0 ? "PASS" : "FAIL"} wp-typia doctor summary: ${checks.length - failedChecks.length}/${checks.length} checks passed`;
165
+ return formatWrappedPrefixedLine(`${failedChecks.length === 0 ? "PASS" : "FAIL"} wp-typia doctor summary: `, `${checks.length - failedChecks.length}/${checks.length} checks passed`, resolveCliWrapColumns(process.stdout.columns)).join("\n");
94
166
  }
95
167
  /**
96
168
  * Build detail lines for doctor failures so the non-interactive formatter can
@@ -2,11 +2,15 @@ import type { DoctorCheck } from "./cli-doctor.js";
2
2
  /**
3
3
  * Collect workspace-scoped doctor checks for the given working directory.
4
4
  *
5
- * When the directory is not an official workspace, the function returns an
6
- * empty array or a single failing "Workspace package metadata" row describing
7
- * the reason. When workspace resolution or metadata parsing throws, the
8
- * corresponding failing row is returned early and the remaining checks are
9
- * skipped.
5
+ * When the directory is not an official workspace, the function returns a
6
+ * "Doctor scope" row explaining that only environment checks ran, plus a
7
+ * failing workspace metadata row when a nearby candidate workspace is invalid.
8
+ * When workspace resolution or metadata parsing throws, the corresponding
9
+ * failing rows are returned early and the remaining checks are skipped.
10
+ * When an official workspace is detected, a passing "Doctor scope" row is
11
+ * 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.
10
14
  *
11
15
  * @param cwd Working directory expected to host an official workspace.
12
16
  * @returns Ordered workspace check rows ready for CLI rendering.
@@ -19,6 +19,9 @@ const WORKSPACE_GENERATED_BLOCK_ARTIFACTS = [
19
19
  function createDoctorCheck(label, status, detail) {
20
20
  return { detail, label, status };
21
21
  }
22
+ function createDoctorScopeCheck(status, detail) {
23
+ return createDoctorCheck("Doctor scope", status, detail);
24
+ }
22
25
  function getWorkspaceBootstrapRelativePath(packageName) {
23
26
  const packageBaseName = packageName.split("/").pop() ?? packageName;
24
27
  return `${packageBaseName}.php`;
@@ -207,11 +210,15 @@ function checkMigrationWorkspaceHint(workspace, packageJson) {
207
210
  /**
208
211
  * Collect workspace-scoped doctor checks for the given working directory.
209
212
  *
210
- * When the directory is not an official workspace, the function returns an
211
- * empty array or a single failing "Workspace package metadata" row describing
212
- * the reason. When workspace resolution or metadata parsing throws, the
213
- * corresponding failing row is returned early and the remaining checks are
214
- * skipped.
213
+ * When the directory is not an official workspace, the function returns a
214
+ * "Doctor scope" row explaining that only environment checks ran, plus a
215
+ * failing workspace metadata row when a nearby candidate workspace is invalid.
216
+ * When workspace resolution or metadata parsing throws, the corresponding
217
+ * failing rows are returned early and the remaining checks are skipped.
218
+ * When an official workspace is detected, a passing "Doctor scope" row is
219
+ * emitted first so the remaining package metadata, inventory, source-tree
220
+ * drift, and optional migration hint rows are clearly framed as workspace
221
+ * diagnostics for that run.
215
222
  *
216
223
  * @param cwd Working directory expected to host an official workspace.
217
224
  * @returns Ordered workspace check rows ready for CLI rendering.
@@ -225,16 +232,21 @@ export function getWorkspaceDoctorChecks(cwd) {
225
232
  workspace = tryResolveWorkspaceProject(cwd);
226
233
  }
227
234
  catch (error) {
235
+ checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace discovery could not continue. Fix the nearby workspace package metadata and rerun `wp-typia doctor`."));
228
236
  checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
229
237
  return checks;
230
238
  }
231
239
  if (!workspace) {
232
240
  if (invalidWorkspaceReason) {
241
+ checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace diagnostics could not continue because a nearby wp-typia workspace candidate is invalid. Fix the workspace package metadata and rerun `wp-typia doctor`."));
233
242
  checks.push(createDoctorCheck("Workspace package metadata", "fail", invalidWorkspaceReason));
234
243
  }
244
+ else {
245
+ checks.push(createDoctorScopeCheck("pass", "No official wp-typia workspace root was detected, so this run only covered environment readiness. Re-run `wp-typia doctor` from a workspace root if you expected package metadata, inventory, or generated artifact checks."));
246
+ }
235
247
  return checks;
236
248
  }
237
- checks.push(createDoctorCheck("Workspace marker", "pass", `Official workspace detected for ${workspace.workspace.namespace}`));
249
+ checks.push(createDoctorScopeCheck("pass", `Official workspace detected for ${workspace.workspace.namespace}; environment readiness checks ran and workspace-scoped diagnostics are enabled for the package metadata, inventory, source-tree drift, and any configured migration hint rows below.`));
238
250
  let workspacePackageJson;
239
251
  try {
240
252
  workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
@@ -39,7 +39,7 @@ Notes:
39
39
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
40
40
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
41
41
  \`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
42
- \`wp-typia doctor\` checks environment readiness plus workspace inventory and source-tree drift.
42
+ \`wp-typia doctor\` always checks environment readiness and reports when it only ran environment-level diagnostics; official workspace roots also get inventory and source-tree drift checks.
43
43
  \`wp-typia migrate doctor --all\` checks migration target alignment, snapshots, fixtures, and generated migration artifacts.
44
44
  \`migrate\` is the canonical migration command; \`migrations\` is no longer supported.`;
45
45
  }
@@ -78,7 +78,14 @@ export declare function runScaffoldFlow({ projectInput, cwd, templateId, dataSto
78
78
  projectDir: string;
79
79
  projectInput: string;
80
80
  packageManager: PackageManagerId;
81
- result: import("./scaffold.js").ScaffoldProjectResult;
82
81
  nextSteps: string[];
82
+ result: {
83
+ warnings: string[];
84
+ packageManager: PackageManagerId;
85
+ projectDir: string;
86
+ selectedVariant: string | null;
87
+ templateId: string;
88
+ variables: import("./scaffold.js").ScaffoldTemplateVariables;
89
+ };
83
90
  }>;
84
91
  export {};