@genex-ai/cli-demo 0.20.0 → 0.21.0

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
@@ -1399,6 +1399,9 @@ async function runPublish(opts) {
1399
1399
  if (opts.description) body.description = opts.description;
1400
1400
  if (opts.regenerateCover) body.regenerateCover = true;
1401
1401
  if (opts.categories?.length) body.categories = opts.categories;
1402
+ if (opts.sourceRepoUrl) body.sourceRepoUrl = opts.sourceRepoUrl;
1403
+ if (opts.sourceAuthor) body.sourceAuthor = opts.sourceAuthor;
1404
+ if (opts.license) body.license = opts.license;
1402
1405
  if (detections.embedSdkVersion) body.embedSdkVersion = detections.embedSdkVersion;
1403
1406
  body.multiplayer = detections.multiplayer;
1404
1407
  body.matchmaking = detections.matchmaking ?? null;
@@ -1906,6 +1909,72 @@ async function exists2(p) {
1906
1909
  }
1907
1910
  }
1908
1911
 
1912
+ // src/commands/explore.ts
1913
+ async function runExplore(opts) {
1914
+ const log = createLogger({ quiet: opts.quiet });
1915
+ const query = opts.query?.trim();
1916
+ if (!query) {
1917
+ log.error('Missing query. Usage: genex explore "<what you need>"');
1918
+ process.exitCode = 1;
1919
+ return;
1920
+ }
1921
+ const apiUrl = getApiUrl(opts.apiUrl);
1922
+ let res;
1923
+ try {
1924
+ res = await fetch(`${apiUrl}/api/gallery/search-corpus?curated=1`);
1925
+ } catch {
1926
+ log.error("Couldn't reach Genex \u2014 please try again.");
1927
+ process.exitCode = 1;
1928
+ return;
1929
+ }
1930
+ if (!res.ok) {
1931
+ log.error(`explore: API returned ${res.status} \u2014 please try again.`);
1932
+ process.exitCode = 1;
1933
+ return;
1934
+ }
1935
+ const { items } = await res.json();
1936
+ const ranked = rank(items, query).slice(0, 5);
1937
+ if (opts.json) {
1938
+ console.log(JSON.stringify(ranked, null, 2));
1939
+ return;
1940
+ }
1941
+ if (ranked.length === 0) {
1942
+ log.plain(
1943
+ `No curated projects matched "${query}". Try a broader term (terrain, grass, vehicle, water, building, dungeon, fish, shader).`
1944
+ );
1945
+ return;
1946
+ }
1947
+ for (const [i, p] of ranked.entries()) {
1948
+ const meta = [
1949
+ p.categories.join(", "),
1950
+ p.license ?? "",
1951
+ p.sourceAuthor ? `originally by ${p.sourceAuthor}` : ""
1952
+ ].filter(Boolean).join(" \xB7 ");
1953
+ log.plain(`${c.bold(`${i + 1}. ${p.title}`)} \u2014 ${meta}`);
1954
+ if (p.description) log.plain(` ${p.description.slice(0, 160)}`);
1955
+ if (p.playUrl) log.plain(` play: ${p.playUrl}`);
1956
+ if (p.cloneUrl) log.plain(` clone: ${p.cloneUrl} (editable source on main)`);
1957
+ if (p.sourceRepoUrl) log.plain(` upstream: ${p.sourceRepoUrl}`);
1958
+ }
1959
+ log.plain("");
1960
+ log.plain(
1961
+ `To use one: clone it as a starting template, or borrow parts into your current game \u2014 the genex-explore skill has the exact steps. Keep the credits.`
1962
+ );
1963
+ }
1964
+ function rank(items, query) {
1965
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
1966
+ return items.map((p) => {
1967
+ const hay = `${p.title} ${p.description} ${p.categories.join(" ")}`.toLowerCase();
1968
+ let score = 0;
1969
+ for (const t of terms) {
1970
+ if (p.title.toLowerCase().includes(t)) score += 3;
1971
+ if (p.categories.some((cat) => cat.includes(t))) score += 2;
1972
+ if (hay.includes(t)) score += 1;
1973
+ }
1974
+ return { p, score };
1975
+ }).filter((x) => x.score > 0).sort((a, b) => b.score - a.score || b.p.playsCount - a.p.playsCount).map((x) => x.p);
1976
+ }
1977
+
1909
1978
  // src/index.ts
1910
1979
  var GEN_KINDS = /* @__PURE__ */ new Set(["model", "skybox", "sfx", "texture"]);
1911
1980
  function getVersion() {
@@ -1934,6 +2003,8 @@ ${c.bold("Usage")}
1934
2003
  genex texture "<prompt>" [options] Generate a PBR texture into public/assets/textures.
1935
2004
  genex controller <type> [--force] Install a physics controller (character|car|drone)
1936
2005
  into src/controllers (+ assets into public/assets).
2006
+ genex explore "<query>" [options] Search the curated community gallery \u2014 proven
2007
+ Three.js systems you can clone or borrow parts from.
1937
2008
 
1938
2009
  ${c.bold("Options for the generators (`model` `skybox` `sfx` `texture`)")}
1939
2010
  --terrain (texture) seamless tiling surface for terrain/ground.
@@ -1972,9 +2043,17 @@ ${c.bold("Options for `preview` / `publish`")}
1972
2043
  --categories <list> (publish) 1-3 gallery categories, comma-separated.
1973
2044
  Valid: games, assets, physics, terrain, lighting, vfx.
1974
2045
  --regenerate-cover (publish) Re-mint the disc cover (concept changed).
2046
+ --source-repo-url <url> (publish) Upstream repo this game is a port of \u2014
2047
+ shown as "Ported from \u2026" credit on the game page.
2048
+ --source-author <name> (publish) Upstream author to credit.
2049
+ --license <label> (publish) Upstream license label (e.g. MIT).
1975
2050
  --api-url <url> (publish) Override the API base URL.
1976
2051
  --env <path> Token env file (default: ~/.genex/env).
1977
2052
 
2053
+ ${c.bold("Options for `explore`")}
2054
+ --json Print the top matches as JSON (for tools/agents).
2055
+ --api-url <url> Override the API base URL.
2056
+
1978
2057
  ${c.bold("Global")}
1979
2058
  --quiet Reduce output.
1980
2059
  -h, --help Show this help.
@@ -1999,6 +2078,7 @@ ${c.bold("Examples")}
1999
2078
  genex sfx "punchy laser zap" --duration 2
2000
2079
  genex texture "mossy cracked cobblestone" --terrain
2001
2080
  genex controller character
2081
+ genex explore "grass"
2002
2082
  `;
2003
2083
  function parseArgs(argv) {
2004
2084
  const parsed = {
@@ -2019,7 +2099,10 @@ function parseArgs(argv) {
2019
2099
  "--description",
2020
2100
  "--categories",
2021
2101
  "--timeout",
2022
- "--duration"
2102
+ "--duration",
2103
+ "--source-repo-url",
2104
+ "--source-author",
2105
+ "--license"
2023
2106
  ]);
2024
2107
  let i = 0;
2025
2108
  while (i < argv.length) {
@@ -2058,6 +2141,9 @@ function parseArgs(argv) {
2058
2141
  case "--quiet":
2059
2142
  parsed.options.quiet = true;
2060
2143
  break;
2144
+ case "--json":
2145
+ parsed.options.json = true;
2146
+ break;
2061
2147
  default: {
2062
2148
  if (needsValue.has(arg)) {
2063
2149
  const value = argv[i++];
@@ -2073,6 +2159,8 @@ function parseArgs(argv) {
2073
2159
  parsed.command = arg;
2074
2160
  } else if (parsed.options.name === void 0) {
2075
2161
  parsed.options.name = arg;
2162
+ } else if (parsed.command === "explore") {
2163
+ parsed.options.name = `${parsed.options.name} ${arg}`;
2076
2164
  } else {
2077
2165
  parsed.error = `Unexpected argument: ${arg}`;
2078
2166
  return parsed;
@@ -2115,6 +2203,15 @@ function applyValueFlag(options, flag, value) {
2115
2203
  case "--categories":
2116
2204
  options.categories = value.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
2117
2205
  break;
2206
+ case "--source-repo-url":
2207
+ options.sourceRepoUrl = value;
2208
+ break;
2209
+ case "--source-author":
2210
+ options.sourceAuthor = value;
2211
+ break;
2212
+ case "--license":
2213
+ options.license = value;
2214
+ break;
2118
2215
  case "--timeout": {
2119
2216
  const n = Number(value);
2120
2217
  if (!Number.isFinite(n) || n <= 0) {
@@ -2180,6 +2277,9 @@ async function main() {
2180
2277
  case "publish":
2181
2278
  await runPublish(parsed.options);
2182
2279
  break;
2280
+ case "explore":
2281
+ await runExplore({ ...parsed.options, query: parsed.options.name });
2282
+ break;
2183
2283
  default:
2184
2284
  log.error(`Unknown command: ${parsed.command}`);
2185
2285
  log.plain(`Run ${c.cyan("genex --help")} for usage.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
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": {
@@ -32,25 +32,33 @@ lives under `public/` so Vite ships it in the build (load it as `./assets/models
32
32
  the `public/` prefix is stripped when served). It is **committed by `npx genex publish`**,
33
33
  so it ships inside your published game.
34
34
 
35
- ## Hero models must face forward
36
-
37
- For the model the player controls (a ship, car, character — the "hero"), the mesh should
38
- **face forward**: its front pointing down the direction of travel (conventionally `+Z`,
39
- i.e. "looking forward"). A hero whose nose points sideways reads as broken at a glance.
40
-
41
- - **In the prompt:** when it's the player's craft, ask for it facing forward — e.g.
42
- `"...game-ready, facing forward, front toward +Z"`.
43
- - **On load (fallback):** if the imported GLB still isn't forward, rotate it once so its
44
- nose aligns with travel. Wrap the model in a parent `Object3D` and apply the correction
45
- to the child, so your movement code can rotate the parent cleanly:
35
+ ## Models that point somewhere must face forward
36
+
37
+ Any model with a "front" the game aims — the hero the player controls, an **NPC that
38
+ walks toward or looks at players**, a turret, a vehicle should face **forward**: its
39
+ front toward `+Z` (glTF's "looking forward"), so yaw/`lookAt` code points it correctly.
40
+ A hero whose nose points sideways — or an enemy that chases you while staring off to the
41
+ side reads as broken at a glance.
42
+
43
+ - **In the prompt:** ask for it e.g. `"...game-ready, facing forward, front toward +Z"`.
44
+ Treat this as a hint, not a guarantee: generated meshes regularly come back rotated
45
+ anyway, so the check below is never optional.
46
+ - **On load (always check — hero AND NPC):** if the GLB isn't `+Z`-forward, rotate it
47
+ once. Wrap the model in a parent `Object3D` and put the correction on the child, so
48
+ game code rotates the parent cleanly:
46
49
  ```ts
47
- const hero = new THREE.Object3D();
50
+ const rig = new THREE.Object3D();
48
51
  gltf.scene.rotation.y = Math.PI / 2; // one-time facing correction — tune per model
49
- hero.add(gltf.scene);
50
- scene.add(hero); // move/rotate `hero`, not gltf.scene
52
+ rig.add(gltf.scene);
53
+ scene.add(rig); // move/rotate/lookAt `rig`, not gltf.scene
51
54
  ```
52
- - Orientation **is** visible in a still confirm the hero faces forward in your
53
- self-check screenshot before moving on.
55
+ Clones inherit the fix only if you clone the corrected child (or the whole rig) —
56
+ never the raw `gltf.scene`.
57
+ - **Verify it, don't assume it.** Orientation **is** visible in a still — in your
58
+ self-check screenshot confirm the hero faces its travel direction AND that NPCs driven
59
+ by chase/aim code face their target (an enemy rotated 90° from its victim is this
60
+ pipeline's most common visible bug). If you can't capture real gameplay (a draft's
61
+ sign-in gate is up), say so plainly instead of skipping the check silently.
54
62
 
55
63
  ## Load it into the scene
56
64
 
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: genex-explore
3
+ description: Search the curated Genex community gallery with `npx genex explore` before hand-writing hard visual or physics systems. Use when the game needs terrain, grass, water, vehicles, buildings, dungeons, flocking/boids, or advanced shader work — proven open-source Three.js implementations you can clone as a starting point or borrow parts from, credits included.
4
+ ---
5
+
6
+ # Genex Explore — proven building blocks from the community gallery
7
+
8
+ Some systems are notoriously hard to get right from scratch: believable grass,
9
+ vehicle physics, procedural buildings and dungeons, fish/boid flocking, water,
10
+ advanced shader effects. The curated community gallery holds **faithful ports of
11
+ proven open-source Three.js projects** — playable, cloneable, and licensed for
12
+ reuse. Search it before hand-writing one of those systems.
13
+
14
+ ## When to use
15
+
16
+ Before building a hard visual/physics system by hand, run:
17
+
18
+ ```bash
19
+ npx genex explore "<what you need>"
20
+ ```
21
+
22
+ Examples: `npx genex explore "grass"`, `npx genex explore "vehicle physics"`,
23
+ `npx genex explore "procedural building"`. No sign-in needed — this works even
24
+ before `genex init`.
25
+
26
+ Each result prints everything you need to act on it:
27
+
28
+ - `play:` — open it in the browser to judge whether it fits.
29
+ - `clone:` — the public repo with **editable source on `main`**.
30
+ - `upstream:` — the original source repo, with its author and license.
31
+
32
+ Add `--json` for machine-readable output.
33
+
34
+ ## How to integrate a result
35
+
36
+ **As a NEW game (start from the whole project):**
37
+
38
+ 1. `git clone <clone URL from the output> <name>` — pick a short one-word name.
39
+ 2. `cd <name>`, then `npx @genex-ai/cli-demo@latest init <name>` — choose
40
+ "Ignore files and continue" if it warns the folder isn't empty; never use
41
+ `--force`. This creates your own project; the original is untouched.
42
+ 3. `npm install`, keep `base: './'` in `vite.config`, then build your changes
43
+ and ship with `npx genex preview`.
44
+
45
+ **To BORROW parts into the current project:**
46
+
47
+ 1. Clone the result somewhere temporary, separate from your project
48
+ (e.g. `/tmp/genex-explore-src`).
49
+ 2. Study how it implements the part you need, then bring **just that** over —
50
+ real code and assets, adapted to your file paths. Don't wholesale-overwrite
51
+ your game.
52
+ 3. Reused assets must live under `public/assets/` to ship. New assets are
53
+ generated as usual with `$genex-ai-model`, `$genex-ai-skybox`,
54
+ `$genex-ai-sfx`, or `$genex-ai-texture`.
55
+
56
+ ## Credits rule (non-negotiable)
57
+
58
+ Every curated result carries an upstream link, author, and license — that
59
+ credit is part of the deal that makes these projects reusable. Whatever you
60
+ build from one:
61
+
62
+ - keep the attribution block in the README (upstream source repo URL, author,
63
+ license) exactly as the port carries it;
64
+ - keep any in-game credits screens or notices in place;
65
+ - carry the credit into anything you publish or remix from it.
66
+
67
+ If nothing matches your query, try a broader term (terrain, grass, vehicle,
68
+ water, building, dungeon, fish, shader) — or build it with the regular
69
+ `genex-threejs-*` skills instead.
@@ -33,6 +33,18 @@ Read [references/camera-rigs.md](references/camera-rigs.md)
33
33
  for exact chase/side/orbit rigs, projection values, transition
34
34
  rules, floating-origin shot, pointer controls, and implementation limits.
35
35
 
36
+ ## Aiming and pointer lock
37
+
38
+ Camera-aimed action — a third-person shooter reticle, first-person look —
39
+ wants **pointer lock**, not drag-orbit: request it on canvas click
40
+ (`canvas.requestPointerLock()`), drive yaw/pitch from `mousemove` deltas while
41
+ locked, and treat lock loss (Esc) as aim-paused — show a small "click to aim"
42
+ hint whenever unlocked. Keep drag-orbit for non-combat cameras (exploration,
43
+ building, spectating). Pointer lock works everywhere a Genex game runs:
44
+ standalone the game is the top-level page, and the platform's game frame
45
+ already grants the pointer-lock permission — no setup needed. Re-sync
46
+ yaw/pitch from the camera whenever lock is acquired (rule below).
47
+
36
48
  ## Non-negotiable rules
37
49
 
38
50
  - Use subject dimensions to derive offsets; do not tune one fixed distance for
@@ -260,6 +260,24 @@ instead. Don't code around any of this: no `?`/`#` URL params of yours will
260
260
  be affected, and `isEmbedded()` / the return-trip handling are internal SDK
261
261
  concerns.
262
262
 
263
+ ### Validating a draft (read before self-testing)
264
+
265
+ An unpublished draft shows the sign-in gate to any browser that isn't signed
266
+ in as the owner — **including your own test browser** (Playwright, headless
267
+ Chrome). The game still boots behind the overlay: console logs, DOM snapshots,
268
+ and key events all work — but every screenshot shows the gate, not the game,
269
+ and a gate capture is NOT visual evidence.
270
+
271
+ - Validate what the gate can't hide: a clean console, the canvas booting, the
272
+ HUD present in a DOM snapshot, controls registering.
273
+ - Do NOT work around the gate: don't dig through the SDK's internals for
274
+ undocumented URL fragments, and don't drive the user's own signed-in
275
+ browser.
276
+ - For the visual pass on a draft, hand it off plainly: "open your draft page
277
+ and tell me what you see" — the owner's view is the real check. Once the
278
+ game is **published**, any fresh browser gets in as a guest, so your own
279
+ test browser works again for full visual validation.
280
+
263
281
  ## Checklist
264
282
 
265
283
  - [ ] `initGameSentry({ slug: GENEX.slug })` is the very first call in `main.ts`.
@@ -33,6 +33,10 @@ no matter how good it looks.
33
33
  itself, or apply them only to hand-rolled movement.)
34
34
  - If an action can't fire (cooldown, no ammo), say so instantly — a click, a
35
35
  dimmed icon — silence reads as broken input.
36
+ - Camera-aimed shooting wants **pointer lock** (click to aim, Esc releases —
37
+ `$genex-threejs-camera-direction` has the rig rules); firing at a reticle
38
+ with an unlocked drag-to-turn camera feels imprecise no matter how tight
39
+ the numbers are.
36
40
 
37
41
  ## Movement: snappy beats realistic
38
42
 
@@ -32,7 +32,15 @@ this is the whole acceptance gate, and it is also the minimum for every game:
32
32
  2. Press each documented control once (keys, pointer); assert a **visible
33
33
  response** to every one — the player moves, the camera turns, the button
34
34
  fires.
35
- 3. Capture one screenshot of live gameplay.
35
+ 3. Capture one screenshot of live gameplay — of the **game**, not a sign-in
36
+ gate or loading screen. A capture of the SDK's "Sign in to play" overlay is
37
+ NOT gameplay evidence; if a draft's gate blocks the view, say so plainly
38
+ (see the embed-auth skill's "Validating a draft" note) instead of passing
39
+ the capture off as validation.
40
+ 4. In that screenshot, check oriented models: the hero faces its travel
41
+ direction, and NPCs driven by chase/aim code face their target. A model
42
+ rotated 90° reads as broken — `$genex-ai-model` has the one-time facing
43
+ fix.
36
44
 
37
45
  Everything deeper (baselines, seed sweeps, mosaics, budgets) belongs to
38
46
  visual-system work — the sequence above.