@genex-ai/cli-demo 0.59.0-dev.133 → 0.59.0-dev.136

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
@@ -72,16 +72,20 @@ function resolveAgentTargets(opts = {}) {
72
72
  return [{ id: "custom", label: "workspace", baseDir: path.resolve(opts.dir), full: true }];
73
73
  }
74
74
  const home = os.homedir();
75
+ const projectRoot = process.cwd();
75
76
  let ids;
76
77
  if (opts.agents && opts.agents.length > 0) {
77
78
  ids = opts.agents.filter((id) => KNOWN_AGENTS[id]);
78
79
  } else {
79
- ids = KNOWN_AGENT_IDS.filter((id) => isDir(path.join(home, KNOWN_AGENTS[id].dirName)));
80
+ ids = KNOWN_AGENT_IDS.filter((id) => {
81
+ const dirName = KNOWN_AGENTS[id].dirName;
82
+ return isDir(path.join(home, dirName)) || isDir(path.join(projectRoot, dirName));
83
+ });
80
84
  if (ids.length === 0) ids = ["claude"];
81
85
  }
82
86
  return ids.map((id) => {
83
87
  const def = KNOWN_AGENTS[id];
84
- return { id, label: def.label, baseDir: path.join(home, def.dirName), full: def.full };
88
+ return { id, label: def.label, baseDir: path.join(projectRoot, def.dirName), full: def.full };
85
89
  });
86
90
  }
87
91
  function isDir(p) {
@@ -242,6 +246,7 @@ async function exists(p) {
242
246
 
243
247
  // src/lib/updates.ts
244
248
  import fs3 from "fs/promises";
249
+ import os3 from "os";
245
250
  import path3 from "path";
246
251
  function parseSemver(v) {
247
252
  const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v.trim());
@@ -292,8 +297,53 @@ async function syncSkillsForTarget(target, templatesDir = getTemplatesDir(), ver
292
297
  await writeSkillsMarker(skillsDir, version);
293
298
  return true;
294
299
  }
300
+ async function cleanupLegacyGlobalSkills(log) {
301
+ try {
302
+ const home = os3.homedir();
303
+ const [realCwd, realHome] = await Promise.all([
304
+ fs3.realpath(process.cwd()).catch(() => path3.resolve(process.cwd())),
305
+ fs3.realpath(home).catch(() => path3.resolve(home))
306
+ ]);
307
+ if (realCwd === realHome) return false;
308
+ let removed = false;
309
+ for (const dirName of [".claude", ".codex", ".cursor"]) {
310
+ const skillsDir = path3.join(home, dirName, "skills");
311
+ let entries = [];
312
+ try {
313
+ entries = await fs3.readdir(skillsDir);
314
+ } catch {
315
+ continue;
316
+ }
317
+ for (const name of entries) {
318
+ if (!name.startsWith("genex-")) continue;
319
+ try {
320
+ await fs3.rm(path3.join(skillsDir, name), { recursive: true });
321
+ removed = true;
322
+ } catch {
323
+ }
324
+ }
325
+ }
326
+ for (const rel of [
327
+ path3.join("agents", "genex-helper.md"),
328
+ path3.join("commands", "genex-status.md")
329
+ ]) {
330
+ try {
331
+ await fs3.rm(path3.join(home, ".claude", rel));
332
+ removed = true;
333
+ } catch {
334
+ }
335
+ }
336
+ if (removed) {
337
+ log?.plain("\u{1F9F9} Removed legacy GLOBAL Genex skills \u2014 installs are project-local now");
338
+ }
339
+ return removed;
340
+ } catch {
341
+ return false;
342
+ }
343
+ }
295
344
  async function syncSkills(log) {
296
345
  try {
346
+ await cleanupLegacyGlobalSkills(log);
297
347
  const version = getCliVersion();
298
348
  const templatesDir = getTemplatesDir();
299
349
  let refreshed = false;
@@ -1123,6 +1173,7 @@ async function runInit(opts) {
1123
1173
  const log = createLogger({ quiet: opts.quiet });
1124
1174
  log.plain(c.bold("genex init"));
1125
1175
  log.plain("");
1176
+ await cleanupLegacyGlobalSkills(log);
1126
1177
  const templatesDir = getTemplatesDir();
1127
1178
  const targets = resolveAgentTargets({ dir: opts.dir, agents: opts.agents });
1128
1179
  log.step(
@@ -1439,7 +1490,7 @@ function relTime(iso) {
1439
1490
  import { spawn as spawn3 } from "child_process";
1440
1491
  import crypto3 from "crypto";
1441
1492
  import fs9 from "fs/promises";
1442
- import os3 from "os";
1493
+ import os4 from "os";
1443
1494
  import path10 from "path";
1444
1495
  function run(cmd, args, env) {
1445
1496
  return new Promise((resolve) => {
@@ -1666,7 +1717,7 @@ async function pushWorktree(cwd, pushUrl, managed, log) {
1666
1717
  log.error("Couldn't save your game's source \u2014 please try again.");
1667
1718
  return false;
1668
1719
  };
1669
- const gitDir = await fs9.mkdtemp(path10.join(os3.tmpdir(), "genex-source-"));
1720
+ const gitDir = await fs9.mkdtemp(path10.join(os4.tmpdir(), "genex-source-"));
1670
1721
  const base = { GIT_DIR: gitDir };
1671
1722
  const ident = {
1672
1723
  GIT_AUTHOR_NAME: "genex",
@@ -13545,7 +13596,7 @@ async function uiSeams(opts, log) {
13545
13596
 
13546
13597
  // src/index.ts
13547
13598
  var GEN_KINDS = /* @__PURE__ */ new Set(["model", "skybox", "sfx", "texture", "image", "video"]);
13548
- var HELP = `${c.bold("genex")} \u2014 set up your ~/.claude workspace, authorize, and publish 3D games.
13599
+ var HELP = `${c.bold("genex")} \u2014 set up your project's agent workspace, authorize, and publish 3D games.
13549
13600
 
13550
13601
  ${c.bold("Usage")}
13551
13602
  genex init [<name>] [options] Scaffold + authorize + create the draft project.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.59.0-dev.133",
4
- "description": "Set up your ~/.claude workspace, authorize, create a game project, generate AI assets, and publish (genex CLI).",
3
+ "version": "0.59.0-dev.136",
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": {
7
7
  "genex": "./dist/index.js"
@@ -1,9 +1,10 @@
1
1
  # Genex workspace
2
2
 
3
- These files were installed by `genex init` into your coding agent's workspace —
4
- Claude Code (`~/.claude`), Codex (`~/.codex/skills`), and Cursor
5
- (`~/.cursor/skills`), whichever it detected. They give your agent superpowers
6
- for making 3D games in the browser.
3
+ These files were installed by `genex init` into THIS project's agent
4
+ workspace — Claude Code (`.claude/`), Codex (`.codex/skills/`), and Cursor
5
+ (`.cursor/skills/`) inside the game folder, whichever agents it detected.
6
+ Nothing is installed globally; delete the folder and every trace is gone.
7
+ They give your agent superpowers for making 3D games in the browser.
7
8
 
8
9
  Genex is built around agent superpowers for browser games: Three.js skills,
9
10
  one-click publishing, multiplayer-ready architecture, and team workflows.
@@ -6,8 +6,9 @@ tools: Read, Grep, Glob
6
6
 
7
7
  You are the Genex helper agent — an example subagent installed by `genex init`.
8
8
 
9
- Your job is to help the user understand and navigate their `~/.claude`
10
- workspace: the skills, agents, and commands installed by Genex.
9
+ Your job is to help the user understand and navigate this project's `.claude`
10
+ workspace: the skills, agents, and commands Genex installed into the game
11
+ folder (Genex never installs agent files globally).
11
12
 
12
13
  Guidelines:
13
14
  - Be concise and concrete. Point at real files with paths.
@@ -4,8 +4,8 @@ description: Show the current Genex setup — workspace location, installed skil
4
4
 
5
5
  Report the user's Genex status:
6
6
 
7
- 1. Confirm the workspace directory (`~/.claude`) exists and list the
8
- `skills/`, `agents/`, and `commands/` it contains.
7
+ 1. Confirm the project's workspace directory (`.claude/` in the game folder)
8
+ exists and list the `skills/`, `agents/`, and `commands/` it contains.
9
9
  2. Check whether a `GENEX_TOKEN` is present in the project's `.env` (do not
10
10
  print the token value — only whether it is set).
11
11
  3. If anything looks missing, suggest running `npx genex init`.
@@ -11,11 +11,16 @@ architecture, and team-ready workflows.
11
11
 
12
12
  ## What got installed
13
13
 
14
- `genex init` installs the Genex skills into **every coding agent it detects**
15
- Claude Code (`~/.claude`), Codex (`~/.codex`), and Cursor (`~/.cursor`) — so the
16
- same skills are available whichever agent you build with. After that, every
14
+ `genex init` installs the Genex skills **into this project's folder** for
15
+ every coding agent it detects — Claude Code (`.claude/`), Codex (`.codex/`),
16
+ and Cursor (`.cursor/`) — so the same skills are available whichever agent you
17
+ build with, and nothing is ever installed globally on the machine (only your
18
+ auth token lives outside the project, in `~/.genex/env`). After that, every
17
19
  `genex` command keeps the genex-owned skills in sync with the installed CLI
18
20
  automatically (re-running `init` does too); your own files are never touched.
21
+ If an older Genex version ever installed skills globally into `~/.claude`,
22
+ `~/.codex` or `~/.cursor`, any `genex` command now sweeps those legacy copies
23
+ out automatically — only `genex-*`-named files are removed, never yours.
19
24
 
20
25
  - **skills/** - reusable Genex skills for 3D browser-game work (all agents).
21
26
  - **agents/** - example subagent definitions (Claude Code).
@@ -23,8 +28,8 @@ automatically (re-running `init` does too); your own files are never touched.
23
28
 
24
29
  Start with `$genex-threejs-skill-router` for broad game or graphics requests.
25
30
  It routes the agent to focused skills for cameras, procedural geometry,
26
- materials, atmosphere, water, weather, VFX, post-processing, and visual
27
- validation.
31
+ materials, atmosphere, water, weather, VFX, lighting, post-processing, and
32
+ visual validation.
28
33
 
29
34
  ## Generating real assets
30
35
 
@@ -0,0 +1,125 @@
1
+ ---
2
+ name: genex-threejs-lighting-design
3
+ description: Light Genex Three.js games from visible causes, and decide WHERE light belongs — walk the scene's sources one by one, the way the visual-direction gate walks surfaces and the moment gate walks moments. Use for light rigs, sun/moon key lights, practical lights (campfire, torch, neon, lava), emissive-to-light coupling, light shafts and visible beams, fog mood, gameplay light signals, flicker, light budgets, and scenes that read flat or uniformly lit.
4
+ ---
5
+
6
+ # Genex Three.js Lighting Design
7
+
8
+ Light from causes — every light in the frame is the visible consequence of
9
+ something the player can point at. Avoid the uniform ambient wash that lights
10
+ a cave like an office.
11
+
12
+ ## The source gate — run this BEFORE writing any lighting code
13
+
14
+ The visual-direction gate walks **surfaces**; the moment gate walks
15
+ **moments**. Light lives on neither — it lives on **sources**: what the sky
16
+ pours in and what the fiction burns, glows, or screens. Walk the sources the
17
+ same way, and decide each one.
18
+
19
+ List the sources the scene contract already names — never invent them. Three
20
+ places to look:
21
+
22
+ - **the sky** — day, night, overcast, underground, deep space? That one answer
23
+ sets the rig: a warm sun key with cool sky fill, a dim hard moon, or no sky
24
+ at all — a cave is keyed by its practicals;
25
+ - **the fiction's emitters** — campfire, torch, lava, neon sign, a monitor,
26
+ headlights, a portal, a crack of daylight in the roof;
27
+ - **the gameplay signals** — the objective beacon, the telegraph, the
28
+ checkpoint. Light is information the player navigates by, and it is the
29
+ cheapest wayfinding you own.
30
+
31
+ When gameplay needs light where the fiction names no source, add the cause
32
+ first — a lamp prop, embers, a fissure — and then light it; the light alone is
33
+ still a bug. Then judge each source:
34
+
35
+ > **Does it change what the player sees AROUND it — on the ground, the walls,
36
+ > the player — or does it only need to be seen itself?**
37
+
38
+ Four verdicts, all legitimate, one forbidden:
39
+
40
+ - **Seen itself** — an emissive material, no light object. Most sources land
41
+ here, and that is what keeps the frame cheap; how much it glows is
42
+ `$genex-threejs-bloom`'s question.
43
+ - **Lights its surroundings** — a real `PointLight` or `SpotLight`, coupled to
44
+ its emitter (below). A campfire that doesn't paint the ground orange is a
45
+ prop, not a fire.
46
+ - **Shapes the whole frame** — the key: a sun or moon `DirectionalLight`, the
47
+ arena floods. One or two per scene, and they own the shadows.
48
+ - **The air itself is lit** — the beam is the point: the cave crack, a dusty
49
+ window shaft, canopy rays. Build the shaft as geometry first — fog and
50
+ volumetric passes are scene-wide decisions, never per-beam tools (recipes
51
+ in the reference).
52
+ - **Not deciding — the only wrong answer.** One white `AmbientLight` over
53
+ everything is not a rig; it is the unlit look with extra steps, and it is
54
+ why shipped scenes read the same at noon and at midnight.
55
+
56
+ "The neon is emissive-only — it doesn't reach the street" is a real answer;
57
+ say it in one line and move on. Then check the inverse: **a light with no
58
+ visible cause reads as a bug**, not mood — the player asks why the floor
59
+ glows. And when the walk returns more real lights than the scene can afford,
60
+ demote the dimmest back to emissive-only: dynamic lights are the scarcest
61
+ resource in the frame, and the eye forgives an unlit distant torch far sooner
62
+ than a dropped frame.
63
+
64
+ ## A practical is coupled, not placed
65
+
66
+ The light a source throws and the mesh that emits it are one thing. Drive both
67
+ from one envelope — separate flickers, or a light hovering near an unlit prop,
68
+ read instantly as fake:
69
+
70
+ ```ts
71
+ // Values assume a locked renderer baseline — relationships, not mandates.
72
+ const fire = new THREE.PointLight(0xff9142, 14, 18, 2); // range-capped, physical falloff
73
+ fire.position.set(0, 0.9, 0); // inside the flame, above the fuel
74
+ campfire.add(fire);
75
+ const flameMat = flame.material as THREE.MeshStandardMaterial;
76
+ flameMat.emissive.set(0xff7a1e);
77
+
78
+ function flicker(t: number): number {
79
+ // two incommensurate sines — coherent, frame-rate independent
80
+ return 0.82 + 0.12 * Math.sin(t * 11.3) + 0.06 * Math.sin(t * 23.7 + 1.7);
81
+ }
82
+
83
+ function updateFire(elapsed: number): void {
84
+ const e = flicker(elapsed);
85
+ fire.intensity = 14 * e;
86
+ flameMat.emissiveIntensity = 2.4 * e; // mesh and light breathe together
87
+ fire.position.x = 0.05 * Math.sin(elapsed * 7.1); // the pool sways with the flame
88
+ }
89
+ ```
90
+
91
+ The same couple serves a neon sign that hums, a monitor whose picture spills
92
+ onto the desk, a muzzle flash (one envelope drives light, emissive, and the
93
+ sound cue). And when `npx genex skybox` has set `scene.environment`, that
94
+ image IS the ambient fill — do not stack an `AmbientLight` on top of it.
95
+
96
+ Read [references/light-recipes.md](references/light-recipes.md) for rig
97
+ baselines per environment (day, night, interior, cave, space), the shaft and
98
+ fog recipes, light-as-information patterns, budget relationships, and the
99
+ kill-switch diagnostic.
100
+
101
+ ## Rules
102
+
103
+ - Every light names its cause; a light nothing explains is a bug, not mood.
104
+ - Couple a practical to its emitter: one color, one envelope, one on/off state.
105
+ - The key owns shadows; practicals cast none until a shot proves they must.
106
+ - Tune intensities only after the renderer baseline is locked — retuning the
107
+ whole rig after a tone-mapping change is self-inflicted.
108
+ - Never repair unbalanced light ratios with exposure — fix the lights.
109
+ - Flicker from elapsed time, never per-frame randomness.
110
+ - Dispose lights and their shadow maps with the level that spawned them.
111
+ - The rig must read with post off: time of day and where-to-go, before bloom.
112
+
113
+ ## Routing boundary
114
+
115
+ Tone mapping, exposure, and grading are
116
+ `$genex-threejs-exposure-color-grading`. How much an emissive glows is
117
+ `$genex-threejs-bloom`; whether it also illuminates is this skill. Authoring
118
+ the emissive surface itself — lava, hot rock, a screen's picture — is
119
+ `$genex-threejs-procedural-materials`. `$genex-threejs-shadow-systems` owns
120
+ large roaming-world shadows (cascades, clipmaps) — a bounded scene's one
121
+ shadow-casting key with a tight frustum lives here. Sky scattering, haze, and the sun disc are
122
+ `$genex-threejs-atmosphere-aerial-perspective`; shafts inside clouds are
123
+ `$genex-threejs-volumetric-clouds`; ordering a volumetric pass among other
124
+ post effects is `$genex-threejs-image-pipeline`; the environment map that
125
+ feeds the fill is `$genex-ai-skybox`.
@@ -0,0 +1,137 @@
1
+ # Light Recipes
2
+
3
+ Contents: rig baselines by environment, the practical couple, shafts and
4
+ visible air, light as information, budgets and lifecycle, the kill-switch
5
+ diagnostic.
6
+
7
+ Numbers below are relationships, not mandates — validate them against the
8
+ project's renderer baseline (`$genex-threejs-exposure-color-grading`), because
9
+ tone mapping and exposure change what every intensity means.
10
+
11
+ ## Rig baselines by environment
12
+
13
+ - **Outdoor day** — a warm sun `DirectionalLight` key plus cool sky fill
14
+ (`HemisphereLight`, or the skybox IBL when one is set). Fill sits well below
15
+ the key: when fill approaches key strength, shadow shapes die and the scene
16
+ flattens. The sun owns shadows: size the shadow camera's ortho bounds to the
17
+ playfield edges, fix acne at contact points with `bias`/`normalBias`, and
18
+ bump `mapSize` before loosening the frustum. A roaming world (racer, open
19
+ map) outgrows one frustum — that cascade question is
20
+ `$genex-threejs-shadow-systems`.
21
+ - **Outdoor night** — the moon is a dim, cool, hard key, not a gray day.
22
+ Practicals carry visibility; the moon's job is silhouette and geography.
23
+ Push the fiction's emitters up the ladder before brightening the moon. No
24
+ moon (a city street)? The settlement's own skyglow is the fill — a dim,
25
+ warm-tinted hemisphere — and the emitters carry the rest.
26
+ - **Interior** — the openings and fixtures ARE the rig: a window is a
27
+ `SpotLight` aimed the way the sun outside would aim (or a shaft, below);
28
+ fixtures are coupled practicals. IBL still fills, well below the windows.
29
+ When the hard sun-rectangle on the floor IS the shot (a big window, a long
30
+ room), swap the spot for a shadow-casting `DirectionalLight` through the
31
+ opening — parallel light keeps the patch's edges straight where a spot's
32
+ cone diverges.
33
+ - **Cave / underground** — no sky, no free fill. The brightest practical or
34
+ the crack shaft is the key; darkness is part of the palette, and the
35
+ player's own torch is a gameplay object. Ration the black, don't erase it.
36
+ - **Space** — one hard star key, black fill, bounce only from IBL or
37
+ planetshine. The harsh terminator IS the look; softening it reads as a
38
+ studio shoot.
39
+
40
+ ## The practical couple
41
+
42
+ - Contain the light: set `distance` so the falloff dies inside the space the
43
+ source serves, and keep `decay` at the physical `2`. An uncontained point
44
+ light climbs walls three rooms away, and the scene creeps toward flat.
45
+ - Position the light inside the emitter, slightly above the visible flame or
46
+ tube — the pool it throws must sit centered under the thing that explains it.
47
+ - An area emitter — neon tube, screen, softbox — is by default a tinted
48
+ `PointLight` or `SpotLight` sunk into it. Reach for `RectAreaLight` only
49
+ when the rectangular wash on a nearby wall IS the shot, and know its terms:
50
+ `RectAreaLightUniformsLib.init()` first, no shadows, standard/physical
51
+ materials only.
52
+ - When the player roams, the budget follows them — but never add or remove a
53
+ light mid-play: changing the light count recompiles every shader that sees
54
+ it, a visible hitch. Allocate a small fixed pool up front, reassign members'
55
+ position and color to the nearest sources, and retire one by driving its
56
+ intensity to 0.
57
+
58
+ ## Shafts and visible air
59
+
60
+ The default shaft is geometry, not a render pass — an open cone or tapered
61
+ cylinder from the aperture, additive, fading along its length:
62
+
63
+ ```ts
64
+ function beamGradient(): THREE.Texture {
65
+ const c = document.createElement("canvas");
66
+ c.width = 1; c.height = 64;
67
+ const ctx = c.getContext("2d")!;
68
+ const g = ctx.createLinearGradient(0, 0, 0, 64);
69
+ g.addColorStop(0, "rgba(255,255,255,0.9)");
70
+ g.addColorStop(1, "rgba(255,255,255,0)");
71
+ ctx.fillStyle = g;
72
+ ctx.fillRect(0, 0, 1, 64);
73
+ const t = new THREE.Texture(c);
74
+ t.needsUpdate = true;
75
+ return t;
76
+ }
77
+
78
+ const shaft = new THREE.Mesh(
79
+ new THREE.CylinderGeometry(0.25, 1.6, 7, 24, 1, true), // narrow at the crack, wide at the floor
80
+ new THREE.MeshBasicMaterial({
81
+ map: beamGradient(), // flip the gradient if the bright end lands on the floor
82
+ transparent: true,
83
+ opacity: 0.35,
84
+ blending: THREE.AdditiveBlending,
85
+ depthWrite: false,
86
+ side: THREE.DoubleSide,
87
+ }),
88
+ );
89
+ ```
90
+
91
+ - Pair the shaft with a real `SpotLight` from the same aperture: the bright
92
+ pool at its foot is what sells the beam. A shaft with nothing at its foot
93
+ reads as a hologram.
94
+ - If the scene earns its one ambient-particle layer (the moment gate's
95
+ budget), inside the beam is where it pays — slow dust motes, the
96
+ `$genex-threejs-procedural-vfx` dot-texture recipe.
97
+ - `scene.fog` (or `FogExp2`) is a scene-wide mood decision: every light pool
98
+ becomes a cone and contrast falls everywhere — choose it for the whole look.
99
+ - A full-screen shaft hanging off the sun disc rides the atmosphere pass
100
+ (`$genex-threejs-atmosphere-aerial-perspective`); this skill's shafts are
101
+ per-aperture beams built as geometry. A real raymarched volumetric pass is
102
+ for scenes whose identity is shafts.
103
+
104
+ ## Light as information
105
+
106
+ - Reserve a signal hue: pickups and objectives get a color no environment
107
+ light uses, and it never changes meaning mid-game.
108
+ - Aim the player with brightness before UI: the lit doorway beats the arrow.
109
+ The eye lands on the highest contrast in frame — put it where the player
110
+ should go.
111
+ - Telegraph danger with the light's shape — a red cone where the boss will
112
+ sweep, a pulsing ring under the falling crate — timed to the same event
113
+ feedback the moment gate placed.
114
+ - A vertical beacon (shaft recipe above) reads across the whole map and over
115
+ occluding walls — the map's own "you are here".
116
+
117
+ ## Budgets and lifecycle
118
+
119
+ - The whole rig of most shipped scenes: one shadow-casting key, IBL or
120
+ hemisphere fill, and a handful of coupled practicals. Each real light
121
+ beyond that carries a stated reason.
122
+ - A shadow-casting point light renders the scene six more times; a
123
+ shadow-casting spot, once. Prefer the spot — and prefer no shadow at all on
124
+ a flickering source, because the moving pool already sells it.
125
+ - `scene.remove(light)` does not free it: call `light.dispose()` when the
126
+ level unloads, and the shadow map goes with it.
127
+
128
+ ## The kill-switch diagnostic
129
+
130
+ Wire a debug toggle per light, the same way the other systems expose debug
131
+ views. Turn each light off alone and name what died — "the campfire pool",
132
+ "the corridor signal" — watching the frame time as well as the frame: a light
133
+ whose absence changes nothing is dead weight to delete, and a light that costs
134
+ more milliseconds than the mood it adds is over budget. That observation is
135
+ the budget — there is no magic count. Then take two screenshots with post
136
+ off — the darkest playable corner and the brightest — and check both: if the
137
+ subject and the way forward read in each, the rig holds.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: genex-threejs-skill-router
3
- description: Route Genex 3D browser-game work to the smallest useful Three.js skill set. Use for new game scenes, graphics upgrades, visual reference matching, game-feel passes, render-pipeline work, publishing readiness, multiplayer-aware scene architecture, or requests spanning cameras, geometry, materials, atmosphere, water, VFX, shadows, post-processing, and validation.
3
+ description: Route Genex 3D browser-game work to the smallest useful Three.js skill set. Use for new game scenes, graphics upgrades, visual reference matching, game-feel passes, render-pipeline work, publishing readiness, multiplayer-aware scene architecture, or requests spanning cameras, geometry, materials, atmosphere, water, VFX, lighting, shadows, post-processing, and validation.
4
4
  ---
5
5
 
6
6
  # Genex Three.js Skill Router
@@ -36,6 +36,7 @@ map, execution order, and acceptance gate.
36
36
  | curved-ray black holes, accretion disks, wormholes | `$genex-threejs-raymarched-space-effects` |
37
37
  | particles, trails, plasma, shockwaves, layered event effects | `$genex-threejs-procedural-vfx` |
38
38
  | accumulated screen frost, touch clearing, reduced blur, and refraction masks | `$genex-threejs-temporal-surfaces` |
39
+ | the light rig and where light belongs: sun/moon key, practical lights (campfire, torch, neon, lava), emissive-to-light coupling, light shafts and visible beams, fog mood, light signals, flicker, a scene that reads flat or uniformly lit | `$genex-threejs-lighting-design` |
39
40
  | stable large-world shadows, cascades, clipmaps, cached updates | `$genex-threejs-shadow-systems` |
40
41
  | GTAO, bent normals, bilateral reconstruction | `$genex-threejs-screen-space-ambient-occlusion` |
41
42
  | HDR bloom and selective emission contribution | `$genex-threejs-bloom` |
@@ -94,7 +95,8 @@ it in the running game), one named
94
95
  **ambient-motion loop** that keeps
95
96
  the scene alive at rest (emissive pulse, shimmer, drifting dust — shader
96
97
  work, zero generations), and the lighting/atmosphere mood from that
97
- same brief. Planning is not building effects still land last in the
98
+ same brief walked source by source, every light with a visible cause
99
+ (`$genex-threejs-lighting-design` owns that gate). Planning is not building — effects still land last in the
98
100
  execution order.
99
101
 
100
102
  **Multiplayer is mandatory routing:** if the game has 2+ players sharing a world, loading
@@ -105,8 +105,18 @@ Three.js release or branch, and do not blindly copy demo architecture.
105
105
  texture goes on this box" but "what shape is this energy": a primitive
106
106
  standing in for an effect is a placeholder no matter how deliberate the
107
107
  comment says it is. `$genex-threejs-procedural-vfx` owns this gate;
108
- - **lighting/atmosphere mood** from the SAME shared style brief the UI gate
109
- wrote one art direction across scene and UI.
108
+ - **every light has a cause — walk the sources one by one**: the
109
+ lighting/atmosphere mood comes from the SAME shared style brief the UI
110
+ gate wrote — one art direction across scene and UI — but a mood is not a
111
+ rig. Walk the sources the contract already names: the sky (day, night,
112
+ underground — that one answer sets the key), the fiction's emitters
113
+ (campfire, torch, neon, a monitor, a crack of daylight in the roof), and
114
+ the gameplay signals (beacon, telegraph, checkpoint). For each: does it
115
+ change what the player sees around it, or only need to be seen itself?
116
+ Emissive-only is a real answer said in one line; a coupled practical
117
+ light is the step up; one white ambient wash over everything is the
118
+ unlit look with extra steps, and it reads the same at noon and at
119
+ midnight. `$genex-threejs-lighting-design` owns this gate;
110
120
  Planning is not building: effects still land LAST (steps 10–11); this step
111
121
  only fixes the target so the look isn't improvised pass-by-pass at the end.
112
122
  5. Wire the gameplay layer for the player verb: the physics world via
@@ -137,7 +147,9 @@ Three.js release or branch, and do not blindly copy demo architecture.
137
147
  8. Add procedural animation when object motion needs authored phases,
138
148
  convergence, looping, or deterministic timelines.
139
149
  9. Add shared fields before writing multiple independent noise layers.
140
- 10. Add lighting, atmosphere, and shadows only after the no-post baseline reads.
150
+ 10. Add lighting, atmosphere, and shadows only after the no-post baseline
151
+ reads: the step-4 source walk builds now — `$genex-threejs-lighting-design`
152
+ owns the rig.
141
153
  11. Add image-pipeline, bloom, exposure, grading, or AO last — building out the
142
154
  step-4 post plan, not inventing one now. This is a completion gate: the
143
155
  named post stack must be BUILT before the game is called done, published,