@genex-ai/cli-demo 0.51.0-dev.101 → 0.51.0-dev.110

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
@@ -1652,7 +1652,13 @@ async function callPublish(ctx, commit, opts, log) {
1652
1652
  async function pushSource(cwd, ctx, log) {
1653
1653
  const target = await fetchPushUrl(ctx, log);
1654
1654
  if (!target) return false;
1655
- return pushWorktree(cwd, target.pushUrl, target.managed, log);
1655
+ if (await pushWorktree(cwd, target.pushUrl, target.managed, log)) return true;
1656
+ if (!target.managed) return false;
1657
+ log.info("Retrying the source push\u2026");
1658
+ await new Promise((r) => setTimeout(r, 2e3));
1659
+ const fresh = await fetchPushUrl(ctx, log);
1660
+ if (!fresh) return false;
1661
+ return pushWorktree(cwd, fresh.pushUrl, fresh.managed, log);
1656
1662
  }
1657
1663
  async function pushWorktree(cwd, pushUrl, managed, log) {
1658
1664
  const EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
@@ -2119,6 +2125,44 @@ async function runPreview(opts) {
2119
2125
  }
2120
2126
  }
2121
2127
 
2128
+ // src/lib/open.ts
2129
+ import { spawn as spawn4 } from "child_process";
2130
+ function tokenize(cmd) {
2131
+ return cmd.trim().split(/\s+/).filter(Boolean);
2132
+ }
2133
+ function openUrl(url) {
2134
+ let command;
2135
+ let args;
2136
+ const custom = (process.env.GENEX_BROWSER || process.env.BROWSER)?.trim();
2137
+ const customParts = custom ? tokenize(custom) : [];
2138
+ if (customParts.length > 0) {
2139
+ command = customParts[0];
2140
+ args = [...customParts.slice(1), url];
2141
+ } else {
2142
+ switch (process.platform) {
2143
+ case "darwin":
2144
+ command = "open";
2145
+ args = [url];
2146
+ break;
2147
+ case "win32":
2148
+ command = "cmd";
2149
+ args = ["/c", "start", "", url];
2150
+ break;
2151
+ default:
2152
+ command = "xdg-open";
2153
+ args = [url];
2154
+ break;
2155
+ }
2156
+ }
2157
+ try {
2158
+ const child = spawn4(command, args, { stdio: "ignore", detached: true });
2159
+ child.on("error", () => {
2160
+ });
2161
+ child.unref();
2162
+ } catch {
2163
+ }
2164
+ }
2165
+
2122
2166
  // src/lib/sse.ts
2123
2167
  async function* readSSE(body) {
2124
2168
  const decoder = new TextDecoder();
@@ -2255,9 +2299,9 @@ async function runGenerate(kind, opts) {
2255
2299
  log.dim(" (Re-running the generate command would start \u2014 and bill \u2014 a NEW generation.)");
2256
2300
  return;
2257
2301
  }
2258
- await awaitAndReport(apiUrl, token, id, kind, log);
2302
+ await awaitAndReport(apiUrl, token, id, kind, log, opts.open);
2259
2303
  }
2260
- async function awaitAndReport(apiUrl, token, id, kind, log) {
2304
+ async function awaitAndReport(apiUrl, token, id, kind, log, open = false) {
2261
2305
  log.step("Generating\u2026 (this can take up to a minute)");
2262
2306
  const onProgress = (p) => log.dim(` ${p}%`);
2263
2307
  const timeoutMs = waitTimeoutFor(kind);
@@ -2269,9 +2313,9 @@ async function awaitAndReport(apiUrl, token, id, kind, log) {
2269
2313
  process.exitCode = 1;
2270
2314
  return;
2271
2315
  }
2272
- await reportTerminal(kind, view, log);
2316
+ await reportTerminal(kind, view, log, open);
2273
2317
  }
2274
- async function reportTerminal(kind, view, log) {
2318
+ async function reportTerminal(kind, view, log, open = false) {
2275
2319
  if (view.status !== "completed") {
2276
2320
  log.error(`Generation ${view.status}${view.error ? `: ${view.error}` : ""}.`);
2277
2321
  process.exitCode = 1;
@@ -2290,6 +2334,14 @@ async function reportTerminal(kind, view, log) {
2290
2334
  log.plain(` ${label}${f.url}`);
2291
2335
  }
2292
2336
  log.plain("");
2337
+ if (open && files[0]) {
2338
+ openUrl(files[0].url);
2339
+ log.plain(
2340
+ ` ${c.bold("\u{1F441} Show this to the user")} \u2014 it just opened in their browser; paste this link in chat too so they can open it themselves:`
2341
+ );
2342
+ log.plain(` ${files[0].url}`);
2343
+ log.plain("");
2344
+ }
2293
2345
  printHint(kind, files, log);
2294
2346
  await firstPreviewNudge(log);
2295
2347
  }
@@ -2440,10 +2492,10 @@ async function runWait(opts) {
2440
2492
  log.dim(` ${kind} ${id}`);
2441
2493
  log.plain("");
2442
2494
  if (TERMINAL2.has(view.status)) {
2443
- await reportTerminal(kind, view, log);
2495
+ await reportTerminal(kind, view, log, opts.open);
2444
2496
  return;
2445
2497
  }
2446
- await awaitAndReport(apiUrl, token, id, kind, log);
2498
+ await awaitAndReport(apiUrl, token, id, kind, log, opts.open);
2447
2499
  }
2448
2500
 
2449
2501
  // src/commands/controller.ts
@@ -4048,6 +4100,8 @@ ${c.bold("Options for the generators (`model` `skybox` `sfx` `texture` `image` `
4048
4100
  --terrain (texture) seamless tiling surface for terrain/ground.
4049
4101
  --duration <sec> (sfx, video) target clip length in seconds.
4050
4102
  --transparent (image) transparent background (alpha) \u2014 for decals/stickers.
4103
+ --open (image/video) open the finished asset in the user's browser +
4104
+ print a link \u2014 show them the concept frame / anything they approve.
4051
4105
  --aspect <ratio> (image) aspect preset (e.g. square, landscape, portrait).
4052
4106
  --quality <q> (image) quality preset: low | medium | high.
4053
4107
  --candidates <n> (image) generate 2-4 variants in ONE call and print every
@@ -4232,6 +4286,9 @@ function parseArgs(argv) {
4232
4286
  case "--no-wait":
4233
4287
  parsed.options.noWait = true;
4234
4288
  break;
4289
+ case "--open":
4290
+ parsed.options.open = true;
4291
+ break;
4235
4292
  case "--terrain":
4236
4293
  parsed.options.terrain = true;
4237
4294
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.51.0-dev.101",
3
+ "version": "0.51.0-dev.110",
4
4
  "description": "Set up your ~/.claude workspace, authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -79,13 +79,25 @@ lap counter in a game without laps). `--aspect 16:9 --quality high
79
79
  --no-wait`, enqueued FIRST of all art; the URL goes into the style-brief
80
80
  comment.
81
81
 
82
- **Share it before building on it.** The moment the frame lands, show it to
83
- the user "this is roughly how the game will look: keep it, or change
84
- something?" as ONE structured question with 2–3 concrete adjustment
85
- options. Keep building gameplay-neutral work (scaffold, physics, netcode)
86
- while waiting, but do NOT anchor further art to an unapproved frame; if the
87
- user redirects, regenerate with their notes before the HUD mockup and menu
88
- frame go out.
82
+ **Share it before building on it actually SHOW the image, don't just ask.**
83
+ When the concept lands, pick it up with `genex wait <id> --open` (or generate it
84
+ with `--open`): it opens in the user's browser AND prints the link. Paste that
85
+ URL as a clickable link in your approval message — a URL is invisible in a
86
+ terminal, and "do you like it?" with no picture in front of the user is the #1
87
+ way this checkpoint fails (they end up digging logs for the file path). Then ask
88
+ ONE structured question — "this is roughly how the game will look: keep it, or
89
+ change something?" — with 2–3 concrete adjustment options. Keep building
90
+ gameplay-neutral work (scaffold, physics, netcode) while waiting, but do NOT
91
+ anchor further art to an unapproved frame. **Approval is a LOOP, not a
92
+ one-shot:** if the user dislikes ANYTHING, regenerate with their exact notes
93
+ (`--candidates 2–3` gives them options to choose from), open + link the new
94
+ frame, and ask again — repeat until they actually approve. Carry every note
95
+ forward so each round compounds; if two rounds don't converge, offer 2–3
96
+ distinct directions as a structured question instead of re-rolling blind. The
97
+ HUD mockup, the menu, and every downstream asset wait for a frame the user has
98
+ signed off — a single "meh" is never permission to move on. The same "open it +
99
+ paste the link" rule covers every image the user weighs in on — the menu still,
100
+ the HUD mockup candidates.
89
101
 
90
102
  **The concept anchors STYLE, not truth.** Palette, materials, light, and
91
103
  register come from the frame; CONTENT comes from the game contract. When