@auraindustry/aurajs 0.1.3 → 0.1.5

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 (108) hide show
  1. package/README.md +7 -0
  2. package/benchmarks/perf-thresholds.json +27 -0
  3. package/package.json +6 -1
  4. package/src/ai-guidance.mjs +302 -0
  5. package/src/authored-project.mjs +498 -2
  6. package/src/build-contract/capabilities.mjs +87 -1
  7. package/src/build-contract/constants.mjs +1 -0
  8. package/src/build-contract.mjs +2 -0
  9. package/src/bundler.mjs +143 -13
  10. package/src/cli.mjs +681 -13
  11. package/src/commands/packs.mjs +741 -0
  12. package/src/commands/project-authoring.mjs +128 -1
  13. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
  14. package/src/conformance/cases/core-runtime-cases.mjs +6 -2
  15. package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
  16. package/src/conformance/cases/systems-and-gameplay-cases.mjs +265 -4
  17. package/src/conformance-mobile.mjs +166 -0
  18. package/src/conformance.mjs +89 -30
  19. package/src/evidence-bundle.mjs +242 -0
  20. package/src/headless-test/runtime-coordinator.mjs +186 -33
  21. package/src/headless-test.mjs +2 -0
  22. package/src/helpers/2d/index.mjs +183 -0
  23. package/src/helpers/index.mjs +26 -0
  24. package/src/helpers/starter-utils/adventure-objectives.js +102 -0
  25. package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
  26. package/src/helpers/starter-utils/animation-2d.js +337 -0
  27. package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
  28. package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
  29. package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
  30. package/src/helpers/starter-utils/avatar-3d.js +404 -0
  31. package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
  32. package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
  33. package/src/helpers/starter-utils/core.js +150 -0
  34. package/src/helpers/starter-utils/dialogue-2d.js +351 -0
  35. package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
  36. package/src/helpers/starter-utils/index.js +26 -0
  37. package/src/helpers/starter-utils/inventory-2d.js +268 -0
  38. package/src/helpers/starter-utils/journal-2d.js +267 -0
  39. package/src/helpers/starter-utils/platformer-3d.js +132 -0
  40. package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
  41. package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
  42. package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
  43. package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
  44. package/src/helpers/starter-utils/triggers.js +662 -0
  45. package/src/helpers/starter-utils/tween-2d.js +615 -0
  46. package/src/helpers/starter-utils/wave-director.js +101 -0
  47. package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
  48. package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
  49. package/src/mobile/android/build.mjs +606 -0
  50. package/src/mobile/android/host-artifact.mjs +280 -0
  51. package/src/mobile/ios/build.mjs +1323 -0
  52. package/src/mobile/ios/host-artifact.mjs +819 -0
  53. package/src/mobile/shared/capabilities.mjs +174 -0
  54. package/src/packs/catalog.mjs +259 -0
  55. package/src/perf-benchmark-runner.mjs +17 -12
  56. package/src/perf-benchmark.mjs +408 -4
  57. package/src/publish-command.mjs +303 -6
  58. package/src/replay-runtime.mjs +257 -0
  59. package/src/scaffold/config.mjs +2 -0
  60. package/src/scaffold/fs.mjs +8 -1
  61. package/src/scaffold/project-docs.mjs +43 -1
  62. package/src/scaffold.mjs +4 -0
  63. package/src/session-runtime.mjs +4 -3
  64. package/src/web-conformance.mjs +0 -36
  65. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
  66. package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
  67. package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
  68. package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
  69. package/templates/create/2d-adventure/docs/design/loop.md +4 -3
  70. package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
  71. package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
  72. package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
  73. package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
  74. package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
  75. package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
  76. package/templates/create/3d/scenes/gameplay.scene.js +30 -3
  77. package/templates/create/3d/src/runtime/capabilities.js +5 -0
  78. package/templates/create/3d/src/runtime/materials.js +10 -0
  79. package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
  80. package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
  81. package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
  82. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
  83. package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
  84. package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
  85. package/templates/create/shared/src/runtime/ui-forms.js +552 -0
  86. package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
  87. package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
  88. package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
  89. package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
  90. package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
  91. package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
  92. package/templates/create/shared/src/starter-utils/index.js +15 -1
  93. package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
  94. package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
  95. package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
  96. package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
  97. package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
  98. package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
  99. package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
  100. package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
  101. package/templates/create-bin/play.js +36 -7
  102. package/templates/skills/auramaxx/SKILL.md +46 -0
  103. package/templates/skills/auramaxx/project-requirements.md +68 -0
  104. package/templates/skills/auramaxx/starter-recipes.md +104 -0
  105. package/templates/skills/auramaxx/validation-checklist.md +49 -0
  106. package/templates/skills/aurajs/SKILL.md +0 -96
  107. package/templates/skills/aurajs/api-contract-3d.md +0 -7
  108. package/templates/skills/aurajs/api-contract.md +0 -7
@@ -0,0 +1,253 @@
1
+ function finite(value, fallback = 0) {
2
+ const numeric = Number(value);
3
+ return Number.isFinite(numeric) ? numeric : fallback;
4
+ }
5
+
6
+ function positiveInteger(value, fallback = null) {
7
+ const numeric = Math.floor(finite(value, Number.NaN));
8
+ if (numeric >= 1) return numeric;
9
+ return fallback;
10
+ }
11
+
12
+ function normalizeText(value, fallback) {
13
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : fallback;
14
+ }
15
+
16
+ function resolveAura(auraRef = globalThis.aura) {
17
+ return auraRef && typeof auraRef === 'object' ? auraRef : null;
18
+ }
19
+
20
+ function normalizeStageIds(value) {
21
+ if (!Array.isArray(value) || value.length === 0) {
22
+ return ['scene', 'light', 'final'];
23
+ }
24
+
25
+ const stageIds = [];
26
+ const seen = new Set();
27
+ for (const entry of value) {
28
+ const id = normalizeText(entry, null);
29
+ if (!id || seen.has(id)) continue;
30
+ seen.add(id);
31
+ stageIds.push(id);
32
+ }
33
+ return stageIds.length > 0 ? stageIds : ['scene', 'light', 'final'];
34
+ }
35
+
36
+ function createTarget(aura, width, height) {
37
+ if (!aura?.draw2d || typeof aura.draw2d.createRenderTarget !== 'function') return null;
38
+ const target = aura.draw2d.createRenderTarget(width, height);
39
+ return target && typeof target === 'object' && target.ok === true ? target : null;
40
+ }
41
+
42
+ function destroyTarget(aura, target) {
43
+ if (!target || typeof target !== 'object' || target.ok !== true) return false;
44
+ if (!aura?.draw2d || typeof aura.draw2d.destroyRenderTarget !== 'function') return false;
45
+ aura.draw2d.destroyRenderTarget(target);
46
+ return true;
47
+ }
48
+
49
+ function resolveDimensions(compositor, options = {}) {
50
+ const width = positiveInteger(
51
+ options.width ?? compositor.fixedWidth,
52
+ null,
53
+ );
54
+ const height = positiveInteger(
55
+ options.height ?? compositor.fixedHeight,
56
+ null,
57
+ );
58
+ if (width == null || height == null) return null;
59
+ return { width, height };
60
+ }
61
+
62
+ function getStageRecord(compositor, stageId) {
63
+ return Array.isArray(compositor?.stages)
64
+ ? compositor.stages.find((entry) => entry.id === stageId) || null
65
+ : null;
66
+ }
67
+
68
+ function normalizeStageDefinitions(compositor, stageDefinitions) {
69
+ if (Array.isArray(stageDefinitions)) {
70
+ return compositor.stageIds.map((stageId) => (
71
+ stageDefinitions.find((entry) => entry?.id === stageId) || { id: stageId }
72
+ ));
73
+ }
74
+
75
+ if (stageDefinitions && typeof stageDefinitions === 'object') {
76
+ return compositor.stageIds.map((stageId) => {
77
+ const entry = stageDefinitions[stageId];
78
+ if (typeof entry === 'function') {
79
+ return { id: stageId, draw: entry };
80
+ }
81
+ if (entry && typeof entry === 'object') {
82
+ return { id: stageId, ...entry };
83
+ }
84
+ return { id: stageId };
85
+ });
86
+ }
87
+
88
+ return compositor.stageIds.map((stageId) => ({ id: stageId }));
89
+ }
90
+
91
+ export function createWorldCompositor2D(options = {}) {
92
+ const stageIds = normalizeStageIds(options.stageIds);
93
+ const finalStageId = stageIds.includes(options.finalStageId)
94
+ ? options.finalStageId
95
+ : stageIds[stageIds.length - 1];
96
+ return {
97
+ graphName: normalizeText(options.graphName, 'world-compositor-2d'),
98
+ stageIds,
99
+ finalStageId,
100
+ fixedWidth: positiveInteger(options.width ?? options.fixedWidth, null),
101
+ fixedHeight: positiveInteger(options.height ?? options.fixedHeight, null),
102
+ width: 0,
103
+ height: 0,
104
+ targets: null,
105
+ stages: null,
106
+ };
107
+ }
108
+
109
+ export function destroyWorldCompositor2D(compositor, auraRef = globalThis.aura) {
110
+ const aura = resolveAura(auraRef);
111
+ if (!compositor || typeof compositor !== 'object') return false;
112
+ if (compositor.targets && typeof compositor.targets === 'object') {
113
+ for (const stageId of compositor.stageIds || []) {
114
+ destroyTarget(aura, compositor.targets[stageId]);
115
+ }
116
+ }
117
+ compositor.width = 0;
118
+ compositor.height = 0;
119
+ compositor.targets = null;
120
+ compositor.stages = null;
121
+ return true;
122
+ }
123
+
124
+ export function ensureWorldCompositorTargets2D(compositor, options = {}, auraRef = globalThis.aura) {
125
+ const aura = resolveAura(auraRef);
126
+ if (!compositor || typeof compositor !== 'object') {
127
+ throw new Error('world compositor state is required.');
128
+ }
129
+
130
+ const dimensions = resolveDimensions(compositor, options);
131
+ if (!dimensions) return null;
132
+ if (
133
+ compositor.targets
134
+ && compositor.width === dimensions.width
135
+ && compositor.height === dimensions.height
136
+ ) {
137
+ return compositor.targets;
138
+ }
139
+
140
+ destroyWorldCompositor2D(compositor, aura);
141
+
142
+ const targets = {};
143
+ for (const stageId of compositor.stageIds) {
144
+ const target = createTarget(aura, dimensions.width, dimensions.height);
145
+ if (!target) {
146
+ for (const created of Object.values(targets)) {
147
+ destroyTarget(aura, created);
148
+ }
149
+ return null;
150
+ }
151
+ targets[stageId] = target;
152
+ }
153
+
154
+ compositor.width = dimensions.width;
155
+ compositor.height = dimensions.height;
156
+ compositor.targets = targets;
157
+ return targets;
158
+ }
159
+
160
+ export function runWorldCompositor2D(
161
+ compositor,
162
+ stageDefinitions,
163
+ options = {},
164
+ auraRef = globalThis.aura,
165
+ ) {
166
+ const aura = resolveAura(auraRef);
167
+ if (!compositor || typeof compositor !== 'object') {
168
+ throw new Error('world compositor state is required.');
169
+ }
170
+
171
+ const dimensions = resolveDimensions(compositor, options);
172
+ const fallbackDraw = typeof options.fallbackDraw === 'function' ? options.fallbackDraw : null;
173
+ if (!dimensions) {
174
+ if (fallbackDraw) fallbackDraw({ reasonCode: 'invalid_world_compositor_size' });
175
+ return {
176
+ ok: false,
177
+ reasonCode: 'invalid_world_compositor_size',
178
+ graph: null,
179
+ targets: null,
180
+ finalStage: null,
181
+ width: 0,
182
+ height: 0,
183
+ };
184
+ }
185
+
186
+ const targets = ensureWorldCompositorTargets2D(compositor, dimensions, aura);
187
+ if (!targets) {
188
+ if (fallbackDraw) fallbackDraw({ reasonCode: 'draw2d_render_target_unavailable' });
189
+ return {
190
+ ok: false,
191
+ reasonCode: 'draw2d_render_target_unavailable',
192
+ graph: null,
193
+ targets: null,
194
+ finalStage: null,
195
+ width: dimensions.width,
196
+ height: dimensions.height,
197
+ };
198
+ }
199
+
200
+ const definitions = normalizeStageDefinitions(compositor, stageDefinitions);
201
+ compositor.stages = definitions;
202
+ if (!aura?.draw2d || typeof aura.draw2d.runCompositorGraph !== 'function') {
203
+ if (fallbackDraw) {
204
+ fallbackDraw({
205
+ reasonCode: 'draw2d_compositor_unavailable',
206
+ width: dimensions.width,
207
+ height: dimensions.height,
208
+ targets,
209
+ });
210
+ }
211
+ return {
212
+ ok: false,
213
+ reasonCode: 'draw2d_compositor_unavailable',
214
+ graph: null,
215
+ targets,
216
+ finalStage: targets[compositor.finalStageId] || null,
217
+ width: dimensions.width,
218
+ height: dimensions.height,
219
+ };
220
+ }
221
+
222
+ const graph = aura.draw2d.runCompositorGraph(
223
+ compositor.graphName,
224
+ definitions.map((entry) => ({
225
+ id: entry.id,
226
+ target: targets[entry.id],
227
+ draw: () => {
228
+ if (typeof entry.draw !== 'function') return;
229
+ entry.draw({
230
+ id: entry.id,
231
+ width: dimensions.width,
232
+ height: dimensions.height,
233
+ graphName: compositor.graphName,
234
+ targets,
235
+ aura,
236
+ stage: getStageRecord(compositor, entry.id),
237
+ finalStageId: compositor.finalStageId,
238
+ });
239
+ },
240
+ })),
241
+ );
242
+
243
+ const finalStageIndex = compositor.stageIds.indexOf(compositor.finalStageId);
244
+ return {
245
+ ok: graph?.ok === true,
246
+ reasonCode: graph?.reasonCode || null,
247
+ graph,
248
+ targets,
249
+ finalStage: graph?.stages?.[finalStageIndex] || targets[compositor.finalStageId] || null,
250
+ width: dimensions.width,
251
+ height: dimensions.height,
252
+ };
253
+ }
@@ -0,0 +1,180 @@
1
+ import { syncStreamedWorldFocus2D } from './streamed-world-2d.js';
2
+
3
+ function cloneSnapshot(value) {
4
+ if (value == null) return value;
5
+ return JSON.parse(JSON.stringify(value));
6
+ }
7
+
8
+ function resolveStorage(auraRef = globalThis.aura) {
9
+ const storage = auraRef?.storage;
10
+ if (!storage || typeof storage !== 'object') return null;
11
+ return storage;
12
+ }
13
+
14
+ function storageSave(storage, key, value) {
15
+ if (!storage || typeof key !== 'string' || key.length === 0) return false;
16
+ if (typeof storage.save === 'function') {
17
+ storage.save(key, value);
18
+ return true;
19
+ }
20
+ if (typeof storage.set === 'function') {
21
+ storage.set(key, value);
22
+ return true;
23
+ }
24
+ return false;
25
+ }
26
+
27
+ function storageLoad(storage, key, fallback = null) {
28
+ if (!storage || typeof key !== 'string' || key.length === 0) return fallback;
29
+ if (typeof storage.load === 'function') {
30
+ return storage.load(key, fallback);
31
+ }
32
+ if (typeof storage.get === 'function') {
33
+ return storage.get(key, fallback);
34
+ }
35
+ return fallback;
36
+ }
37
+
38
+ function storageDelete(storage, key) {
39
+ if (!storage || typeof key !== 'string' || key.length === 0) return false;
40
+ if (typeof storage.delete === 'function') {
41
+ storage.delete(key);
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+
47
+ function normalizeSnapshot(snapshot) {
48
+ if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return null;
49
+ return {
50
+ version: Number.isFinite(Number(snapshot.version)) ? Number(snapshot.version) : 1,
51
+ scope: typeof snapshot.scope === 'string' ? snapshot.scope : 'streamed-world-2d',
52
+ activeRegionId: typeof snapshot.activeRegionId === 'string' ? snapshot.activeRegionId : null,
53
+ focus: snapshot.focus && typeof snapshot.focus === 'object'
54
+ ? {
55
+ x: Number(snapshot.focus.x) || 0,
56
+ y: Number(snapshot.focus.y) || 0,
57
+ }
58
+ : null,
59
+ world: snapshot.world == null ? null : cloneSnapshot(snapshot.world),
60
+ regions: Array.isArray(snapshot.regions)
61
+ ? snapshot.regions
62
+ .filter((entry) => entry && typeof entry === 'object' && typeof entry.id === 'string')
63
+ .map((entry) => ({
64
+ id: entry.id,
65
+ data: cloneSnapshot(entry.data),
66
+ }))
67
+ : [],
68
+ };
69
+ }
70
+
71
+ export function captureStreamedWorldSnapshot2D(stream, options = {}) {
72
+ if (!stream || !(stream.regionById instanceof Map)) {
73
+ return null;
74
+ }
75
+ const captureRegion = typeof options.captureRegion === 'function' ? options.captureRegion : null;
76
+ const captureWorld = typeof options.captureWorld === 'function' ? options.captureWorld : null;
77
+ const regionIds = Array.isArray(options.regionIds)
78
+ ? options.regionIds.filter((entry) => typeof entry === 'string')
79
+ : stream.regions.map((entry) => entry.id);
80
+ const regions = [];
81
+
82
+ regionIds.forEach((regionId) => {
83
+ const descriptor = stream.regionById.get(regionId) || null;
84
+ if (!descriptor || !captureRegion) return;
85
+ const record = stream.loaded?.get(regionId) || null;
86
+ const data = captureRegion(descriptor, record, stream, options);
87
+ if (data == null) return;
88
+ regions.push({
89
+ id: regionId,
90
+ data: cloneSnapshot(data),
91
+ });
92
+ });
93
+
94
+ return {
95
+ version: 1,
96
+ scope: options.scope ?? 'streamed-world-2d',
97
+ activeRegionId: stream.activeRegionId ?? null,
98
+ focus: stream.focus ? { x: Number(stream.focus.x) || 0, y: Number(stream.focus.y) || 0 } : null,
99
+ world: captureWorld ? cloneSnapshot(captureWorld(stream, options)) : null,
100
+ regions,
101
+ };
102
+ }
103
+
104
+ export function applyStreamedWorldSnapshot2D(stream, snapshot, options = {}) {
105
+ if (!stream || !(stream.regionById instanceof Map)) {
106
+ return {
107
+ ok: false,
108
+ reasonCode: 'invalid_stream',
109
+ appliedRegionIds: [],
110
+ };
111
+ }
112
+ const normalized = normalizeSnapshot(snapshot);
113
+ if (!normalized) {
114
+ return {
115
+ ok: false,
116
+ reasonCode: 'invalid_snapshot',
117
+ appliedRegionIds: [],
118
+ };
119
+ }
120
+ const applyRegion = typeof options.applyRegion === 'function' ? options.applyRegion : null;
121
+ const applyWorld = typeof options.applyWorld === 'function' ? options.applyWorld : null;
122
+ const appliedRegionIds = [];
123
+
124
+ if (applyWorld && normalized.world != null) {
125
+ applyWorld(cloneSnapshot(normalized.world), stream, options);
126
+ }
127
+
128
+ normalized.regions.forEach((entry) => {
129
+ const descriptor = stream.regionById.get(entry.id) || null;
130
+ if (!descriptor || !applyRegion) return;
131
+ const record = stream.loaded?.get(entry.id) || null;
132
+ applyRegion(descriptor, record, cloneSnapshot(entry.data), stream, options);
133
+ appliedRegionIds.push(entry.id);
134
+ });
135
+
136
+ if (options.restoreFocus === true && normalized.focus) {
137
+ syncStreamedWorldFocus2D(stream, options.auraRef, normalized.focus);
138
+ }
139
+
140
+ return {
141
+ ok: true,
142
+ reasonCode: null,
143
+ appliedRegionIds,
144
+ restoredFocus: options.restoreFocus === true && normalized.focus != null,
145
+ };
146
+ }
147
+
148
+ export function saveStreamedWorldSnapshot2D(key, stream, auraRef = globalThis.aura, options = {}) {
149
+ const storage = resolveStorage(auraRef);
150
+ const snapshot = captureStreamedWorldSnapshot2D(stream, options);
151
+ if (!snapshot) {
152
+ return {
153
+ ok: false,
154
+ reasonCode: 'invalid_stream',
155
+ snapshot: null,
156
+ };
157
+ }
158
+ if (!storageSave(storage, key, snapshot)) {
159
+ return {
160
+ ok: false,
161
+ reasonCode: 'storage_unavailable',
162
+ snapshot,
163
+ };
164
+ }
165
+ return {
166
+ ok: true,
167
+ reasonCode: null,
168
+ snapshot,
169
+ };
170
+ }
171
+
172
+ export function loadStreamedWorldSnapshot2D(key, auraRef = globalThis.aura, fallback = null) {
173
+ const storage = resolveStorage(auraRef);
174
+ const snapshot = storageLoad(storage, key, fallback);
175
+ return normalizeSnapshot(snapshot);
176
+ }
177
+
178
+ export function deleteStreamedWorldSnapshot2D(key, auraRef = globalThis.aura) {
179
+ return storageDelete(resolveStorage(auraRef), key);
180
+ }
@@ -62,18 +62,47 @@ function resolveAuraMaxxBinary() {
62
62
  return process.platform === 'win32' ? 'auramaxx.cmd' : 'auramaxx';
63
63
  }
64
64
 
65
+ function resolveAuraAliasBinary() {
66
+ return process.platform === 'win32' ? 'aura.cmd' : 'aura';
67
+ }
68
+
65
69
  function resolveNpmBinary() {
66
70
  return process.platform === 'win32' ? 'npm.cmd' : 'npm';
67
71
  }
68
72
 
69
- function isAuraMaxxInstalled() {
70
- const probe = spawnSync(resolveAuraMaxxBinary(), ['--version'], {
73
+ function resolveInvocationCwd() {
74
+ const forwardedCwd = process.env.AURA_INVOKE_CWD;
75
+ if (forwardedCwd && isAbsolute(forwardedCwd)) {
76
+ return resolve(forwardedCwd);
77
+ }
78
+
79
+ const shellPwd = process.env.PWD;
80
+ if (shellPwd && isAbsolute(shellPwd)) {
81
+ return resolve(shellPwd);
82
+ }
83
+
84
+ return resolve(process.cwd());
85
+ }
86
+
87
+ function canProbeBinary(binary) {
88
+ const probe = spawnSync(binary, ['--help'], {
71
89
  stdio: 'ignore',
72
90
  env: process.env,
73
91
  });
74
92
  return !probe.error && probe.status === 0;
75
93
  }
76
94
 
95
+ function isAuraMaxxInstalled() {
96
+ if (process.env.AURAMAXX_CLI_AVAILABLE === '1') {
97
+ return true;
98
+ }
99
+ if (localAuraCli) {
100
+ return true;
101
+ }
102
+ const candidates = [resolveAuraMaxxBinary(), resolveAuraAliasBinary()];
103
+ return candidates.some((binary, index) => candidates.indexOf(binary) === index && canProbeBinary(binary));
104
+ }
105
+
77
106
  async function installAuraMaxxGlobally() {
78
107
  return new Promise((resolveInstall) => {
79
108
  const child = spawn(
@@ -373,7 +402,7 @@ async function promptSelect(message, options, defaultValue) {
373
402
 
374
403
  function normalizeDisplayPath(targetPath) {
375
404
  const resolvedPath = resolve(targetPath);
376
- const relativePath = relative(process.cwd(), resolvedPath).replaceAll('\\', '/');
405
+ const relativePath = relative(resolveInvocationCwd(), resolvedPath).replaceAll('\\', '/');
377
406
  if (!relativePath) return '.';
378
407
  if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
379
408
  return `./${relativePath}`;
@@ -402,10 +431,10 @@ function resolveUniqueDestination(preferredPath) {
402
431
 
403
432
  function resolveDefaultForkDestination() {
404
433
  const defaultBaseName = `${toPackageShortName(packageName)}-fork`;
405
- const cwd = resolve(process.cwd());
406
- const baseDir = isSubpath(projectRoot, cwd)
434
+ const invocationCwd = resolveInvocationCwd();
435
+ const baseDir = isSubpath(projectRoot, invocationCwd)
407
436
  ? dirname(projectRoot)
408
- : cwd;
437
+ : invocationCwd;
409
438
  return resolveUniqueDestination(resolve(baseDir, defaultBaseName));
410
439
  }
411
440
 
@@ -454,7 +483,7 @@ function parseForkArgs(args) {
454
483
  async function resolveForkDestination(args) {
455
484
  const parsed = parseForkArgs(args);
456
485
  if (parsed.destination) {
457
- return resolve(process.cwd(), parsed.destination);
486
+ return resolve(resolveInvocationCwd(), parsed.destination);
458
487
  }
459
488
 
460
489
  const suggestedDestination = resolveDefaultForkDestination();
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: auramaxx
3
+ description: Use when building or modifying AuraMaxx or AuraJS game projects. Helps agents infer game requirements from local project files, pick the right starter-owned edit points, and validate gameplay work with the current CLI and project wrapper commands.
4
+ ---
5
+
6
+ # AuraMaxx Skill
7
+
8
+ Use this skill when working inside an AuraMaxx or AuraJS game project.
9
+
10
+ ## Working Set
11
+
12
+ Read these files in order:
13
+
14
+ 1. `project-requirements.md`
15
+ 2. `starter-recipes.md`
16
+ 3. `validation-checklist.md`
17
+ 4. the current project files named by those docs
18
+
19
+ Use `https://www.aurajs.gg/llm.txt` only when you need broader public docs
20
+ context than the local project and installed package sources provide.
21
+
22
+ ## Rules
23
+
24
+ - Treat the current project files as the primary source of truth.
25
+ - Keep `src/main.js` thin; prefer the registry-backed runtime flow already in the scaffold.
26
+ - Put durable authored data in `config/` or `content/`, not scene-local constants.
27
+ - Put mutable runtime state in scenes or `src/runtime/app-state.js`.
28
+ - Extend `src/starter-utils/` before inventing one-off helpers.
29
+ - Use `auramaxx make` instead of hand-wiring new scaffold nouns when a generator exists.
30
+
31
+ ## Default Loop
32
+
33
+ 1. Infer the current game requirements with `project-requirements.md`.
34
+ 2. Choose the starter-specific edit path in `starter-recipes.md`.
35
+ 3. Implement the smallest playable slice that changes one mechanic at a time.
36
+ 4. Run the relevant checks from `validation-checklist.md`.
37
+
38
+ ## Retrieval Boundary
39
+
40
+ Open local project files first. If you still need package-level behavior or
41
+ template intent, inspect:
42
+
43
+ - `node_modules/@auraindustry/aurajs/src/`
44
+ - `node_modules/@auraindustry/aurajs/templates/`
45
+
46
+ Use `aurajs.gg/llm.txt` after that, not before.
@@ -0,0 +1,68 @@
1
+ # AuraMaxx Project Requirements
2
+
3
+ Use this checklist to understand the game before you edit it.
4
+
5
+ ## 1. Identity
6
+
7
+ - Read `package.json` and `aura.config.json`.
8
+ - Capture: package name, bin name, project title, version, runtime targets, and
9
+ whether the project is intended to be published.
10
+
11
+ ## 2. Template and module shape
12
+
13
+ - Read `aura.capabilities.json` and `config/gameplay/game.config.json`.
14
+ - Capture: template id, required APIs, optional modules, `startingSceneId`,
15
+ `gameplaySceneId`, `hudScreenId`, and `playerPrefabId`.
16
+
17
+ ## 3. Player promise
18
+
19
+ - Read `README.md`, `RUNBOOK.md`, `docs/design/game-pillars.md`,
20
+ `docs/design/loop.md`, and `assets/starter/dev-notes.md`.
21
+ - Write down:
22
+ - genre and player fantasy
23
+ - first 60-second goal
24
+ - controls
25
+ - win and lose states
26
+ - what “done for this task” should feel like on screen
27
+
28
+ ## 4. Authored owners
29
+
30
+ - Read `src/runtime/project-registry.js`, `config/gameplay/*`,
31
+ `content/gameplay/*`, `content/registries/*`, `prefabs/*.prefab.js`,
32
+ `scenes/*.scene.js`, and `ui/*.screen.js`.
33
+ - Map which files own:
34
+ - loop logic
35
+ - tuning values
36
+ - authored nouns and layout
37
+ - HUD and modal UI
38
+ - progression, save, dialogue, inventory, or multiplayer state
39
+
40
+ ## 5. Assets and starter data
41
+
42
+ - Read `assets/starter/*` first, then only the `assets/**` files actually used
43
+ by the touched scene or prefab.
44
+ - Capture: placeholder assets still in use, required art/audio hooks, and any
45
+ starter JSON or notes that define layout, waves, cards, checkpoints, or room
46
+ behavior.
47
+
48
+ ## 6. Decide where the change belongs
49
+
50
+ - Stable tunable: `config/`
51
+ - Authored noun or layout data: `content/`
52
+ - Reusable entity or world descriptor: `prefabs/`
53
+ - Live mechanic or per-frame flow: `scenes/`
54
+ - HUD, overlay, or modal surface: `ui/`
55
+ - Cross-scene mutable app state: `src/runtime/app-state.js`
56
+ - Reusable starter helper: `src/starter-utils/`
57
+
58
+ ## 7. Discovery commands
59
+
60
+ - `auramaxx explain`
61
+ - `auramaxx check`
62
+ - `auramaxx make list --json`
63
+ - `npm run dev`
64
+ - `npm run state -- export --compact`
65
+ - `npm run action -- schema --compact`
66
+
67
+ Use these to confirm what the project already exposes before you add new
68
+ structure.