@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 +101 -1
- package/package.json +1 -1
- package/templates/skills/genex-ai-model/SKILL.md +24 -16
- package/templates/skills/genex-explore/SKILL.md +69 -0
- package/templates/skills/genex-threejs-camera-direction/SKILL.md +12 -0
- package/templates/skills/genex-threejs-embed-auth/SKILL.md +18 -0
- package/templates/skills/genex-threejs-game-feel/SKILL.md +4 -0
- package/templates/skills/genex-threejs-visual-validation/SKILL.md +9 -1
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
|
@@ -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
|
-
##
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- **
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
50
|
+
const rig = new THREE.Object3D();
|
|
48
51
|
gltf.scene.rotation.y = Math.PI / 2; // one-time facing correction — tune per model
|
|
49
|
-
|
|
50
|
-
scene.add(
|
|
52
|
+
rig.add(gltf.scene);
|
|
53
|
+
scene.add(rig); // move/rotate/lookAt `rig`, not gltf.scene
|
|
51
54
|
```
|
|
52
|
-
|
|
53
|
-
|
|
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.
|