@doodle-engine/cli 0.0.15 → 0.0.17

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.
package/dist/cli.js CHANGED
@@ -1,84 +1,84 @@
1
1
  #!/usr/bin/env node
2
- import { Command as q } from "commander";
3
- import { crayon as i } from "crayon.js";
4
- import { createServer as G, build as $ } from "vite";
2
+ import { Command as x } from "commander";
3
+ import { crayon as s } from "crayon.js";
4
+ import { createServer as q, build as $ } from "vite";
5
5
  import S from "@vitejs/plugin-react";
6
6
  import { watch as H } from "chokidar";
7
- import { readdir as p, readFile as f, mkdir as D, writeFile as k } from "fs/promises";
8
- import { join as u, extname as g, relative as b, dirname as M } from "path";
9
- import { parse as y } from "yaml";
10
- import { parseDialogue as E } from "@doodle-engine/core";
7
+ import { readdir as p, readFile as f, mkdir as D, writeFile as w } from "fs/promises";
8
+ import { join as h, extname as g, relative as y, dirname as M } from "path";
9
+ import { parse as b } from "yaml";
10
+ import { parseDialogue as k } from "@doodle-engine/core";
11
11
  import F from "prompts";
12
- function I(t, a) {
12
+ function N(n, a) {
13
13
  const r = [];
14
- for (const o of Object.values(t.dialogues)) {
15
- const n = a.get(o.id) || `dialogue:${o.id}`;
16
- r.push(...P(o, n));
14
+ for (const o of Object.values(n.dialogues)) {
15
+ const t = a.get(o.id) || `dialogue:${o.id}`;
16
+ r.push(...B(o, t));
17
17
  }
18
- for (const o of Object.values(t.characters))
19
- if (o.dialogue && !t.dialogues[o.dialogue]) {
20
- const n = a.get(o.id) || `character:${o.id}`;
18
+ for (const o of Object.values(n.characters))
19
+ if (o.dialogue && !n.dialogues[o.dialogue]) {
20
+ const t = a.get(o.id) || `character:${o.id}`;
21
21
  r.push({
22
- file: n,
22
+ file: t,
23
23
  message: `Character "${o.id}" references non-existent dialogue "${o.dialogue}"`,
24
24
  suggestion: `Create dialogue "${o.dialogue}" or fix the reference`
25
25
  });
26
26
  }
27
- return r.push(...W(t, a)), r;
27
+ return r.push(...W(n, a)), r;
28
28
  }
29
- function P(t, a) {
29
+ function B(n, a) {
30
30
  const r = [], o = /* @__PURE__ */ new Set();
31
- for (const n of t.nodes)
32
- o.has(n.id) && r.push({
31
+ for (const t of n.nodes)
32
+ o.has(t.id) && r.push({
33
33
  file: a,
34
- message: `Duplicate node ID "${n.id}"`,
34
+ message: `Duplicate node ID "${t.id}"`,
35
35
  suggestion: "Node IDs must be unique within a dialogue"
36
- }), o.add(n.id);
37
- o.has(t.startNode) || r.push({
36
+ }), o.add(t.id);
37
+ o.has(n.startNode) || r.push({
38
38
  file: a,
39
- message: `Start node "${t.startNode}" not found`,
40
- suggestion: `Add a NODE ${t.startNode} or fix the startNode reference`
39
+ message: `Start node "${n.startNode}" not found`,
40
+ suggestion: `Add a NODE ${n.startNode} or fix the startNode reference`
41
41
  });
42
- for (const n of t.nodes)
43
- r.push(...B(n, o, a));
42
+ for (const t of n.nodes)
43
+ r.push(...L(t, o, a));
44
44
  return r;
45
45
  }
46
- function B(t, a, r) {
46
+ function L(n, a, r) {
47
47
  const o = [];
48
- if (t.next && !a.has(t.next) && o.push({
48
+ if (n.next && !a.has(n.next) && o.push({
49
49
  file: r,
50
- message: `Node "${t.id}" GOTO "${t.next}" points to non-existent node`,
51
- suggestion: `Add NODE ${t.next} or fix the GOTO target`
52
- }), t.conditionalNext)
53
- for (const n of t.conditionalNext)
54
- a.has(n.next) || o.push({
50
+ message: `Node "${n.id}" GOTO "${n.next}" points to non-existent node`,
51
+ suggestion: `Add NODE ${n.next} or fix the GOTO target`
52
+ }), n.conditionalNext)
53
+ for (const t of n.conditionalNext)
54
+ a.has(t.next) || o.push({
55
55
  file: r,
56
- message: `Node "${t.id}" IF block GOTO "${n.next}" points to non-existent node`,
57
- suggestion: `Add NODE ${n.next} or fix the GOTO target`
58
- }), o.push(...v(n.condition, t.id, r));
59
- for (const n of t.choices) {
60
- if (!n.effects?.some(
56
+ message: `Node "${n.id}" IF block GOTO "${t.next}" points to non-existent node`,
57
+ suggestion: `Add NODE ${t.next} or fix the GOTO target`
58
+ }), o.push(...v(t.condition, n.id, r));
59
+ for (const t of n.choices) {
60
+ if (!t.effects?.some(
61
61
  (e) => e.type === "endDialogue" || e.type === "goToLocation" || e.type === "startDialogue"
62
- ) && n.next && !a.has(n.next) && o.push({
62
+ ) && t.next && !a.has(t.next) && o.push({
63
63
  file: r,
64
- message: `Node "${t.id}" choice "${n.id}" GOTO "${n.next}" points to non-existent node`,
65
- suggestion: `Add NODE ${n.next} or fix the GOTO target`
66
- }), n.conditions)
67
- for (const e of n.conditions)
68
- o.push(...v(e, t.id, r));
69
- if (n.effects)
70
- for (const e of n.effects)
71
- o.push(...C(e, t.id, r));
64
+ message: `Node "${n.id}" choice "${t.id}" GOTO "${t.next}" points to non-existent node`,
65
+ suggestion: `Add NODE ${t.next} or fix the GOTO target`
66
+ }), t.conditions)
67
+ for (const e of t.conditions)
68
+ o.push(...v(e, n.id, r));
69
+ if (t.effects)
70
+ for (const e of t.effects)
71
+ o.push(...C(e, n.id, r));
72
72
  }
73
- if (t.conditions)
74
- for (const n of t.conditions)
75
- o.push(...v(n, t.id, r));
76
- if (t.effects)
77
- for (const n of t.effects)
78
- o.push(...C(n, t.id, r));
73
+ if (n.conditions)
74
+ for (const t of n.conditions)
75
+ o.push(...v(t, n.id, r));
76
+ if (n.effects)
77
+ for (const t of n.effects)
78
+ o.push(...C(t, n.id, r));
79
79
  return o;
80
80
  }
81
- const L = {
81
+ const P = {
82
82
  hasFlag: ["flag"],
83
83
  notFlag: ["flag"],
84
84
  hasItem: ["itemId"],
@@ -123,137 +123,141 @@ const L = {
123
123
  showInterlude: ["interludeId"],
124
124
  roll: ["variable", "min", "max"]
125
125
  };
126
- function v(t, a, r) {
126
+ function v(n, a, r) {
127
127
  const o = [];
128
- if (!t.type)
128
+ if (!n.type)
129
129
  return o.push({
130
130
  file: r,
131
131
  message: `Node "${a}" has condition with missing type`
132
132
  }), o;
133
- if (t.type === "timeIs")
134
- return t.hour === void 0 && t.day === void 0 && o.push({
133
+ if (n.type === "timeIs")
134
+ return n.hour === void 0 && n.day === void 0 && o.push({
135
135
  file: r,
136
136
  message: `Node "${a}" condition "timeIs" must have at least one of "hour" or "day" argument`
137
137
  }), o;
138
- const n = L[t.type];
139
- if (!n)
138
+ const t = P[n.type];
139
+ if (!t)
140
140
  return o;
141
- for (const s of n)
142
- (t[s] === void 0 || t[s] === null || t[s] === "") && o.push({
141
+ for (const i of t)
142
+ (n[i] === void 0 || n[i] === null || n[i] === "") && o.push({
143
143
  file: r,
144
- message: `Node "${a}" condition "${t.type}" missing required "${s}" argument`
144
+ message: `Node "${a}" condition "${n.type}" missing required "${i}" argument`
145
145
  });
146
146
  return o;
147
147
  }
148
- function C(t, a, r) {
148
+ function C(n, a, r) {
149
149
  const o = [];
150
- if (!t.type)
150
+ if (!n.type)
151
151
  return o.push({
152
152
  file: r,
153
153
  message: `Node "${a}" has effect with missing type`
154
154
  }), o;
155
- const n = U[t.type];
156
- if (!n)
155
+ const t = U[n.type];
156
+ if (!t)
157
157
  return o;
158
- for (const s of n)
159
- (t[s] === void 0 || t[s] === null || t[s] === "") && o.push({
158
+ for (const i of t)
159
+ (n[i] === void 0 || n[i] === null || n[i] === "") && o.push({
160
160
  file: r,
161
- message: `Node "${a}" effect "${t.type}" missing required "${s}" argument`
161
+ message: `Node "${a}" effect "${n.type}" missing required "${i}" argument`
162
162
  });
163
163
  return o;
164
164
  }
165
- function W(t, a) {
165
+ function W(n, a) {
166
166
  const r = [], o = /* @__PURE__ */ new Set();
167
- for (const e of Object.values(t.locales))
167
+ for (const e of Object.values(n.locales))
168
168
  for (const c of Object.keys(e))
169
169
  o.add(c);
170
- const n = (e) => e.startsWith("@"), s = (e, c, l) => {
170
+ const t = (e) => e.startsWith("@"), i = (e, c, l) => {
171
171
  const d = e.slice(1);
172
172
  if (!o.has(d)) {
173
- const h = a.get(c) || `${l}:${c}`;
173
+ const u = a.get(c) || `${l}:${c}`;
174
174
  r.push({
175
- file: h,
175
+ file: u,
176
176
  message: `Localization key "${e}" not found in any locale file`,
177
177
  suggestion: `Add "${d}: ..." to your locale files`
178
178
  });
179
179
  }
180
180
  };
181
- for (const e of Object.values(t.locations))
182
- n(e.name) && s(e.name, e.id, "location"), n(e.description) && s(e.description, e.id, "location");
183
- for (const e of Object.values(t.characters))
184
- n(e.name) && s(e.name, e.id, "character"), n(e.biography) && s(e.biography, e.id, "character");
185
- for (const e of Object.values(t.items))
186
- n(e.name) && s(e.name, e.id, "item"), n(e.description) && s(e.description, e.id, "item");
187
- for (const e of Object.values(t.quests)) {
188
- n(e.name) && s(e.name, e.id, "quest"), n(e.description) && s(e.description, e.id, "quest");
181
+ for (const e of Object.values(n.locations))
182
+ t(e.name) && i(e.name, e.id, "location"), t(e.description) && i(e.description, e.id, "location");
183
+ for (const e of Object.values(n.characters))
184
+ t(e.name) && i(e.name, e.id, "character"), t(e.biography) && i(e.biography, e.id, "character");
185
+ for (const e of Object.values(n.items))
186
+ t(e.name) && i(e.name, e.id, "item"), t(e.description) && i(e.description, e.id, "item");
187
+ for (const e of Object.values(n.quests)) {
188
+ t(e.name) && i(e.name, e.id, "quest"), t(e.description) && i(e.description, e.id, "quest");
189
189
  for (const c of e.stages)
190
- n(c.description) && s(c.description, e.id, "quest");
190
+ t(c.description) && i(c.description, e.id, "quest");
191
191
  }
192
- for (const e of Object.values(t.journalEntries))
193
- n(e.title) && s(e.title, e.id, "journal"), n(e.text) && s(e.text, e.id, "journal");
194
- for (const e of Object.values(t.dialogues))
192
+ for (const e of Object.values(n.journalEntries))
193
+ t(e.title) && i(e.title, e.id, "journal"), t(e.text) && i(e.text, e.id, "journal");
194
+ for (const e of Object.values(n.dialogues))
195
195
  for (const c of e.nodes) {
196
- n(c.text) && s(c.text, e.id, "dialogue");
196
+ t(c.text) && i(c.text, e.id, "dialogue");
197
197
  for (const l of c.choices)
198
- n(l.text) && s(l.text, e.id, "dialogue");
198
+ t(l.text) && i(l.text, e.id, "dialogue");
199
199
  }
200
- for (const e of Object.values(t.interludes))
201
- n(e.text) && s(e.text, e.id, "interlude");
200
+ for (const e of Object.values(n.interludes))
201
+ t(e.text) && i(e.text, e.id, "interlude");
202
202
  return r;
203
203
  }
204
- function N(t) {
205
- if (t.length === 0) {
206
- console.log(i.green("✓ No validation errors"));
204
+ function I(n) {
205
+ if (n.length === 0) {
206
+ console.log(s.green("✓ No validation errors"));
207
207
  return;
208
208
  }
209
- console.log(i.red(`
210
- ✗ Found ${t.length} validation error${t.length === 1 ? "" : "s"}:
209
+ console.log(s.red(`
210
+ ✗ Found ${n.length} validation error${n.length === 1 ? "" : "s"}:
211
211
  `));
212
- for (const a of t)
213
- console.log(i.bold(a.file) + (a.line ? `:${a.line}` : "")), console.log(" " + i.red(a.message)), a.suggestion && console.log(" " + i.dim(a.suggestion)), console.log();
212
+ for (const a of n)
213
+ console.log(s.bold(a.file) + (a.line ? `:${a.line}` : "")), console.log(" " + s.red(a.message)), a.suggestion && console.log(" " + s.dim(a.suggestion)), console.log();
214
214
  }
215
215
  const R = "ðŸū", J = "âœĻ", Q = "✏ïļ", Y = "➕";
216
216
  async function V() {
217
- const t = process.cwd(), a = u(t, "content");
218
- console.log(""), console.log(i.bold.magenta(` ${R} Doodle Engine Dev Server ${R}`)), console.log("");
217
+ const n = process.cwd(), a = h(n, "content");
218
+ console.log(""), console.log(s.bold.magenta(` ${R} Doodle Engine Dev Server ${R}`)), console.log("");
219
219
  const r = {
220
220
  name: "doodle-content-loader",
221
- configureServer(n) {
222
- n.middlewares.use("/api/content", async (l, d) => {
221
+ configureServer(t) {
222
+ t.middlewares.use("/api/content", async (d, u) => {
223
223
  try {
224
- const h = await K(a);
225
- d.setHeader("Content-Type", "application/json"), d.end(JSON.stringify(h));
226
- } catch (h) {
227
- console.error(i.red(" Error loading content:"), h), d.statusCode = 500, d.end(JSON.stringify({ error: "Failed to load content" }));
224
+ const m = await K(a);
225
+ u.setHeader("Content-Type", "application/json"), u.end(JSON.stringify(m));
226
+ } catch (m) {
227
+ console.error(s.red(" Error loading content:"), m), u.statusCode = 500, u.end(JSON.stringify({ error: "Failed to load content" }));
228
228
  }
229
229
  });
230
- const s = H(a, {
230
+ const i = H(a, {
231
231
  ignored: /(^|[\/\\])\../,
232
232
  persistent: !0
233
233
  });
234
- let e = null;
235
- const c = (l) => {
236
- e && clearTimeout(e), e = setTimeout(async () => {
237
- e = null, console.log(l), await z(a), n.ws.send({ type: "full-reload", path: "*" });
234
+ let e = !1;
235
+ i.on("ready", () => {
236
+ e = !0;
237
+ });
238
+ let c = null;
239
+ const l = (d) => {
240
+ c && clearTimeout(c), c = setTimeout(async () => {
241
+ c = null, console.log(d), await z(a), t.ws.send({ type: "full-reload", path: "*" });
238
242
  }, 50);
239
243
  };
240
- s.on("change", (l) => {
241
- c(i.yellow(` ${Q} Content changed: ${l}`));
242
- }), s.on("add", (l) => {
243
- c(i.green(` ${Y} Content added: ${l}`));
244
+ i.on("change", (d) => {
245
+ l(s.yellow(` ${Q} Content changed: ${d}`));
246
+ }), i.on("add", (d) => {
247
+ e && l(s.green(` ${Y} Content added: ${d}`));
244
248
  });
245
249
  }
246
- }, o = await G({
247
- root: t,
250
+ }, o = await q({
251
+ root: n,
248
252
  plugins: [S(), r],
249
253
  server: {
250
254
  port: 3e3,
251
255
  open: !0
252
256
  }
253
257
  });
254
- await o.listen(), o.printUrls(), console.log(""), console.log(i.dim(` ${J} Watching content files for changes...`)), console.log("");
258
+ await o.listen(), o.printUrls(), console.log(""), console.log(s.dim(` ${J} Watching content files for changes...`)), console.log("");
255
259
  }
256
- async function K(t) {
260
+ async function K(n) {
257
261
  const a = {
258
262
  locations: {},
259
263
  characters: {},
@@ -275,41 +279,41 @@ async function K(t) {
275
279
  { dir: "journal", key: "journalEntries" },
276
280
  { dir: "interludes", key: "interludes" }
277
281
  ];
278
- for (const { dir: n, key: s } of o) {
279
- const e = u(t, n);
282
+ for (const { dir: t, key: i } of o) {
283
+ const e = h(n, t);
280
284
  try {
281
285
  const c = await p(e);
282
286
  for (const l of c)
283
287
  if (g(l) === ".yaml" || g(l) === ".yml") {
284
- const d = u(e, l), h = await f(d, "utf-8"), m = y(h);
285
- m && m.id && (a[s][m.id] = m);
288
+ const d = h(e, l), u = await f(d, "utf-8"), m = b(u);
289
+ m && m.id && (a[i][m.id] = m);
286
290
  }
287
291
  } catch {
288
292
  }
289
293
  }
290
294
  try {
291
- const n = u(t, "locales"), s = await p(n);
292
- for (const e of s)
295
+ const t = h(n, "locales"), i = await p(t);
296
+ for (const e of i)
293
297
  if (g(e) === ".yaml" || g(e) === ".yml") {
294
- const c = u(n, e), l = await f(c, "utf-8"), d = y(l), h = e.replace(/\.(yaml|yml)$/, "");
295
- a.locales[h] = d ?? {};
298
+ const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
299
+ a.locales[u] = d ?? {};
296
300
  }
297
301
  } catch {
298
302
  }
299
303
  try {
300
- const n = u(t, "dialogues"), s = await p(n);
301
- for (const e of s)
304
+ const t = h(n, "dialogues"), i = await p(t);
305
+ for (const e of i)
302
306
  if (g(e) === ".dlg") {
303
- const c = u(n, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), h = E(l, d);
304
- a.dialogues[h.id] = h;
307
+ const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
308
+ a.dialogues[u.id] = u;
305
309
  }
306
310
  } catch {
307
311
  }
308
312
  try {
309
- const n = u(t, "game.yaml"), s = await f(n, "utf-8");
310
- r = y(s);
313
+ const t = h(n, "game.yaml"), i = await f(t, "utf-8");
314
+ r = b(i);
311
315
  } catch {
312
- console.warn(i.yellow(" No game.yaml found, using defaults")), r = {
316
+ console.warn(s.yellow(" No game.yaml found, using defaults")), r = {
313
317
  startLocation: "tavern",
314
318
  startTime: { day: 1, hour: 8 },
315
319
  startFlags: {},
@@ -319,15 +323,15 @@ async function K(t) {
319
323
  }
320
324
  return { registry: a, config: r };
321
325
  }
322
- async function z(t) {
326
+ async function z(n) {
323
327
  try {
324
- const { registry: a, fileMap: r } = await X(t), o = I(a, r);
325
- o.length > 0 && (console.log(""), N(o), console.log(""));
328
+ const { registry: a, fileMap: r } = await X(n), o = N(a, r);
329
+ o.length > 0 && (console.log(""), I(o), console.log(""));
326
330
  } catch (a) {
327
- console.error(i.red(" Error running validation:"), a);
331
+ console.error(s.red(" Error running validation:"), a);
328
332
  }
329
333
  }
330
- async function X(t) {
334
+ async function X(n) {
331
335
  const a = {
332
336
  locations: {},
333
337
  characters: {},
@@ -347,65 +351,65 @@ async function X(t) {
347
351
  { dir: "journal", key: "journalEntries" },
348
352
  { dir: "interludes", key: "interludes" }
349
353
  ];
350
- for (const { dir: n, key: s } of o) {
351
- const e = u(t, n);
354
+ for (const { dir: t, key: i } of o) {
355
+ const e = h(n, t);
352
356
  try {
353
357
  const c = await p(e);
354
358
  for (const l of c)
355
359
  if (g(l) === ".yaml" || g(l) === ".yml") {
356
- const d = u(e, l), h = await f(d, "utf-8"), m = y(h);
357
- m && m.id && (a[s][m.id] = m, r.set(m.id, b(process.cwd(), d)));
360
+ const d = h(e, l), u = await f(d, "utf-8"), m = b(u);
361
+ m && m.id && (a[i][m.id] = m, r.set(m.id, y(process.cwd(), d)));
358
362
  }
359
363
  } catch {
360
364
  }
361
365
  }
362
366
  try {
363
- const n = u(t, "locales"), s = await p(n);
364
- for (const e of s)
367
+ const t = h(n, "locales"), i = await p(t);
368
+ for (const e of i)
365
369
  if (g(e) === ".yaml" || g(e) === ".yml") {
366
- const c = u(n, e), l = await f(c, "utf-8"), d = y(l), h = e.replace(/\.(yaml|yml)$/, "");
367
- a.locales[h] = d ?? {};
370
+ const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
371
+ a.locales[u] = d ?? {};
368
372
  }
369
373
  } catch {
370
374
  }
371
375
  try {
372
- const n = u(t, "dialogues"), s = await p(n);
373
- for (const e of s)
376
+ const t = h(n, "dialogues"), i = await p(t);
377
+ for (const e of i)
374
378
  if (g(e) === ".dlg") {
375
- const c = u(n, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), h = E(l, d);
376
- a.dialogues[h.id] = h, r.set(h.id, b(process.cwd(), c));
379
+ const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
380
+ a.dialogues[u.id] = u, r.set(u.id, y(process.cwd(), c));
377
381
  }
378
382
  } catch {
379
383
  }
380
384
  return { registry: a, fileMap: r };
381
385
  }
382
386
  async function Z() {
383
- const t = process.cwd(), a = u(t, "content");
384
- console.log(""), console.log(i.bold.magenta("🐕 Building Doodle Engine game...")), console.log(""), console.log(i.dim("Validating content..."));
387
+ const n = process.cwd(), a = h(n, "content");
388
+ console.log(""), console.log(s.bold.magenta("🐕 Building Doodle Engine game...")), console.log(""), console.log(s.dim("Validating content..."));
385
389
  let r;
386
390
  try {
387
- const { registry: o, fileMap: n, config: s } = await ee(a), e = I(o, n);
388
- N(e), e.length > 0 && (console.log(i.red("Build failed due to validation errors.")), console.log(""), process.exit(1)), r = { registry: o, config: s };
391
+ const { registry: o, fileMap: t, config: i } = await ee(a), e = N(o, t);
392
+ I(e), e.length > 0 && (console.log(s.red("Build failed due to validation errors.")), console.log(""), process.exit(1)), r = { registry: o, config: i };
389
393
  } catch (o) {
390
- console.error(i.red("Error loading content:"), o), process.exit(1);
394
+ console.error(s.red("Error loading content:"), o), process.exit(1);
391
395
  }
392
396
  console.log("");
393
397
  try {
394
398
  await $({
395
- root: t,
399
+ root: n,
396
400
  plugins: [S()],
397
401
  build: {
398
402
  outDir: "dist",
399
403
  emptyOutDir: !0
400
404
  }
401
405
  });
402
- const o = u(t, "dist", "api");
403
- await D(o, { recursive: !0 }), await k(u(o, "content"), JSON.stringify(r)), console.log(""), console.log(i.green("✅ Build complete! Output in dist/")), console.log(""), console.log("To preview the build:"), console.log(i.dim(" yarn preview")), console.log("");
406
+ const o = h(n, "dist", "api");
407
+ await D(o, { recursive: !0 }), await w(h(o, "content"), JSON.stringify(r)), console.log(""), console.log(s.green("✅ Build complete! Output in dist/")), console.log(""), console.log("To preview the build:"), console.log(s.dim(" yarn preview")), console.log("");
404
408
  } catch (o) {
405
- console.error(i.red("Build failed:"), o), process.exit(1);
409
+ console.error(s.red("Build failed:"), o), process.exit(1);
406
410
  }
407
411
  }
408
- async function ee(t) {
412
+ async function ee(n) {
409
413
  const a = {
410
414
  locations: {},
411
415
  characters: {},
@@ -425,56 +429,56 @@ async function ee(t) {
425
429
  { dir: "journal", key: "journalEntries" },
426
430
  { dir: "interludes", key: "interludes" }
427
431
  ];
428
- for (const { dir: s, key: e } of o) {
429
- const c = u(t, s);
432
+ for (const { dir: i, key: e } of o) {
433
+ const c = h(n, i);
430
434
  try {
431
435
  const l = await p(c);
432
436
  for (const d of l)
433
437
  if (g(d) === ".yaml" || g(d) === ".yml") {
434
- const h = u(c, d), m = await f(h, "utf-8"), w = y(m);
435
- w && w.id && (a[e][w.id] = w, r.set(w.id, b(process.cwd(), h)));
438
+ const u = h(c, d), m = await f(u, "utf-8"), E = b(m);
439
+ E && E.id && (a[e][E.id] = E, r.set(E.id, y(process.cwd(), u)));
436
440
  }
437
441
  } catch {
438
442
  }
439
443
  }
440
444
  try {
441
- const s = u(t, "locales"), e = await p(s);
445
+ const i = h(n, "locales"), e = await p(i);
442
446
  for (const c of e)
443
447
  if (g(c) === ".yaml" || g(c) === ".yml") {
444
- const l = u(s, c), d = await f(l, "utf-8"), h = y(d), m = c.replace(/\.(yaml|yml)$/, "");
445
- a.locales[m] = h ?? {};
448
+ const l = h(i, c), d = await f(l, "utf-8"), u = b(d), m = c.replace(/\.(yaml|yml)$/, "");
449
+ a.locales[m] = u ?? {};
446
450
  }
447
451
  } catch {
448
452
  }
449
453
  try {
450
- const s = u(t, "dialogues"), e = await p(s);
454
+ const i = h(n, "dialogues"), e = await p(i);
451
455
  for (const c of e)
452
456
  if (g(c) === ".dlg") {
453
- const l = u(s, c), d = await f(l, "utf-8"), h = c.replace(".dlg", ""), m = E(d, h);
454
- a.dialogues[m.id] = m, r.set(m.id, b(process.cwd(), l));
457
+ const l = h(i, c), d = await f(l, "utf-8"), u = c.replace(".dlg", ""), m = k(d, u);
458
+ a.dialogues[m.id] = m, r.set(m.id, y(process.cwd(), l));
455
459
  }
456
460
  } catch {
457
461
  }
458
- let n = null;
462
+ let t = null;
459
463
  try {
460
- const s = u(t, "game.yaml"), e = await f(s, "utf-8");
461
- n = y(e);
464
+ const i = h(n, "game.yaml"), e = await f(i, "utf-8");
465
+ t = b(e);
462
466
  } catch {
463
- n = { id: "game", startLocation: "", startTime: { day: 1, hour: 8 }, startFlags: {}, startVariables: {}, startInventory: [] };
467
+ t = { id: "game", startLocation: "", startTime: { day: 1, hour: 8 }, startFlags: {}, startVariables: {}, startInventory: [] };
464
468
  }
465
- return { registry: a, fileMap: r, config: n };
469
+ return { registry: a, fileMap: r, config: t };
466
470
  }
467
- async function te() {
468
- const t = process.cwd(), a = u(t, "content");
469
- console.log(""), console.log(i.bold.magenta("ðŸū Validating Doodle Engine content...")), console.log("");
471
+ async function ne() {
472
+ const n = process.cwd(), a = h(n, "content");
473
+ console.log(""), console.log(s.bold.magenta("ðŸū Validating Doodle Engine content...")), console.log("");
470
474
  try {
471
- const { registry: r, fileMap: o } = await ne(a), n = I(r, o);
472
- N(n), n.length > 0 && process.exit(1);
475
+ const { registry: r, fileMap: o } = await te(a), t = N(r, o);
476
+ I(t), t.length > 0 && process.exit(1);
473
477
  } catch (r) {
474
- console.error(i.red("Error loading content:"), r), process.exit(1);
478
+ console.error(s.red("Error loading content:"), r), process.exit(1);
475
479
  }
476
480
  }
477
- async function ne(t) {
481
+ async function te(n) {
478
482
  const a = {
479
483
  locations: {},
480
484
  characters: {},
@@ -492,33 +496,33 @@ async function ne(t) {
492
496
  { dir: "quests", key: "quests" },
493
497
  { dir: "journal", key: "journalEntries" }
494
498
  ];
495
- for (const { dir: n, key: s } of o) {
496
- const e = u(t, n);
499
+ for (const { dir: t, key: i } of o) {
500
+ const e = h(n, t);
497
501
  try {
498
502
  const c = await p(e);
499
503
  for (const l of c)
500
504
  if (g(l) === ".yaml" || g(l) === ".yml") {
501
- const d = u(e, l), h = await f(d, "utf-8"), m = y(h);
502
- m && m.id && (a[s][m.id] = m, r.set(m.id, b(process.cwd(), d)));
505
+ const d = h(e, l), u = await f(d, "utf-8"), m = b(u);
506
+ m && m.id && (a[i][m.id] = m, r.set(m.id, y(process.cwd(), d)));
503
507
  }
504
508
  } catch {
505
509
  }
506
510
  }
507
511
  try {
508
- const n = u(t, "locales"), s = await p(n);
509
- for (const e of s)
512
+ const t = h(n, "locales"), i = await p(t);
513
+ for (const e of i)
510
514
  if (g(e) === ".yaml" || g(e) === ".yml") {
511
- const c = u(n, e), l = await f(c, "utf-8"), d = y(l), h = e.replace(/\.(yaml|yml)$/, "");
512
- a.locales[h] = d ?? {};
515
+ const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
516
+ a.locales[u] = d ?? {};
513
517
  }
514
518
  } catch {
515
519
  }
516
520
  try {
517
- const n = u(t, "dialogues"), s = await p(n);
518
- for (const e of s)
521
+ const t = h(n, "dialogues"), i = await p(t);
522
+ for (const e of i)
519
523
  if (g(e) === ".dlg") {
520
- const c = u(n, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), h = E(l, d);
521
- a.dialogues[h.id] = h, r.set(h.id, b(process.cwd(), c));
524
+ const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
525
+ a.dialogues[u.id] = u, r.set(u.id, y(process.cwd(), c));
522
526
  }
523
527
  } catch {
524
528
  }
@@ -557,14 +561,14 @@ dist
557
561
  },
558
562
  "include": ["src"]
559
563
  }
560
- `, se = `id: bartender
564
+ `, ie = `id: bartender
561
565
  name: "@character.bartender.name"
562
566
  biography: "@character.bartender.bio"
563
567
  portrait: ""
564
568
  location: tavern
565
569
  dialogue: bartender_greeting
566
570
  stats: {}
567
- `, ie = `id: merchant
571
+ `, se = `id: merchant
568
572
  name: "@character.merchant.name"
569
573
  biography: "@character.merchant.bio"
570
574
  portrait: ""
@@ -648,9 +652,9 @@ NODE order_drink
648
652
  GOTO after_drink
649
653
  END
650
654
 
651
- # Try to bluff a free drink — launches the skill check dialogue (demonstrates: START dialogue)
655
+ # Try to bluff a free drink — rolls dice inline (demonstrates: ROLL + GOTO within one dialogue)
652
656
  CHOICE @bartender.choice.try_bluff
653
- START dialogue bluff_check
657
+ GOTO bluff_attempt
654
658
  END
655
659
 
656
660
  # Always available as an out
@@ -658,6 +662,41 @@ NODE order_drink
658
662
  GOTO start
659
663
  END
660
664
 
665
+ NODE bluff_attempt
666
+ # Roll happens immediately on entering this node — auto-advances to success or failure
667
+ ROLL bluffRoll 1 20
668
+ IF variableGreaterThan bluffRoll 14
669
+ GOTO bluff_success
670
+ END
671
+ GOTO bluff_failure
672
+
673
+ NODE bluff_success
674
+ BARTENDER: @bluff.success.bartender
675
+ ADD relationship bartender 2
676
+ SET flag bluffedMarcus
677
+
678
+ CHOICE @bluff.choice.cheers
679
+ GOTO start
680
+ END
681
+
682
+ NODE bluff_failure
683
+ BARTENDER: @bluff.failure.bartender
684
+
685
+ CHOICE @bluff.choice.fine_pay
686
+ REQUIRE variableGreaterThan gold 4
687
+ ADD variable gold -5
688
+ ADD variable _drinksBought 1
689
+ ADD variable reputation 1
690
+ SET flag hadDrink
691
+ ADD relationship bartender 1
692
+ NOTIFY @notification.bought_drink
693
+ GOTO start
694
+ END
695
+
696
+ CHOICE @bluff.choice.walk_away
697
+ GOTO start
698
+ END
699
+
661
700
  NODE after_drink
662
701
  BARTENDER: @bartender.after_drink
663
702
 
@@ -718,13 +757,17 @@ NODE work_done
718
757
  NODE farewell
719
758
  BARTENDER: @bartender.farewell
720
759
  END dialogue
721
- `, le = `# Skill check example — demonstrates dice rolling and variable interpolation.
760
+ `, le = `# Standalone skill-check demo — demonstrates dice syntax in isolation.
722
761
  #
723
762
  # ROLL <variable> <min> <max> — rolls a random integer and stores it in a variable.
724
763
  # {varName} — in dialogue text, replaced with the variable's value.
725
764
  # roll <min> <max> <threshold> — condition: rolls and returns true if result >= threshold.
726
765
  #
727
- # This dialogue is NOT auto-triggered — start it from another node with:
766
+ # The actual bluff check in this scaffold is inlined into bartender_greeting.dlg
767
+ # so it can flow back into the bartender conversation (GOTO after_drink, GOTO start).
768
+ #
769
+ # Use START dialogue to launch a self-contained dialogue that doesn't need to
770
+ # return to the caller. For example, from a CHOICE in another dialogue:
728
771
  # START dialogue bluff_check
729
772
 
730
773
  NODE start
@@ -753,14 +796,31 @@ NODE resolve
753
796
  GOTO failure
754
797
 
755
798
  NODE success
756
- NARRATOR: @bluff.success
799
+ NARRATOR: @bluff.success.narration
800
+ BARTENDER: @bluff.success.bartender
757
801
  ADD relationship bartender 2
758
802
  SET flag bluffedMarcus
759
- END dialogue
803
+
804
+ CHOICE @bluff.choice.cheers
805
+ END dialogue
806
+ END
760
807
 
761
808
  NODE failure
762
- NARRATOR: @bluff.failure
763
- END dialogue
809
+ NARRATOR: @bluff.failure.narration
810
+ BARTENDER: @bluff.failure.bartender
811
+
812
+ # Still have to pay — or just walk away
813
+ CHOICE @bluff.choice.fine_pay
814
+ REQUIRE variableGreaterThan gold 4
815
+ ADD variable gold -5
816
+ SET flag hadDrink
817
+ NOTIFY @notification.bought_drink
818
+ END dialogue
819
+ END
820
+
821
+ CHOICE @bluff.choice.walk_away
822
+ END dialogue
823
+ END
764
824
  `, de = `# One-time narrator intro for the market. Same pattern as tavern_intro.dlg.
765
825
 
766
826
  TRIGGER market
@@ -958,11 +1018,11 @@ stats: {}
958
1018
  title: "@journal.market_square.title"
959
1019
  text: "@journal.market_square.text"
960
1020
  category: places
961
- `, ye = `id: odd_jobs_accepted
1021
+ `, be = `id: odd_jobs_accepted
962
1022
  title: "@journal.odd_jobs_accepted.title"
963
1023
  text: "@journal.odd_jobs_accepted.text"
964
1024
  category: quests
965
- `, be = `id: tavern_discovery
1025
+ `, ye = `id: tavern_discovery
966
1026
  title: "@journal.tavern_discovery.title"
967
1027
  text: "@journal.tavern_discovery.text"
968
1028
  category: places
@@ -1104,21 +1164,26 @@ bluff.choice.attempt: "Try to bluff him."
1104
1164
  bluff.choice.back_down: "Actually, never mind."
1105
1165
  bluff.backed_down: "Some fights aren't worth picking."
1106
1166
  bluff.rolled: "You spin the tale with {bluffRoll} on your roll — and Marcus listens carefully."
1107
- bluff.success: "The story lands perfectly. Marcus laughs and slides a free drink across the bar. Not bad at all, he admits."
1108
- bluff.failure: "Marcus raises an eyebrow, entirely unconvinced. That will be five gold, he says flatly."
1109
- `, we = `id: market
1167
+ bluff.success.narration: "The story lands perfectly. Marcus laughs and slides a free drink across the bar."
1168
+ bluff.success.bartender: "Sure, this one's on the house."
1169
+ bluff.failure.narration: "Marcus raises an eyebrow, entirely unconvinced."
1170
+ bluff.failure.bartender: "Nope. That'll be five gold."
1171
+ bluff.choice.cheers: "Cheers!"
1172
+ bluff.choice.fine_pay: "Fine. Here's five gold."
1173
+ bluff.choice.walk_away: "Never mind then."
1174
+ `, Ee = `id: market
1110
1175
  name: "@location.market.name"
1111
1176
  description: "@location.market.description"
1112
1177
  banner: ""
1113
1178
  music: ""
1114
1179
  ambient: ""
1115
- `, ke = `id: tavern
1180
+ `, we = `id: tavern
1116
1181
  name: "@location.tavern.name"
1117
1182
  description: "@location.tavern.description"
1118
1183
  banner: ""
1119
1184
  music: ""
1120
1185
  ambient: ""
1121
- `, Ee = `id: town
1186
+ `, ke = `id: town
1122
1187
  name: "@map.town.name"
1123
1188
  image: ""
1124
1189
  scale: 1
@@ -1268,7 +1333,7 @@ export function App() {
1268
1333
  -webkit-font-smoothing: antialiased;
1269
1334
  -moz-osx-font-smoothing: grayscale;
1270
1335
  }
1271
- `, Ie = `import { StrictMode } from 'react'
1336
+ `, Ne = `import { StrictMode } from 'react'
1272
1337
  import { createRoot } from 'react-dom/client'
1273
1338
  import { App } from './App'
1274
1339
  import './index.css'
@@ -1278,12 +1343,12 @@ createRoot(document.getElementById('root')!).render(
1278
1343
  <App />
1279
1344
  </StrictMode>,
1280
1345
  )
1281
- `, O = "ðŸū", Ne = "🐕", j = "ðŸĶī", Ce = "âœĻ", x = "📁", T = "✅", Re = "🚀", A = /* @__PURE__ */ Object.assign({
1346
+ `, O = "ðŸū", Ie = "🐕", j = "ðŸĶī", Ce = "âœĻ", G = "📁", T = "✅", Re = "🚀", A = /* @__PURE__ */ Object.assign({
1282
1347
  "./templates/_root/_gitignore": oe,
1283
1348
  "./templates/_root/index.html": ae,
1284
1349
  "./templates/_root/tsconfig.json": re,
1285
- "./templates/content/characters/bartender.yaml": se,
1286
- "./templates/content/characters/merchant.yaml": ie,
1350
+ "./templates/content/characters/bartender.yaml": ie,
1351
+ "./templates/content/characters/merchant.yaml": se,
1287
1352
  "./templates/content/dialogues/bartender_greeting.dlg": ce,
1288
1353
  "./templates/content/dialogues/bluff_check.dlg": le,
1289
1354
  "./templates/content/dialogues/market_intro.dlg": de,
@@ -1293,20 +1358,20 @@ createRoot(document.getElementById('root')!).render(
1293
1358
  "./templates/content/interludes/chapter_one.yaml": ge,
1294
1359
  "./templates/content/items/old_coin.yaml": fe,
1295
1360
  "./templates/content/journal/market_square.yaml": pe,
1296
- "./templates/content/journal/odd_jobs_accepted.yaml": ye,
1297
- "./templates/content/journal/tavern_discovery.yaml": be,
1361
+ "./templates/content/journal/odd_jobs_accepted.yaml": be,
1362
+ "./templates/content/journal/tavern_discovery.yaml": ye,
1298
1363
  "./templates/content/locales/en.yaml": _e,
1299
- "./templates/content/locations/market.yaml": we,
1300
- "./templates/content/locations/tavern.yaml": ke,
1301
- "./templates/content/maps/town.yaml": Ee,
1364
+ "./templates/content/locations/market.yaml": Ee,
1365
+ "./templates/content/locations/tavern.yaml": we,
1366
+ "./templates/content/maps/town.yaml": ke,
1302
1367
  "./templates/content/quests/odd_jobs.yaml": ve,
1303
1368
  "./templates/src/App.custom.tsx": Oe,
1304
1369
  "./templates/src/App.default.tsx": De,
1305
1370
  "./templates/src/index.css": Te,
1306
- "./templates/src/main.tsx": Ie
1371
+ "./templates/src/main.tsx": Ne
1307
1372
  });
1308
- function Ae(t) {
1309
- const a = t.replace("./templates/", "");
1373
+ function Ae(n) {
1374
+ const a = n.replace("./templates/", "");
1310
1375
  if (a === "src/App.default.tsx" || a === "src/App.custom.tsx") return null;
1311
1376
  if (a.startsWith("_root/")) {
1312
1377
  const r = a.slice(6);
@@ -1314,19 +1379,19 @@ function Ae(t) {
1314
1379
  }
1315
1380
  return a;
1316
1381
  }
1317
- async function Se(t) {
1318
- const a = u(process.cwd(), t);
1319
- console.log(""), console.log(i.bold.magenta(` ${O} Doodle Engine ${O}`)), console.log(i.dim(" Text-based RPG and Adventure Game Scaffolder")), console.log(""), console.log(` ${Ne} Creating new game: ${i.bold.cyan(t)}`), console.log("");
1382
+ async function Se(n) {
1383
+ const a = h(process.cwd(), n);
1384
+ console.log(""), console.log(s.bold.magenta(` ${O} Doodle Engine ${O}`)), console.log(s.dim(" Text-based RPG and Adventure Game Scaffolder")), console.log(""), console.log(` ${Ie} Creating new game: ${s.bold.cyan(n)}`), console.log("");
1320
1385
  const { useDefaultRenderer: r } = await F({
1321
1386
  type: "confirm",
1322
1387
  name: "useDefaultRenderer",
1323
1388
  message: "Use default renderer?",
1324
1389
  initial: !0
1325
1390
  });
1326
- r === void 0 && (console.log(i.yellow(`
1327
- ${j} No worries, maybe next time! Woof!`)), process.exit(0)), console.log(""), await je(a, t, r), console.log(""), console.log(i.bold.green(` ${T} Project created successfully!`)), console.log(""), console.log(i.dim(` ${x} ${a}`)), console.log(""), console.log(i.bold(" Next steps:")), console.log(i.cyan(` cd ${t}`)), console.log(i.cyan(" npm install ") + i.dim("# or: yarn install / pnpm install")), console.log(i.cyan(" npm run dev ") + i.dim("# or: yarn dev / pnpm dev")), console.log(""), console.log(i.dim(` ${Re} Happy game making! ${O}`)), console.log("");
1391
+ r === void 0 && (console.log(s.yellow(`
1392
+ ${j} No worries, maybe next time! Woof!`)), process.exit(0)), console.log(""), await je(a, n, r), console.log(""), console.log(s.bold.green(` ${T} Project created successfully!`)), console.log(""), console.log(s.dim(` ${G} ${a}`)), console.log(""), console.log(s.bold(" Next steps:")), console.log(s.cyan(` cd ${n}`)), console.log(s.cyan(" npm install ") + s.dim("# or: yarn install / pnpm install")), console.log(s.cyan(" npm run dev ") + s.dim("# or: yarn dev / pnpm dev")), console.log(""), console.log(s.dim(` ${Re} Happy game making! ${O}`)), console.log("");
1328
1393
  }
1329
- async function je(t, a, r) {
1394
+ async function je(n, a, r) {
1330
1395
  const o = [
1331
1396
  "content/locations",
1332
1397
  "content/characters",
@@ -1346,11 +1411,11 @@ async function je(t, a, r) {
1346
1411
  "assets/audio/voice",
1347
1412
  "src"
1348
1413
  ];
1349
- console.log(` ${x} ${i.bold("Creating directories...")}`);
1414
+ console.log(` ${G} ${s.bold("Creating directories...")}`);
1350
1415
  for (const e of o)
1351
- await D(u(t, e), { recursive: !0 });
1352
- console.log(i.green(` ${T} Directories created`)), console.log("");
1353
- const n = {
1416
+ await D(h(n, e), { recursive: !0 });
1417
+ console.log(s.green(` ${T} Directories created`)), console.log("");
1418
+ const t = {
1354
1419
  name: a,
1355
1420
  version: "0.1.0",
1356
1421
  type: "module",
@@ -1374,20 +1439,20 @@ async function je(t, a, r) {
1374
1439
  vite: "^6.0.0"
1375
1440
  }
1376
1441
  };
1377
- console.log(` ${Ce} ${i.bold("Writing project files...")}`), await k(u(t, "package.json"), JSON.stringify(n, null, 2));
1442
+ console.log(` ${Ce} ${s.bold("Writing project files...")}`), await w(h(n, "package.json"), JSON.stringify(t, null, 2));
1378
1443
  for (const [e, c] of Object.entries(A)) {
1379
1444
  const l = Ae(e);
1380
1445
  if (l === null) continue;
1381
- const d = u(t, l);
1382
- await D(M(d), { recursive: !0 }), await k(d, c);
1446
+ const d = h(n, l);
1447
+ await D(M(d), { recursive: !0 }), await w(d, c);
1383
1448
  }
1384
- const s = r ? "./templates/src/App.default.tsx" : "./templates/src/App.custom.tsx";
1385
- await k(u(t, "src/App.tsx"), A[s]), console.log(i.green(` ${T} Source files created`)), console.log(""), console.log(` ${j} ${i.bold("Starter content written")}`), console.log(""), console.log(i.dim(" Content includes:")), console.log(i.dim(" 2 locations (tavern, market)")), console.log(i.dim(" 2 characters (bartender, merchant)")), console.log(i.dim(" 1 item (old coin)")), console.log(i.dim(" 1 map (town with 2 locations)")), console.log(i.dim(" 1 quest (odd jobs, 3 stages)")), console.log(i.dim(" 3 journal entries")), console.log(i.dim(" 1 interlude (chapter one, auto-triggers at tavern)")), console.log(i.dim(" 5 dialogues (2 narrator intros, 2 NPC conversations, 1 skill check)")), console.log(i.dim(" English locale with all strings"));
1449
+ const i = r ? "./templates/src/App.default.tsx" : "./templates/src/App.custom.tsx";
1450
+ await w(h(n, "src/App.tsx"), A[i]), console.log(s.green(` ${T} Source files created`)), console.log(""), console.log(` ${j} ${s.bold("Starter content written")}`), console.log(""), console.log(s.dim(" Content includes:")), console.log(s.dim(" 2 locations (tavern, market)")), console.log(s.dim(" 2 characters (bartender, merchant)")), console.log(s.dim(" 1 item (old coin)")), console.log(s.dim(" 1 map (town with 2 locations)")), console.log(s.dim(" 1 quest (odd jobs, 3 stages)")), console.log(s.dim(" 3 journal entries")), console.log(s.dim(" 1 interlude (chapter one, auto-triggers at tavern)")), console.log(s.dim(" 5 dialogues (2 narrator intros, 2 NPC conversations, 1 skill check)")), console.log(s.dim(" English locale with all strings"));
1386
1451
  }
1387
- const _ = new q();
1388
- _.name("doodle").description(i.magenta("ðŸū Doodle Engine") + i.dim(" — Narrative RPG development tools")).version("0.0.1");
1389
- _.command("create <project-name>").description("Scaffold a new Doodle Engine game project").action(async (t) => {
1390
- await Se(t);
1452
+ const _ = new x();
1453
+ _.name("doodle").description(s.magenta("ðŸū Doodle Engine") + s.dim(" — Narrative RPG development tools")).version("0.0.1");
1454
+ _.command("create <project-name>").description("Scaffold a new Doodle Engine game project").action(async (n) => {
1455
+ await Se(n);
1391
1456
  });
1392
1457
  _.command("dev").description("Start development server with hot reload").action(async () => {
1393
1458
  await V();
@@ -1396,6 +1461,6 @@ _.command("build").description("Build game for production").action(async () => {
1396
1461
  await Z();
1397
1462
  });
1398
1463
  _.command("validate").description("Validate game content").action(async () => {
1399
- await te();
1464
+ await ne();
1400
1465
  });
1401
1466
  _.parse();