@pix3/cli 1.6.0 → 1.6.2

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 (126) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +788 -754
  3. package/dist/index.js +208 -203
  4. package/dist/runtime-types.json +1 -1
  5. package/dist/smoke/prebuilt/{chunk-J2SJVMDO.mjs → chunk-7MZGPAUK.mjs} +1 -1
  6. package/dist/smoke/prebuilt/chunk-7WOFGLW3.mjs +239 -0
  7. package/dist/smoke/prebuilt/runtime.mjs +1 -1
  8. package/dist/smoke/prebuilt/smoke-worker.mjs +1 -1
  9. package/dist/smoke/prebuilt/tree-defaults.mjs +1 -1
  10. package/dist/validate/prebuilt/bundle-main.js +13 -13
  11. package/dist/validate/prebuilt/chunk-HKG6YI6Y.js +239 -0
  12. package/dist/validate/prebuilt/runtime.js +1 -1
  13. package/kit/files/.claude/skills/pix3-nodes/SKILL.md +210 -210
  14. package/kit/files/.claude/skills/pix3-nodes/reference.md +32 -24
  15. package/kit/files/.claude/skills/pix3-scene-format/SKILL.md +238 -235
  16. package/kit/files/.claude/skills/pix3-scene-format/reference.md +28 -28
  17. package/kit/files/.claude/skills/pix3-scripts/SKILL.md +285 -281
  18. package/kit/files/.claude/skills/pix3-scripts/reference.md +69 -52
  19. package/kit/files/.claude/skills/pix3-verify/SKILL.md +298 -256
  20. package/kit/files/.claude/skills/pix3-verify/reference.md +21 -21
  21. package/kit/files/AGENTS.md +203 -203
  22. package/kit/files/CLAUDE.md +1 -1
  23. package/kit/kit.json +2 -2
  24. package/package.json +54 -54
  25. package/templates/empty-2d/files/README.md +20 -20
  26. package/templates/empty-2d/files/scenes/main.pix3scene +68 -68
  27. package/templates/empty-2d/template.yaml +7 -7
  28. package/templates/empty-3d/files/README.md +20 -20
  29. package/templates/empty-3d/files/scenes/main.pix3scene +108 -108
  30. package/templates/empty-3d/template.yaml +7 -7
  31. package/templates/idea-blank/files/README.md +22 -22
  32. package/templates/idea-blank/files/scenes/main.pix3scene +37 -37
  33. package/templates/idea-blank/template.yaml +10 -10
  34. package/templates/minigame-2d/files/README.md +44 -44
  35. package/templates/minigame-2d/files/design/tests/reachability.json +1 -1
  36. package/templates/minigame-2d/files/design/tests/routines/mute-music.json +44 -44
  37. package/templates/minigame-2d/files/scenes/main.pix3scene +84 -84
  38. package/templates/minigame-2d/files/scenes/menu.pix3scene +118 -118
  39. package/templates/minigame-2d/files/scenes/ui/settings-window.pix3scene +138 -138
  40. package/templates/minigame-2d/files/scripts/GameFlow.ts +133 -133
  41. package/templates/minigame-2d/files/scripts/MenuFlow.ts +217 -217
  42. package/templates/minigame-2d/files/scripts/SettingsWindow.ts +198 -198
  43. package/templates/minigame-2d/template.yaml +10 -10
  44. package/templates/playable-2d/files/README.md +30 -30
  45. package/templates/playable-2d/files/design/recipe.md +83 -83
  46. package/templates/playable-2d/files/design/tests/reachability.json +4 -4
  47. package/templates/playable-2d/files/design/tests/routines/intro-to-cta.json +32 -32
  48. package/templates/playable-2d/files/scenes/main.pix3scene +111 -111
  49. package/templates/playable-2d/files/scenes/ui/end-screen.pix3scene +91 -91
  50. package/templates/playable-2d/files/scenes/ui/intro.pix3scene +65 -65
  51. package/templates/playable-2d/files/scripts/GameFlow.ts +256 -256
  52. package/templates/playable-2d/template.yaml +9 -9
  53. package/templates/playable-3d/files/README.md +32 -32
  54. package/templates/playable-3d/files/design/recipe.md +127 -127
  55. package/templates/playable-3d/files/design/tests/reachability.json +4 -4
  56. package/templates/playable-3d/files/design/tests/routines/intro-to-cta.json +32 -32
  57. package/templates/playable-3d/files/scenes/main.pix3scene +230 -230
  58. package/templates/playable-3d/files/scenes/ui/end-screen.pix3scene +91 -91
  59. package/templates/playable-3d/files/scenes/ui/intro.pix3scene +65 -65
  60. package/templates/playable-3d/files/scripts/GameFlow.ts +309 -309
  61. package/templates/playable-3d/files/scripts/GameRules.ts +290 -290
  62. package/templates/playable-3d/files/scripts/ScoreHud.ts +113 -113
  63. package/templates/playable-3d/template.yaml +9 -9
  64. package/templates/recipe-arena-2d/files/README.md +48 -48
  65. package/templates/recipe-arena-2d/files/design/recipe.md +127 -127
  66. package/templates/recipe-arena-2d/files/scenes/main.pix3scene +292 -292
  67. package/templates/recipe-arena-2d/files/scenes/menu.pix3scene +100 -100
  68. package/templates/recipe-arena-2d/files/scenes/prefabs/hazard.pix3scene +42 -42
  69. package/templates/recipe-arena-2d/files/scenes/prefabs/pickup.pix3scene +45 -45
  70. package/templates/recipe-arena-2d/files/scenes/ui/result.pix3scene +85 -85
  71. package/templates/recipe-arena-2d/files/scripts/GameRules.ts +368 -368
  72. package/templates/recipe-arena-2d/files/scripts/MenuFlow.ts +106 -106
  73. package/templates/recipe-arena-2d/files/scripts/PlayerController.ts +151 -151
  74. package/templates/recipe-arena-2d/files/scripts/ScoreHud.ts +94 -94
  75. package/templates/recipe-arena-2d/files/scripts/Spawner.ts +214 -214
  76. package/templates/recipe-arena-2d/files/scripts/TouchRules.ts +174 -174
  77. package/templates/recipe-arena-2d/template.yaml +10 -10
  78. package/templates/recipe-blank-2d/files/README.md +61 -61
  79. package/templates/recipe-blank-2d/files/design/recipe.md +148 -148
  80. package/templates/recipe-blank-2d/files/design/tests/reachability.json +4 -4
  81. package/templates/recipe-blank-2d/files/design/tests/routines/restart.json +27 -27
  82. package/templates/recipe-blank-2d/files/scenes/main.pix3scene +224 -224
  83. package/templates/recipe-blank-2d/files/scenes/ui/result.pix3scene +85 -85
  84. package/templates/recipe-blank-2d/files/scripts/GameRules.ts +350 -350
  85. package/templates/recipe-blank-2d/files/scripts/ScoreHud.ts +110 -110
  86. package/templates/recipe-blank-2d/template.yaml +10 -10
  87. package/templates/recipe-bouncer-2d/files/README.md +81 -81
  88. package/templates/recipe-bouncer-2d/files/design/recipe.md +146 -146
  89. package/templates/recipe-bouncer-2d/files/scenes/main.pix3scene +615 -615
  90. package/templates/recipe-bouncer-2d/files/scenes/menu.pix3scene +191 -191
  91. package/templates/recipe-bouncer-2d/files/scenes/ui/result.pix3scene +87 -87
  92. package/templates/recipe-bouncer-2d/files/scripts/BallBody.ts +298 -298
  93. package/templates/recipe-bouncer-2d/files/scripts/GameRules.ts +377 -377
  94. package/templates/recipe-bouncer-2d/files/scripts/MenuFlow.ts +109 -109
  95. package/templates/recipe-bouncer-2d/files/scripts/PaddleController.ts +112 -112
  96. package/templates/recipe-bouncer-2d/files/scripts/ScoreHud.ts +94 -94
  97. package/templates/recipe-bouncer-2d/files/scripts/TouchRules.ts +263 -263
  98. package/templates/recipe-bouncer-2d/files/scripts/ball-collision.ts +307 -307
  99. package/templates/recipe-bouncer-2d/template.yaml +10 -10
  100. package/templates/recipe-grid-3d/files/README.md +22 -22
  101. package/templates/recipe-grid-3d/files/design/recipe.md +110 -110
  102. package/templates/recipe-grid-3d/files/scenes/main.pix3scene +169 -169
  103. package/templates/recipe-grid-3d/files/scenes/menu.pix3scene +100 -100
  104. package/templates/recipe-grid-3d/files/scenes/ui/result.pix3scene +105 -105
  105. package/templates/recipe-grid-3d/files/scripts/GridBoard.ts +319 -319
  106. package/templates/recipe-grid-3d/files/scripts/GridRules.ts +278 -278
  107. package/templates/recipe-grid-3d/files/scripts/MenuFlow.ts +106 -106
  108. package/templates/recipe-grid-3d/files/scripts/ScoreHud.ts +94 -94
  109. package/templates/recipe-grid-3d/template.yaml +10 -10
  110. package/templates/recipe-tapper-2d/files/README.md +39 -39
  111. package/templates/recipe-tapper-2d/files/design/recipe.md +132 -132
  112. package/templates/recipe-tapper-2d/files/design/tests/reachability.json +4 -4
  113. package/templates/recipe-tapper-2d/files/design/tests/routines/terminal-retry.json +23 -23
  114. package/templates/recipe-tapper-2d/files/scenes/main.pix3scene +269 -269
  115. package/templates/recipe-tapper-2d/files/scenes/menu.pix3scene +100 -100
  116. package/templates/recipe-tapper-2d/files/scenes/prefabs/hazard.pix3scene +42 -42
  117. package/templates/recipe-tapper-2d/files/scenes/prefabs/target.pix3scene +45 -45
  118. package/templates/recipe-tapper-2d/files/scenes/ui/result.pix3scene +85 -85
  119. package/templates/recipe-tapper-2d/files/scripts/GameRules.ts +368 -368
  120. package/templates/recipe-tapper-2d/files/scripts/MenuFlow.ts +106 -106
  121. package/templates/recipe-tapper-2d/files/scripts/ScoreHud.ts +94 -94
  122. package/templates/recipe-tapper-2d/files/scripts/Spawner.ts +214 -214
  123. package/templates/recipe-tapper-2d/files/scripts/TouchRules.ts +192 -192
  124. package/templates/recipe-tapper-2d/template.yaml +10 -10
  125. package/dist/smoke/prebuilt/chunk-OJXQXIIQ.mjs +0 -239
  126. package/dist/validate/prebuilt/chunk-BS2OZM7U.js +0 -239
@@ -1,133 +1,133 @@
1
- /**
2
- * GameFlow — drives the game scene (main.pix3scene).
3
- *
4
- * Attach to the game scene root. Wires the MENU button to transition back to the
5
- * menu scene via a fade. Extend this with your win/lose flow — call
6
- * `this.scene.changeScene('res://scenes/menu.pix3scene')` on game over
7
- * to return to the menu, or transition to a results scene of your own.
8
- *
9
- * Two conventions in here are worth keeping as you build on this template:
10
- *
11
- * 1. **Intent-first handlers.** Every reaction to the UI lives in a named public
12
- * method marked `Intent:`, registered as a **command** (`scene.commands`)
13
- * that the signal handler dispatches. Tools (and you, from the console) then
14
- * trigger the behaviour with `scene.commands.dispatch('return-to-menu')`
15
- * instead of faking a click, and every raised intent is journalled with the
16
- * frame it happened on.
17
- * 2. **A debug provider.** `registerGameDebug` publishes a JSON snapshot of the
18
- * game's state so agents and dev tooling can read what the game is doing
19
- * instead of guessing from pixels. Add YOUR state to `snapshot()` — score,
20
- * lives, level, whatever the game owns — and keep every value JSON-safe.
21
- */
22
- import { Script, registerGameDebug, type PropertySchema } from '@pix3/runtime';
23
-
24
- /** Coarse state of the game scene; extend with your own phases (`won`, `lost`, …). */
25
- type GamePhase = 'idle' | 'playing' | 'leaving';
26
-
27
- export class GameFlow extends Script {
28
- private phase: GamePhase = 'idle';
29
- private menuButtonReady = false;
30
- private disposeDebug: (() => void) | null = null;
31
- private disposeCommands: (() => void)[] = [];
32
-
33
- constructor(id: string, type: string) {
34
- super(id, type);
35
- this.config = {
36
- // res:// path of the scene the MENU button transitions to.
37
- menuScene: 'res://scenes/menu.pix3scene',
38
- // Node id/name of the button that returns to the menu.
39
- menuButton: 'menu-button',
40
- };
41
- }
42
-
43
- static getPropertySchema(): PropertySchema {
44
- const stringProp = (name: string, label: string) => ({
45
- name,
46
- type: 'string' as const,
47
- ui: { label, group: 'Game' },
48
- getValue: (s: unknown) => (s as GameFlow).config[name],
49
- setValue: (s: unknown, v: unknown) => {
50
- (s as GameFlow).config[name] = typeof v === 'string' ? v : '';
51
- },
52
- });
53
-
54
- return {
55
- nodeType: 'GameFlow',
56
- properties: [stringProp('menuScene', 'Menu Scene'), stringProp('menuButton', 'Menu Button')],
57
- groups: { Game: { label: 'Game Flow', expanded: true } },
58
- };
59
- }
60
-
61
- onStart(): void {
62
- this.phase = 'playing';
63
- const commands = this.scene?.commands;
64
- this.disposeCommands = [
65
- commands?.register('return-to-menu', () => this.returnToMenu(), {
66
- description: 'Leave the game and transition back to the menu scene.',
67
- }),
68
- ].filter((dispose): dispose is () => void => dispose !== undefined);
69
-
70
- this.disposeDebug = registerGameDebug({
71
- name: 'minigame-2d',
72
- version: 1,
73
- // The registry IS the action list — never a second, hand-kept copy.
74
- actions: () => this.scene?.commands.list().map(command => command.name) ?? [],
75
- // What "the start" means for this scene, so a test can put the game back
76
- // without restarting it. All this template owns is `phase`, and `playing`
77
- // is the value `onStart` sets — a run that had already dispatched
78
- // `return-to-menu` would otherwise be stuck in `leaving`. Reset YOUR state
79
- // here as you add it (score, level, spawned things), and seed your RNG from
80
- // `seed` if you want two runs to be comparable and not merely to start
81
- // from the same values.
82
- reset: () => {
83
- this.phase = 'playing';
84
- },
85
- // Everything returned here must be JSON-serialisable — plain values only,
86
- // never live nodes or Three.js objects. Add your own state as you build.
87
- snapshot: () => ({
88
- phase: this.phase,
89
- menuScene: String(this.config.menuScene ?? ''),
90
- menuButtonReady: this.menuButtonReady,
91
- }),
92
- });
93
-
94
- const query = String(this.config.menuButton ?? '');
95
- if (!query) {
96
- return;
97
- }
98
- const button = this.findNode(query);
99
- if (button) {
100
- this.menuButtonReady = true;
101
- // The button raises the intent through the registry, so a single tap in a
102
- // test proves this binding and every later scenario can skip the tap.
103
- button.connect('pressed', this, () => {
104
- this.scene?.commands.dispatch('return-to-menu');
105
- });
106
- } else {
107
- console.warn(`[GameFlow] Button "${query}" not found.`);
108
- }
109
- }
110
-
111
- onDetach(): void {
112
- this.disposeDebug?.();
113
- this.disposeDebug = null;
114
- for (const dispose of this.disposeCommands) {
115
- dispose();
116
- }
117
- this.disposeCommands = [];
118
- this.phase = 'idle';
119
- this.menuButtonReady = false;
120
- super.onDetach();
121
- }
122
-
123
- /** Intent: leave the game and transition back to the menu scene (also on game over). */
124
- returnToMenu(): void {
125
- const menuScene = String(this.config.menuScene ?? '');
126
- if (!menuScene) {
127
- console.warn('[GameFlow] No menu scene configured.');
128
- return;
129
- }
130
- this.phase = 'leaving';
131
- void this.scene?.changeScene(menuScene, { transition: 'fade', durationSec: 0.3 });
132
- }
133
- }
1
+ /**
2
+ * GameFlow — drives the game scene (main.pix3scene).
3
+ *
4
+ * Attach to the game scene root. Wires the MENU button to transition back to the
5
+ * menu scene via a fade. Extend this with your win/lose flow — call
6
+ * `this.scene.changeScene('res://scenes/menu.pix3scene')` on game over
7
+ * to return to the menu, or transition to a results scene of your own.
8
+ *
9
+ * Two conventions in here are worth keeping as you build on this template:
10
+ *
11
+ * 1. **Intent-first handlers.** Every reaction to the UI lives in a named public
12
+ * method marked `Intent:`, registered as a **command** (`scene.commands`)
13
+ * that the signal handler dispatches. Tools (and you, from the console) then
14
+ * trigger the behaviour with `scene.commands.dispatch('return-to-menu')`
15
+ * instead of faking a click, and every raised intent is journalled with the
16
+ * frame it happened on.
17
+ * 2. **A debug provider.** `registerGameDebug` publishes a JSON snapshot of the
18
+ * game's state so agents and dev tooling can read what the game is doing
19
+ * instead of guessing from pixels. Add YOUR state to `snapshot()` — score,
20
+ * lives, level, whatever the game owns — and keep every value JSON-safe.
21
+ */
22
+ import { Script, registerGameDebug, type PropertySchema } from '@pix3/runtime';
23
+
24
+ /** Coarse state of the game scene; extend with your own phases (`won`, `lost`, …). */
25
+ type GamePhase = 'idle' | 'playing' | 'leaving';
26
+
27
+ export class GameFlow extends Script {
28
+ private phase: GamePhase = 'idle';
29
+ private menuButtonReady = false;
30
+ private disposeDebug: (() => void) | null = null;
31
+ private disposeCommands: (() => void)[] = [];
32
+
33
+ constructor(id: string, type: string) {
34
+ super(id, type);
35
+ this.config = {
36
+ // res:// path of the scene the MENU button transitions to.
37
+ menuScene: 'res://scenes/menu.pix3scene',
38
+ // Node id/name of the button that returns to the menu.
39
+ menuButton: 'menu-button',
40
+ };
41
+ }
42
+
43
+ static getPropertySchema(): PropertySchema {
44
+ const stringProp = (name: string, label: string) => ({
45
+ name,
46
+ type: 'string' as const,
47
+ ui: { label, group: 'Game' },
48
+ getValue: (s: unknown) => (s as GameFlow).config[name],
49
+ setValue: (s: unknown, v: unknown) => {
50
+ (s as GameFlow).config[name] = typeof v === 'string' ? v : '';
51
+ },
52
+ });
53
+
54
+ return {
55
+ nodeType: 'GameFlow',
56
+ properties: [stringProp('menuScene', 'Menu Scene'), stringProp('menuButton', 'Menu Button')],
57
+ groups: { Game: { label: 'Game Flow', expanded: true } },
58
+ };
59
+ }
60
+
61
+ onStart(): void {
62
+ this.phase = 'playing';
63
+ const commands = this.scene?.commands;
64
+ this.disposeCommands = [
65
+ commands?.register('return-to-menu', () => this.returnToMenu(), {
66
+ description: 'Leave the game and transition back to the menu scene.',
67
+ }),
68
+ ].filter((dispose): dispose is () => void => dispose !== undefined);
69
+
70
+ this.disposeDebug = registerGameDebug({
71
+ name: 'minigame-2d',
72
+ version: 1,
73
+ // The registry IS the action list — never a second, hand-kept copy.
74
+ actions: () => this.scene?.commands.list().map(command => command.name) ?? [],
75
+ // What "the start" means for this scene, so a test can put the game back
76
+ // without restarting it. All this template owns is `phase`, and `playing`
77
+ // is the value `onStart` sets — a run that had already dispatched
78
+ // `return-to-menu` would otherwise be stuck in `leaving`. Reset YOUR state
79
+ // here as you add it (score, level, spawned things), and seed your RNG from
80
+ // `seed` if you want two runs to be comparable and not merely to start
81
+ // from the same values.
82
+ reset: () => {
83
+ this.phase = 'playing';
84
+ },
85
+ // Everything returned here must be JSON-serialisable — plain values only,
86
+ // never live nodes or Three.js objects. Add your own state as you build.
87
+ snapshot: () => ({
88
+ phase: this.phase,
89
+ menuScene: String(this.config.menuScene ?? ''),
90
+ menuButtonReady: this.menuButtonReady,
91
+ }),
92
+ });
93
+
94
+ const query = String(this.config.menuButton ?? '');
95
+ if (!query) {
96
+ return;
97
+ }
98
+ const button = this.findNode(query);
99
+ if (button) {
100
+ this.menuButtonReady = true;
101
+ // The button raises the intent through the registry, so a single tap in a
102
+ // test proves this binding and every later scenario can skip the tap.
103
+ button.connect('pressed', this, () => {
104
+ this.scene?.commands.dispatch('return-to-menu');
105
+ });
106
+ } else {
107
+ console.warn(`[GameFlow] Button "${query}" not found.`);
108
+ }
109
+ }
110
+
111
+ onDetach(): void {
112
+ this.disposeDebug?.();
113
+ this.disposeDebug = null;
114
+ for (const dispose of this.disposeCommands) {
115
+ dispose();
116
+ }
117
+ this.disposeCommands = [];
118
+ this.phase = 'idle';
119
+ this.menuButtonReady = false;
120
+ super.onDetach();
121
+ }
122
+
123
+ /** Intent: leave the game and transition back to the menu scene (also on game over). */
124
+ returnToMenu(): void {
125
+ const menuScene = String(this.config.menuScene ?? '');
126
+ if (!menuScene) {
127
+ console.warn('[GameFlow] No menu scene configured.');
128
+ return;
129
+ }
130
+ this.phase = 'leaving';
131
+ void this.scene?.changeScene(menuScene, { transition: 'fade', durationSec: 0.3 });
132
+ }
133
+ }