@jgengine/shell 0.18.0 → 0.19.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.
Files changed (148) hide show
  1. package/CHANGELOG.md +265 -2
  2. package/dist/GamePlayerShell.js +27 -4
  3. package/dist/JoinGate.d.ts +17 -0
  4. package/dist/JoinGate.js +14 -0
  5. package/dist/Shell3dPresentation.d.ts +9 -8
  6. package/dist/Shell3dPresentation.js +57 -8
  7. package/dist/ShellChrome.d.ts +2 -0
  8. package/dist/ShellChrome.js +6 -0
  9. package/dist/ShellHudPresentation.d.ts +9 -1
  10. package/dist/ShellHudPresentation.js +21 -0
  11. package/dist/TerritoryOverlay.d.ts +21 -0
  12. package/dist/TerritoryOverlay.js +9 -0
  13. package/dist/audio/AudioComponents.js +13 -2
  14. package/dist/audio/audioEngine.d.ts +15 -1
  15. package/dist/audio/audioEngine.js +132 -11
  16. package/dist/audio/audioWire.d.ts +3 -0
  17. package/dist/audio/audioWire.js +7 -1
  18. package/dist/audio/loopParams.d.ts +39 -0
  19. package/dist/audio/loopParams.js +43 -0
  20. package/dist/camera/GameCameraRig.js +1 -1
  21. package/dist/camera/GameFirstPersonCamera.d.ts +4 -2
  22. package/dist/camera/GameFirstPersonCamera.js +52 -15
  23. package/dist/camera/Viewports.d.ts +34 -0
  24. package/dist/camera/Viewports.js +123 -0
  25. package/dist/camera/cameraRigs.js +77 -62
  26. package/dist/camera/orbitCameraMath.js +2 -2
  27. package/dist/camera/rigMath.d.ts +82 -4
  28. package/dist/camera/rigMath.js +133 -15
  29. package/dist/defineGame.d.ts +9 -0
  30. package/dist/defineGame.js +38 -6
  31. package/dist/devtools/DevtoolsOverlay.js +2 -0
  32. package/dist/devtools/KeysPanel.js +3 -3
  33. package/dist/devtools/agentBridge.d.ts +6 -0
  34. package/dist/devtools/agentBridge.js +15 -1
  35. package/dist/diagnostics/FpsCounter.d.ts +17 -0
  36. package/dist/diagnostics/FpsCounter.js +32 -0
  37. package/dist/drivers/FrameDriver.d.ts +5 -1
  38. package/dist/drivers/FrameDriver.js +74 -26
  39. package/dist/drivers/FrameRateLimiter.d.ts +9 -0
  40. package/dist/drivers/FrameRateLimiter.js +39 -0
  41. package/dist/drivers/HudOnlyDriver.js +10 -5
  42. package/dist/environment/DayNightSky.js +5 -5
  43. package/dist/environment/Daylight.d.ts +35 -1
  44. package/dist/environment/Daylight.js +98 -28
  45. package/dist/environment/EnvironmentScene.js +1 -1
  46. package/dist/environment/VolumetricClouds.js +20 -2
  47. package/dist/environment/daylightCycle.d.ts +20 -0
  48. package/dist/environment/daylightCycle.js +59 -7
  49. package/dist/environment/sunShadowMath.d.ts +45 -0
  50. package/dist/environment/sunShadowMath.js +50 -0
  51. package/dist/input/gamepadMerge.d.ts +10 -0
  52. package/dist/input/gamepadMerge.js +12 -0
  53. package/dist/input/gamepadPoll.d.ts +47 -0
  54. package/dist/input/gamepadPoll.js +142 -0
  55. package/dist/input/gamepadSource.d.ts +22 -0
  56. package/dist/input/gamepadSource.js +153 -0
  57. package/dist/input/mouseLook.js +2 -1
  58. package/dist/input/padHaptics.d.ts +17 -0
  59. package/dist/input/padHaptics.js +32 -0
  60. package/dist/input/pointerLock.d.ts +11 -0
  61. package/dist/input/pointerLock.js +19 -0
  62. package/dist/materialOverride.d.ts +4 -1
  63. package/dist/materialOverride.js +10 -1
  64. package/dist/postfx/PostProcessing.d.ts +11 -8
  65. package/dist/postfx/PostProcessing.js +38 -16
  66. package/dist/postfx/gradeShader.js +13 -0
  67. package/dist/postfx/stylizePass.d.ts +23 -0
  68. package/dist/postfx/stylizePass.js +216 -0
  69. package/dist/render/CascadedShadows.d.ts +11 -0
  70. package/dist/render/CascadedShadows.js +19 -8
  71. package/dist/render/EnvironmentLighting.d.ts +4 -1
  72. package/dist/render/EnvironmentLighting.js +45 -9
  73. package/dist/render/SceneLighting.d.ts +16 -2
  74. package/dist/render/SceneLighting.js +56 -6
  75. package/dist/render/SceneModels.js +45 -9
  76. package/dist/render/SkinnedInstances.d.ts +91 -0
  77. package/dist/render/SkinnedInstances.js +249 -0
  78. package/dist/render/TextureFiltering.d.ts +16 -0
  79. package/dist/render/TextureFiltering.js +64 -0
  80. package/dist/render/displayColor.d.ts +33 -0
  81. package/dist/render/displayColor.js +138 -0
  82. package/dist/render/modelLoad.d.ts +39 -0
  83. package/dist/render/modelLoad.js +89 -1
  84. package/dist/render/modelRender.d.ts +3 -0
  85. package/dist/render/modelRender.js +6 -2
  86. package/dist/render/resolveModel.js +4 -0
  87. package/dist/render/useFootIk.d.ts +58 -0
  88. package/dist/render/useFootIk.js +216 -0
  89. package/dist/render/useModelAnimation.d.ts +26 -0
  90. package/dist/render/useModelAnimation.js +184 -107
  91. package/dist/scene/AuthoredScene.d.ts +9 -0
  92. package/dist/scene/AuthoredScene.js +17 -1
  93. package/dist/scene/CityRenderer.js +666 -241
  94. package/dist/scene/cityWindowMaterial.d.ts +8 -6
  95. package/dist/scene/cityWindowMaterial.js +80 -21
  96. package/dist/settings/QuickControls.js +1 -1
  97. package/dist/settings/SettingsMenu.js +1 -1
  98. package/dist/settings/appliedSettings.d.ts +10 -2
  99. package/dist/settings/appliedSettings.js +9 -5
  100. package/dist/settings/settingsController.d.ts +3 -0
  101. package/dist/settings/settingsController.js +47 -3
  102. package/dist/structures/BuildingKitBatch.js +26 -22
  103. package/dist/structures/GeneratedBuilding.d.ts +3 -15
  104. package/dist/structures/GeneratedBuilding.js +21 -15
  105. package/dist/structures/buildingSurface.d.ts +21 -0
  106. package/dist/structures/buildingSurface.js +134 -0
  107. package/dist/terrain/terrainDetailMaterial.js +7 -3
  108. package/dist/useShellMultiplayerSync.d.ts +9 -2
  109. package/dist/useShellMultiplayerSync.js +30 -8
  110. package/dist/visibility/CullingProvider.d.ts +2 -1
  111. package/dist/visibility/CullingProvider.js +7 -2
  112. package/dist/world/GridWorldScene.js +4 -0
  113. package/dist/world/TileLayerRenderer.d.ts +9 -0
  114. package/dist/world/TileLayerRenderer.js +64 -0
  115. package/dist/world/WorldParticles.d.ts +31 -0
  116. package/dist/world/WorldParticles.js +128 -0
  117. package/dist/world/WorldScene.d.ts +2 -1
  118. package/dist/world/WorldScene.js +46 -4
  119. package/dist/world/entityPose.d.ts +4 -0
  120. package/dist/world/entityPose.js +9 -0
  121. package/dist/world/tileLayerInstances.d.ts +11 -0
  122. package/dist/world/tileLayerInstances.js +28 -0
  123. package/dist/worldSync.d.ts +5 -1
  124. package/dist/worldSync.js +25 -4
  125. package/package.json +4 -4
  126. package/skills/ce-handoff/SKILL.md +2 -2
  127. package/skills/game-design/SKILL.md +11 -1
  128. package/skills/jgengine/SKILL.md +9 -5
  129. package/skills/jgengine/api.md +205 -101
  130. package/skills/jgengine/capabilities.md +56 -0
  131. package/skills/jgengine/recipes/minimal-game.md +3 -0
  132. package/skills/jgengine-ui/SKILL.md +20 -15
  133. package/skills/jgengine-ui/api.md +538 -326
  134. package/skills/jgengine-ui/capabilities.md +37 -0
  135. package/skills/jgengine-ui/reference-primitives.md +89 -0
  136. package/skills/jgengine-ui/reference.md +2 -78
  137. package/skills/jgengine-world/SKILL.md +21 -3
  138. package/skills/jgengine-world/api.md +1172 -625
  139. package/skills/jgengine-world/capabilities.md +229 -3
  140. package/skills/jgengine-world/recipes/character-animation.md +39 -0
  141. package/skills/jgengine-world/recipes/controls.md +44 -0
  142. package/skills/jgengine-world/recipes/flight-feel.md +76 -0
  143. package/skills/jgengine-world/recipes/npc-senses.md +43 -0
  144. package/skills/jgengine-world/recipes/vehicle-feel.md +85 -0
  145. package/skills/jgengine-world/reference.md +31 -2
  146. package/skills/jgengine-world/visibility.md +112 -0
  147. package/skills/level-design/SKILL.md +1 -1
  148. package/skills/jgengine-ui/references/visual-scorecard.md +0 -73
@@ -3,6 +3,15 @@ export function writeEntityPose(target, source) {
3
3
  target.position.set(source.position[0], source.position[1], source.position[2]);
4
4
  target.rotation.y = source.rotationY;
5
5
  }
6
+ /** Write the interpolated render pose from `ctx.sim` (falls back to `live` when no history exists). @internal */
7
+ export function writeRenderPose(target, ctx, entityId, live, scratch) {
8
+ if (ctx.sim.renderPose(entityId, scratch)) {
9
+ target.position.set(scratch[0], scratch[1], scratch[2]);
10
+ target.rotation.y = scratch[3];
11
+ return;
12
+ }
13
+ writeEntityPose(target, live);
14
+ }
6
15
  /** @internal */
7
16
  export function posesEqual(a, b) {
8
17
  return (a.position[0] === b.position[0] &&
@@ -0,0 +1,11 @@
1
+ import type { TilemapWorldConfig } from "@jgengine/core/world/features";
2
+ /** One resolved map cell with its world position and atlas UV rectangle. */
3
+ export interface TileLayerInstance {
4
+ x: number;
5
+ z: number;
6
+ frame: string;
7
+ uvOffset: readonly [number, number];
8
+ uvScale: readonly [number, number];
9
+ }
10
+ /** Resolve map glyphs to atlas frames and normalized UV rectangles for deterministic tests and renderers. */
11
+ export declare function resolveTileLayerInstances(config: TilemapWorldConfig): readonly TileLayerInstance[];
@@ -0,0 +1,28 @@
1
+ /** Resolve map glyphs to atlas frames and normalized UV rectangles for deterministic tests and renderers. */
2
+ export function resolveTileLayerInstances(config) {
3
+ if (config.tileSet === undefined)
4
+ return [];
5
+ const { atlas, tiles } = config.tileSet;
6
+ const rows = config.map.replace(/\r\n/g, "\n").split("\n").filter((row) => row.length > 0);
7
+ const width = Math.max(0, ...rows.map((row) => row.length));
8
+ const instances = [];
9
+ for (let row = 0; row < rows.length; row += 1) {
10
+ const line = rows[row].padEnd(width, " ");
11
+ for (let column = 0; column < width; column += 1) {
12
+ const frame = tiles[line[column]];
13
+ if (frame === undefined)
14
+ continue;
15
+ const rect = atlas.frames[frame];
16
+ if (rect === undefined)
17
+ throw new Error(`tileSet frame "${frame}" is missing from the atlas`);
18
+ instances.push({
19
+ x: column - (width - 1) / 2,
20
+ z: row - (rows.length - 1) / 2,
21
+ frame,
22
+ uvOffset: [rect.x / atlas.size[0], 1 - (rect.y + rect.h) / atlas.size[1]],
23
+ uvScale: [rect.w / atlas.size[0], rect.h / atlas.size[1]],
24
+ });
25
+ }
26
+ }
27
+ return instances;
28
+ }
@@ -1,5 +1,7 @@
1
1
  import type { GameContext } from "@jgengine/core/runtime/gameContext";
2
2
  import type { GameRuntimeFeeds } from "@jgengine/core/runtime/transport";
3
+ /** Receives the revision of an authoritative world frame after it is applied locally. */
4
+ export type AuthoritativeFrameHandler = (revision: number) => void;
3
5
  /**
4
6
  * Client half of host-authoritative play: subscribe to the server-state channel and mirror each authoritative
5
7
  * `WorldSnapshot` (carried in `serverState`) into the local `ctx`, so the game renders the host's world instead
@@ -8,4 +10,6 @@ import type { GameRuntimeFeeds } from "@jgengine/core/runtime/transport";
8
10
  * opts into `authority: "server"`.
9
11
  * @internal
10
12
  */
11
- export declare function attachWorldSync(feeds: Pick<GameRuntimeFeeds, "subscribeServer">, serverId: string, ctx: Pick<GameContext, "hydrate">): () => void;
13
+ export declare function attachWorldSync(feeds: Pick<GameRuntimeFeeds, "subscribeServer" | "requestServerBaseline">, serverId: string, ctx: Pick<GameContext, "hydrate">,
14
+ /** Called after each accepted authoritative baseline or diff, for local prediction reconciliation. */
15
+ onAuthoritativeFrame?: AuthoritativeFrameHandler): () => void;
package/dist/worldSync.js CHANGED
@@ -7,14 +7,35 @@ import { createWorldMirror } from "@jgengine/core/runtime/worldMirror";
7
7
  * opts into `authority: "server"`.
8
8
  * @internal
9
9
  */
10
- export function attachWorldSync(feeds, serverId, ctx) {
10
+ export function attachWorldSync(feeds, serverId, ctx,
11
+ /** Called after each accepted authoritative baseline or diff, for local prediction reconciliation. */
12
+ onAuthoritativeFrame) {
11
13
  const mirror = createWorldMirror(ctx);
12
14
  return feeds.subscribeServer(serverId, (view) => {
13
15
  if (view === null)
14
16
  return;
15
- const snapshot = view.serverState;
16
- if (snapshot === null || snapshot === undefined)
17
+ const frame = view.serverState;
18
+ if (frame === null || frame === undefined)
17
19
  return;
18
- mirror.applyBaseline(view.revision, snapshot);
20
+ if (isWorldSyncFrame(frame)) {
21
+ if (frame.kind === "baseline") {
22
+ mirror.applyBaseline(frame.revision, frame.snapshot);
23
+ onAuthoritativeFrame?.(frame.revision);
24
+ }
25
+ else {
26
+ mirror.applyDiff(frame.diff);
27
+ onAuthoritativeFrame?.(frame.revision);
28
+ if (mirror.needsResync())
29
+ feeds.requestServerBaseline?.(serverId);
30
+ }
31
+ return;
32
+ }
33
+ mirror.applyBaseline(view.revision, frame);
34
+ onAuthoritativeFrame?.(view.revision);
19
35
  });
20
36
  }
37
+ function isWorldSyncFrame(value) {
38
+ return (typeof value === "object" &&
39
+ value !== null &&
40
+ (value.kind === "baseline" || value.kind === "diff"));
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jgengine/shell",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Game player shell for JGengine: React Three Fiber canvas, orbit camera, input tracking, HUD mounting, GameUiPreview, and a demo game. Consumers supply a GameRegistry.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -35,9 +35,9 @@
35
35
  "test": "bun test src"
36
36
  },
37
37
  "dependencies": {
38
- "@jgengine/core": "^0.18.0",
39
- "@jgengine/react": "^0.18.0",
40
- "@jgengine/ws": "^0.18.0"
38
+ "@jgengine/core": "^0.19.0",
39
+ "@jgengine/react": "^0.19.0",
40
+ "@jgengine/ws": "^0.19.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@react-three/drei": "^10.0.0",
@@ -143,7 +143,7 @@ When sufficient, return a concise orientation: objective, progress, decisions, c
143
143
 
144
144
  ## JGengine notes
145
145
 
146
- - Multi-session engine work often spans `docs/plans/`, GitHub issues, and stacked PRs — point at those; do not paste full plan bodies.
146
+ - Multi-session engine work spans GitHub issues and PRs — point at those; do not paste full plan bodies.
147
147
  - Prefer `bun run agent:bootstrap --check` status, branch names, and PR links as state anchors.
148
148
  - Local parallelism uses `.claude/worktrees/`; never invent `C:\tmp\…` Codex-shared trees.
149
- - Papercuts and durable process changes belong in `PAPERCUTS.md` / skills, not only the handoff.
149
+ - Durable process changes belong in skills, not only the handoff.
@@ -11,7 +11,7 @@ Own the intended player experience and the rules that produce it: fantasy, pilla
11
11
 
12
12
  Use `level-design` for spatial realization; route implementation to `jgengine-gameplay`, `jgengine-world`, `jgengine-combat`, `jgengine-ui`, `jgengine-multiplayer` once intent is coherent.
13
13
 
14
- Read [references/application-playbook.md](references/application-playbook.md) only when the task explicitly calls for design depth — a design brief, a redesign, or "make this fun/better" work; it is not a mandatory step of creating a game. Read [references/field-guide.md](references/field-guide.md) when the task needs deeper loop, economy, progression, genre, accessibility, or research guidance.
14
+ For a greenfield build, read [references/application-playbook.md](references/application-playbook.md) and produce its greenfield contract (promise, pillars, anti-pillars, core decision, and falsifier) before writing code. Audits may read only the playbook's audit sections. Read [references/field-guide.md](references/field-guide.md) when the task needs deeper loop, economy, progression, genre, accessibility, or research guidance.
15
15
 
16
16
  ## Choose the mode
17
17
 
@@ -28,3 +28,13 @@ Every design pass yields an observed journey, a north star (promise, pillars, an
28
28
  An audit is not completion for a build/improve request. Finish when the slice is playable, a fresh player can state goal/choice/consequence, failure/recovery behave as intended, and evidence supports the pillars. Use `jgengine-verify` and `workflow` to ship.
29
29
 
30
30
  **Required: player death is a designed, visible moment.** If the game can kill or down the player, death is part of the experience contract, not an invisible state reset. A lethal hit must resolve into an authored beat the player perceives and understands — a death or downed moment, the stakes it carries, and a legible path back into play (respawn, revive, restart) — never a silent teleport to spawn. Design what death means for this pitch (permadeath, checkpoint, bleed-out-and-revive, run reset) and treat "player dies with no acknowledged consequence" as a failure/recovery defect. `jgengine-ui` owns building the screen and respawn feedback.
31
+
32
+ ## Persistent-builder checks
33
+
34
+ Before content, write a first-hour balance sheet: starting cash, time to first income, net income per hour at starting hardware and at ten times that hardware, bill cadence versus income, and the insolvency recovery path. Insolvency resets the affected player or changes their options; it never deletes the world. Express clicks and event rewards as a fraction of the designed income rate, never an unrelated flat payout.
35
+
36
+ Currency precision, elapsed-time anchoring, and reset mechanics are implemented per `jgengine-gameplay` "Persistent economies and input"; test fractional income over real tick cadences before tuning prices.
37
+
38
+ The first-60-seconds gate requires the first tutorial verb to be executable from spawn with starting inventory. Grant dependencies such as land, power, and slots or let players buy them in-context; the tutorial cannot lock the surface needed to complete itself. Placement previews show cost and affordability inline.
39
+
40
+ For a persistent shared-world builder, verify capacity and first-player onboarding before scaling the content catalog; presence, command read scopes, territory, chat, and join retry UI come from their owners in `jgengine-world`, `jgengine-ui`, and `jgengine-multiplayer`.
@@ -5,10 +5,14 @@ description: Route game work to the smallest useful JGengine guidance set.
5
5
 
6
6
  # JGengine intake and router
7
7
 
8
- Read the JGengine README for package truth, commands, layering, and license — the repo-root [`README.md`](../../../README.md) in the monorepo, or [jgengine.com](https://jgengine.com) from a scaffolded consumer project (where the relative link does not resolve). This skill decides what to load; it is not an engine manual.
8
+ This skill decides what to load; it is not an engine manual. Package map, commands, layering, and license live in the JGengine README (repo-root [`README.md`](../../../README.md) in the monorepo, [jgengine.com](https://jgengine.com) from a created project); open it when you need one of those facts.
9
+
10
+ A created project installs only `jgengine`, `jgengine-editor`, `jgengine-verify`, `game-design`, and `jgengine-ui`. Run `npx jgengine skills -p --all` before loading any other domain named below. Those projects do not ship `api.md`; read exact signatures from the `.d.ts` files under `node_modules/@jgengine/<pkg>/dist/`.
9
11
 
10
12
  ## 1. Establish the target
11
13
 
14
+ For greenfield builds, write `src/art-direction.md` before any code.
15
+
12
16
  Treat the pitch as a unique composition of needs, not a genre to fill in — never reach for a genre kit, preset, archetype, or class template ("default sports car", "default RPG", "default FPS") in the SDK or skills (see [AGENTS.md](../../../AGENTS.md)). Use `game-design` to turn the pitch into a testable player promise, verbs, loops, progression, failure, and completion scenario. Use `level-design` when play depends on authored spaces, routes, encounters, exploration, or spatial pacing. Capture only decisions that change architecture:
13
17
 
14
18
  - player point of view and controls
@@ -18,7 +22,7 @@ Treat the pitch as a unique composition of needs, not a genre to fill in — nev
18
22
  - HUD/menu states and **custom** UI art direction (compose shipped building blocks + HudTheme; no unskinned stock face)
19
23
  - one observable completion scenario
20
24
 
21
- For a new in-repo game, scaffold with `bun run new:game "<id-or-title>"` (thin script alias for `jgengine create`, auto-detects the in-repo variant). Outside this monorepo, start with `npx jgengine create`; never copy `Games/*`.
25
+ Pick the dimension first: `create` defaults to a 3D world (terrain, sky, player model, editor scene); a grid, card, tile, or other board game with no 3D scene takes `create --2d` (a `board` world drawn by GameUI over `@jgengine/core/puzzle/cellGrid`). For a new in-repo game, scaffold with `bun run new:game "<id-or-title>"` (thin script alias for `jgengine create`, auto-detects the in-repo variant). Outside this monorepo, start with `npx jgengine create`; never copy `Games/*`.
22
26
 
23
27
  For an existing game asked to pick up a new engine release ("jgengine published a new version, grab it"), run `npx jgengine upgrade` in the project first: it diffs installed `@jgengine/*` versions against the latest release and prints every Migrate step and Adopt-worthy addition in between (`--json` for structured output). Bump, migrate oldest-first, then work the Adopt lists instead of reconstructing the changelog by hand.
24
28
 
@@ -44,9 +48,9 @@ World content routes editor-first: any request that adds, moves, restyles, or re
44
48
 
45
49
  Within each selected domain:
46
50
 
47
- 0. Fastest start for a common composition: `npx jgengine recipe <name>` prints a vetted, SDK-typechecked snippet (imports + wired code) for `combat-loop`, `boss-telegraph`, `loot`, `quest`, `coop-presence`, `third-person-camera`; `npx jgengine recipe` lists them. Copy that instead of reconning per-file.
48
- 1. Search `capabilities.md` by intent; it maps needs to imports. Outside the monorepo (or before a domain skill is even loaded), `npx jgengine find <intent>` greps every shipped capability index at once and prints the primitive + import — reach for it the moment you're tempted to hand-roll a HUD window, inventory grid, paperdoll, hotkey listener, minimap, stat bar, or character motion. If you're writing a `z-index`, a `keydown` for a panel, a `<div>` inventory, or a limb bob, a shipped drop-in almost certainly exists (`usePanels`/`PanelHost`, `InventoryGrid`, `CharacterSheet`/`Paperdoll`, `PartMotionRig`, `EntityPreview`); games own the *skin*, not the re-derivation.
49
- 2. Open `api.md` only when exact signatures or export inventory are needed.
51
+ 0. Fastest start for a common composition: `npx jgengine recipe <name>` prints a vetted, SDK-typechecked snippet (imports + wired code) (`combat-loop`, `loot`, `world-drops`, `click-target`, `ability-bar`, `hitscan-weapon`, `rolled-gear`, `enter-vehicle` and more); `npx jgengine recipe` lists them. Copy that instead of reconning per-file.
52
+ 1. Search capabilities by intent; do not read `capabilities.md` top to bottom. `npx jgengine find <intent>` searches every domain's index and every recipe at once, installed or not, and prints the primitive and its import (or the recipe to open); in the monorepo, `grep -i "<intent>" .claude/skills/*/capabilities.md` does the same. Search the moment you are tempted to hand-roll a HUD window, inventory grid, paperdoll, hotkey listener, minimap, stat bar, or character motion. A `z-index`, a `keydown` for a panel, a `<div>` inventory, or a limb bob almost certainly has a shipped drop-in (`usePanels`/`PanelHost`, `InventoryGrid`, `CharacterSheet`/`Paperdoll`, `PartMotionRig`, `EntityPreview`); games own the *skin*, not the re-derivation.
53
+ 2. Open `api.md` (monorepo) or the package `.d.ts` only when exact signatures are needed; grep it, never read it whole.
50
54
  3. Open the linked reference only for a deeper recipe or trap.
51
55
  4. For how several primitives wire into a running loop, read the domain's `recipes/` — connected, genre-free walkthroughs organized by composition seam; this skill's own [recipes/minimal-game.md](recipes/minimal-game.md) is the default whole-game walkthrough. Prefer a recipe over reading a game's source; the games under `Games/*` are not templates or references, and a hybrid game is just a different composition of the same primitives.
52
56