wp-typia 0.20.5 → 0.22.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.
@@ -3,7 +3,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "wp-typia",
6
- version: "0.20.5",
6
+ version: "0.22.0",
7
7
  description: "Canonical CLI package for wp-typia scaffolding and project workflows",
8
8
  packageManager: "bun@1.3.11",
9
9
  type: "module",
@@ -17,7 +17,9 @@ var package_default = {
17
17
  "package.json"
18
18
  ],
19
19
  scripts: {
20
- generate: "bun scripts/generate-bunli-metadata.ts",
20
+ "generate:routing": "node scripts/generate-routing-metadata.mjs",
21
+ "validate:routing": "node scripts/generate-routing-metadata.mjs --check",
22
+ generate: "node scripts/generate-routing-metadata.mjs && bun scripts/generate-bunli-metadata.ts",
21
23
  build: "bun run generate && bun scripts/build-bunli-runtime.ts",
22
24
  "build:standalone": "bun scripts/build-standalone-runtime.ts --targets native --outdir ./dist-standalone",
23
25
  "build:standalone:release": "bun scripts/build-standalone-runtime.ts --targets darwin-arm64,darwin-x64,linux-arm64,linux-x64,windows-x64 --outdir ./.cache/standalone/raw",
@@ -71,7 +73,7 @@ var package_default = {
71
73
  "@bunli/tui": "0.6.0",
72
74
  "@bunli/utils": "0.6.0",
73
75
  "@wp-typia/api-client": "^0.4.5",
74
- "@wp-typia/project-tools": "0.20.2",
76
+ "@wp-typia/project-tools": "0.22.0",
75
77
  "better-result": "^2.7.0",
76
78
  react: "^19.2.5",
77
79
  "react-dom": "^19.2.5",
@@ -95,8 +97,9 @@ var package_default = {
95
97
 
96
98
  // src/node-cli.ts
97
99
  import {
98
- CLI_DIAGNOSTIC_CODES,
100
+ CLI_DIAGNOSTIC_CODES as CLI_DIAGNOSTIC_CODES5,
99
101
  createCliCommandError,
102
+ createCliDiagnosticCodeError as createCliDiagnosticCodeError5,
100
103
  formatCliDiagnosticError,
101
104
  serializeCliDiagnosticError
102
105
  } from "@wp-typia/project-tools/cli-diagnostics";
@@ -199,8 +202,12 @@ var ADD_OPTION_METADATA = {
199
202
  description: "Anchor block name for hooked-block workflows.",
200
203
  type: "string"
201
204
  },
205
+ attribute: {
206
+ description: "Target block attribute for end-to-end binding-source workflows.",
207
+ type: "string"
208
+ },
202
209
  block: {
203
- description: "Target block slug for variation workflows.",
210
+ description: "Target block slug for variation, style, and end-to-end binding-source workflows.",
204
211
  type: "string"
205
212
  },
206
213
  "data-storage": {
@@ -220,6 +227,10 @@ var ADD_OPTION_METADATA = {
220
227
  description: "Local path, GitHub locator, or npm package that exposes wp-typia.layers.json for built-in block templates.",
221
228
  type: "string"
222
229
  },
230
+ from: {
231
+ description: "Source full block name (namespace/block) for transform workflows.",
232
+ type: "string"
233
+ },
223
234
  "inner-blocks-preset": {
224
235
  description: "Compound-only InnerBlocks preset (freeform, ordered, horizontal, locked-structure).",
225
236
  type: "string"
@@ -241,11 +252,31 @@ var ADD_OPTION_METADATA = {
241
252
  type: "string"
242
253
  },
243
254
  slot: {
244
- description: "Document editor shell slot for editor-plugin workflows.",
255
+ description: "Document editor shell slot for editor-plugin workflows (sidebar or document-setting-panel).",
256
+ type: "string"
257
+ },
258
+ source: {
259
+ description: "Optional data source locator for admin-view workflows, such as rest-resource:products or core-data:postType/post.",
245
260
  type: "string"
246
261
  },
247
262
  template: {
248
- description: "Built-in block family for the new block.",
263
+ description: "Optional built-in block family for the new block; interactive flows let you choose it when omitted and non-interactive runs default to basic.",
264
+ type: "string"
265
+ },
266
+ to: {
267
+ description: "Target workspace block slug or full block name for transform workflows.",
268
+ type: "string"
269
+ }
270
+ };
271
+ var INIT_OPTION_METADATA = {
272
+ apply: {
273
+ argumentKind: "flag",
274
+ description: "Write the planned package.json updates and retrofit helper files instead of previewing only.",
275
+ type: "boolean"
276
+ },
277
+ "package-manager": {
278
+ description: "Package manager to use for emitted scripts and next steps.",
279
+ short: "p",
249
280
  type: "string"
250
281
  }
251
282
  };
@@ -297,6 +328,12 @@ var SYNC_OPTION_METADATA = {
297
328
  type: "boolean"
298
329
  }
299
330
  };
331
+ var DOCTOR_OPTION_METADATA = {
332
+ format: {
333
+ description: "Use `json` for machine-readable doctor check output.",
334
+ type: "string"
335
+ }
336
+ };
300
337
  var TEMPLATES_OPTION_METADATA = {
301
338
  id: {
302
339
  description: "Template id for `templates inspect`.",
@@ -322,6 +359,30 @@ var GLOBAL_OPTION_METADATA = {
322
359
  type: "string"
323
360
  }
324
361
  };
362
+ var COMMAND_OPTION_METADATA_BY_GROUP = {
363
+ add: ADD_OPTION_METADATA,
364
+ create: CREATE_OPTION_METADATA,
365
+ doctor: DOCTOR_OPTION_METADATA,
366
+ global: GLOBAL_OPTION_METADATA,
367
+ init: INIT_OPTION_METADATA,
368
+ migrate: MIGRATE_OPTION_METADATA,
369
+ sync: SYNC_OPTION_METADATA,
370
+ templates: TEMPLATES_OPTION_METADATA
371
+ };
372
+ var COMMAND_OPTION_GROUP_NAMES = Object.keys(COMMAND_OPTION_METADATA_BY_GROUP);
373
+ function collectCommandOptionMetadata(...groupNames) {
374
+ const metadata = {};
375
+ for (const groupName of groupNames) {
376
+ for (const [optionName, option] of Object.entries(COMMAND_OPTION_METADATA_BY_GROUP[groupName])) {
377
+ metadata[optionName] = {
378
+ ...metadata[optionName] ?? {},
379
+ ...option
380
+ };
381
+ }
382
+ }
383
+ return metadata;
384
+ }
385
+ var ALL_COMMAND_OPTION_METADATA = collectCommandOptionMetadata(...COMMAND_OPTION_GROUP_NAMES);
325
386
  function collectOptionNamesByType(metadata, type) {
326
387
  return Object.entries(metadata).filter(([, option]) => option.type === type).map(([name]) => name);
327
388
  }
@@ -332,13 +393,99 @@ function formatNodeFallbackOptionHelp(metadata) {
332
393
  });
333
394
  }
334
395
  function buildCommandOptionParser(...metadataMaps) {
335
- const metadata = Object.assign({}, ...metadataMaps);
396
+ const metadata = {};
397
+ for (const metadataMap of metadataMaps) {
398
+ for (const [optionName, option] of Object.entries(metadataMap)) {
399
+ metadata[optionName] = {
400
+ ...metadata[optionName] ?? {},
401
+ ...option
402
+ };
403
+ }
404
+ }
336
405
  return {
337
406
  booleanOptionNames: new Set(collectOptionNamesByType(metadata, "boolean")),
338
407
  shortFlagMap: new Map(Object.entries(metadata).flatMap(([name, option]) => option.short ? [[option.short, { name, type: option.type }]] : [])),
339
408
  stringOptionNames: new Set(collectOptionNamesByType(metadata, "string"))
340
409
  };
341
410
  }
411
+ function buildArgvWalkerRoutingMetadata(...metadataMaps) {
412
+ const parser = buildCommandOptionParser(...metadataMaps);
413
+ return {
414
+ longValueOptions: Array.from(parser.stringOptionNames).map((optionName) => `--${optionName}`).sort((left, right) => left.localeCompare(right)),
415
+ shortValueOptions: Array.from(parser.shortFlagMap.entries()).filter(([, option]) => option.type === "string").map(([short]) => `-${short}`).sort((left, right) => left.localeCompare(right))
416
+ };
417
+ }
418
+ var COMMAND_ROUTING_METADATA = buildArgvWalkerRoutingMetadata(ALL_COMMAND_OPTION_METADATA);
419
+ function extractKnownOptionValuesFromArgv(argv, options) {
420
+ const flags = {};
421
+ const nextArgv = [];
422
+ const optionNames = new Set(options.optionNames);
423
+ for (let index = 0;index < argv.length; index += 1) {
424
+ const arg = argv[index];
425
+ if (!arg) {
426
+ continue;
427
+ }
428
+ if (arg === "--") {
429
+ nextArgv.push(...argv.slice(index));
430
+ break;
431
+ }
432
+ if (arg.length === 2 && arg.startsWith("-")) {
433
+ const shortFlag = options.parser.shortFlagMap.get(arg.slice(1));
434
+ if (!shortFlag || !optionNames.has(shortFlag.name)) {
435
+ nextArgv.push(arg);
436
+ continue;
437
+ }
438
+ if (shortFlag.type === "boolean") {
439
+ flags[shortFlag.name] = true;
440
+ continue;
441
+ }
442
+ const next = argv[index + 1];
443
+ if (!next || next.startsWith("-")) {
444
+ throw new Error(`\`${arg}\` requires a value.`);
445
+ }
446
+ flags[shortFlag.name] = next;
447
+ index += 1;
448
+ continue;
449
+ }
450
+ if (arg.startsWith("--")) {
451
+ const option = arg.slice(2);
452
+ const separatorIndex = option.indexOf("=");
453
+ const rawName = separatorIndex === -1 ? option : option.slice(0, separatorIndex);
454
+ const inlineValue = separatorIndex === -1 ? undefined : option.slice(separatorIndex + 1);
455
+ if (!optionNames.has(rawName)) {
456
+ nextArgv.push(arg);
457
+ continue;
458
+ }
459
+ if (options.parser.booleanOptionNames.has(rawName)) {
460
+ flags[rawName] = true;
461
+ continue;
462
+ }
463
+ if (!options.parser.stringOptionNames.has(rawName)) {
464
+ nextArgv.push(arg);
465
+ continue;
466
+ }
467
+ if (inlineValue !== undefined) {
468
+ if (!inlineValue) {
469
+ throw new Error(`\`--${rawName}\` requires a value.`);
470
+ }
471
+ flags[rawName] = inlineValue;
472
+ continue;
473
+ }
474
+ const next = argv[index + 1];
475
+ if (!next || next.startsWith("-")) {
476
+ throw new Error(`\`--${rawName}\` requires a value.`);
477
+ }
478
+ flags[rawName] = next;
479
+ index += 1;
480
+ continue;
481
+ }
482
+ nextArgv.push(arg);
483
+ }
484
+ return {
485
+ argv: nextArgv,
486
+ flags
487
+ };
488
+ }
342
489
  function parseCommandArgvWithMetadata(argv, options) {
343
490
  const flags = {};
344
491
  const positionals = [];
@@ -435,17 +582,10 @@ import {
435
582
  } from "@wp-typia/project-tools/cli-templates";
436
583
 
437
584
  // src/add-kind-registry.ts
438
- var ADD_KIND_IDS = [
439
- "block",
440
- "variation",
441
- "pattern",
442
- "binding-source",
443
- "rest-resource",
444
- "ability",
445
- "ai-feature",
446
- "editor-plugin",
447
- "hooked-block"
448
- ];
585
+ import {
586
+ CLI_DIAGNOSTIC_CODES,
587
+ createCliDiagnosticCodeError
588
+ } from "@wp-typia/project-tools/cli-diagnostics";
449
589
  var BLOCK_VISIBLE_FIELD_ORDER = [
450
590
  "kind",
451
591
  "name",
@@ -455,30 +595,123 @@ var BLOCK_VISIBLE_FIELD_ORDER = [
455
595
  "data-storage",
456
596
  "persistence-policy"
457
597
  ];
458
- function isAddKindId(value) {
459
- return typeof value === "string" && ADD_KIND_IDS.includes(value);
598
+ function readOptionalStringFlag(flags, name) {
599
+ const value = flags[name];
600
+ if (value === undefined || value === null) {
601
+ return;
602
+ }
603
+ if (typeof value !== "string" || value.trim().length === 0) {
604
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, `\`--${name}\` requires a value.`);
605
+ }
606
+ return value;
607
+ }
608
+ function requireAddKindName(context, message) {
609
+ if (!context.name) {
610
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, message);
611
+ }
612
+ return context.name;
613
+ }
614
+ function formatExternalLayerSelectHint(option) {
615
+ const details = [
616
+ option.description,
617
+ option.extends.length > 0 ? `extends ${option.extends.join(", ")}` : undefined
618
+ ].filter((value) => typeof value === "string" && value.length > 0);
619
+ return details.length > 0 ? details.join(" · ") : undefined;
620
+ }
621
+ function toExternalLayerPromptOptions(options) {
622
+ return options.map((option) => ({
623
+ hint: formatExternalLayerSelectHint(option),
624
+ label: option.id,
625
+ value: option.id
626
+ }));
627
+ }
628
+ function defineAddKindRegistryEntry(entry) {
629
+ return entry;
460
630
  }
461
631
  function isAddPersistenceTemplate(template) {
462
632
  return template === "persistence" || template === "compound";
463
633
  }
464
634
  var ADD_KIND_REGISTRY = {
465
- "binding-source": {
635
+ "admin-view": defineAddKindRegistryEntry({
636
+ completion: {
637
+ nextSteps: (values) => [
638
+ `Review src/admin-views/${values.adminViewSlug}/ and inc/admin-views/${values.adminViewSlug}.php.`,
639
+ "Run your workspace build or dev command to verify the generated DataViews admin screen."
640
+ ],
641
+ summaryLines: (values, projectDir) => [
642
+ `Admin view: ${values.adminViewSlug}`,
643
+ ...values.source ? [`Source: ${values.source}`] : [],
644
+ `Project directory: ${projectDir}`
645
+ ],
646
+ title: "Added DataViews admin screen"
647
+ },
648
+ description: "Add an opt-in DataViews-powered admin screen",
649
+ nameLabel: "Admin view name",
650
+ async prepareExecution(context) {
651
+ const name = requireAddKindName(context, "`wp-typia add admin-view` requires <name>. Usage: wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>].");
652
+ const source = readOptionalStringFlag(context.flags, "source");
653
+ return {
654
+ execute: (cwd) => context.addRuntime.runAddAdminViewCommand({
655
+ adminViewName: name,
656
+ cwd,
657
+ source
658
+ }),
659
+ getValues: (result) => ({
660
+ adminViewSlug: result.adminViewSlug,
661
+ ...result.source ? { source: result.source } : {}
662
+ })
663
+ };
664
+ },
665
+ sortOrder: 10,
666
+ supportsDryRun: true,
667
+ usage: "wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>] [--dry-run]",
668
+ visibleFieldNames: () => ["kind", "name", "source"]
669
+ }),
670
+ "binding-source": defineAddKindRegistryEntry({
466
671
  completion: {
467
672
  nextSteps: (values) => [
468
673
  `Review src/bindings/${values.bindingSourceSlug}/server.php and src/bindings/${values.bindingSourceSlug}/editor.ts.`,
674
+ ...values.blockSlug && values.attributeName ? [
675
+ `Review src/blocks/${values.blockSlug}/block.json for the ${values.attributeName} bindable attribute.`
676
+ ] : [],
469
677
  "Run your workspace build or dev command to verify the binding source hooks and editor registration."
470
678
  ],
471
679
  summaryLines: (values, projectDir) => [
472
680
  `Binding source: ${values.bindingSourceSlug}`,
681
+ ...values.blockSlug && values.attributeName ? [`Target: ${values.blockSlug}.${values.attributeName}`] : [],
473
682
  `Project directory: ${projectDir}`
474
683
  ],
475
684
  title: "Added binding source"
476
685
  },
477
686
  description: "Add a shared block bindings source",
478
687
  nameLabel: "Binding source name",
479
- visibleFieldNames: () => ["kind", "name"]
480
- },
481
- block: {
688
+ async prepareExecution(context) {
689
+ const name = requireAddKindName(context, "`wp-typia add binding-source` requires <name>. Usage: wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>].");
690
+ const blockName = readOptionalStringFlag(context.flags, "block");
691
+ const attributeName = readOptionalStringFlag(context.flags, "attribute");
692
+ if (Boolean(blockName) !== Boolean(attributeName)) {
693
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add binding-source` requires --block and --attribute to be provided together.");
694
+ }
695
+ return {
696
+ execute: (cwd) => context.addRuntime.runAddBindingSourceCommand({
697
+ attributeName,
698
+ bindingSourceName: name,
699
+ blockName,
700
+ cwd
701
+ }),
702
+ getValues: (result) => ({
703
+ ...result.attributeName ? { attributeName: result.attributeName } : {},
704
+ ...result.blockSlug ? { blockSlug: result.blockSlug } : {},
705
+ bindingSourceSlug: result.bindingSourceSlug
706
+ })
707
+ };
708
+ },
709
+ sortOrder: 70,
710
+ supportsDryRun: true,
711
+ usage: "wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>] [--dry-run]",
712
+ visibleFieldNames: () => ["kind", "name", "block", "attribute"]
713
+ }),
714
+ block: defineAddKindRegistryEntry({
482
715
  completion: {
483
716
  nextSteps: () => [
484
717
  "Review the generated sources under src/blocks/ and the updated scripts/block-config.ts entry.",
@@ -494,6 +727,50 @@ var ADD_KIND_REGISTRY = {
494
727
  description: "Add a real block slice",
495
728
  hiddenStringSubmitFields: ["external-layer-id", "external-layer-source"],
496
729
  nameLabel: "Block name",
730
+ async prepareExecution(context) {
731
+ const name = requireAddKindName(context, "`wp-typia add block` requires <name>. Usage: wp-typia add block <name> [--template <basic|interactivity|persistence|compound>]");
732
+ const externalLayerId = readOptionalStringFlag(context.flags, "external-layer-id");
733
+ const externalLayerSource = readOptionalStringFlag(context.flags, "external-layer-source");
734
+ const shouldPromptForLayerSelection = Boolean(externalLayerSource) && !Boolean(externalLayerId) && context.isInteractiveSession;
735
+ const selectPrompt = shouldPromptForLayerSelection ? await context.getOrCreatePrompt() : undefined;
736
+ const alternateRenderTargets = readOptionalStringFlag(context.flags, "alternate-render-targets");
737
+ const dataStorageMode = readOptionalStringFlag(context.flags, "data-storage");
738
+ const innerBlocksPreset = readOptionalStringFlag(context.flags, "inner-blocks-preset");
739
+ const persistencePolicy = readOptionalStringFlag(context.flags, "persistence-policy");
740
+ let resolvedTemplateId = readOptionalStringFlag(context.flags, "template");
741
+ if (!resolvedTemplateId && context.isInteractiveSession) {
742
+ const templatePrompt = await context.getOrCreatePrompt();
743
+ resolvedTemplateId = await templatePrompt.select("Select a block template", context.addRuntime.ADD_BLOCK_TEMPLATE_IDS.map((templateId) => ({
744
+ hint: `Scaffold the ${templateId} block family`,
745
+ label: templateId,
746
+ value: templateId
747
+ })), 1);
748
+ }
749
+ resolvedTemplateId ??= "basic";
750
+ return {
751
+ execute: (cwd) => context.addRuntime.runAddBlockCommand({
752
+ alternateRenderTargets,
753
+ blockName: name,
754
+ cwd,
755
+ dataStorageMode,
756
+ externalLayerId,
757
+ externalLayerSource,
758
+ innerBlocksPreset,
759
+ persistencePolicy,
760
+ selectExternalLayerId: selectPrompt ? (options) => selectPrompt.select("Select an external layer", toExternalLayerPromptOptions(options), 1) : undefined,
761
+ templateId: resolvedTemplateId
762
+ }),
763
+ getValues: (result) => ({
764
+ blockSlugs: result.blockSlugs.join(", "),
765
+ templateId: result.templateId
766
+ }),
767
+ getWarnings: (result) => result.warnings,
768
+ warnLine: context.warnLine
769
+ };
770
+ },
771
+ sortOrder: 20,
772
+ supportsDryRun: true,
773
+ usage: "wp-typia add block <name> [--template <basic|interactivity|persistence|compound>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--dry-run]",
497
774
  visibleFieldNames: ({ template }) => BLOCK_VISIBLE_FIELD_ORDER.filter((fieldName) => {
498
775
  if (fieldName === "alternate-render-targets") {
499
776
  return isAddPersistenceTemplate(template);
@@ -506,8 +783,8 @@ var ADD_KIND_REGISTRY = {
506
783
  }
507
784
  return true;
508
785
  })
509
- },
510
- ability: {
786
+ }),
787
+ ability: defineAddKindRegistryEntry({
511
788
  completion: {
512
789
  nextSteps: (values) => [
513
790
  `Review src/abilities/${values.abilitySlug}/ and inc/abilities/${values.abilitySlug}.php.`,
@@ -521,9 +798,24 @@ var ADD_KIND_REGISTRY = {
521
798
  },
522
799
  description: "Add a typed server/client workflow ability scaffold",
523
800
  nameLabel: "Ability name",
801
+ async prepareExecution(context) {
802
+ const name = requireAddKindName(context, "`wp-typia add ability` requires <name>. Usage: wp-typia add ability <name>.");
803
+ return {
804
+ execute: (cwd) => context.addRuntime.runAddAbilityCommand({
805
+ abilityName: name,
806
+ cwd
807
+ }),
808
+ getValues: (result) => ({
809
+ abilitySlug: result.abilitySlug
810
+ })
811
+ };
812
+ },
813
+ sortOrder: 90,
814
+ supportsDryRun: true,
815
+ usage: "wp-typia add ability <name> [--dry-run]",
524
816
  visibleFieldNames: () => ["kind", "name"]
525
- },
526
- "editor-plugin": {
817
+ }),
818
+ "editor-plugin": defineAddKindRegistryEntry({
527
819
  completion: {
528
820
  nextSteps: (values) => [
529
821
  `Review src/editor-plugins/${values.editorPluginSlug}/.`,
@@ -536,11 +828,29 @@ var ADD_KIND_REGISTRY = {
536
828
  ],
537
829
  title: "Added editor plugin"
538
830
  },
539
- description: "Add a document-level editor extension shell",
831
+ description: "Add a slot-aware document editor extension shell",
540
832
  nameLabel: "Editor plugin name",
833
+ async prepareExecution(context) {
834
+ const name = requireAddKindName(context, "`wp-typia add editor-plugin` requires <name>. Usage: wp-typia add editor-plugin <name> [--slot <sidebar|document-setting-panel>].");
835
+ const slot = readOptionalStringFlag(context.flags, "slot");
836
+ return {
837
+ execute: (cwd) => context.addRuntime.runAddEditorPluginCommand({
838
+ cwd,
839
+ editorPluginName: name,
840
+ slot
841
+ }),
842
+ getValues: (result) => ({
843
+ editorPluginSlug: result.editorPluginSlug,
844
+ slot: result.slot
845
+ })
846
+ };
847
+ },
848
+ sortOrder: 120,
849
+ supportsDryRun: true,
850
+ usage: "wp-typia add editor-plugin <name> [--slot <sidebar|document-setting-panel>] [--dry-run]",
541
851
  visibleFieldNames: () => ["kind", "name", "slot"]
542
- },
543
- "hooked-block": {
852
+ }),
853
+ "hooked-block": defineAddKindRegistryEntry({
544
854
  completion: {
545
855
  nextSteps: (values) => [
546
856
  `Review src/blocks/${values.blockSlug}/block.json for the new blockHooks entry.`,
@@ -556,9 +866,36 @@ var ADD_KIND_REGISTRY = {
556
866
  },
557
867
  description: "Add block.json hook metadata to an existing block",
558
868
  nameLabel: "Target block",
869
+ async prepareExecution(context) {
870
+ const name = requireAddKindName(context, "`wp-typia add hooked-block` requires <block-slug>. Usage: wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <before|after|firstChild|lastChild>.");
871
+ const anchorBlockName = readOptionalStringFlag(context.flags, "anchor");
872
+ if (!anchorBlockName) {
873
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add hooked-block` requires --anchor <anchor-block-name>.");
874
+ }
875
+ const position = readOptionalStringFlag(context.flags, "position");
876
+ if (!position) {
877
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add hooked-block` requires --position <before|after|firstChild|lastChild>.");
878
+ }
879
+ return {
880
+ execute: (cwd) => context.addRuntime.runAddHookedBlockCommand({
881
+ anchorBlockName,
882
+ blockName: name,
883
+ cwd,
884
+ position
885
+ }),
886
+ getValues: (result) => ({
887
+ anchorBlockName: result.anchorBlockName,
888
+ blockSlug: result.blockSlug,
889
+ position: result.position
890
+ })
891
+ };
892
+ },
893
+ sortOrder: 110,
894
+ supportsDryRun: true,
895
+ usage: "wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <before|after|firstChild|lastChild> [--dry-run]",
559
896
  visibleFieldNames: () => ["kind", "name", "anchor", "position"]
560
- },
561
- pattern: {
897
+ }),
898
+ pattern: defineAddKindRegistryEntry({
562
899
  completion: {
563
900
  nextSteps: (values) => [
564
901
  `Review src/patterns/${values.patternSlug}.php.`,
@@ -572,9 +909,108 @@ var ADD_KIND_REGISTRY = {
572
909
  },
573
910
  description: "Add a PHP block pattern shell",
574
911
  nameLabel: "Pattern name",
912
+ async prepareExecution(context) {
913
+ const name = requireAddKindName(context, "`wp-typia add pattern` requires <name>. Usage: wp-typia add pattern <name>.");
914
+ return {
915
+ execute: (cwd) => context.addRuntime.runAddPatternCommand({
916
+ cwd,
917
+ patternName: name
918
+ }),
919
+ getValues: (result) => ({
920
+ patternSlug: result.patternSlug
921
+ })
922
+ };
923
+ },
924
+ sortOrder: 60,
925
+ supportsDryRun: true,
926
+ usage: "wp-typia add pattern <name> [--dry-run]",
575
927
  visibleFieldNames: () => ["kind", "name"]
576
- },
577
- "rest-resource": {
928
+ }),
929
+ style: defineAddKindRegistryEntry({
930
+ completion: {
931
+ nextSteps: (values) => [
932
+ `Review src/blocks/${values.blockSlug}/styles/${values.styleSlug}.ts.`,
933
+ "Run your workspace build or dev command to verify the new block style registration."
934
+ ],
935
+ summaryLines: (values, projectDir) => [
936
+ `Block style: ${values.styleSlug}`,
937
+ `Target block: ${values.blockSlug}`,
938
+ `Project directory: ${projectDir}`
939
+ ],
940
+ title: "Added block style"
941
+ },
942
+ description: "Add a Block Styles registration to an existing block",
943
+ nameLabel: "Style name",
944
+ async prepareExecution(context) {
945
+ const name = requireAddKindName(context, "`wp-typia add style` requires <name>. Usage: wp-typia add style <name> --block <block-slug>.");
946
+ const blockSlug = readOptionalStringFlag(context.flags, "block");
947
+ if (!blockSlug) {
948
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add style` requires --block <block-slug>.");
949
+ }
950
+ return {
951
+ execute: (cwd) => context.addRuntime.runAddBlockStyleCommand({
952
+ blockName: blockSlug,
953
+ cwd,
954
+ styleName: name
955
+ }),
956
+ getValues: (result) => ({
957
+ blockSlug: result.blockSlug,
958
+ styleSlug: result.styleSlug
959
+ })
960
+ };
961
+ },
962
+ sortOrder: 40,
963
+ supportsDryRun: true,
964
+ usage: "wp-typia add style <name> --block <block-slug> [--dry-run]",
965
+ visibleFieldNames: () => ["kind", "name", "block"]
966
+ }),
967
+ transform: defineAddKindRegistryEntry({
968
+ completion: {
969
+ nextSteps: (values) => [
970
+ `Review src/blocks/${values.blockSlug}/transforms/${values.transformSlug}.ts.`,
971
+ "Run your workspace build or dev command to verify the new block transform registration."
972
+ ],
973
+ summaryLines: (values, projectDir) => [
974
+ `Block transform: ${values.transformSlug}`,
975
+ `From: ${values.fromBlockName}`,
976
+ `To: ${values.toBlockName}`,
977
+ `Project directory: ${projectDir}`
978
+ ],
979
+ title: "Added block transform"
980
+ },
981
+ description: "Add a block-to-block transform into a workspace block",
982
+ nameLabel: "Transform name",
983
+ async prepareExecution(context) {
984
+ const name = requireAddKindName(context, "`wp-typia add transform` requires <name>. Usage: wp-typia add transform <name> --from <namespace/block> --to <block-slug|namespace/block-slug>.");
985
+ const fromBlockName = readOptionalStringFlag(context.flags, "from");
986
+ if (!fromBlockName) {
987
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add transform` requires --from <namespace/block>.");
988
+ }
989
+ const toBlockName = readOptionalStringFlag(context.flags, "to");
990
+ if (!toBlockName) {
991
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add transform` requires --to <block-slug|namespace/block-slug>.");
992
+ }
993
+ return {
994
+ execute: (cwd) => context.addRuntime.runAddBlockTransformCommand({
995
+ cwd,
996
+ fromBlockName,
997
+ toBlockName,
998
+ transformName: name
999
+ }),
1000
+ getValues: (result) => ({
1001
+ blockSlug: result.blockSlug,
1002
+ fromBlockName: result.fromBlockName,
1003
+ toBlockName: result.toBlockName,
1004
+ transformSlug: result.transformSlug
1005
+ })
1006
+ };
1007
+ },
1008
+ sortOrder: 50,
1009
+ supportsDryRun: true,
1010
+ usage: "wp-typia add transform <name> --from <namespace/block> --to <block-slug|namespace/block-slug> [--dry-run]",
1011
+ visibleFieldNames: () => ["kind", "name", "from", "to"]
1012
+ }),
1013
+ "rest-resource": defineAddKindRegistryEntry({
578
1014
  completion: {
579
1015
  nextSteps: (values) => [
580
1016
  `Review src/rest/${values.restResourceSlug}/ and inc/rest/${values.restResourceSlug}.php.`,
@@ -590,9 +1026,30 @@ var ADD_KIND_REGISTRY = {
590
1026
  },
591
1027
  description: "Add a plugin-level typed REST resource",
592
1028
  nameLabel: "REST resource name",
1029
+ async prepareExecution(context) {
1030
+ const name = requireAddKindName(context, "`wp-typia add rest-resource` requires <name>. Usage: wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create>].");
1031
+ const methods = readOptionalStringFlag(context.flags, "methods");
1032
+ const namespace = readOptionalStringFlag(context.flags, "namespace");
1033
+ return {
1034
+ execute: (cwd) => context.addRuntime.runAddRestResourceCommand({
1035
+ cwd,
1036
+ methods,
1037
+ namespace,
1038
+ restResourceName: name
1039
+ }),
1040
+ getValues: (result) => ({
1041
+ methods: result.methods.join(", "),
1042
+ namespace: result.namespace,
1043
+ restResourceSlug: result.restResourceSlug
1044
+ })
1045
+ };
1046
+ },
1047
+ sortOrder: 80,
1048
+ supportsDryRun: true,
1049
+ usage: "wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create,update,delete>] [--dry-run]",
593
1050
  visibleFieldNames: () => ["kind", "name", "namespace", "methods"]
594
- },
595
- "ai-feature": {
1051
+ }),
1052
+ "ai-feature": defineAddKindRegistryEntry({
596
1053
  completion: {
597
1054
  nextSteps: (values) => [
598
1055
  `Review src/ai-features/${values.aiFeatureSlug}/ and inc/ai-features/${values.aiFeatureSlug}.php.`,
@@ -607,9 +1064,29 @@ var ADD_KIND_REGISTRY = {
607
1064
  },
608
1065
  description: "Add a server-owned WordPress AI feature endpoint",
609
1066
  nameLabel: "AI feature name",
1067
+ async prepareExecution(context) {
1068
+ const name = requireAddKindName(context, "`wp-typia add ai-feature` requires <name>. Usage: wp-typia add ai-feature <name> [--namespace <vendor/v1>].");
1069
+ const namespace = readOptionalStringFlag(context.flags, "namespace");
1070
+ return {
1071
+ execute: (cwd) => context.addRuntime.runAddAiFeatureCommand({
1072
+ aiFeatureName: name,
1073
+ cwd,
1074
+ namespace
1075
+ }),
1076
+ getValues: (result) => ({
1077
+ aiFeatureSlug: result.aiFeatureSlug,
1078
+ namespace: result.namespace
1079
+ }),
1080
+ getWarnings: (result) => result.warnings,
1081
+ warnLine: context.warnLine
1082
+ };
1083
+ },
1084
+ sortOrder: 100,
1085
+ supportsDryRun: true,
1086
+ usage: "wp-typia add ai-feature <name> [--namespace <vendor/v1>] [--dry-run]",
610
1087
  visibleFieldNames: () => ["kind", "name", "namespace"]
611
- },
612
- variation: {
1088
+ }),
1089
+ variation: defineAddKindRegistryEntry({
613
1090
  completion: {
614
1091
  nextSteps: (values) => [
615
1092
  `Review src/blocks/${values.blockSlug}/variations/${values.variationSlug}.ts.`,
@@ -624,9 +1101,37 @@ var ADD_KIND_REGISTRY = {
624
1101
  },
625
1102
  description: "Add a variation to an existing block",
626
1103
  nameLabel: "Variation name",
1104
+ async prepareExecution(context) {
1105
+ const name = requireAddKindName(context, "`wp-typia add variation` requires <name>. Usage: wp-typia add variation <name> --block <block-slug>");
1106
+ const blockSlug = readOptionalStringFlag(context.flags, "block");
1107
+ if (!blockSlug) {
1108
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, "`wp-typia add variation` requires --block <block-slug>.");
1109
+ }
1110
+ return {
1111
+ execute: (cwd) => context.addRuntime.runAddVariationCommand({
1112
+ blockName: blockSlug,
1113
+ cwd,
1114
+ variationName: name
1115
+ }),
1116
+ getValues: (result) => ({
1117
+ blockSlug: result.blockSlug,
1118
+ variationSlug: result.variationSlug
1119
+ })
1120
+ };
1121
+ },
1122
+ sortOrder: 30,
1123
+ supportsDryRun: true,
1124
+ usage: "wp-typia add variation <name> --block <block-slug> [--dry-run]",
627
1125
  visibleFieldNames: () => ["kind", "name", "block"]
628
- }
1126
+ })
629
1127
  };
1128
+ var ADD_KIND_IDS = Object.keys(ADD_KIND_REGISTRY).sort((left, right) => ADD_KIND_REGISTRY[left].sortOrder - ADD_KIND_REGISTRY[right].sortOrder);
1129
+ function isAddKindId(value) {
1130
+ return typeof value === "string" && ADD_KIND_IDS.includes(value);
1131
+ }
1132
+ async function getAddKindExecutionPlan(kind, context) {
1133
+ return ADD_KIND_REGISTRY[kind].prepareExecution(context);
1134
+ }
630
1135
  function buildAddKindCompletionDetails(kind, options) {
631
1136
  const descriptor = ADD_KIND_REGISTRY[kind].completion;
632
1137
  return {
@@ -641,12 +1146,19 @@ function formatAddKindList() {
641
1146
  function formatAddKindUsagePlaceholder() {
642
1147
  return `<${ADD_KIND_IDS.join("|")}>`;
643
1148
  }
1149
+ function supportsAddKindDryRun(kind) {
1150
+ return ADD_KIND_REGISTRY[kind].supportsDryRun;
1151
+ }
644
1152
 
645
1153
  // src/config.ts
646
1154
  import fs from "node:fs/promises";
647
1155
  import os from "node:os";
648
1156
  import path from "node:path";
649
1157
  import { isPlainObject as isRecord } from "@wp-typia/api-client/runtime-primitives";
1158
+ import {
1159
+ CLI_DIAGNOSTIC_CODES as CLI_DIAGNOSTIC_CODES2,
1160
+ createCliDiagnosticCodeError as createCliDiagnosticCodeError2
1161
+ } from "@wp-typia/project-tools/cli-diagnostics";
650
1162
  var WP_TYPIA_CONFIG_SOURCES = [
651
1163
  "~/.config/wp-typia/config.json",
652
1164
  ".wp-typiarc",
@@ -668,16 +1180,22 @@ function deepMerge(base, incoming) {
668
1180
  return merged;
669
1181
  }
670
1182
  async function readJsonFile(filePath) {
1183
+ let source;
671
1184
  try {
672
- const source = await fs.readFile(filePath, "utf8");
673
- const parsed = JSON.parse(source);
674
- return isRecord(parsed) ? parsed : null;
1185
+ source = await fs.readFile(filePath, "utf8");
675
1186
  } catch (error) {
676
1187
  if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
677
1188
  return null;
678
1189
  }
679
1190
  throw error;
680
1191
  }
1192
+ try {
1193
+ const parsed = JSON.parse(source);
1194
+ return isRecord(parsed) ? parsed : null;
1195
+ } catch (error) {
1196
+ const message = error instanceof Error ? error.message : String(error);
1197
+ throw createCliDiagnosticCodeError2(CLI_DIAGNOSTIC_CODES2.INVALID_ARGUMENT, `Unable to parse ${filePath}: ${message}`, error instanceof Error ? { cause: error } : undefined);
1198
+ }
681
1199
  }
682
1200
  function resolveConfigPath(cwd, source) {
683
1201
  if (source.startsWith("~/")) {
@@ -715,43 +1233,24 @@ function getAddBlockDefaults(config) {
715
1233
  }
716
1234
 
717
1235
  // src/config-override.ts
1236
+ var GLOBAL_OPTION_PARSER = buildCommandOptionParser(GLOBAL_OPTION_METADATA);
718
1237
  function extractWpTypiaConfigOverride(argv) {
719
- const nextArgv = [];
720
- let configOverridePath;
721
- for (let index = 0;index < argv.length; index += 1) {
722
- const arg = argv[index];
723
- if (!arg) {
724
- continue;
725
- }
726
- if (arg === "--") {
727
- nextArgv.push(...argv.slice(index));
728
- break;
729
- }
730
- if (arg === "--config" || arg === "-c") {
731
- const next = argv[index + 1];
732
- if (!next || next.startsWith("-")) {
733
- throw new Error(`\`${arg}\` requires a value.`);
734
- }
735
- configOverridePath = next;
736
- index += 1;
737
- continue;
738
- }
739
- if (arg.startsWith("--config=")) {
740
- const inlineValue = arg.slice("--config=".length);
741
- if (!inlineValue) {
742
- throw new Error("`--config` requires a value.");
743
- }
744
- configOverridePath = inlineValue;
745
- continue;
746
- }
747
- nextArgv.push(arg);
748
- }
1238
+ const { argv: nextArgv, flags } = extractKnownOptionValuesFromArgv(argv, {
1239
+ optionNames: ["config"],
1240
+ parser: GLOBAL_OPTION_PARSER
1241
+ });
749
1242
  return {
750
1243
  argv: nextArgv,
751
- configOverridePath
1244
+ configOverridePath: typeof flags.config === "string" ? flags.config : undefined
752
1245
  };
753
1246
  }
754
1247
 
1248
+ // src/runtime-bridge.ts
1249
+ import {
1250
+ CLI_DIAGNOSTIC_CODES as CLI_DIAGNOSTIC_CODES4,
1251
+ createCliDiagnosticCodeError as createCliDiagnosticCodeError4
1252
+ } from "@wp-typia/project-tools/cli-diagnostics";
1253
+
755
1254
  // src/runtime-bridge-add-dry-run.ts
756
1255
  import fs2 from "node:fs";
757
1256
  import { promises as fsp } from "node:fs";
@@ -916,6 +1415,9 @@ function readAsciiPreferenceFromEnv(env) {
916
1415
  }
917
1416
  return;
918
1417
  }
1418
+ function hasNoColorPreference(env) {
1419
+ return typeof env.NO_COLOR === "string" && env.NO_COLOR.length > 0;
1420
+ }
919
1421
  function prefersAsciiOutput(options = {}) {
920
1422
  if (options.forceAscii) {
921
1423
  return true;
@@ -925,6 +1427,9 @@ function prefersAsciiOutput(options = {}) {
925
1427
  if (envPreference !== undefined) {
926
1428
  return envPreference;
927
1429
  }
1430
+ if (hasNoColorPreference(env)) {
1431
+ return true;
1432
+ }
928
1433
  const term = options.term ?? env.TERM;
929
1434
  if (term === "dumb") {
930
1435
  return true;
@@ -990,6 +1495,77 @@ ${payload.optionalTitle ?? "Optional:"}`);
990
1495
  printLine(`Note: ${payload.optionalNote}`);
991
1496
  }
992
1497
  }
1498
+ function toNonEmptyArray(values) {
1499
+ return values && values.length > 0 ? values : undefined;
1500
+ }
1501
+ function extractPlannedFiles(payload) {
1502
+ const files = payload.optionalLines?.map((line) => line.match(/^(?:delete|update|write)\s+(.+)$/u)?.[1]).filter((value) => typeof value === "string" && value.length > 0);
1503
+ return toNonEmptyArray(files);
1504
+ }
1505
+ var PROJECT_DIRECTORY_SUMMARY_PREFIX = "Project directory: ";
1506
+ function extractCompletionProjectDir(completion) {
1507
+ const projectDir = completion?.summaryLines?.find((line) => line.startsWith(PROJECT_DIRECTORY_SUMMARY_PREFIX))?.slice(PROJECT_DIRECTORY_SUMMARY_PREFIX.length).trim();
1508
+ return projectDir && projectDir.length > 0 ? projectDir : undefined;
1509
+ }
1510
+ function serializeCompletionPayload(payload) {
1511
+ return {
1512
+ nextSteps: toNonEmptyArray(payload.nextSteps),
1513
+ optionalLines: toNonEmptyArray(payload.optionalLines),
1514
+ optionalNote: payload.optionalNote,
1515
+ optionalTitle: payload.optionalTitle,
1516
+ preambleLines: toNonEmptyArray(payload.preambleLines),
1517
+ summaryLines: toNonEmptyArray(payload.summaryLines),
1518
+ title: stripLeadingOutputMarker(payload.title),
1519
+ warningLines: toNonEmptyArray(payload.warningLines)
1520
+ };
1521
+ }
1522
+ function buildStructuredCompletionSuccessPayload(command, completion, metadata = {}) {
1523
+ const serializedCompletion = completion ? serializeCompletionPayload(completion) : undefined;
1524
+ return {
1525
+ ok: true,
1526
+ data: {
1527
+ ...metadata,
1528
+ command,
1529
+ ...serializedCompletion ? {
1530
+ completion: serializedCompletion,
1531
+ files: extractPlannedFiles(serializedCompletion),
1532
+ nextSteps: serializedCompletion.nextSteps,
1533
+ optionalLines: serializedCompletion.optionalLines,
1534
+ optionalNote: serializedCompletion.optionalNote,
1535
+ optionalTitle: serializedCompletion.optionalTitle,
1536
+ preambleLines: serializedCompletion.preambleLines,
1537
+ summaryLines: serializedCompletion.summaryLines,
1538
+ title: serializedCompletion.title,
1539
+ warnings: serializedCompletion.warningLines
1540
+ } : {}
1541
+ }
1542
+ };
1543
+ }
1544
+ function buildStructuredInitSuccessPayload(plan) {
1545
+ const completion = serializeCompletionPayload(buildInitCompletionPayload(plan));
1546
+ const files = Array.from(new Set([
1547
+ ...plan.plannedFiles.map((filePlan) => filePlan.path),
1548
+ ...plan.commandMode === "preview-only" ? plan.generatedArtifacts : []
1549
+ ]));
1550
+ return {
1551
+ ok: true,
1552
+ data: {
1553
+ command: "init",
1554
+ completion,
1555
+ detectedLayout: plan.detectedLayout,
1556
+ files: toNonEmptyArray(files),
1557
+ mode: plan.commandMode === "apply" ? "apply" : "preview",
1558
+ nextSteps: toNonEmptyArray(plan.nextSteps),
1559
+ packageManager: plan.packageManager,
1560
+ plan,
1561
+ projectDir: plan.projectDir,
1562
+ status: plan.status,
1563
+ summary: plan.summary,
1564
+ title: completion.title,
1565
+ warnings: toNonEmptyArray(plan.notes)
1566
+ }
1567
+ };
1568
+ }
993
1569
  function formatCreateProgressLine(payload, markerOptions) {
994
1570
  return formatOutputMarker("progress", `${payload.title}: ${payload.detail}`, markerOptions);
995
1571
  }
@@ -1035,28 +1611,31 @@ function buildCreateDryRunPayload(flow, markerOptions) {
1035
1611
  };
1036
1612
  }
1037
1613
  function buildInitCompletionPayload(plan, markerOptions) {
1038
- const plannedChanges = [
1614
+ const changeLines = [
1039
1615
  ...plan.packageChanges.addDevDependencies.map((dependency) => `devDependency ${dependency.action} ${dependency.name} -> ${dependency.requiredValue}`),
1040
1616
  ...plan.packageChanges.packageManagerField ? [
1041
1617
  `packageManager ${plan.packageChanges.packageManagerField.action} -> ${plan.packageChanges.packageManagerField.requiredValue}`
1042
1618
  ] : [],
1043
1619
  ...plan.packageChanges.scripts.map((script) => `script ${script.action} ${script.name} -> ${script.requiredValue}`),
1044
- ...plan.plannedFiles.map((filePlan) => `file add ${filePlan.path} (${filePlan.purpose})`),
1045
- ...plan.generatedArtifacts.map((artifactPath) => `generated artifact ${artifactPath}`)
1620
+ ...plan.plannedFiles.map((filePlan) => `file ${filePlan.action} ${filePlan.path} (${filePlan.purpose})`),
1621
+ ...plan.commandMode === "preview-only" ? plan.generatedArtifacts.map((artifactPath) => `generated artifact ${artifactPath}`) : []
1046
1622
  ];
1623
+ const modeLine = plan.commandMode === "apply" ? plan.status === "already-initialized" ? "Mode: apply requested; no files were written because the retrofit surface already existed." : "Mode: apply; package.json and retrofit helper files were written." : "Mode: preview only; no files were written.";
1624
+ const optionalTitle = plan.commandMode === "apply" ? `Applied adoption changes (${changeLines.length}):` : `Planned adoption changes (${changeLines.length}):`;
1625
+ const title = plan.status === "already-initialized" ? formatOutputMarker("success", `wp-typia init: ${plan.projectName} is already initialized`, markerOptions) : plan.commandMode === "apply" ? formatOutputMarker("success", `Applied retrofit init for ${plan.projectName}`, markerOptions) : formatOutputMarker("dryRun", `Retrofit init plan for ${plan.projectName}`, markerOptions);
1047
1626
  return {
1048
1627
  nextSteps: plan.nextSteps,
1049
- optionalLines: plannedChanges,
1628
+ optionalLines: changeLines,
1050
1629
  optionalNote: plan.summary,
1051
- optionalTitle: `Planned adoption changes (${plannedChanges.length}):`,
1630
+ optionalTitle,
1052
1631
  summaryLines: [
1053
1632
  `Project directory: ${plan.projectDir}`,
1054
1633
  `Detected layout: ${plan.detectedLayout.description}`,
1055
1634
  ...plan.detectedLayout.blockNames.length > 0 ? [`Block targets: ${plan.detectedLayout.blockNames.join(", ")}`] : [],
1056
1635
  `Package manager: ${plan.packageManager}`,
1057
- "Mode: preview only; no files were written."
1636
+ modeLine
1058
1637
  ],
1059
- title: plan.status === "already-initialized" ? formatOutputMarker("success", `wp-typia init: ${plan.projectName} is already initialized`, markerOptions) : formatOutputMarker("dryRun", `Retrofit init plan for ${plan.projectName}`, markerOptions),
1638
+ title,
1060
1639
  warningLines: plan.notes
1061
1640
  };
1062
1641
  }
@@ -1145,16 +1724,16 @@ function printBlock(lines, printLine) {
1145
1724
  printLine(line);
1146
1725
  }
1147
1726
  }
1148
- function formatExternalLayerSelectHint(option) {
1727
+ function formatExternalLayerSelectHint2(option) {
1149
1728
  const details = [
1150
1729
  option.description,
1151
1730
  option.extends.length > 0 ? `extends ${option.extends.join(", ")}` : undefined
1152
1731
  ].filter((value) => typeof value === "string" && value.length > 0);
1153
1732
  return details.length > 0 ? details.join(" · ") : undefined;
1154
1733
  }
1155
- function toExternalLayerPromptOptions(options) {
1734
+ function toExternalLayerPromptOptions2(options) {
1156
1735
  return options.map((option) => ({
1157
- hint: formatExternalLayerSelectHint(option),
1736
+ hint: formatExternalLayerSelectHint2(option),
1158
1737
  label: option.id,
1159
1738
  value: option.id
1160
1739
  }));
@@ -1172,6 +1751,10 @@ function isInteractiveTerminal({
1172
1751
  import { spawnSync } from "node:child_process";
1173
1752
  import fs4 from "node:fs";
1174
1753
  import path4 from "node:path";
1754
+ import {
1755
+ CLI_DIAGNOSTIC_CODES as CLI_DIAGNOSTIC_CODES3,
1756
+ createCliDiagnosticCodeError as createCliDiagnosticCodeError3
1757
+ } from "@wp-typia/project-tools/cli-diagnostics";
1175
1758
  var SYNC_INSTALL_MARKERS = [
1176
1759
  "node_modules",
1177
1760
  ".pnp.cjs",
@@ -1186,7 +1769,7 @@ function resolveSyncExecutionTarget(subcommand) {
1186
1769
  if (subcommand === "ai") {
1187
1770
  return "ai";
1188
1771
  }
1189
- throw new Error(`Unknown sync subcommand "${subcommand}". Expected one of: "ai".`);
1772
+ throw createCliDiagnosticCodeError3(CLI_DIAGNOSTIC_CODES3.INVALID_COMMAND, `Unknown sync subcommand "${subcommand}". Expected one of: "ai".`);
1190
1773
  }
1191
1774
  function formatRunScript(packageManagerId, scriptName, extraArgs = "") {
1192
1775
  const args = extraArgs.trim();
@@ -1214,7 +1797,7 @@ function formatInstallCommand(packageManagerId) {
1214
1797
  }
1215
1798
  }
1216
1799
  function getSyncRootError(cwd) {
1217
- return new Error(`No generated wp-typia project root was found at ${cwd}. Run \`wp-typia sync\` from a scaffolded project or official workspace root that already contains generated sync scripts. If you expected this directory to work, cd into the scaffold root first or rerun the scaffold before syncing.`);
1800
+ return createCliDiagnosticCodeError3(CLI_DIAGNOSTIC_CODES3.OUTSIDE_PROJECT_ROOT, `No generated wp-typia project root was found at ${cwd}. Run \`wp-typia sync\` from a scaffolded project or official workspace root that already contains generated sync scripts. If you expected this directory to work, cd into the scaffold root first or rerun the scaffold before syncing.`);
1218
1801
  }
1219
1802
  function inferSyncPackageManager(cwd, packageManagerField) {
1220
1803
  const field = String(packageManagerField ?? "");
@@ -1304,7 +1887,7 @@ function assertSyncDependenciesInstalled(project, target) {
1304
1887
  if (markerDir) {
1305
1888
  return;
1306
1889
  }
1307
- throw new Error(`Project dependencies have not been installed yet. Run \`${formatInstallCommand(project.packageManager)}\` from the project root before \`wp-typia sync\`. The generated sync scripts rely on local tools such as \`tsx\`.`);
1890
+ throw createCliDiagnosticCodeError3(CLI_DIAGNOSTIC_CODES3.DEPENDENCIES_NOT_INSTALLED, `Project dependencies have not been installed yet. Run \`${formatInstallCommand(project.packageManager)}\` from the project root before \`wp-typia sync\`. The generated sync scripts rely on local tools such as \`tsx\`.`);
1308
1891
  }
1309
1892
  function getPackageManagerRunInvocation(packageManager, scriptName, extraArgs) {
1310
1893
  switch (packageManager) {
@@ -1342,7 +1925,7 @@ function buildSyncPlannedCommands(project, extraArgs, target) {
1342
1925
  if (target === "ai") {
1343
1926
  const syncAiCommand2 = createSyncPlannedCommand(project, "sync-ai", extraArgs);
1344
1927
  if (!syncAiCommand2) {
1345
- throw new Error(`Expected ${project.packageJsonPath} to define a \`sync-ai\` script for \`wp-typia sync ai\`.`);
1928
+ throw createCliDiagnosticCodeError3(CLI_DIAGNOSTIC_CODES3.CONFIGURATION_MISSING, `Expected ${project.packageJsonPath} to define a \`sync-ai\` script for \`wp-typia sync ai\`.`);
1346
1929
  }
1347
1930
  return [syncAiCommand2];
1348
1931
  }
@@ -1351,7 +1934,7 @@ function buildSyncPlannedCommands(project, extraArgs, target) {
1351
1934
  }
1352
1935
  const syncTypesCommand = createSyncPlannedCommand(project, "sync-types", extraArgs);
1353
1936
  if (!syncTypesCommand) {
1354
- throw new Error(`Expected ${project.packageJsonPath} to define either a \`sync\` or \`sync-types\` script.`);
1937
+ throw createCliDiagnosticCodeError3(CLI_DIAGNOSTIC_CODES3.CONFIGURATION_MISSING, `Expected ${project.packageJsonPath} to define either a \`sync\` or \`sync-types\` script.`);
1355
1938
  }
1356
1939
  const plannedCommands = [syncTypesCommand];
1357
1940
  const syncRestCommand = createSyncPlannedCommand(project, "sync-rest", extraArgs);
@@ -1437,23 +2020,13 @@ function shouldWrapCliCommandError(options) {
1437
2020
  }
1438
2021
  return true;
1439
2022
  }
1440
- function readOptionalStringFlag(flags, name) {
1441
- const value = flags[name];
1442
- if (value === undefined || value === null) {
1443
- return;
1444
- }
1445
- if (typeof value !== "string" || value.trim().length === 0) {
1446
- throw new Error(`\`--${name}\` requires a value.`);
1447
- }
1448
- return value;
1449
- }
1450
2023
  function readOptionalLooseStringFlag(flags, name) {
1451
2024
  const value = flags[name];
1452
2025
  if (value === undefined || value === null) {
1453
2026
  return;
1454
2027
  }
1455
2028
  if (typeof value !== "string") {
1456
- throw new Error(`\`--${name}\` requires a value.`);
2029
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.MISSING_ARGUMENT, `\`--${name}\` requires a value.`);
1457
2030
  }
1458
2031
  const trimmed = value.trim();
1459
2032
  return trimmed.length > 0 ? trimmed : undefined;
@@ -1468,223 +2041,6 @@ function pushFlag(argv, name, value) {
1468
2041
  }
1469
2042
  argv.push(`--${name}`, String(value));
1470
2043
  }
1471
- function requireAddKindName(context, message) {
1472
- if (!context.name) {
1473
- throw new Error(message);
1474
- }
1475
- return context.name;
1476
- }
1477
- function runRegisteredAddKind(context, plan) {
1478
- return executeWorkspaceAddWithOptionalDryRun({
1479
- buildCompletion: plan.buildCompletion,
1480
- cwd: context.cwd,
1481
- dryRun: context.dryRun,
1482
- emitOutput: context.emitOutput,
1483
- execute: plan.execute,
1484
- printLine: context.printLine,
1485
- warnLine: plan.warnLine
1486
- });
1487
- }
1488
- var ADD_KIND_EXECUTION_REGISTRY = {
1489
- ability: async (context) => {
1490
- const name = requireAddKindName(context, "`wp-typia add ability` requires <name>. Usage: wp-typia add ability <name>.");
1491
- return runRegisteredAddKind(context, {
1492
- buildCompletion: (result) => buildAddCompletionPayload({
1493
- kind: "ability",
1494
- projectDir: result.projectDir,
1495
- values: {
1496
- abilitySlug: result.abilitySlug
1497
- }
1498
- }),
1499
- execute: (targetCwd) => context.addRuntime.runAddAbilityCommand({
1500
- abilityName: name,
1501
- cwd: targetCwd
1502
- })
1503
- });
1504
- },
1505
- "binding-source": async (context) => {
1506
- const name = requireAddKindName(context, "`wp-typia add binding-source` requires <name>. Usage: wp-typia add binding-source <name>.");
1507
- return runRegisteredAddKind(context, {
1508
- buildCompletion: (result) => buildAddCompletionPayload({
1509
- kind: "binding-source",
1510
- projectDir: result.projectDir,
1511
- values: {
1512
- bindingSourceSlug: result.bindingSourceSlug
1513
- }
1514
- }),
1515
- execute: (targetCwd) => context.addRuntime.runAddBindingSourceCommand({
1516
- bindingSourceName: name,
1517
- cwd: targetCwd
1518
- })
1519
- });
1520
- },
1521
- block: async (context) => {
1522
- const name = requireAddKindName(context, "`wp-typia add block` requires <name>. Usage: wp-typia add block <name> [--template <basic|interactivity|persistence|compound>]");
1523
- if (!context.flags.template && context.isInteractiveSession) {
1524
- throw new Error("`wp-typia add block` requires --template <basic|interactivity|persistence|compound> in interactive terminals. Non-interactive runs default to --template basic.");
1525
- }
1526
- const externalLayerId = readOptionalStringFlag(context.flags, "external-layer-id");
1527
- const externalLayerSource = readOptionalStringFlag(context.flags, "external-layer-source");
1528
- const shouldPromptForLayerSelection = Boolean(externalLayerSource) && !Boolean(externalLayerId) && context.isInteractiveSession;
1529
- const selectPrompt = shouldPromptForLayerSelection ? await context.getOrCreatePrompt() : undefined;
1530
- const alternateRenderTargets = readOptionalStringFlag(context.flags, "alternate-render-targets");
1531
- const dataStorageMode = readOptionalStringFlag(context.flags, "data-storage");
1532
- const innerBlocksPreset = readOptionalStringFlag(context.flags, "inner-blocks-preset");
1533
- const persistencePolicy = readOptionalStringFlag(context.flags, "persistence-policy");
1534
- const resolvedTemplateId = readOptionalStringFlag(context.flags, "template") ?? "basic";
1535
- return runRegisteredAddKind(context, {
1536
- buildCompletion: (result) => buildAddCompletionPayload({
1537
- kind: "block",
1538
- projectDir: result.projectDir,
1539
- values: {
1540
- blockSlugs: result.blockSlugs.join(", "),
1541
- templateId: result.templateId
1542
- },
1543
- warnings: result.warnings
1544
- }),
1545
- execute: (targetCwd) => context.addRuntime.runAddBlockCommand({
1546
- alternateRenderTargets,
1547
- blockName: name,
1548
- cwd: targetCwd,
1549
- dataStorageMode,
1550
- externalLayerId,
1551
- externalLayerSource,
1552
- innerBlocksPreset,
1553
- persistencePolicy,
1554
- selectExternalLayerId: selectPrompt ? (options) => selectPrompt.select("Select an external layer", toExternalLayerPromptOptions(options), 1) : undefined,
1555
- templateId: resolvedTemplateId
1556
- }),
1557
- warnLine: context.warnLine
1558
- });
1559
- },
1560
- "editor-plugin": async (context) => {
1561
- const name = requireAddKindName(context, "`wp-typia add editor-plugin` requires <name>. Usage: wp-typia add editor-plugin <name> [--slot <PluginSidebar>].");
1562
- const slot = readOptionalStringFlag(context.flags, "slot");
1563
- return runRegisteredAddKind(context, {
1564
- buildCompletion: (result) => buildAddCompletionPayload({
1565
- kind: "editor-plugin",
1566
- projectDir: result.projectDir,
1567
- values: {
1568
- editorPluginSlug: result.editorPluginSlug,
1569
- slot: result.slot
1570
- }
1571
- }),
1572
- execute: (targetCwd) => context.addRuntime.runAddEditorPluginCommand({
1573
- cwd: targetCwd,
1574
- editorPluginName: name,
1575
- slot
1576
- })
1577
- });
1578
- },
1579
- "ai-feature": async (context) => {
1580
- const name = requireAddKindName(context, "`wp-typia add ai-feature` requires <name>. Usage: wp-typia add ai-feature <name> [--namespace <vendor/v1>].");
1581
- const namespace = readOptionalStringFlag(context.flags, "namespace");
1582
- return runRegisteredAddKind(context, {
1583
- buildCompletion: (result) => buildAddCompletionPayload({
1584
- kind: "ai-feature",
1585
- projectDir: result.projectDir,
1586
- values: {
1587
- aiFeatureSlug: result.aiFeatureSlug,
1588
- namespace: result.namespace
1589
- },
1590
- warnings: result.warnings
1591
- }),
1592
- execute: (targetCwd) => context.addRuntime.runAddAiFeatureCommand({
1593
- aiFeatureName: name,
1594
- cwd: targetCwd,
1595
- namespace
1596
- })
1597
- });
1598
- },
1599
- "hooked-block": async (context) => {
1600
- const name = requireAddKindName(context, "`wp-typia add hooked-block` requires <block-slug>. Usage: wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <before|after|firstChild|lastChild>.");
1601
- const anchorBlockName = readOptionalStringFlag(context.flags, "anchor");
1602
- if (!anchorBlockName) {
1603
- throw new Error("`wp-typia add hooked-block` requires --anchor <anchor-block-name>.");
1604
- }
1605
- const position = readOptionalStringFlag(context.flags, "position");
1606
- if (!position) {
1607
- throw new Error("`wp-typia add hooked-block` requires --position <before|after|firstChild|lastChild>.");
1608
- }
1609
- return runRegisteredAddKind(context, {
1610
- buildCompletion: (result) => buildAddCompletionPayload({
1611
- kind: "hooked-block",
1612
- projectDir: result.projectDir,
1613
- values: {
1614
- anchorBlockName: result.anchorBlockName,
1615
- blockSlug: result.blockSlug,
1616
- position: result.position
1617
- }
1618
- }),
1619
- execute: (targetCwd) => context.addRuntime.runAddHookedBlockCommand({
1620
- anchorBlockName,
1621
- blockName: name,
1622
- cwd: targetCwd,
1623
- position
1624
- })
1625
- });
1626
- },
1627
- pattern: async (context) => {
1628
- const name = requireAddKindName(context, "`wp-typia add pattern` requires <name>. Usage: wp-typia add pattern <name>.");
1629
- return runRegisteredAddKind(context, {
1630
- buildCompletion: (result) => buildAddCompletionPayload({
1631
- kind: "pattern",
1632
- projectDir: result.projectDir,
1633
- values: {
1634
- patternSlug: result.patternSlug
1635
- }
1636
- }),
1637
- execute: (targetCwd) => context.addRuntime.runAddPatternCommand({
1638
- cwd: targetCwd,
1639
- patternName: name
1640
- })
1641
- });
1642
- },
1643
- "rest-resource": async (context) => {
1644
- const name = requireAddKindName(context, "`wp-typia add rest-resource` requires <name>. Usage: wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create>].");
1645
- const methods = readOptionalStringFlag(context.flags, "methods");
1646
- const namespace = readOptionalStringFlag(context.flags, "namespace");
1647
- return runRegisteredAddKind(context, {
1648
- buildCompletion: (result) => buildAddCompletionPayload({
1649
- kind: "rest-resource",
1650
- projectDir: result.projectDir,
1651
- values: {
1652
- methods: result.methods.join(", "),
1653
- namespace: result.namespace,
1654
- restResourceSlug: result.restResourceSlug
1655
- }
1656
- }),
1657
- execute: (targetCwd) => context.addRuntime.runAddRestResourceCommand({
1658
- cwd: targetCwd,
1659
- methods,
1660
- namespace,
1661
- restResourceName: name
1662
- })
1663
- });
1664
- },
1665
- variation: async (context) => {
1666
- const name = requireAddKindName(context, "`wp-typia add variation` requires <name>. Usage: wp-typia add variation <name> --block <block-slug>");
1667
- const blockSlug = readOptionalStringFlag(context.flags, "block");
1668
- if (!blockSlug) {
1669
- throw new Error("`wp-typia add variation` requires --block <block-slug>.");
1670
- }
1671
- return runRegisteredAddKind(context, {
1672
- buildCompletion: (result) => buildAddCompletionPayload({
1673
- kind: "variation",
1674
- projectDir: result.projectDir,
1675
- values: {
1676
- blockSlug: result.blockSlug,
1677
- variationSlug: result.variationSlug
1678
- }
1679
- }),
1680
- execute: (targetCwd) => context.addRuntime.runAddVariationCommand({
1681
- blockName: blockSlug,
1682
- cwd: targetCwd,
1683
- variationName: name
1684
- })
1685
- });
1686
- }
1687
- };
1688
2044
  async function executeWorkspaceAddWithOptionalDryRun(options) {
1689
2045
  const simulated = options.dryRun ? await simulateWorkspaceAddDryRun({
1690
2046
  cwd: options.cwd,
@@ -1708,6 +2064,26 @@ async function executeWorkspaceAddWithOptionalDryRun(options) {
1708
2064
  warnLine: options.warnLine
1709
2065
  });
1710
2066
  }
2067
+ function executePreparedAddKind(kind, context, plan) {
2068
+ return executeWorkspaceAddWithOptionalDryRun({
2069
+ buildCompletion: (result) => buildAddCompletionPayload({
2070
+ kind,
2071
+ projectDir: result.projectDir,
2072
+ values: plan.getValues(result),
2073
+ warnings: plan.getWarnings?.(result)
2074
+ }),
2075
+ cwd: context.cwd,
2076
+ dryRun: context.dryRun,
2077
+ emitOutput: context.emitOutput,
2078
+ execute: plan.execute,
2079
+ printLine: context.printLine,
2080
+ warnLine: plan.warnLine
2081
+ });
2082
+ }
2083
+ async function executePlannedAddKind(kind, executionContext, context) {
2084
+ const plan = await getAddKindExecutionPlan(kind, executionContext);
2085
+ return executePreparedAddKind(kind, context, plan);
2086
+ }
1711
2087
  var PACKAGE_MANAGER_PROMPT_OPTIONS = [
1712
2088
  { label: "npm", value: "npm", hint: "Use npm" },
1713
2089
  { label: "pnpm", value: "pnpm", hint: "Use pnpm" },
@@ -1796,7 +2172,7 @@ async function executeCreateCommand({
1796
2172
  promptText: activePrompt ? (message, defaultValue, validate) => activePrompt.text(message, defaultValue, validate) : undefined,
1797
2173
  queryPostType: readOptionalLooseStringFlag(flags, "query-post-type"),
1798
2174
  selectDataStorage: activePrompt ? () => activePrompt.select("Select a data storage mode", [...DATA_STORAGE_PROMPT_OPTIONS], 1) : undefined,
1799
- selectExternalLayerId: shouldPromptForExternalLayerSelection && activePrompt ? (options) => activePrompt.select("Select an external layer", toExternalLayerPromptOptions(options), 1) : undefined,
2175
+ selectExternalLayerId: shouldPromptForExternalLayerSelection && activePrompt ? (options) => activePrompt.select("Select an external layer", toExternalLayerPromptOptions2(options), 1) : undefined,
1800
2176
  selectPackageManager: activePrompt ? () => activePrompt.select("Select a package manager", [...PACKAGE_MANAGER_PROMPT_OPTIONS], 1) : undefined,
1801
2177
  selectPersistencePolicy: activePrompt ? () => activePrompt.select("Select a persistence policy", [...PERSISTENCE_POLICY_PROMPT_OPTIONS], 1) : undefined,
1802
2178
  selectTemplate: activePrompt ? () => activePrompt.select("Select a template", getTemplateSelectOptions(), 1) : undefined,
@@ -1849,16 +2225,17 @@ async function executeAddCommand({
1849
2225
  if (emitOutput) {
1850
2226
  printLine(addRuntime.formatAddHelpText());
1851
2227
  }
1852
- throw new Error(`\`wp-typia add\` requires <kind>. Usage: wp-typia add ${formatAddKindUsagePlaceholder()} ...`);
2228
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.MISSING_ARGUMENT, `\`wp-typia add\` requires <kind>. Usage: wp-typia add ${formatAddKindUsagePlaceholder()} ...`);
1853
2229
  }
1854
2230
  if (!isAddKindId(kind)) {
1855
- throw new Error(`Unknown add kind "${kind}". Expected one of: ${formatAddKindList()}.`);
2231
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.INVALID_COMMAND, `Unknown add kind "${kind}". Expected one of: ${formatAddKindList()}.`);
2232
+ }
2233
+ if (dryRun && !supportsAddKindDryRun(kind)) {
2234
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.INVALID_ARGUMENT, `\`wp-typia add ${kind}\` does not support \`--dry-run\` yet.`);
1856
2235
  }
1857
- return await ADD_KIND_EXECUTION_REGISTRY[kind]({
2236
+ const executionContext = {
1858
2237
  addRuntime,
1859
2238
  cwd,
1860
- dryRun,
1861
- emitOutput,
1862
2239
  flags,
1863
2240
  getOrCreatePrompt: async () => {
1864
2241
  if (activePrompt) {
@@ -1870,8 +2247,13 @@ async function executeAddCommand({
1870
2247
  },
1871
2248
  isInteractiveSession,
1872
2249
  name,
1873
- printLine,
1874
2250
  warnLine
2251
+ };
2252
+ return await executePlannedAddKind(kind, executionContext, {
2253
+ cwd,
2254
+ dryRun,
2255
+ emitOutput,
2256
+ printLine
1875
2257
  });
1876
2258
  } catch (error) {
1877
2259
  if (!shouldWrapCliCommandError({ emitOutput })) {
@@ -1901,22 +2283,26 @@ async function executeTemplatesCommand({ flags }, printLine = console.log) {
1901
2283
  }
1902
2284
  if (subcommand === "inspect") {
1903
2285
  if (!flags.id) {
1904
- throw new Error("`wp-typia templates inspect` requires <template-id>.");
2286
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.MISSING_ARGUMENT, "`wp-typia templates inspect` requires <template-id>.");
1905
2287
  }
1906
2288
  const template = getTemplateById(flags.id);
1907
2289
  if (!template) {
1908
- throw new Error(`Unknown template "${flags.id}".`);
2290
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.INVALID_ARGUMENT, `Unknown template "${flags.id}".`);
1909
2291
  }
1910
2292
  printBlock([formatTemplateDetails(template)], printLine);
1911
2293
  return;
1912
2294
  }
1913
- throw new Error(`Unknown templates subcommand "${subcommand}". Expected list or inspect.`);
2295
+ throw createCliDiagnosticCodeError4(CLI_DIAGNOSTIC_CODES4.INVALID_COMMAND, `Unknown templates subcommand "${subcommand}". Expected list or inspect.`);
1914
2296
  }
1915
- async function executeInitCommand({ cwd, projectDir }, options = {}) {
2297
+ async function executeInitCommand({ apply, cwd, packageManager, projectDir }, options = {}) {
1916
2298
  try {
1917
- const { getInitPlan } = await loadCliInitRuntime();
2299
+ const { runInitCommand } = await loadCliInitRuntime();
1918
2300
  const resolvedProjectDir = projectDir ? (await import("node:path")).resolve(cwd, projectDir) : cwd;
1919
- const plan = getInitPlan(resolvedProjectDir);
2301
+ const plan = await runInitCommand({
2302
+ apply,
2303
+ packageManager,
2304
+ projectDir: resolvedProjectDir
2305
+ });
1920
2306
  const completion = buildInitCompletionPayload(plan);
1921
2307
  if (options.emitOutput ?? true) {
1922
2308
  printCompletionPayload(completion, {
@@ -1926,6 +2312,9 @@ async function executeInitCommand({ cwd, projectDir }, options = {}) {
1926
2312
  }
1927
2313
  return plan;
1928
2314
  } catch (error) {
2315
+ if (!shouldWrapCliCommandError({ emitOutput: options.emitOutput })) {
2316
+ throw error;
2317
+ }
1929
2318
  throw await wrapCliCommandError("init", error);
1930
2319
  }
1931
2320
  }
@@ -1992,57 +2381,14 @@ async function executeMigrateCommand({
1992
2381
 
1993
2382
  // src/command-contract.ts
1994
2383
  import path5 from "node:path";
1995
- var WP_TYPIA_CANONICAL_CREATE_USAGE = "wp-typia create <project-dir>";
1996
- var WP_TYPIA_POSITIONAL_ALIAS_USAGE = "wp-typia <project-dir>";
1997
- var WP_TYPIA_CANONICAL_MIGRATE_USAGE = "wp-typia migrate <subcommand>";
1998
- var WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES = [
1999
- "create",
2000
- "init",
2001
- "sync",
2002
- "add",
2003
- "migrate",
2004
- "templates",
2005
- "doctor",
2006
- "mcp",
2007
- "help",
2008
- "version",
2009
- "skills",
2010
- "completions",
2011
- "complete"
2012
- ];
2013
- var WP_TYPIA_NODE_FALLBACK_TOP_LEVEL_COMMAND_NAMES = [
2014
- "create",
2015
- "init",
2016
- "sync",
2017
- "add",
2018
- "migrate",
2019
- "templates",
2020
- "doctor",
2021
- "help",
2022
- "version"
2023
- ];
2024
- var NODE_FALLBACK_TOP_LEVEL_COMMAND_NAME_SET = new Set(WP_TYPIA_NODE_FALLBACK_TOP_LEVEL_COMMAND_NAMES);
2025
- var WP_TYPIA_BUN_REQUIRED_TOP_LEVEL_COMMAND_NAMES = WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES.filter((name) => !NODE_FALLBACK_TOP_LEVEL_COMMAND_NAME_SET.has(name));
2026
- var SHARED_OPTION_PARSER = buildCommandOptionParser(ADD_OPTION_METADATA, GLOBAL_OPTION_METADATA, CREATE_OPTION_METADATA, MIGRATE_OPTION_METADATA, TEMPLATES_OPTION_METADATA);
2027
- var STRING_OPTION_NAMES_BY_COMMAND = {
2028
- add: new Set(collectOptionNamesByType(ADD_OPTION_METADATA, "string")),
2029
- create: new Set(collectOptionNamesByType(CREATE_OPTION_METADATA, "string")),
2030
- migrate: new Set(collectOptionNamesByType(MIGRATE_OPTION_METADATA, "string")),
2031
- templates: new Set(collectOptionNamesByType(TEMPLATES_OPTION_METADATA, "string"))
2032
- };
2033
- var GLOBAL_STRING_OPTION_NAMES = new Set(collectOptionNamesByType(GLOBAL_OPTION_METADATA, "string"));
2034
- var SHORT_OPTION_NAMES_WITH_VALUES = new Set([...SHARED_OPTION_PARSER.shortFlagMap.entries()].filter(([, option]) => option.type === "string").map(([short]) => short));
2035
- function isLongOptionValueConsumer(optionName) {
2036
- if (GLOBAL_STRING_OPTION_NAMES.has(optionName)) {
2037
- return true;
2038
- }
2039
- return Object.values(STRING_OPTION_NAMES_BY_COMMAND).some((optionNames) => optionNames.has(optionName));
2040
- }
2041
- function findFirstPositionalIndex(argv) {
2042
- const positionalIndexes = collectPositionalIndexes(argv);
2043
- return positionalIndexes[0] ?? -1;
2384
+
2385
+ // bin/argv-walker.js
2386
+ function normalizeOptionSet(values) {
2387
+ return values instanceof Set ? values : new Set(values);
2044
2388
  }
2045
- function collectPositionalIndexes(argv) {
2389
+ function collectPositionalIndexes(argv, metadata) {
2390
+ const longValueOptionSet = normalizeOptionSet(metadata.longValueOptions);
2391
+ const shortValueOptionSet = normalizeOptionSet(metadata.shortValueOptions);
2046
2392
  const positionalIndexes = [];
2047
2393
  for (let index = 0;index < argv.length; index += 1) {
2048
2394
  const arg = argv[index];
@@ -2060,37 +2406,67 @@ function collectPositionalIndexes(argv) {
2060
2406
  if (arg.includes("=")) {
2061
2407
  continue;
2062
2408
  }
2063
- if (isLongOptionValueConsumer(arg.slice(2))) {
2409
+ const next = argv[index + 1];
2410
+ if (longValueOptionSet.has(arg) && next && !next.startsWith("-")) {
2064
2411
  index += 1;
2065
2412
  }
2066
2413
  continue;
2067
2414
  }
2068
- if (arg.length === 2 && SHORT_OPTION_NAMES_WITH_VALUES.has(arg.slice(1))) {
2415
+ if (arg.length === 2 && shortValueOptionSet.has(arg) && argv[index + 1] && !argv[index + 1].startsWith("-")) {
2069
2416
  index += 1;
2070
2417
  }
2071
2418
  }
2072
2419
  return positionalIndexes;
2073
2420
  }
2074
- var WP_TYPIA_FUTURE_COMMAND_TREE = [
2421
+ function findFirstPositionalIndex(argv, metadata) {
2422
+ const positionalIndexes = collectPositionalIndexes(argv, metadata);
2423
+ return positionalIndexes[0] ?? -1;
2424
+ }
2425
+
2426
+ // src/command-registry.ts
2427
+ var WP_TYPIA_CANONICAL_CREATE_USAGE = "wp-typia create <project-dir>";
2428
+ var WP_TYPIA_POSITIONAL_ALIAS_USAGE = "wp-typia <project-dir>";
2429
+ var WP_TYPIA_CANONICAL_MIGRATE_USAGE = "wp-typia migrate <subcommand>";
2430
+ var WP_TYPIA_COMMAND_REGISTRY = [
2075
2431
  {
2432
+ commandTree: true,
2076
2433
  description: "Scaffold a new wp-typia project.",
2077
- name: "create"
2434
+ interactiveRuntime: true,
2435
+ name: "create",
2436
+ nodeFallback: true,
2437
+ optionGroups: ["create"],
2438
+ requiresBunRuntime: false
2078
2439
  },
2079
2440
  {
2080
- description: "Preview the minimum retrofit plan for an existing project.",
2081
- name: "init"
2441
+ commandTree: true,
2442
+ description: "Preview or apply the minimum retrofit plan for an existing project.",
2443
+ name: "init",
2444
+ nodeFallback: true,
2445
+ optionGroups: ["init"],
2446
+ requiresBunRuntime: false
2082
2447
  },
2083
2448
  {
2449
+ commandTree: true,
2084
2450
  description: "Run the common generated-project sync workflow.",
2085
2451
  name: "sync",
2452
+ nodeFallback: true,
2453
+ optionGroups: ["sync"],
2454
+ requiresBunRuntime: false,
2086
2455
  subcommands: ["ai"]
2087
2456
  },
2088
2457
  {
2458
+ commandTree: true,
2089
2459
  description: "Extend an official wp-typia workspace.",
2460
+ interactiveRuntime: true,
2090
2461
  name: "add",
2462
+ nodeFallback: true,
2463
+ optionGroups: ["add"],
2464
+ requiresBunRuntime: false,
2091
2465
  subcommands: [
2092
2466
  "block",
2093
2467
  "variation",
2468
+ "style",
2469
+ "transform",
2094
2470
  "pattern",
2095
2471
  "binding-source",
2096
2472
  "rest-resource",
@@ -2099,8 +2475,13 @@ var WP_TYPIA_FUTURE_COMMAND_TREE = [
2099
2475
  ]
2100
2476
  },
2101
2477
  {
2478
+ commandTree: true,
2102
2479
  description: "Run migration workflows.",
2480
+ interactiveRuntime: true,
2103
2481
  name: "migrate",
2482
+ nodeFallback: true,
2483
+ optionGroups: ["migrate"],
2484
+ requiresBunRuntime: false,
2104
2485
  subcommands: [
2105
2486
  "init",
2106
2487
  "snapshot",
@@ -2115,25 +2496,99 @@ var WP_TYPIA_FUTURE_COMMAND_TREE = [
2115
2496
  ]
2116
2497
  },
2117
2498
  {
2499
+ commandTree: true,
2118
2500
  description: "Inspect scaffold templates.",
2119
2501
  name: "templates",
2502
+ nodeFallback: true,
2503
+ optionGroups: ["templates"],
2504
+ requiresBunRuntime: false,
2120
2505
  subcommands: ["list", "inspect"]
2121
2506
  },
2122
2507
  {
2508
+ commandTree: true,
2123
2509
  description: "Run repository and project diagnostics.",
2124
- name: "doctor"
2510
+ name: "doctor",
2511
+ nodeFallback: true,
2512
+ optionGroups: ["doctor"],
2513
+ requiresBunRuntime: false
2125
2514
  },
2126
2515
  {
2516
+ commandTree: true,
2127
2517
  description: "Inspect or sync schema-driven MCP metadata.",
2128
2518
  name: "mcp",
2519
+ nodeFallback: false,
2520
+ optionGroups: [],
2521
+ requiresBunRuntime: true,
2129
2522
  subcommands: ["list", "sync"]
2523
+ },
2524
+ {
2525
+ commandTree: false,
2526
+ name: "help",
2527
+ nodeFallback: true,
2528
+ optionGroups: [],
2529
+ requiresBunRuntime: false
2530
+ },
2531
+ {
2532
+ commandTree: false,
2533
+ name: "version",
2534
+ nodeFallback: true,
2535
+ optionGroups: [],
2536
+ requiresBunRuntime: false
2537
+ },
2538
+ {
2539
+ commandTree: false,
2540
+ name: "skills",
2541
+ nodeFallback: false,
2542
+ optionGroups: [],
2543
+ requiresBunRuntime: true
2544
+ },
2545
+ {
2546
+ commandTree: false,
2547
+ name: "completions",
2548
+ nodeFallback: false,
2549
+ optionGroups: [],
2550
+ requiresBunRuntime: true
2551
+ },
2552
+ {
2553
+ commandTree: false,
2554
+ name: "complete",
2555
+ nodeFallback: false,
2556
+ optionGroups: [],
2557
+ requiresBunRuntime: true
2130
2558
  }
2131
2559
  ];
2560
+ var WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES = WP_TYPIA_COMMAND_REGISTRY.map((command) => command.name);
2561
+ var WP_TYPIA_NODE_FALLBACK_TOP_LEVEL_COMMAND_NAMES = WP_TYPIA_COMMAND_REGISTRY.filter((command) => command.nodeFallback).map((command) => command.name);
2562
+ var WP_TYPIA_INTERACTIVE_RUNTIME_TOP_LEVEL_COMMAND_NAMES = WP_TYPIA_COMMAND_REGISTRY.filter((command) => ("interactiveRuntime" in command) && command.interactiveRuntime).map((command) => command.name);
2563
+ var WP_TYPIA_TOP_LEVEL_COMMAND_NAMES = WP_TYPIA_COMMAND_REGISTRY.filter((command) => command.commandTree).map((command) => command.name);
2564
+ var WP_TYPIA_BUN_REQUIRED_TOP_LEVEL_COMMAND_NAMES = WP_TYPIA_COMMAND_REGISTRY.filter((command) => command.requiresBunRuntime).map((command) => command.name);
2565
+ var WP_TYPIA_FUTURE_COMMAND_TREE = WP_TYPIA_COMMAND_REGISTRY.filter((command) => command.commandTree).map((command) => ({
2566
+ description: command.description,
2567
+ name: command.name,
2568
+ subcommands: "subcommands" in command ? command.subcommands : undefined
2569
+ }));
2570
+ var commandOptionGroupNamesByTopLevelCommand = {};
2571
+ for (const command of WP_TYPIA_COMMAND_REGISTRY) {
2572
+ commandOptionGroupNamesByTopLevelCommand[command.name] = command.optionGroups;
2573
+ }
2574
+ var WP_TYPIA_COMMAND_OPTION_GROUP_NAMES_BY_TOP_LEVEL_COMMAND = commandOptionGroupNamesByTopLevelCommand;
2575
+ // src/command-contract.ts
2576
+ var SHARED_OPTION_PARSER = buildCommandOptionParser(ALL_COMMAND_OPTION_METADATA);
2577
+ var STRING_OPTION_NAMES_BY_GROUP = Object.fromEntries(Object.entries(COMMAND_OPTION_METADATA_BY_GROUP).map(([groupName, metadata]) => [
2578
+ groupName,
2579
+ new Set(collectOptionNamesByType(metadata, "string"))
2580
+ ]));
2581
+ var STRING_OPTION_NAMES_BY_COMMAND = Object.fromEntries(WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES.map((commandName) => [
2582
+ commandName,
2583
+ new Set(WP_TYPIA_COMMAND_OPTION_GROUP_NAMES_BY_TOP_LEVEL_COMMAND[commandName].flatMap((groupName) => Array.from(STRING_OPTION_NAMES_BY_GROUP[groupName])))
2584
+ ]));
2585
+ var GLOBAL_STRING_OPTION_NAMES = new Set(collectOptionNamesByType(GLOBAL_OPTION_METADATA, "string"));
2586
+ var SHORT_OPTION_NAMES_WITH_VALUES = new Set([...SHARED_OPTION_PARSER.shortFlagMap.entries()].filter(([, option]) => option.type === "string").map(([short]) => short));
2132
2587
  function isReservedTopLevelCommandName(value) {
2133
2588
  return WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES.includes(value);
2134
2589
  }
2135
2590
  function assertStringOptionValues(argv) {
2136
- const firstPositionalIndex = findFirstPositionalIndex(argv);
2591
+ const firstPositionalIndex = findFirstPositionalIndex(argv, COMMAND_ROUTING_METADATA);
2137
2592
  if (firstPositionalIndex === -1) {
2138
2593
  return;
2139
2594
  }
@@ -2199,7 +2654,7 @@ function assertPositionalAliasProjectDir(projectDir) {
2199
2654
  }
2200
2655
  }
2201
2656
  function normalizeWpTypiaArgv(argv) {
2202
- const positionalIndexes = collectPositionalIndexes(argv);
2657
+ const positionalIndexes = collectPositionalIndexes(argv, COMMAND_ROUTING_METADATA);
2203
2658
  const firstPositionalIndex = positionalIndexes[0] ?? -1;
2204
2659
  if (firstPositionalIndex === -1) {
2205
2660
  return argv;
@@ -2230,13 +2685,14 @@ function normalizeWpTypiaArgv(argv) {
2230
2685
  }
2231
2686
 
2232
2687
  // src/node-cli.ts
2233
- var NODE_FALLBACK_OPTION_PARSER = buildCommandOptionParser(GLOBAL_OPTION_METADATA, CREATE_OPTION_METADATA, ADD_OPTION_METADATA, MIGRATE_OPTION_METADATA, SYNC_OPTION_METADATA, TEMPLATES_OPTION_METADATA);
2688
+ var NODE_FALLBACK_OPTION_PARSER = buildCommandOptionParser(ALL_COMMAND_OPTION_METADATA);
2234
2689
  var NODE_FALLBACK_BOOLEAN_OPTION_NAMES = ["help", "version"];
2235
2690
  var STANDALONE_GUIDANCE_LINE = "Prefer not to install Bun? Use the standalone wp-typia binary from the GitHub release assets.";
2236
2691
  var NODE_FALLBACK_RUNTIME_SUMMARY_LINES = [
2237
2692
  "Runtime: Node fallback",
2238
2693
  "Human-readable fallback for common non-interactive create/init/add/migrate flows, doctor, sync, templates, --help, and --version when Bun is unavailable.",
2239
- `Install Bun 1.3.11+ or use \`bunx wp-typia ...\` for the full Bunli/OpenTUI runtime and Bun-only command surfaces such as \`skills\`, \`completions\`, and \`mcp\`. ${STANDALONE_GUIDANCE_LINE}`
2694
+ `Install Bun 1.3.11+ or use \`bunx wp-typia ...\` for the full Bunli/OpenTUI runtime and Bun-only command surfaces such as \`skills\`, \`completions\`, and \`mcp\`. ${STANDALONE_GUIDANCE_LINE}`,
2695
+ "Output markers: WP_TYPIA_ASCII=1 forces ASCII markers, WP_TYPIA_ASCII=0 opts back into Unicode markers, and non-empty NO_COLOR requests ASCII markers when WP_TYPIA_ASCII is unset."
2240
2696
  ];
2241
2697
  function printLine(line = "") {
2242
2698
  console.log(line);
@@ -2258,44 +2714,24 @@ function hasFlagBeforeTerminator(argv, flag) {
2258
2714
  return false;
2259
2715
  }
2260
2716
  function parseGlobalFlags(argv) {
2261
- const nextArgv = [];
2262
- const flags = {};
2263
- for (let index = 0;index < argv.length; index += 1) {
2264
- const arg = argv[index];
2265
- if (!arg) {
2266
- continue;
2267
- }
2268
- if (arg === "--") {
2269
- nextArgv.push(...argv.slice(index));
2270
- break;
2271
- }
2272
- if (arg === "--format" || arg === "--id") {
2273
- const next = argv[index + 1];
2274
- if (!next || next.startsWith("-")) {
2275
- throw new Error(`\`${arg}\` requires a value.`);
2276
- }
2277
- if (arg === "--format") {
2278
- flags.format = next;
2279
- } else {
2280
- flags.id = next;
2717
+ try {
2718
+ const { argv: nextArgv, flags } = extractKnownOptionValuesFromArgv(argv, {
2719
+ optionNames: ["format", "id"],
2720
+ parser: NODE_FALLBACK_OPTION_PARSER
2721
+ });
2722
+ return {
2723
+ argv: nextArgv,
2724
+ flags: {
2725
+ format: typeof flags.format === "string" ? flags.format : undefined,
2726
+ id: typeof flags.id === "string" ? flags.id : undefined
2281
2727
  }
2282
- index += 1;
2283
- continue;
2284
- }
2285
- if (arg.startsWith("--format=")) {
2286
- flags.format = arg.slice("--format=".length);
2287
- continue;
2288
- }
2289
- if (arg.startsWith("--id=")) {
2290
- flags.id = arg.slice("--id=".length);
2291
- continue;
2728
+ };
2729
+ } catch (error) {
2730
+ if (error instanceof Error && /\`--format\` requires a value\.|\`--id\` requires a value\./.test(error.message)) {
2731
+ throw createCliDiagnosticCodeError5(CLI_DIAGNOSTIC_CODES5.MISSING_ARGUMENT, error.message);
2292
2732
  }
2293
- nextArgv.push(arg);
2733
+ throw error;
2294
2734
  }
2295
- return {
2296
- argv: nextArgv,
2297
- flags
2298
- };
2299
2735
  }
2300
2736
  async function applyNodeFallbackConfigDefaults(command, subcommand, flags, configOverridePath, cwd) {
2301
2737
  let config = await loadWpTypiaUserConfig(cwd);
@@ -2373,7 +2809,10 @@ function renderInitHelp() {
2373
2809
  "",
2374
2810
  ...NODE_FALLBACK_RUNTIME_SUMMARY_LINES,
2375
2811
  "",
2376
- "Preview-only retrofit planner for existing WordPress block or plugin projects. No files are written yet."
2812
+ "Preview-by-default retrofit planner for existing WordPress block or plugin projects. Re-run with --apply to write package.json updates and helper scripts.",
2813
+ "",
2814
+ "Supported flags:",
2815
+ ...formatNodeFallbackOptionHelp(INIT_OPTION_METADATA)
2377
2816
  ]);
2378
2817
  }
2379
2818
  function renderAddHelp() {
@@ -2408,6 +2847,27 @@ function renderSyncHelp() {
2408
2847
  ...formatNodeFallbackOptionHelp(SYNC_OPTION_METADATA)
2409
2848
  ]);
2410
2849
  }
2850
+ function renderDoctorHelp() {
2851
+ printBlock2([
2852
+ "Usage: wp-typia doctor [--format json]",
2853
+ "",
2854
+ ...NODE_FALLBACK_RUNTIME_SUMMARY_LINES,
2855
+ "",
2856
+ "Runs read-only environment readiness checks. Official wp-typia workspace roots also get inventory, source-tree drift, iframe/API v3 compatibility, and shared convention checks.",
2857
+ "",
2858
+ "Supported flags:",
2859
+ ...formatNodeFallbackOptionHelp(DOCTOR_OPTION_METADATA)
2860
+ ]);
2861
+ }
2862
+ var NODE_FALLBACK_HELP_RENDERERS = {
2863
+ add: renderAddHelp,
2864
+ create: renderCreateHelp,
2865
+ doctor: renderDoctorHelp,
2866
+ init: renderInitHelp,
2867
+ migrate: renderMigrateHelp,
2868
+ sync: renderSyncHelp,
2869
+ templates: renderTemplatesHelp
2870
+ };
2411
2871
  function renderVersion(options = {}) {
2412
2872
  if (options.format === "json") {
2413
2873
  printLine(JSON.stringify({
@@ -2432,7 +2892,7 @@ function renderTemplatesJson(flags, subcommand) {
2432
2892
  const templateId = flags.id;
2433
2893
  if (!templateId) {
2434
2894
  throw createCliCommandError({
2435
- code: CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT,
2895
+ code: CLI_DIAGNOSTIC_CODES5.MISSING_ARGUMENT,
2436
2896
  command: "templates",
2437
2897
  detailLines: ["`wp-typia templates inspect` requires <template-id>."]
2438
2898
  });
@@ -2440,7 +2900,7 @@ function renderTemplatesJson(flags, subcommand) {
2440
2900
  const template = getTemplateById(templateId);
2441
2901
  if (!template) {
2442
2902
  throw createCliCommandError({
2443
- code: CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT,
2903
+ code: CLI_DIAGNOSTIC_CODES5.INVALID_ARGUMENT,
2444
2904
  command: "templates",
2445
2905
  detailLines: [`Unknown template "${templateId}".`]
2446
2906
  });
@@ -2451,7 +2911,7 @@ function renderTemplatesJson(flags, subcommand) {
2451
2911
  }
2452
2912
  function renderUnsupportedCommand(command) {
2453
2913
  throw createCliCommandError({
2454
- code: CLI_DIAGNOSTIC_CODES.UNSUPPORTED_COMMAND,
2914
+ code: CLI_DIAGNOSTIC_CODES5.UNSUPPORTED_COMMAND,
2455
2915
  command,
2456
2916
  detailLines: [
2457
2917
  [
@@ -2477,118 +2937,24 @@ async function renderDoctorJson() {
2477
2937
  }, null, 2));
2478
2938
  if (checks.some((check) => check.status === "fail")) {
2479
2939
  throw createCliCommandError2({
2940
+ code: CLI_DIAGNOSTIC_CODES5.DOCTOR_CHECK_FAILED,
2480
2941
  command: "doctor",
2481
2942
  detailLines: getDoctorFailureDetailLines(checks),
2482
2943
  summary: "One or more doctor checks failed."
2483
2944
  });
2484
2945
  }
2485
2946
  }
2486
- async function runNodeCli(argv = process.argv.slice(2)) {
2487
- const normalizedArgv = normalizeWpTypiaArgv(argv);
2488
- const { argv: argvWithoutConfigOverride, configOverridePath } = extractWpTypiaConfigOverride(normalizedArgv);
2489
- const { argv: cliArgv, flags } = parseGlobalFlags(argvWithoutConfigOverride);
2490
- const { flags: commandFlags, positionals } = parseArgv(cliArgv);
2491
- const rawMergedFlags = {
2492
- ...commandFlags,
2493
- ...flags
2494
- };
2495
- const [command, subcommand] = positionals;
2496
- const helpRequested = cliArgv.length === 0 || hasFlagBeforeTerminator(cliArgv, "--help") || command === "help";
2497
- const versionRequested = hasFlagBeforeTerminator(cliArgv, "--version") || command === "version";
2498
- if (helpRequested) {
2499
- if (command === "templates") {
2500
- renderTemplatesHelp();
2501
- return;
2502
- }
2503
- if (command === "create") {
2504
- renderCreateHelp();
2505
- return;
2506
- }
2507
- if (command === "init") {
2508
- renderInitHelp();
2509
- return;
2510
- }
2511
- if (command === "add") {
2512
- renderAddHelp();
2513
- return;
2514
- }
2515
- if (command === "migrate") {
2516
- renderMigrateHelp();
2517
- return;
2518
- }
2519
- if (command === "sync") {
2520
- renderSyncHelp();
2521
- return;
2522
- }
2523
- renderGeneralHelp();
2524
- return;
2525
- }
2526
- if (versionRequested) {
2527
- renderVersion({
2528
- format: typeof rawMergedFlags.format === "string" ? rawMergedFlags.format : undefined
2529
- });
2530
- return;
2531
- }
2532
- const mergedFlags = await applyNodeFallbackConfigDefaults(command, subcommand, rawMergedFlags, configOverridePath, process.cwd());
2533
- if (command === "templates") {
2534
- const templateId = typeof mergedFlags.id === "string" ? mergedFlags.id : positionals[2];
2535
- const resolvedSubcommand = templateId ? "inspect" : subcommand ?? "list";
2536
- if (resolvedSubcommand !== "list" && resolvedSubcommand !== "inspect") {
2537
- throw new Error(`Unknown templates subcommand "${resolvedSubcommand}". Expected list or inspect.`);
2538
- }
2539
- if (mergedFlags.format === "json") {
2540
- renderTemplatesJson({
2541
- format: mergedFlags.format,
2542
- id: templateId
2543
- }, resolvedSubcommand);
2544
- return;
2545
- }
2546
- await executeTemplatesCommand({
2547
- flags: {
2548
- id: templateId,
2549
- subcommand: resolvedSubcommand
2550
- }
2551
- });
2552
- return;
2553
- }
2554
- if (command === "create") {
2555
- const projectDir = positionals[1];
2556
- if (!projectDir) {
2557
- throw createCliCommandError({
2558
- command: "create",
2559
- detailLines: [
2560
- "`wp-typia create` requires <project-dir>.",
2561
- "`--dry-run` still needs a logical project directory name because wp-typia derives slugs, package names, and planned file paths from it."
2562
- ]
2563
- });
2564
- }
2565
- await executeCreateCommand({
2566
- cwd: process.cwd(),
2567
- flags: mergedFlags,
2568
- interactive: false,
2569
- projectDir
2570
- });
2571
- return;
2572
- }
2573
- if (command === "init") {
2574
- const plan = await executeInitCommand({
2575
- cwd: process.cwd(),
2576
- projectDir: positionals[1]
2577
- }, {
2578
- emitOutput: mergedFlags.format !== "json"
2579
- });
2580
- if (mergedFlags.format === "json") {
2581
- printLine(JSON.stringify({
2582
- init: plan
2583
- }, null, 2));
2584
- }
2585
- return;
2586
- }
2587
- if (command === "add") {
2947
+ var NODE_FALLBACK_COMMAND_DISPATCHERS = {
2948
+ add: async ({
2949
+ cwd,
2950
+ mergedFlags,
2951
+ positionals
2952
+ }) => {
2588
2953
  if (!positionals[1]) {
2589
2954
  const { formatAddHelpText } = await import("@wp-typia/project-tools/cli-add");
2590
2955
  printLine(formatAddHelpText());
2591
2956
  throw createCliCommandError({
2957
+ code: CLI_DIAGNOSTIC_CODES5.MISSING_ARGUMENT,
2592
2958
  command: "add",
2593
2959
  detailLines: [
2594
2960
  `\`wp-typia add\` requires <kind>. Usage: wp-typia add ${formatAddKindUsagePlaceholder()} ...`
@@ -2599,7 +2965,7 @@ async function runNodeCli(argv = process.argv.slice(2)) {
2599
2965
  let completion;
2600
2966
  try {
2601
2967
  completion = await executeAddCommand({
2602
- cwd: process.cwd(),
2968
+ cwd,
2603
2969
  emitOutput: false,
2604
2970
  flags: mergedFlags,
2605
2971
  interactive: false,
@@ -2612,43 +2978,107 @@ async function runNodeCli(argv = process.argv.slice(2)) {
2612
2978
  error
2613
2979
  });
2614
2980
  }
2615
- printLine(JSON.stringify({
2616
- completion
2617
- }, null, 2));
2981
+ printLine(JSON.stringify(buildStructuredCompletionSuccessPayload("add", completion, {
2982
+ dryRun: Boolean(mergedFlags["dry-run"]),
2983
+ kind: positionals[1],
2984
+ name: positionals[2],
2985
+ projectDir: extractCompletionProjectDir(completion) ?? cwd
2986
+ }), null, 2));
2618
2987
  return;
2619
2988
  }
2620
2989
  await executeAddCommand({
2621
- cwd: process.cwd(),
2990
+ cwd,
2622
2991
  flags: mergedFlags,
2623
- interactive: false,
2992
+ interactive: undefined,
2624
2993
  kind: positionals[1],
2625
2994
  name: positionals[2]
2626
2995
  });
2627
- return;
2628
- }
2629
- if (command === "migrate") {
2630
- await executeMigrateCommand({
2631
- command: positionals[1],
2632
- cwd: process.cwd(),
2633
- flags: mergedFlags
2634
- });
2635
- return;
2636
- }
2637
- if (command === "doctor") {
2996
+ },
2997
+ create: async ({
2998
+ cwd,
2999
+ mergedFlags,
3000
+ positionals
3001
+ }) => {
3002
+ const projectDir = positionals[1];
3003
+ if (!projectDir) {
3004
+ throw createCliCommandError({
3005
+ code: CLI_DIAGNOSTIC_CODES5.MISSING_ARGUMENT,
3006
+ command: "create",
3007
+ detailLines: [
3008
+ "`wp-typia create` requires <project-dir>.",
3009
+ "`--dry-run` still needs a logical project directory name because wp-typia derives slugs, package names, and planned file paths from it."
3010
+ ]
3011
+ });
3012
+ }
3013
+ let completion;
3014
+ try {
3015
+ completion = await executeCreateCommand({
3016
+ cwd,
3017
+ emitOutput: mergedFlags.format !== "json",
3018
+ flags: mergedFlags,
3019
+ interactive: mergedFlags.format === "json" ? false : undefined,
3020
+ projectDir
3021
+ });
3022
+ } catch (error) {
3023
+ throw createCliCommandError({
3024
+ command: "create",
3025
+ error
3026
+ });
3027
+ }
3028
+ if (mergedFlags.format === "json") {
3029
+ printLine(JSON.stringify(buildStructuredCompletionSuccessPayload("create", completion, {
3030
+ dryRun: Boolean(mergedFlags["dry-run"]),
3031
+ projectDir: extractCompletionProjectDir(completion) ?? projectDir,
3032
+ template: typeof mergedFlags.template === "string" ? mergedFlags.template : undefined
3033
+ }), null, 2));
3034
+ }
3035
+ },
3036
+ doctor: async ({ cwd, mergedFlags }) => {
2638
3037
  if (mergedFlags.format === "json") {
2639
3038
  await renderDoctorJson();
2640
3039
  return;
2641
3040
  }
2642
- await executeDoctorCommand(process.cwd());
2643
- return;
2644
- }
2645
- if (command === "sync") {
3041
+ await executeDoctorCommand(cwd);
3042
+ },
3043
+ init: async ({
3044
+ cwd,
3045
+ mergedFlags,
3046
+ positionals
3047
+ }) => {
3048
+ const plan = await executeInitCommand({
3049
+ apply: Boolean(mergedFlags.apply),
3050
+ cwd,
3051
+ packageManager: typeof mergedFlags["package-manager"] === "string" ? mergedFlags["package-manager"] : undefined,
3052
+ projectDir: positionals[1]
3053
+ }, {
3054
+ emitOutput: mergedFlags.format !== "json"
3055
+ });
3056
+ if (mergedFlags.format === "json") {
3057
+ printLine(JSON.stringify(buildStructuredInitSuccessPayload(plan), null, 2));
3058
+ }
3059
+ },
3060
+ migrate: async ({
3061
+ cwd,
3062
+ mergedFlags,
3063
+ positionals
3064
+ }) => {
3065
+ await executeMigrateCommand({
3066
+ command: positionals[1],
3067
+ cwd,
3068
+ flags: mergedFlags
3069
+ });
3070
+ },
3071
+ sync: async ({
3072
+ cwd,
3073
+ mergedFlags,
3074
+ positionals
3075
+ }) => {
2646
3076
  try {
2647
3077
  const syncTarget = resolveSyncExecutionTarget(positionals[1]);
2648
3078
  const sync = await executeSyncCommand({
2649
3079
  captureOutput: mergedFlags.format === "json" && !Boolean(mergedFlags["dry-run"]),
2650
3080
  check: Boolean(mergedFlags.check),
2651
- cwd: process.cwd(),
3081
+ cwd,
2652
3082
  dryRun: Boolean(mergedFlags["dry-run"]),
2653
3083
  target: syncTarget
2654
3084
  });
@@ -2673,6 +3103,83 @@ async function runNodeCli(argv = process.argv.slice(2)) {
2673
3103
  error
2674
3104
  });
2675
3105
  }
3106
+ },
3107
+ templates: async ({
3108
+ mergedFlags,
3109
+ positionals
3110
+ }) => {
3111
+ const subcommand = positionals[1];
3112
+ const templateId = typeof mergedFlags.id === "string" ? mergedFlags.id : positionals[2];
3113
+ const resolvedSubcommand = templateId ? "inspect" : subcommand ?? "list";
3114
+ if (resolvedSubcommand !== "list" && resolvedSubcommand !== "inspect") {
3115
+ throw createCliCommandError({
3116
+ code: CLI_DIAGNOSTIC_CODES5.INVALID_COMMAND,
3117
+ command: "templates",
3118
+ detailLines: [
3119
+ `Unknown templates subcommand "${resolvedSubcommand}". Expected list or inspect.`
3120
+ ]
3121
+ });
3122
+ }
3123
+ if (mergedFlags.format === "json") {
3124
+ renderTemplatesJson({
3125
+ format: mergedFlags.format,
3126
+ id: templateId
3127
+ }, resolvedSubcommand);
3128
+ return;
3129
+ }
3130
+ await executeTemplatesCommand({
3131
+ flags: {
3132
+ id: templateId,
3133
+ subcommand: resolvedSubcommand
3134
+ }
3135
+ });
3136
+ }
3137
+ };
3138
+ async function runNodeCli(argv = process.argv.slice(2)) {
3139
+ const normalizedArgv = normalizeWpTypiaArgv(argv);
3140
+ const { argv: argvWithoutConfigOverride, configOverridePath } = extractWpTypiaConfigOverride(normalizedArgv);
3141
+ const { argv: cliArgv, flags } = parseGlobalFlags(argvWithoutConfigOverride);
3142
+ const { flags: commandFlags, positionals } = parseArgv(cliArgv);
3143
+ const rawMergedFlags = {
3144
+ ...commandFlags,
3145
+ ...flags
3146
+ };
3147
+ const [command, subcommand] = positionals;
3148
+ const helpRequested = cliArgv.length === 0 || hasFlagBeforeTerminator(cliArgv, "--help") || command === "help";
3149
+ const helpTarget = command === "help" ? subcommand : command;
3150
+ const versionRequested = hasFlagBeforeTerminator(cliArgv, "--version") || command === "version";
3151
+ if (helpRequested) {
3152
+ if (helpTarget) {
3153
+ const helpRenderer = NODE_FALLBACK_HELP_RENDERERS[helpTarget];
3154
+ if (helpRenderer) {
3155
+ helpRenderer();
3156
+ return;
3157
+ }
3158
+ if (helpTarget === "help" || helpTarget === "version") {
3159
+ renderGeneralHelp();
3160
+ return;
3161
+ }
3162
+ } else {
3163
+ renderGeneralHelp();
3164
+ return;
3165
+ }
3166
+ renderGeneralHelp();
3167
+ return;
3168
+ }
3169
+ if (versionRequested) {
3170
+ renderVersion({
3171
+ format: typeof rawMergedFlags.format === "string" ? rawMergedFlags.format : undefined
3172
+ });
3173
+ return;
3174
+ }
3175
+ const mergedFlags = await applyNodeFallbackConfigDefaults(command, subcommand, rawMergedFlags, configOverridePath, process.cwd());
3176
+ const commandDispatcher = command && NODE_FALLBACK_COMMAND_DISPATCHERS[command];
3177
+ if (commandDispatcher) {
3178
+ await commandDispatcher({
3179
+ cwd: process.cwd(),
3180
+ mergedFlags,
3181
+ positionals
3182
+ });
2676
3183
  return;
2677
3184
  }
2678
3185
  renderUnsupportedCommand(command ?? "(missing)");
@@ -2707,4 +3214,4 @@ export {
2707
3214
  hasFlagBeforeTerminator
2708
3215
  };
2709
3216
 
2710
- //# debugId=A072E3D67E76AD4064756E2164756E21
3217
+ //# debugId=9D8430CB181D13B964756E2164756E21