@genex-ai/cli-demo 1.2.3 → 1.2.4-dev.345

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/index.js CHANGED
@@ -8,7 +8,7 @@ import fs from "fs";
8
8
  import os from "os";
9
9
  import path from "path";
10
10
  import { fileURLToPath } from "url";
11
- var RAW_CHANNEL = "latest";
11
+ var RAW_CHANNEL = "dev";
12
12
  var CLI_CHANNEL = RAW_CHANNEL === "dev" ? "dev" : "latest";
13
13
  var STANDS = {
14
14
  prod: { api: "https://api.genex.games", dashboard: "https://genex.games" },
@@ -933,6 +933,7 @@ async function runAuth(opts) {
933
933
  }
934
934
 
935
935
  // src/commands/init.ts
936
+ import fs10 from "fs/promises";
936
937
  import path10 from "path";
937
938
 
938
939
  // src/lib/copy-templates.ts
@@ -1010,6 +1011,7 @@ Your capabilities (all via \`npx genex \u2026\`): generate \`model\` \xB7 \`skyb
1010
1011
  16. Never add debug-only code to the game to check your own work \u2014 no hidden test modes, no special URL parameters, no forced-visible flags, no auth mocks, no pixel-sampling hooks. \`?genex_local_test=1\` is the platform's own supported mode and is fine; your own bypass is not. (The multiplayer skill's small build identifier, token-free status line, and connected-quorum watchdog are production supportability, not a bypass \u2014 keep those.)
1011
1012
  17. Input directions match their labels: A/\u2190 moves or turns the player screen-LEFT, D/\u2192 screen-RIGHT, mouse-up looks up, and drag-pan axes share ONE convention. The cursor is either the gameplay tool (RTS, card, builder) or locked away during play \u2014 keyboard-only games included. Check it in every milestone's smoke pass.
1012
1013
  18. v0 is a milestone, not the destination. When the ask was bigger than one loop, every milestone after v0 grows back toward the FULL ask with DESIGN.md's content lines as the checklist \u2014 a slice that previewed well never quietly becomes the game. Cosmetics never jump the queue past promised content.
1014
+ 19. NEVER delete, empty, move, rename, or overwrite anything you did not create yourself. This folder may hold the player's own reference images, notes, sketches, or an earlier attempt \u2014 files that exist nowhere else and have no undo, no trash, no backup. A non-empty folder is normal and is NEVER something to clean up, and "start clean" is never a reason. That rules out \`rm\`/\`rm -rf\`, \`git clean\`, \`git checkout -- .\`, \`git reset --hard\` over their work, deleting to resolve a conflict or a stuck interactive prompt, and every setup tool's offer to empty a directory (\`--force\`, \`--overwrite\`, "Remove existing files") \u2014 scaffold into a fresh subfolder and copy in instead. You may add files and edit the ones you wrote. If a step genuinely cannot continue without removing something of theirs, STOP and ask, naming the exact files, and wait for a yes \u2014 "it looks like junk" is never that yes. This binds hardest during setup, where it runs fast and automatically before the player has asked for anything at all.
1013
1015
  ${CONTRACT_END}
1014
1016
  `;
1015
1017
  var CLAUDE_IMPORT_LINE = "@AGENTS.md";
@@ -1598,10 +1600,39 @@ async function writeGameConfigFiles(meta, log, cwd = process.cwd()) {
1598
1600
  }
1599
1601
 
1600
1602
  // src/commands/init.ts
1603
+ var TOOLING_ENTRIES = /* @__PURE__ */ new Set([
1604
+ ".git",
1605
+ ".genex",
1606
+ ".claude",
1607
+ ".codex",
1608
+ ".cursor",
1609
+ ".DS_Store",
1610
+ "node_modules"
1611
+ ]);
1612
+ async function listPreexistingEntries(dir) {
1613
+ try {
1614
+ return (await fs10.readdir(dir)).filter((name) => !TOOLING_ENTRIES.has(name)).sort();
1615
+ } catch {
1616
+ return [];
1617
+ }
1618
+ }
1619
+ function warnPreexisting(log, entries) {
1620
+ if (entries.length === 0) return;
1621
+ const shown = entries.slice(0, 6).join(", ");
1622
+ const more = entries.length > 6 ? `, \u2026+${entries.length - 6} more` : "";
1623
+ log.plain("");
1624
+ log.warn(`This folder already had files in it before setup \u2014 they are the player's, not yours:`);
1625
+ log.dim(` ${shown}${more}`);
1626
+ log.dim(" Never clear or overwrite them to start clean. A reference image or note here may");
1627
+ log.dim(" exist nowhere else, and deleting it has no undo \u2014 ask before removing anything.");
1628
+ log.dim(' Answer any "directory is not empty" prompt with Ignore/continue \u2014 never --force,');
1629
+ log.dim(' --overwrite, or "Remove existing files", which empty the whole folder.');
1630
+ }
1601
1631
  async function runInit(opts) {
1602
1632
  const log = createLogger({ quiet: opts.quiet });
1603
1633
  log.plain(c.bold("genex init"));
1604
1634
  log.plain("");
1635
+ const preexisting = await listPreexistingEntries(process.cwd());
1605
1636
  await cleanupLegacyGlobalSkills(log);
1606
1637
  const templatesDir = getTemplatesDir();
1607
1638
  const targets = resolveAgentTargets({ dir: opts.dir, agents: opts.agents });
@@ -1715,12 +1746,13 @@ async function runInit(opts) {
1715
1746
  const { path: metaPath } = await writeProject(meta);
1716
1747
  log.dim(` saved ${c.cyan(metaPath)}`);
1717
1748
  await writeGameConfigFiles(meta, log);
1749
+ warnPreexisting(log, preexisting);
1718
1750
  log.plain("");
1719
1751
  log.success("All set. \u{1F680}");
1720
1752
  }
1721
1753
 
1722
1754
  // src/commands/link.ts
1723
- import fs10 from "fs/promises";
1755
+ import fs11 from "fs/promises";
1724
1756
  import path11 from "path";
1725
1757
  async function runLink(opts) {
1726
1758
  const log = createLogger({ quiet: opts.quiet });
@@ -1793,20 +1825,20 @@ async function ensureSlugEnv(slug, log, cwd = process.cwd()) {
1793
1825
  const file = path11.join(cwd, ".env");
1794
1826
  let content;
1795
1827
  try {
1796
- content = await fs10.readFile(file, "utf8");
1828
+ content = await fs11.readFile(file, "utf8");
1797
1829
  } catch {
1798
1830
  return;
1799
1831
  }
1800
1832
  const re = /^(\s*VITE_GENEX_SLUG=)(.*)$/m;
1801
1833
  const m = content.match(re);
1802
1834
  if (!m) {
1803
- await fs10.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
1835
+ await fs11.appendFile(file, `${content.endsWith("\n") ? "" : "\n"}VITE_GENEX_SLUG=${slug}
1804
1836
  `);
1805
1837
  log.dim(` added VITE_GENEX_SLUG=${slug} to .env`);
1806
1838
  return;
1807
1839
  }
1808
1840
  if (m[2].trim() === slug) return;
1809
- await fs10.writeFile(file, content.replace(re, `$1${slug}`));
1841
+ await fs11.writeFile(file, content.replace(re, `$1${slug}`));
1810
1842
  log.dim(` updated .env: VITE_GENEX_SLUG=${slug} (was ${m[2].trim()})`);
1811
1843
  }
1812
1844
  async function fetchOwnProject(apiUrl, token, slug, log) {
@@ -1985,7 +2017,7 @@ function relTime(iso) {
1985
2017
  // src/lib/deploy.ts
1986
2018
  import { spawn as spawn3 } from "child_process";
1987
2019
  import crypto3 from "crypto";
1988
- import fs13 from "fs/promises";
2020
+ import fs14 from "fs/promises";
1989
2021
  import os5 from "os";
1990
2022
  import path13 from "path";
1991
2023
 
@@ -2250,12 +2282,12 @@ function tierFor(estVramMb) {
2250
2282
  }
2251
2283
 
2252
2284
  // src/commands/ui.ts
2253
- import fs12 from "fs/promises";
2285
+ import fs13 from "fs/promises";
2254
2286
  import path12 from "path";
2255
2287
  import { PNG as PNG2 } from "pngjs";
2256
2288
 
2257
2289
  // src/lib/png-tools.ts
2258
- import fs11 from "fs/promises";
2290
+ import fs12 from "fs/promises";
2259
2291
  import { PNG } from "pngjs";
2260
2292
  var ALPHA_TRANSPARENT_MAX = 16;
2261
2293
  var isHttpUrl = (s) => /^https?:\/\//i.test(s);
@@ -2266,12 +2298,12 @@ async function loadPng(input) {
2266
2298
  if (!res.ok) throw new Error(`Couldn't fetch ${input} (HTTP ${res.status}).`);
2267
2299
  buf = Buffer.from(await res.arrayBuffer());
2268
2300
  } else {
2269
- buf = await fs11.readFile(input);
2301
+ buf = await fs12.readFile(input);
2270
2302
  }
2271
2303
  return PNG.sync.read(buf);
2272
2304
  }
2273
2305
  async function writePng(file, png) {
2274
- await fs11.writeFile(file, PNG.sync.write(png));
2306
+ await fs12.writeFile(file, PNG.sync.write(png));
2275
2307
  }
2276
2308
  function cropPng(image, box) {
2277
2309
  const out = new PNG({ width: box.w, height: box.h });
@@ -2571,7 +2603,7 @@ async function uiExtract(opts, log) {
2571
2603
  const dilatePx = opts.dilate ?? 0;
2572
2604
  const sheet = await loadPng(input);
2573
2605
  const { width: W, height: H, data } = sheet;
2574
- await fs12.mkdir(outDir, { recursive: true });
2606
+ await fs13.mkdir(outDir, { recursive: true });
2575
2607
  log.plain(c.bold("genex ui extract"));
2576
2608
  log.dim(` ${input} (${W}x${H}) \u2192 ${outDir}, ${names.length} names`);
2577
2609
  let hasTransparency = false;
@@ -2819,7 +2851,7 @@ async function uiExtract(opts, log) {
2819
2851
  defringed
2820
2852
  };
2821
2853
  const { name: _n, out: _o, ...sidecarBody } = sidecar;
2822
- await fs12.writeFile(
2854
+ await fs13.writeFile(
2823
2855
  outPath.replace(/\.png$/i, "") + ".bbox.json",
2824
2856
  JSON.stringify(sidecarBody, null, 2)
2825
2857
  );
@@ -2829,7 +2861,7 @@ async function uiExtract(opts, log) {
2829
2861
  );
2830
2862
  }
2831
2863
  const debugPath = path12.join(outDir, "extract-debug.json");
2832
- await fs12.writeFile(
2864
+ await fs13.writeFile(
2833
2865
  debugPath,
2834
2866
  JSON.stringify(
2835
2867
  {
@@ -3291,7 +3323,7 @@ async function uiMasks(opts, log) {
3291
3323
  const registrationTolerance = opts.registrationTolerance ?? 0.04;
3292
3324
  const edgeFlushMax = opts.edgeFlushMax ?? 0.04;
3293
3325
  const loosened = registrationTolerance > 0.04 || edgeFlushMax > 0.04 || minCoverage < 0.01 || maxCoverage > 0.85;
3294
- await fs12.mkdir(outDir, { recursive: true });
3326
+ await fs13.mkdir(outDir, { recursive: true });
3295
3327
  log.plain(c.bold("genex ui masks"));
3296
3328
  log.dim(` ${input} (${image.width}x${image.height}), ${pairs.length} pair(s) \u2192 ${outDir}`);
3297
3329
  const sheetComponents = detectSheetComponents(image, opts.minPixels ?? 2e3);
@@ -3395,7 +3427,7 @@ async function uiMasks(opts, log) {
3395
3427
  },
3396
3428
  overlay: overlayPath
3397
3429
  };
3398
- await fs12.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
3430
+ await fs13.writeFile(metaPath, `${JSON.stringify(meta, null, 2)}
3399
3431
  `);
3400
3432
  results.push(meta);
3401
3433
  const fb = converted.bbox;
@@ -3412,7 +3444,7 @@ async function uiMasks(opts, log) {
3412
3444
  }
3413
3445
  }
3414
3446
  const indexPath = path12.join(outDir, "annotated-progress.json");
3415
- await fs12.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
3447
+ await fs13.writeFile(indexPath, `${JSON.stringify({ input, loosened, pairs: results }, null, 2)}
3416
3448
  `);
3417
3449
  log.plain("");
3418
3450
  log.success(`Wrote ${pairs.length} frame+mask pair(s) \u2192 ${outDir} (index: ${indexPath}).`);
@@ -3538,7 +3570,7 @@ async function uiTextColor(opts, log) {
3538
3570
  };
3539
3571
  process.stdout.write(`${JSON.stringify(result, null, 2)}
3540
3572
  `);
3541
- if (opts.out) await fs12.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
3573
+ if (opts.out) await fs13.writeFile(opts.out, `${JSON.stringify(result, null, 2)}
3542
3574
  `);
3543
3575
  if (opts.cropPath) log.dim(` crop \u2192 ${opts.cropPath}`);
3544
3576
  }
@@ -3570,7 +3602,7 @@ async function uiTrim(opts, log) {
3570
3602
  const sidecar = computeBBoxes(trimmed);
3571
3603
  const speckleAllowed = !!(speckle && speckle.ratio > SPECKLE_MAX_RATIO);
3572
3604
  const sidecarPath = outPath.replace(/\.png$/i, "") + ".bbox.json";
3573
- await fs12.writeFile(
3605
+ await fs13.writeFile(
3574
3606
  sidecarPath,
3575
3607
  JSON.stringify(speckleAllowed ? { ...sidecar, speckleAllowed: true } : sidecar, null, 2)
3576
3608
  );
@@ -3842,7 +3874,7 @@ async function walkFiles(dir) {
3842
3874
  const out = [];
3843
3875
  let entries;
3844
3876
  try {
3845
- entries = await fs12.readdir(dir, { withFileTypes: true });
3877
+ entries = await fs13.readdir(dir, { withFileTypes: true });
3846
3878
  } catch {
3847
3879
  return out;
3848
3880
  }
@@ -3857,7 +3889,7 @@ async function walkFiles(dir) {
3857
3889
  async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3858
3890
  const viewportFindings = [];
3859
3891
  try {
3860
- const indexHtml = await fs12.readFile(path12.join(cwd, "index.html"), "utf8");
3892
+ const indexHtml = await fs13.readFile(path12.join(cwd, "index.html"), "utf8");
3861
3893
  if (!/<meta[^>]+name=["']viewport["']/i.test(indexHtml)) {
3862
3894
  viewportFindings.push({
3863
3895
  kind: "viewport-meta",
@@ -3873,7 +3905,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3873
3905
  }
3874
3906
  const absAssets = path12.resolve(cwd, assetDir);
3875
3907
  try {
3876
- if (!(await fs12.stat(absAssets)).isDirectory()) {
3908
+ if (!(await fs13.stat(absAssets)).isDirectory()) {
3877
3909
  return viewportFindings.length > 0 ? viewportFindings : null;
3878
3910
  }
3879
3911
  } catch {
@@ -3883,7 +3915,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3883
3915
  (p) => AUDIT_SRC_EXTS.has(path12.extname(p).toLowerCase())
3884
3916
  );
3885
3917
  try {
3886
- for (const name of await fs12.readdir(cwd)) {
3918
+ for (const name of await fs13.readdir(cwd)) {
3887
3919
  const ext = path12.extname(name).toLowerCase();
3888
3920
  if (ext === ".html" || ext === ".css") srcFiles.push(path12.join(cwd, name));
3889
3921
  }
@@ -3892,7 +3924,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3892
3924
  const sources = [];
3893
3925
  for (const p of srcFiles) {
3894
3926
  try {
3895
- sources.push({ rel: path12.relative(cwd, p), text: await fs12.readFile(p, "utf8") });
3927
+ sources.push({ rel: path12.relative(cwd, p), text: await fs13.readFile(p, "utf8") });
3896
3928
  } catch {
3897
3929
  }
3898
3930
  }
@@ -3906,7 +3938,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3906
3938
  const metaMatch = /^(.+)\.annotated-progress\.json$/.exec(base);
3907
3939
  if (metaMatch) {
3908
3940
  try {
3909
- const meta = JSON.parse(await fs12.readFile(p, "utf8"));
3941
+ const meta = JSON.parse(await fs13.readFile(p, "utf8"));
3910
3942
  metaByName.set(metaMatch[1], {
3911
3943
  cleanCrop: meta.clean?.crop ?? null,
3912
3944
  loosened: meta.loosened === true
@@ -3917,7 +3949,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3917
3949
  }
3918
3950
  if (base.endsWith(".bbox.json")) {
3919
3951
  try {
3920
- const sidecar = JSON.parse(await fs12.readFile(p, "utf8"));
3952
+ const sidecar = JSON.parse(await fs13.readFile(p, "utf8"));
3921
3953
  if (sidecar.sheetBBox) bboxByPng.set(base.replace(/\.bbox\.json$/, ".png"), sidecar.sheetBBox);
3922
3954
  } catch {
3923
3955
  }
@@ -3963,8 +3995,8 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3963
3995
  let frame;
3964
3996
  let mask;
3965
3997
  try {
3966
- frame = PNG2.sync.read(await fs12.readFile(framePath));
3967
- mask = PNG2.sync.read(await fs12.readFile(maskPath));
3998
+ frame = PNG2.sync.read(await fs13.readFile(framePath));
3999
+ mask = PNG2.sync.read(await fs13.readFile(maskPath));
3968
4000
  } catch {
3969
4001
  continue;
3970
4002
  }
@@ -3983,7 +4015,7 @@ async function collectUiAuditFindings(assetDir, srcDir, cwd = process.cwd()) {
3983
4015
  if (!referenced(base)) continue;
3984
4016
  let png;
3985
4017
  try {
3986
- png = PNG2.sync.read(await fs12.readFile(p));
4018
+ png = PNG2.sync.read(await fs13.readFile(p));
3987
4019
  } catch {
3988
4020
  continue;
3989
4021
  }
@@ -4156,7 +4188,7 @@ async function printUiAuditPreflight(log) {
4156
4188
  }
4157
4189
  async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4158
4190
  try {
4159
- const design = await fs13.readFile(path13.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4191
+ const design = await fs14.readFile(path13.join(cwd, "DESIGN.md"), "utf8").catch(() => "");
4160
4192
  const warnings = [];
4161
4193
  if (design.length > 0 && !/##\s*build plan & status/i.test(design)) {
4162
4194
  warnings.push(
@@ -4164,7 +4196,7 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4164
4196
  );
4165
4197
  }
4166
4198
  if (!/player character:/i.test(design)) {
4167
- const hasCharacter = await fs13.access(path13.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4199
+ const hasCharacter = await fs14.access(path13.join(cwd, "public", "assets", "meshy-character.json")).then(() => true, () => false);
4168
4200
  if (!hasCharacter && await loadsPlayerBody(cwd)) {
4169
4201
  warnings.push(
4170
4202
  `Player character is the stock avatar \u2014 no generated character is wired. The game's own generated character is the player's body wherever a human body appears on screen, first-person included (genex-ai-character). Generate it, or record "Player character: VRM \u2014 <reason>" in DESIGN.md (no human body in this game / out of credits / player declined).`
@@ -4178,12 +4210,12 @@ async function printPipelineStatePreflight(log, cwd = process.cwd()) {
4178
4210
  async function loadsPlayerBody(cwd) {
4179
4211
  const BODY_LOADERS = /\bloadPlayerCharacter\s*\(|\bloadVrm(?:Clone)?\s*\(/;
4180
4212
  try {
4181
- const entries = await fs13.readdir(path13.join(cwd, "src"), { recursive: true });
4213
+ const entries = await fs14.readdir(path13.join(cwd, "src"), { recursive: true });
4182
4214
  for (const rel of entries) {
4183
4215
  if (rel.includes("node_modules")) continue;
4184
4216
  if (rel.split(path13.sep)[0] === "controllers" || rel.startsWith("controllers/")) continue;
4185
4217
  if (!/\.(ts|tsx|js|jsx)$/.test(rel)) continue;
4186
- const text = await fs13.readFile(path13.join(cwd, "src", rel), "utf8").catch(() => "");
4218
+ const text = await fs14.readFile(path13.join(cwd, "src", rel), "utf8").catch(() => "");
4187
4219
  if (BODY_LOADERS.test(text)) return true;
4188
4220
  }
4189
4221
  } catch {
@@ -4276,7 +4308,7 @@ async function deployGame(ctx, opts, log) {
4276
4308
  }
4277
4309
  async function hasBuildScript(cwd) {
4278
4310
  try {
4279
- const pkg = JSON.parse(await fs13.readFile(path13.join(cwd, "package.json"), "utf8"));
4311
+ const pkg = JSON.parse(await fs14.readFile(path13.join(cwd, "package.json"), "utf8"));
4280
4312
  return Boolean(pkg.scripts?.build);
4281
4313
  } catch {
4282
4314
  return false;
@@ -4285,12 +4317,12 @@ async function hasBuildScript(cwd) {
4285
4317
  async function collectFiles(root) {
4286
4318
  const out = [];
4287
4319
  const walk2 = async (dir, prefix) => {
4288
- for (const e of await fs13.readdir(dir, { withFileTypes: true })) {
4320
+ for (const e of await fs14.readdir(dir, { withFileTypes: true })) {
4289
4321
  const relPath = prefix ? `${prefix}/${e.name}` : e.name;
4290
4322
  if (e.isDirectory()) {
4291
4323
  if (!EXCLUDE_DIRS.has(e.name)) await walk2(path13.join(dir, e.name), relPath);
4292
4324
  } else if (e.isFile() && !isSecretEnvFile(e.name)) {
4293
- out.push({ relPath, bytes: await fs13.readFile(path13.join(dir, e.name)) });
4325
+ out.push({ relPath, bytes: await fs14.readFile(path13.join(dir, e.name)) });
4294
4326
  }
4295
4327
  }
4296
4328
  };
@@ -4528,7 +4560,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main") {
4528
4560
  log.error("Couldn't save your game's source \u2014 please try again.");
4529
4561
  return false;
4530
4562
  };
4531
- const gitDir = await fs13.mkdtemp(path13.join(os5.tmpdir(), "genex-source-"));
4563
+ const gitDir = await fs14.mkdtemp(path13.join(os5.tmpdir(), "genex-source-"));
4532
4564
  const base = { GIT_DIR: gitDir };
4533
4565
  if (urlHasEmbeddedCredentials(pushUrl)) {
4534
4566
  base.GIT_CONFIG_COUNT = "1";
@@ -4543,7 +4575,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main") {
4543
4575
  };
4544
4576
  try {
4545
4577
  if ((await run("git", ["init", "-q"], base)).code !== 0) return failed();
4546
- await fs13.writeFile(
4578
+ await fs14.writeFile(
4547
4579
  path13.join(gitDir, "info", "exclude"),
4548
4580
  // .env* are secrets — never publish them; `!` keeps the non-secret template.
4549
4581
  ["node_modules/", "dist/", ".git/", ".genex/", ".env", ".env.*", "!.env.example", ""].join("\n")
@@ -4599,7 +4631,7 @@ async function pushWorktree(cwd, pushUrl, managed, log, branch = "main") {
4599
4631
  } catch {
4600
4632
  return failed();
4601
4633
  } finally {
4602
- await fs13.rm(gitDir, { recursive: true, force: true }).catch(() => {
4634
+ await fs14.rm(gitDir, { recursive: true, force: true }).catch(() => {
4603
4635
  });
4604
4636
  }
4605
4637
  }
@@ -4635,7 +4667,7 @@ async function fetchPushUrl(ctx, log) {
4635
4667
  }
4636
4668
  async function isDir2(p) {
4637
4669
  try {
4638
- return (await fs13.stat(p)).isDirectory();
4670
+ return (await fs14.stat(p)).isDirectory();
4639
4671
  } catch {
4640
4672
  return false;
4641
4673
  }
@@ -4770,17 +4802,17 @@ async function promoteBuild(apiUrl, projectId, token, log) {
4770
4802
  }
4771
4803
 
4772
4804
  // src/lib/detect-features.ts
4773
- import fs15 from "fs/promises";
4805
+ import fs16 from "fs/promises";
4774
4806
  import path15 from "path";
4775
4807
 
4776
4808
  // src/lib/generation-ledger.ts
4777
- import fs14 from "fs/promises";
4809
+ import fs15 from "fs/promises";
4778
4810
  import path14 from "path";
4779
4811
  var ledgerPath = (cwd) => path14.join(cwd, ".genex", "generations.ndjson");
4780
4812
  async function append(cwd, event) {
4781
4813
  try {
4782
- await fs14.access(path14.join(cwd, ".genex"));
4783
- await fs14.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
4814
+ await fs15.access(path14.join(cwd, ".genex"));
4815
+ await fs15.appendFile(ledgerPath(cwd), `${JSON.stringify(event)}
4784
4816
  `, "utf8");
4785
4817
  } catch {
4786
4818
  }
@@ -4788,7 +4820,7 @@ async function append(cwd, event) {
4788
4820
  async function readLedger(cwd = process.cwd()) {
4789
4821
  let raw;
4790
4822
  try {
4791
- raw = await fs14.readFile(ledgerPath(cwd), "utf8");
4823
+ raw = await fs15.readFile(ledgerPath(cwd), "utf8");
4792
4824
  } catch {
4793
4825
  return [];
4794
4826
  }
@@ -4842,7 +4874,7 @@ async function countFailed(kind, cwd = process.cwd()) {
4842
4874
  // src/lib/detect-features.ts
4843
4875
  async function detectEmbedSdkVersion(cwd = process.cwd()) {
4844
4876
  try {
4845
- const raw = await fs15.readFile(path15.join(cwd, "package.json"), "utf8");
4877
+ const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
4846
4878
  const pkg = JSON.parse(raw);
4847
4879
  const version = pkg.dependencies?.["@genex-ai/embed-sdk"] ?? pkg.devDependencies?.["@genex-ai/embed-sdk"];
4848
4880
  return typeof version === "string" && version ? version : null;
@@ -4852,7 +4884,7 @@ async function detectEmbedSdkVersion(cwd = process.cwd()) {
4852
4884
  }
4853
4885
  async function detectMultiplayer(cwd = process.cwd()) {
4854
4886
  try {
4855
- const raw = await fs15.readFile(path15.join(cwd, "package.json"), "utf8");
4887
+ const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
4856
4888
  const pkg = JSON.parse(raw);
4857
4889
  return Boolean(
4858
4890
  pkg.dependencies?.["@genex-ai/multiplayer"] ?? pkg.devDependencies?.["@genex-ai/multiplayer"]
@@ -4864,7 +4896,7 @@ async function detectMultiplayer(cwd = process.cwd()) {
4864
4896
  async function detectMatchmaking(log, cwd = process.cwd()) {
4865
4897
  let pkg;
4866
4898
  try {
4867
- pkg = JSON.parse(await fs15.readFile(path15.join(cwd, "package.json"), "utf8"));
4899
+ pkg = JSON.parse(await fs16.readFile(path15.join(cwd, "package.json"), "utf8"));
4868
4900
  } catch (err) {
4869
4901
  log.dim(` (skipping matchmaking \u2014 couldn't read package.json: ${String(err)})`);
4870
4902
  return null;
@@ -4882,7 +4914,7 @@ async function detectMatchmaking(log, cwd = process.cwd()) {
4882
4914
  var TOUCH_KIT_MARKERS = /controllers\/touch\/|controllers\/character\/touch-joystick|TouchJoystick|VirtualButton|DragZone|RotateOverlay/;
4883
4915
  async function detectMobileControls(cwd = process.cwd()) {
4884
4916
  try {
4885
- const raw = await fs15.readFile(path15.join(cwd, "package.json"), "utf8");
4917
+ const raw = await fs16.readFile(path15.join(cwd, "package.json"), "utf8");
4886
4918
  const pkg = JSON.parse(raw);
4887
4919
  if (pkg.genex?.mobileControls === true) return true;
4888
4920
  } catch {
@@ -4890,7 +4922,7 @@ async function detectMobileControls(cwd = process.cwd()) {
4890
4922
  const srcDir = path15.join(cwd, "src");
4891
4923
  let entries;
4892
4924
  try {
4893
- entries = await fs15.readdir(srcDir, { recursive: true });
4925
+ entries = await fs16.readdir(srcDir, { recursive: true });
4894
4926
  } catch {
4895
4927
  return false;
4896
4928
  }
@@ -4898,7 +4930,7 @@ async function detectMobileControls(cwd = process.cwd()) {
4898
4930
  if (rel.includes("node_modules")) continue;
4899
4931
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
4900
4932
  try {
4901
- const content = await fs15.readFile(path15.join(srcDir, rel), "utf8");
4933
+ const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
4902
4934
  if (TOUCH_KIT_MARKERS.test(content)) return true;
4903
4935
  } catch {
4904
4936
  }
@@ -4910,7 +4942,7 @@ async function detectGameStateUsage(cwd = process.cwd()) {
4910
4942
  const srcDir = path15.join(cwd, "src");
4911
4943
  let entries;
4912
4944
  try {
4913
- entries = await fs15.readdir(srcDir, { recursive: true });
4945
+ entries = await fs16.readdir(srcDir, { recursive: true });
4914
4946
  } catch {
4915
4947
  return false;
4916
4948
  }
@@ -4918,7 +4950,7 @@ async function detectGameStateUsage(cwd = process.cwd()) {
4918
4950
  if (rel.includes("node_modules")) continue;
4919
4951
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(rel)) continue;
4920
4952
  try {
4921
- const content = await fs15.readFile(path15.join(srcDir, rel), "utf8");
4953
+ const content = await fs16.readFile(path15.join(srcDir, rel), "utf8");
4922
4954
  if (GAME_STATE_CALLS.test(content)) return true;
4923
4955
  } catch {
4924
4956
  }
@@ -4970,14 +5002,14 @@ async function detectSurfaceScan(cwd = process.cwd()) {
4970
5002
  const srcDir = path15.join(cwd, "src");
4971
5003
  let entries;
4972
5004
  try {
4973
- entries = await fs15.readdir(srcDir, { recursive: true });
5005
+ entries = await fs16.readdir(srcDir, { recursive: true });
4974
5006
  } catch {
4975
5007
  return found;
4976
5008
  }
4977
5009
  for (const nativeRel of entries) {
4978
5010
  if (nativeRel.includes("node_modules")) continue;
4979
5011
  if (!/\.(ts|js|mts|mjs|tsx|jsx)$/.test(nativeRel)) continue;
4980
- const raw = await fs15.readFile(path15.join(srcDir, nativeRel), "utf8").catch(() => "");
5012
+ const raw = await fs16.readFile(path15.join(srcDir, nativeRel), "utf8").catch(() => "");
4981
5013
  if (!raw) continue;
4982
5014
  const rel = nativeRel.split(path15.sep).join("/");
4983
5015
  const content = raw.replace(/\/\*[\s\S]*?\*\//g, (m2) => m2.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/[^\n]*/gm, (m2, p1) => p1 + " ".repeat(m2.length - p1.length));
@@ -5037,12 +5069,12 @@ async function detectGenerationAudit(cwd = process.cwd()) {
5037
5069
  let haystack = "";
5038
5070
  const read = async (file) => {
5039
5071
  try {
5040
- haystack += await fs15.readFile(file, "utf8");
5072
+ haystack += await fs16.readFile(file, "utf8");
5041
5073
  } catch {
5042
5074
  }
5043
5075
  };
5044
5076
  try {
5045
- for (const entry of await fs15.readdir(cwd, { withFileTypes: true })) {
5077
+ for (const entry of await fs16.readdir(cwd, { withFileTypes: true })) {
5046
5078
  if (entry.isFile() && /\.(ts|tsx|js|jsx|css|html|json)$/.test(entry.name)) {
5047
5079
  await read(path15.join(cwd, entry.name));
5048
5080
  }
@@ -5051,7 +5083,7 @@ async function detectGenerationAudit(cwd = process.cwd()) {
5051
5083
  }
5052
5084
  for (const sub of ["src", "public"]) {
5053
5085
  try {
5054
- const entries = await fs15.readdir(path15.join(cwd, sub), { recursive: true });
5086
+ const entries = await fs16.readdir(path15.join(cwd, sub), { recursive: true });
5055
5087
  for (const rel of entries) {
5056
5088
  if (rel.includes("node_modules")) continue;
5057
5089
  if (!/\.(ts|tsx|js|jsx|css|html|json|txt)$/.test(rel)) continue;
@@ -5187,7 +5219,7 @@ async function borrowEvidence(meta, cwd) {
5187
5219
  } catch {
5188
5220
  return true;
5189
5221
  }
5190
- const gitConfig = await fs15.readFile(path15.join(cwd, ".git", "config"), "utf8").catch(() => "");
5222
+ const gitConfig = await fs16.readFile(path15.join(cwd, ".git", "config"), "utf8").catch(() => "");
5191
5223
  for (const m of gitConfig.matchAll(/url\s*=\s*(\S+)/g)) {
5192
5224
  try {
5193
5225
  const u = new URL(m[1]);
@@ -5195,7 +5227,7 @@ async function borrowEvidence(meta, cwd) {
5195
5227
  } catch {
5196
5228
  }
5197
5229
  }
5198
- const readme = await fs15.readFile(path15.join(cwd, "README.md"), "utf8").catch(() => "");
5230
+ const readme = await fs16.readFile(path15.join(cwd, "README.md"), "utf8").catch(() => "");
5199
5231
  return readme.includes(host) || /\b(upstream|originally by|ported from|borrowed from)\b/i.test(readme);
5200
5232
  }
5201
5233
 
@@ -5445,7 +5477,7 @@ async function runPromote(opts) {
5445
5477
  }
5446
5478
 
5447
5479
  // src/commands/generate.ts
5448
- import fs16 from "fs/promises";
5480
+ import fs17 from "fs/promises";
5449
5481
  import path16 from "path";
5450
5482
  import { PNG as PNG4 } from "pngjs";
5451
5483
 
@@ -5616,7 +5648,7 @@ var isRemoteRef = (value) => /^(https?:\/\/|data:)/i.test(value);
5616
5648
  async function inlineLocalImage(filePath, flag) {
5617
5649
  let bytes;
5618
5650
  try {
5619
- bytes = await fs16.readFile(filePath);
5651
+ bytes = await fs17.readFile(filePath);
5620
5652
  } catch {
5621
5653
  return { ok: false, error: `Couldn't read the ${flag} file at ${filePath}.` };
5622
5654
  }
@@ -5822,7 +5854,7 @@ async function runGenerate(kind, opts) {
5822
5854
  return;
5823
5855
  }
5824
5856
  try {
5825
- const bytes = await fs16.readFile(opts.inpaintUrl);
5857
+ const bytes = await fs17.readFile(opts.inpaintUrl);
5826
5858
  opts = { ...opts, inpaintUrl: `data:image/png;base64,${bytes.toString("base64")}` };
5827
5859
  } catch {
5828
5860
  log.error(`Couldn't read the --inpaint mask at ${opts.inpaintUrl}.`);
@@ -5946,7 +5978,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
5946
5978
  return;
5947
5979
  }
5948
5980
  await recordTerminal(view.id, "completed", files.map((f) => f.url));
5949
- await fs16.mkdir(outDir, { recursive: true });
5981
+ await fs17.mkdir(outDir, { recursive: true });
5950
5982
  const solved = [];
5951
5983
  for (let i = 0; i < files.length; i++) {
5952
5984
  const f = files[i];
@@ -5979,7 +6011,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
5979
6011
  continue;
5980
6012
  }
5981
6013
  const outPath = path16.join(outDir, `glass-${i + 1}.png`);
5982
- await fs16.writeFile(outPath, PNG4.sync.write(r.png));
6014
+ await fs17.writeFile(outPath, PNG4.sync.write(r.png));
5983
6015
  solved.push({
5984
6016
  path: outPath,
5985
6017
  url: f.url,
@@ -6573,7 +6605,7 @@ async function toRow(e, v, cwd) {
6573
6605
  }
6574
6606
 
6575
6607
  // src/commands/controller.ts
6576
- import fs18 from "fs/promises";
6608
+ import fs19 from "fs/promises";
6577
6609
  import path18 from "path";
6578
6610
 
6579
6611
  // ../../packages/meshy-animation-catalog/src/index.ts
@@ -15704,7 +15736,7 @@ function searchMeshyAnimations(query, options = {}) {
15704
15736
  }
15705
15737
 
15706
15738
  // src/lib/anims.ts
15707
- import fs17 from "fs/promises";
15739
+ import fs18 from "fs/promises";
15708
15740
  import path17 from "path";
15709
15741
  var ANIMS_DEST = path17.join("public", "assets", "anims");
15710
15742
  var HIDDEN_TAG = "reference";
@@ -15734,7 +15766,7 @@ async function runAnims(opts) {
15734
15766
  const destDir = path17.join(root, ANIMS_DEST);
15735
15767
  const gameManifestPath = path17.join(destDir, "manifest.json");
15736
15768
  if (opts.reset) {
15737
- await fs17.rm(destDir, { recursive: true, force: true });
15769
+ await fs18.rm(destDir, { recursive: true, force: true });
15738
15770
  log.step(`Cleared ${c.cyan(ANIMS_DEST + path17.sep)} (--reset)`);
15739
15771
  }
15740
15772
  if (selectors.length === 0) {
@@ -15777,8 +15809,8 @@ async function runAnims(opts) {
15777
15809
  opts.cacheDir ?? getAnimsCacheDir(),
15778
15810
  `${manifest.library}-v${manifest.version}`
15779
15811
  );
15780
- await fs17.mkdir(cacheDir, { recursive: true });
15781
- await fs17.mkdir(destDir, { recursive: true });
15812
+ await fs18.mkdir(cacheDir, { recursive: true });
15813
+ await fs18.mkdir(destDir, { recursive: true });
15782
15814
  const base = getAnimsBase(opts.animsBase);
15783
15815
  let installedCount = 0;
15784
15816
  let presentCount = 0;
@@ -15796,9 +15828,9 @@ async function runAnims(opts) {
15796
15828
  const res = await fetch(base + entry.file);
15797
15829
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
15798
15830
  const buf = Buffer.from(await res.arrayBuffer());
15799
- await fs17.writeFile(cached, buf);
15831
+ await fs18.writeFile(cached, buf);
15800
15832
  }
15801
- await fs17.copyFile(cached, dest);
15833
+ await fs18.copyFile(cached, dest);
15802
15834
  installedCount++;
15803
15835
  addedBytes += entry.bytes;
15804
15836
  log.dim(` ${path17.join(ANIMS_DEST, entry.file)} (${formatMb(entry.bytes)})`);
@@ -15817,7 +15849,7 @@ async function runAnims(opts) {
15817
15849
  version: manifest.version,
15818
15850
  clips: [...union].sort((a, b) => a.localeCompare(b))
15819
15851
  };
15820
- await fs17.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
15852
+ await fs18.writeFile(gameManifestPath, JSON.stringify(gameManifest, null, 2) + "\n");
15821
15853
  log.plain("");
15822
15854
  const parts = [`${installedCount} clip${installedCount === 1 ? "" : "s"} installed`];
15823
15855
  if (presentCount > 0) parts.push(`${presentCount} already present`);
@@ -15856,7 +15888,7 @@ async function loadManifest(baseOverride) {
15856
15888
  } catch {
15857
15889
  }
15858
15890
  const snapshotPath = path17.join(getTemplatesDir(), "controllers", "assets", "anims-manifest.json");
15859
- const manifest = JSON.parse(await fs17.readFile(snapshotPath, "utf8"));
15891
+ const manifest = JSON.parse(await fs18.readFile(snapshotPath, "utf8"));
15860
15892
  return { manifest, source: "snapshot" };
15861
15893
  }
15862
15894
  function resolveSelectors(manifest, selectors) {
@@ -15974,21 +16006,21 @@ function printCatalog(log, manifest, selectors) {
15974
16006
  }
15975
16007
  async function readGameManifest(file) {
15976
16008
  try {
15977
- return JSON.parse(await fs17.readFile(file, "utf8"));
16009
+ return JSON.parse(await fs18.readFile(file, "utf8"));
15978
16010
  } catch {
15979
16011
  return null;
15980
16012
  }
15981
16013
  }
15982
16014
  async function hasSize(file, bytes) {
15983
16015
  try {
15984
- return (await fs17.stat(file)).size === bytes;
16016
+ return (await fs18.stat(file)).size === bytes;
15985
16017
  } catch {
15986
16018
  return false;
15987
16019
  }
15988
16020
  }
15989
16021
  async function exists2(p) {
15990
16022
  try {
15991
- await fs17.access(p);
16023
+ await fs18.access(p);
15992
16024
  return true;
15993
16025
  } catch {
15994
16026
  return false;
@@ -16198,8 +16230,8 @@ async function runController(opts) {
16198
16230
  log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
16199
16231
  continue;
16200
16232
  }
16201
- await fs18.mkdir(path18.dirname(dest), { recursive: true });
16202
- await fs18.copyFile(path18.join(srcDir, file.from), dest);
16233
+ await fs19.mkdir(path18.dirname(dest), { recursive: true });
16234
+ await fs19.copyFile(path18.join(srcDir, file.from), dest);
16203
16235
  copied++;
16204
16236
  log.dim(` ${file.rel}`);
16205
16237
  }
@@ -16285,8 +16317,8 @@ async function installMeshyCharacterManifest(args) {
16285
16317
  }
16286
16318
  assertCompleteMeshyControllerPack(manifest);
16287
16319
  const destination = path18.join(args.root, ASSETS_DEST, "meshy-character.json");
16288
- await fs18.mkdir(path18.dirname(destination), { recursive: true });
16289
- await fs18.writeFile(
16320
+ await fs19.mkdir(path18.dirname(destination), { recursive: true });
16321
+ await fs19.writeFile(
16290
16322
  destination,
16291
16323
  `${JSON.stringify(manifest, null, 2)}
16292
16324
  `
@@ -16425,13 +16457,13 @@ function assertCompleteMeshyControllerPack(manifest) {
16425
16457
  async function installFallbackAvatar(args) {
16426
16458
  const { root, srcDir, log } = args;
16427
16459
  const dest = path18.join(root, ASSETS_DEST, "avatar.vrm");
16428
- await fs18.mkdir(path18.dirname(dest), { recursive: true });
16429
- await fs18.copyFile(path18.join(srcDir, "assets", "default-avatar.vrm"), dest);
16460
+ await fs19.mkdir(path18.dirname(dest), { recursive: true });
16461
+ await fs19.copyFile(path18.join(srcDir, "assets", "default-avatar.vrm"), dest);
16430
16462
  log.dim(" public/assets/avatar.vrm (fallback avatar \u2014 bundled CC0 default)");
16431
16463
  }
16432
16464
  async function exists3(p) {
16433
16465
  try {
16434
- await fs18.access(p);
16466
+ await fs19.access(p);
16435
16467
  return true;
16436
16468
  } catch {
16437
16469
  return false;
@@ -17271,7 +17303,7 @@ function rank(items, query) {
17271
17303
  }
17272
17304
 
17273
17305
  // src/commands/motion.ts
17274
- import fs19 from "fs/promises";
17306
+ import fs20 from "fs/promises";
17275
17307
  import path19 from "path";
17276
17308
 
17277
17309
  // src/lib/motion/npz.ts
@@ -18523,7 +18555,7 @@ async function motionGen(opts, log) {
18523
18555
  }
18524
18556
  if (opts.constraintsPath !== void 0) {
18525
18557
  try {
18526
- const raw = await fs19.readFile(opts.constraintsPath, "utf8");
18558
+ const raw = await fs20.readFile(opts.constraintsPath, "utf8");
18527
18559
  generationOptions.constraints = JSON.parse(raw);
18528
18560
  } catch {
18529
18561
  log.error(`Couldn't read the --constraints JSON at ${opts.constraintsPath}.`);
@@ -18547,9 +18579,9 @@ async function motionGen(opts, log) {
18547
18579
  async function expandTakes(selectors) {
18548
18580
  const out = [];
18549
18581
  for (const sel of selectors) {
18550
- const st = await fs19.stat(sel).catch(() => null);
18582
+ const st = await fs20.stat(sel).catch(() => null);
18551
18583
  if (st?.isDirectory()) {
18552
- const names = await fs19.readdir(sel);
18584
+ const names = await fs20.readdir(sel);
18553
18585
  for (const n of names.sort()) if (n.endsWith(".npz")) out.push(path19.join(sel, n));
18554
18586
  } else if (st?.isFile()) {
18555
18587
  out.push(sel);
@@ -18585,7 +18617,7 @@ async function motionVerify(opts, log) {
18585
18617
  let gates = DEFAULT_GATES;
18586
18618
  if (opts.gatesPath) {
18587
18619
  try {
18588
- gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs19.readFile(opts.gatesPath, "utf8")));
18620
+ gates = mergeGates(DEFAULT_GATES, JSON.parse(await fs20.readFile(opts.gatesPath, "utf8")));
18589
18621
  } catch {
18590
18622
  log.error(`Couldn't read the --gates JSON at ${opts.gatesPath}.`);
18591
18623
  process.exitCode = 1;
@@ -18609,7 +18641,7 @@ async function motionVerify(opts, log) {
18609
18641
  for (const file of files) {
18610
18642
  const stem = path19.basename(file).replace(/\.npz$/, "");
18611
18643
  try {
18612
- reports.push(analyzeTake(stem, await fs19.readFile(file), gates));
18644
+ reports.push(analyzeTake(stem, await fs20.readFile(file), gates));
18613
18645
  } catch (err) {
18614
18646
  reports.push({
18615
18647
  take: stem,
@@ -18647,7 +18679,7 @@ async function motionCompile(opts, log) {
18647
18679
  let cfg = DEFAULT_MOTION_CONFIG;
18648
18680
  if (opts.configPath) {
18649
18681
  try {
18650
- const patch = JSON.parse(await fs19.readFile(opts.configPath, "utf8"));
18682
+ const patch = JSON.parse(await fs20.readFile(opts.configPath, "utf8"));
18651
18683
  cfg = { ...DEFAULT_MOTION_CONFIG, ...patch, idleLoop: { ...DEFAULT_MOTION_CONFIG.idleLoop, ...patch.idleLoop } };
18652
18684
  } catch {
18653
18685
  log.error(`Couldn't read the --config JSON at ${opts.configPath}.`);
@@ -18667,7 +18699,7 @@ async function motionCompile(opts, log) {
18667
18699
  for (const file of files) {
18668
18700
  const stem = path19.basename(file).replace(/\.npz$/, "");
18669
18701
  try {
18670
- inputs.push({ stem, take: loadTake(await fs19.readFile(file)) });
18702
+ inputs.push({ stem, take: loadTake(await fs20.readFile(file)) });
18671
18703
  } catch (err) {
18672
18704
  log.error(`${stem}: ${err instanceof Error ? err.message : String(err)}`);
18673
18705
  process.exitCode = 1;
@@ -18689,9 +18721,9 @@ async function motionCompile(opts, log) {
18689
18721
  process.exitCode = 1;
18690
18722
  return;
18691
18723
  }
18692
- await fs19.mkdir(path19.dirname(path19.resolve(opts.out)), { recursive: true });
18724
+ await fs20.mkdir(path19.dirname(path19.resolve(opts.out)), { recursive: true });
18693
18725
  const json = JSON.stringify(result.data);
18694
- await fs19.writeFile(opts.out, json);
18726
+ await fs20.writeFile(opts.out, json);
18695
18727
  if (opts.json) {
18696
18728
  writeJson({ out: opts.out, set: setName, tracks: Object.keys(result.data.gaits), bytes: json.length });
18697
18729
  return;
@@ -18727,14 +18759,14 @@ async function motionInstall(opts, log) {
18727
18759
  try {
18728
18760
  for (const rel of files) {
18729
18761
  const dest = path19.join(root, MOTION_DEST, rel);
18730
- const exists4 = await fs19.access(dest).then(() => true, () => false);
18762
+ const exists4 = await fs20.access(dest).then(() => true, () => false);
18731
18763
  if (!opts.force && exists4) {
18732
18764
  skipped++;
18733
18765
  log.dim(` skipped ${path19.join(MOTION_DEST, rel)} (exists \u2014 use --force to overwrite)`);
18734
18766
  continue;
18735
18767
  }
18736
- await fs19.mkdir(path19.dirname(dest), { recursive: true });
18737
- await fs19.copyFile(path19.join(srcDir, rel), dest);
18768
+ await fs20.mkdir(path19.dirname(dest), { recursive: true });
18769
+ await fs20.copyFile(path19.join(srcDir, rel), dest);
18738
18770
  copied++;
18739
18771
  log.dim(` ${path19.join(MOTION_DEST, rel)}`);
18740
18772
  }
@@ -18777,7 +18809,7 @@ async function motionConstraints(opts, log) {
18777
18809
  }
18778
18810
  const doc = directionConstraint(dir, speed, duration);
18779
18811
  const out = opts.out ?? "constraints.json";
18780
- await fs19.writeFile(out, JSON.stringify(doc));
18812
+ await fs20.writeFile(out, JSON.stringify(doc));
18781
18813
  if (opts.json) {
18782
18814
  writeJson({ out, dir: dirName, speed, duration, waypoints: doc[0].frame_indices.length });
18783
18815
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.2.3",
3
+ "version": "1.2.4-dev.345",
4
4
  "description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -116,7 +116,7 @@ scene.background = texture; // keep the raw texture for the visible sky
116
116
 
117
117
  ## Troubleshooting
118
118
 
119
- - **"Not authorized"** — run `npx @genex-ai/cli-demo@latest init` first (it writes your `GENEX_TOKEN`).
119
+ - **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
120
120
  - **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
121
121
  this skybox generation. Tell the user the facts the CLI printed: their balance, this
122
122
  generation's cost, and when their credits refill. Then offer to continue the build
@@ -225,7 +225,7 @@ first one is the one a screenshot of the whole arena will not show you.
225
225
 
226
226
  ## Troubleshooting
227
227
 
228
- - **"Not authorized"** — run `npx @genex-ai/cli-demo@latest init` first (it writes your `GENEX_TOKEN`).
228
+ - **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
229
229
  - **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
230
230
  this texture generation. Tell the user the facts the CLI printed: their balance,
231
231
  this generation's cost, and when their credits refill. Then offer to continue the
@@ -151,7 +151,7 @@ set belongs to `$genex-ai-hud` — both build on `npx genex image`/`video`.
151
151
 
152
152
  ## Troubleshooting
153
153
 
154
- - **"Not authorized"** — run `npx @genex-ai/cli-demo@latest init` first (it writes your `GENEX_TOKEN`).
154
+ - **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
155
155
  - **"Prompt rejected"** — the provider's content-safety filter blocked the prompt.
156
156
  This is non-retryable; retrying the same wording fails again. Rewrite the prompt.
157
157
  - **Nothing plays / black surface** — the first `video.play()` must run inside a user
@@ -74,6 +74,23 @@ the game is ready — players should never stare at a black screen. This is a
74
74
  recommendation, not a rule: even a one-line "Loading…" overlay over the dark
75
75
  page background is enough.
76
76
 
77
+ ## The folder's existing files are the user's
78
+
79
+ Whatever was in this folder before setup belongs to the user — reference
80
+ images, notes, sketches, an earlier attempt, files an agent won't recognize.
81
+ They may exist nowhere else, and deleting one has no undo. So: add files, edit
82
+ the ones you created, and never delete, empty, move, rename, or overwrite
83
+ anything you didn't. A non-empty folder is normal and is never something to
84
+ fix, and "start clean" is never a reason.
85
+
86
+ That rules out `rm -rf`, `git clean`, `git checkout -- .`, `git reset --hard`
87
+ over the user's work, and every scaffolder's offer to empty the directory
88
+ (`--force`, `--overwrite`, "Remove existing files") — answer a "directory is
89
+ not empty" prompt with Ignore/continue, or scaffold into a fresh subfolder and
90
+ copy across what's missing. If a step genuinely cannot continue without
91
+ removing something of theirs, stop and ask, naming the exact files, and wait
92
+ for a yes. This is law 19 of the build contract in the project's `AGENTS.md`.
93
+
77
94
  ## Remixing an existing game
78
95
 
79
96
  If the project folder already contains a game (a remix or any existing
@@ -142,7 +159,7 @@ and re-link the clone to the same live game:
142
159
  ```bash
143
160
  git clone <the game's repo url> my-game && cd my-game
144
161
  npm install
145
- npx @genex-ai/cli-demo@latest link <slug> # slug = the name in the play URL
162
+ npx @genex-ai/cli-demo@dev link <slug> # slug = the name in the play URL
146
163
  ```
147
164
 
148
165
  Don't know the slug? **`npx genex list`** prints every game on your account —
@@ -169,7 +186,7 @@ Safe to run any time — genex-owned skills are refreshed to the latest version,
169
186
  and your own files are never touched:
170
187
 
171
188
  ```bash
172
- npx @genex-ai/cli-demo@latest init
189
+ npx @genex-ai/cli-demo@dev init
173
190
  ```
174
191
 
175
192
  Use `--force` only if you intentionally want your own existing files overwritten
@@ -38,7 +38,7 @@ update, so update immediately.)
38
38
  Run exactly the command the nudge printed, from the game project root:
39
39
 
40
40
  ```bash
41
- npm i -D @genex-ai/cli-demo@latest # the genex CLI (a dev dependency)
41
+ npm i -D @genex-ai/cli-demo@dev # the genex CLI (a dev dependency)
42
42
  npm i @genex-ai/embed-sdk@latest # identity/saves SDK (ships inside the game)
43
43
  npm i @genex-ai/multiplayer@latest # multiplayer SDK (only if the game uses it)
44
44
  ```