@mpgd/cli 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/index.js +137 -8
  2. package/dist/microsoft-store-starter.d.ts +28 -0
  3. package/dist/microsoft-store-starter.js +655 -0
  4. package/dist/production-target-readiness.js +3 -1
  5. package/package.json +7 -7
  6. package/templates/phaser-game/.agents/skills/release-microsoft-store/SKILL.md +60 -0
  7. package/templates/phaser-game/.agents/skills/release-microsoft-store/agents/openai.yaml +4 -0
  8. package/templates/phaser-game/.agents/skills/use-mpgd-kit/SKILL.md +45 -0
  9. package/templates/phaser-game/.agents/skills/use-mpgd-kit/agents/openai.yaml +4 -0
  10. package/templates/phaser-game/AGENTS.md +34 -0
  11. package/templates/phaser-game/README.md +50 -25
  12. package/templates/phaser-game/agent/acceptance.md +8 -0
  13. package/templates/phaser-game/agent/brief.md +13 -1
  14. package/templates/phaser-game/agent/game-manifest.json +7 -0
  15. package/templates/phaser-game/apps/target-ait/granite.config.ts +35 -0
  16. package/templates/phaser-game/apps/target-ait/index.html +15 -0
  17. package/templates/phaser-game/apps/target-ait/package.json +33 -0
  18. package/templates/phaser-game/apps/target-ait/src/main.ts +34 -0
  19. package/templates/phaser-game/apps/target-ait/src/styles.css +33 -0
  20. package/templates/phaser-game/apps/target-ait/tsconfig.json +8 -0
  21. package/templates/phaser-game/apps/target-ait/vite.config.ts +53 -0
  22. package/templates/phaser-game/docs/MPGD_KIT_WORKFLOWS.md +128 -0
  23. package/templates/phaser-game/gitignore +6 -0
  24. package/templates/phaser-game/mpgd.targets.json +9 -2
  25. package/templates/phaser-game/package.json +10 -10
  26. package/templates/phaser-game/vite.shared.ts +13 -0
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, writeFileSync, } from 'node:fs';
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync, } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import completion from '@gunshi/plugin-completion';
@@ -10,6 +10,7 @@ import { defaultGameAcceptanceCommandTimeoutMs, resolveGameAcceptanceReleaseMani
10
10
  import { resolveGameplayE2EReportFile } from './gameplay-e2e.js';
11
11
  import { createMicrosoftStorePackageAcceptanceRuntime, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
12
12
  import { runMicrosoftStorePackageGeneration } from './microsoft-store-package-generation.js';
13
+ import { defaultMpgdKitPath, initializeMicrosoftStoreStarter, listMicrosoftStoreInitializerTemplateFiles, prepareBaseGameTemplateFile, } from './microsoft-store-starter.js';
13
14
  import { runMicrosoftStoreSubmissionPreflight } from './microsoft-store-submission.js';
14
15
  export { renderGameAcceptanceMarkdown, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, defaultGameAcceptanceCommandTimeoutMs, maximumGameplayE2EReportBytes, } from './game-acceptance.js';
15
16
  export { createBrowserGameplayE2EDriver, } from './browser-gameplay-e2e.js';
@@ -25,6 +26,10 @@ const detectedKitRoot = resolveDefaultKitRoot();
25
26
  const gameTemplateDir = path.resolve(packageRoot, 'templates/phaser-game');
26
27
  const cliVersion = readPackageVersion(packageRoot);
27
28
  const recommendedMatrixTargets = 'web,microsoft-store,verse8,ait,reddit';
29
+ const recommendedMatrixTargetsWithoutMicrosoftStore = recommendedMatrixTargets
30
+ .split(',')
31
+ .filter((target) => target !== 'microsoft-store')
32
+ .join(',');
28
33
  const defaultDependencyVersion = `^${cliVersion}`;
29
34
  const standaloneTemplateDependencyVersionFallbacks = {
30
35
  // Initial-published independently from the CLI release line; package metadata wins once concrete.
@@ -189,6 +194,10 @@ const gameCommand = defineI18n({
189
194
  en: 'Print files that would be generated without writing them.',
190
195
  ko: '파일을 쓰지 않고 생성 예정 목록만 출력합니다.',
191
196
  },
197
+ 'microsoft-store': {
198
+ en: 'Include the Microsoft Store PWA target and release workflow.',
199
+ ko: 'Microsoft Store PWA 타깃과 출시 워크플로를 포함합니다.',
200
+ },
192
201
  }),
193
202
  args: {
194
203
  directory: {
@@ -226,6 +235,11 @@ const gameCommand = defineI18n({
226
235
  required: false,
227
236
  description: 'Print files that would be generated without writing them.',
228
237
  },
238
+ 'microsoft-store': {
239
+ type: 'boolean',
240
+ required: false,
241
+ description: 'Include the Microsoft Store PWA target and release workflow.',
242
+ },
229
243
  },
230
244
  run: (ctx) => {
231
245
  const positionals = readLocalPositionals(ctx.positionals, ['game', 'create']);
@@ -244,6 +258,7 @@ const gameCommand = defineI18n({
244
258
  workspace: ctx.values.workspace === true,
245
259
  ...(kitPath === undefined ? {} : { kitPath }),
246
260
  dryRun: ctx.values['dry-run'] === true,
261
+ microsoftStore: ctx.values['microsoft-store'] === true,
247
262
  });
248
263
  },
249
264
  }),
@@ -739,6 +754,79 @@ const targetCommand = defineI18n({
739
754
  ko: '타깃 출시 산출물을 빌드하고 스모크, 패키지 생성, 제출 사전 검증 및 인수 검증을 수행합니다.',
740
755
  }),
741
756
  subCommands: {
757
+ init: defineI18n({
758
+ name: 'init',
759
+ description: 'Initialize an optional target workflow in an existing game.',
760
+ resource: commandResource({
761
+ en: 'Initialize an optional target workflow in an existing game without overwriting conflicts.',
762
+ ko: '충돌 파일을 덮어쓰지 않고 기존 게임에 선택적 타깃 워크플로를 초기화합니다.',
763
+ }, {
764
+ target: {
765
+ en: 'Target to initialize. Currently supports microsoft-store.',
766
+ ko: '초기화할 타깃. 현재 microsoft-store를 지원합니다.',
767
+ },
768
+ game: {
769
+ en: 'Existing game root. Defaults to the current directory.',
770
+ ko: '기존 게임 루트. 기본값은 현재 디렉터리입니다.',
771
+ },
772
+ 'kit-path': {
773
+ en: 'Default mpgd-kit path written into generated target scripts.',
774
+ ko: '생성되는 타깃 스크립트에 기록할 기본 mpgd-kit 경로.',
775
+ },
776
+ 'dry-run': {
777
+ en: 'Validate and print planned file changes without writing them.',
778
+ ko: '파일을 쓰지 않고 예정 변경을 검증하고 출력합니다.',
779
+ },
780
+ }),
781
+ args: {
782
+ target: {
783
+ type: 'positional',
784
+ required: true,
785
+ description: 'Target to initialize. Currently supports microsoft-store.',
786
+ },
787
+ game: {
788
+ type: 'string',
789
+ required: false,
790
+ default: '.',
791
+ description: 'Existing game root. Defaults to the current directory.',
792
+ },
793
+ 'kit-path': {
794
+ type: 'string',
795
+ required: false,
796
+ description: 'Default mpgd-kit path written into generated target scripts.',
797
+ },
798
+ 'dry-run': {
799
+ type: 'boolean',
800
+ required: false,
801
+ description: 'Validate and print planned file changes without writing them.',
802
+ },
803
+ },
804
+ run: (ctx) => {
805
+ const positionals = readLocalPositionals(ctx.positionals, ['target', 'init']);
806
+ const requestedTarget = readRequiredPositional(positionals, 0, 'target');
807
+ const target = normalizeConfiguredBuildTarget(requestedTarget);
808
+ if (target !== 'microsoft-store') {
809
+ throw new Error(`Target initialization is only available for microsoft-store; received: ${requestedTarget}`);
810
+ }
811
+ const gameRoot = path.resolve(readOptionalString(ctx.values.game) ?? '.');
812
+ const configuredKitPath = readOptionalString(ctx.values['kit-path']);
813
+ const resolvedKitPath = configuredKitPath === undefined
814
+ ? detectedKitRoot
815
+ : assertKitRoot(path.resolve(configuredKitPath));
816
+ const defaultKitPath = resolvedKitPath === undefined
817
+ ? defaultMpgdKitPath
818
+ : toTemplatePath(path.relative(gameRoot, resolvedKitPath) || '.');
819
+ const dryRun = ctx.values['dry-run'] === true;
820
+ const result = initializeMicrosoftStoreStarter({
821
+ gameRoot,
822
+ templateRoot: gameTemplateDir,
823
+ defaultKitPath,
824
+ dryRun,
825
+ });
826
+ for (const file of result.changedFiles) {
827
+ }
828
+ },
829
+ }),
742
830
  build: defineI18n({
743
831
  name: 'build',
744
832
  description: 'Build one target artifact.',
@@ -1483,17 +1571,54 @@ function createGameApp(input) {
1483
1571
  workspace: input.workspace,
1484
1572
  ...(input.kitPath === undefined ? {} : { kitPath: input.kitPath }),
1485
1573
  ...(input.title === undefined ? {} : { title: input.title }),
1574
+ microsoftStore: input.microsoftStore,
1575
+ });
1576
+ const files = collectTemplateFiles(gameTemplateDir).flatMap((file) => {
1577
+ const prepared = prepareBaseGameTemplateFile({
1578
+ relativePath: file.relativePath,
1579
+ content: file.content,
1580
+ });
1581
+ return prepared === undefined
1582
+ ? []
1583
+ : [{ ...file, content: renderTemplate(prepared, context) }];
1486
1584
  });
1487
- const files = collectTemplateFiles(gameTemplateDir);
1488
1585
  if (input.dryRun) {
1489
- for (const file of files) {
1586
+ const plannedFiles = input.microsoftStore
1587
+ ? [...files.map((file) => file.relativePath), ...listMicrosoftStoreInitializerTemplateFiles()]
1588
+ : files.map((file) => file.relativePath);
1589
+ for (const relativePath of [...new Set(plannedFiles)].sort()) {
1490
1590
  }
1491
1591
  return;
1492
1592
  }
1493
- for (const file of files) {
1494
- const outputFile = path.join(appDir, file.relativePath);
1495
- mkdirSync(path.dirname(outputFile), { recursive: true });
1496
- writeFileSync(outputFile, renderTemplate(file.content, context));
1593
+ let createdAppDirectory = false;
1594
+ try {
1595
+ mkdirSync(path.dirname(appDir), { recursive: true });
1596
+ mkdirSync(appDir);
1597
+ createdAppDirectory = true;
1598
+ for (const file of files) {
1599
+ const outputFile = path.join(appDir, file.relativePath);
1600
+ mkdirSync(path.dirname(outputFile), { recursive: true });
1601
+ writeFileSync(outputFile, file.content);
1602
+ }
1603
+ if (input.microsoftStore) {
1604
+ initializeMicrosoftStoreStarter({
1605
+ gameRoot: appDir,
1606
+ templateRoot: gameTemplateDir,
1607
+ defaultKitPath: context.defaultKitPath,
1608
+ dryRun: false,
1609
+ });
1610
+ }
1611
+ }
1612
+ catch (error) {
1613
+ if (createdAppDirectory) {
1614
+ try {
1615
+ rmSync(appDir, { force: true, recursive: true });
1616
+ }
1617
+ catch (cleanupError) {
1618
+ console.warn(`Failed to clean up incomplete game directory ${appDir}: ${formatError(cleanupError)}`);
1619
+ }
1620
+ }
1621
+ throw error;
1497
1622
  }
1498
1623
  }
1499
1624
  function assertValidGameName(name) {
@@ -1567,7 +1692,10 @@ function createTemplateContext(input) {
1567
1692
  workspaceI18nBuildPrefix: input.workspace
1568
1693
  ? `pnpm --dir ${workspacePrefix} i18n:build && `
1569
1694
  : '',
1570
- defaultKitPath: kitPath === undefined ? '../mpgd-kit' : workspacePrefix,
1695
+ defaultKitPath: kitPath === undefined ? defaultMpgdKitPath : workspacePrefix,
1696
+ recommendedMatrixTargets: input.microsoftStore
1697
+ ? recommendedMatrixTargets
1698
+ : recommendedMatrixTargetsWithoutMicrosoftStore,
1571
1699
  pnpmWorkspaceKitPackages: input.workspace
1572
1700
  ? [
1573
1701
  ` - '${workspacePrefix}/packages/*'`,
@@ -1659,6 +1787,7 @@ function renderTemplate(content, context) {
1659
1787
  .replaceAll('__TSCONFIG_WORKSPACE_EXCLUDES__', context.tsconfigWorkspaceExcludes)
1660
1788
  .replaceAll('__WORKSPACE_I18N_BUILD_PREFIX__', context.workspaceI18nBuildPrefix)
1661
1789
  .replaceAll('__DEFAULT_KIT_PATH__', context.defaultKitPath)
1790
+ .replaceAll('__RECOMMENDED_MATRIX_TARGETS__', context.recommendedMatrixTargets)
1662
1791
  .replaceAll('__PNPM_WORKSPACE_KIT_PACKAGES__', context.pnpmWorkspaceKitPackages)
1663
1792
  .replaceAll('__PASCAL_NAME__', context.pascalName)
1664
1793
  .replaceAll('__CAMEL_NAME__', context.camelName);
@@ -0,0 +1,28 @@
1
+ export declare const microsoftStoreBlockStart = "<!-- mpgd:microsoft-store:start -->";
2
+ export declare const microsoftStoreBlockEnd = "<!-- mpgd:microsoft-store:end -->";
3
+ export declare const defaultMpgdKitPath = "../mpgd-kit";
4
+ export declare const microsoftStoreDocumentationAnchors: {
5
+ readonly 'README.md': '- Verse8 is a game-owned iframe web target';
6
+ readonly 'agent/acceptance.md': '## Verse8 Agent8 Structured Server';
7
+ readonly 'agent/brief.md': undefined;
8
+ };
9
+ export interface InitializeMicrosoftStoreStarterInput {
10
+ readonly gameRoot: string;
11
+ readonly templateRoot: string;
12
+ readonly defaultKitPath: string;
13
+ readonly dryRun: boolean;
14
+ }
15
+ export interface InitializeMicrosoftStoreStarterResult {
16
+ readonly changedFiles: readonly string[];
17
+ }
18
+ interface MicrosoftStoreStarterApplyRuntime {
19
+ readonly beforeCommit?: (relativePath: string) => void;
20
+ readonly beforeRollbackEntry?: (relativePath: string) => void;
21
+ }
22
+ export declare function prepareBaseGameTemplateFile(input: {
23
+ readonly relativePath: string;
24
+ readonly content: string;
25
+ }): string | undefined;
26
+ export declare function listMicrosoftStoreInitializerTemplateFiles(): readonly string[];
27
+ export declare function initializeMicrosoftStoreStarter(input: InitializeMicrosoftStoreStarterInput, runtime?: MicrosoftStoreStarterApplyRuntime): InitializeMicrosoftStoreStarterResult;
28
+ export {};