@genex-ai/cli-demo 1.35.0-dev.736 → 1.35.0-dev.737

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
@@ -916,12 +916,18 @@ var TOOLS_CONTRACT_OFFER = `5. **When the game is built and playable \u2014 neve
916
916
  var TOOLS_CONTRACT_HOSTED_LAWS = `5. **This game is hosted on Genex now, and publishing is glue \u2014 never a rebuild.** The game ships exactly as the user built it: \`initEmbed()\` first in the boot code (the \`genex-threejs-embed-auth\` card has the call; the renderer draws before any \`await waitForPlayer()\`), a static build into \`dist/\` with relative asset paths, then \`npx genex preview\`. Never restructure, reformat or "improve" the game to ship it, never scaffold a new app around it, and never start a design document or a build plan for it \u2014 the user's own process is theirs. Load the \`genex-tool-publish\` card before the first preview; it owns the vocabulary, the links and the limits.
917
917
  6. **Ship first, then report.** \`preview\` prints a preflight \u2014 phone memory, a missing volume slider, an asset nobody wired, a viewport line. It never blocks a deploy, so push the build as it is, hand over the link, THEN relay each preflight line to the user in one plain sentence with an offer to fix it, and fix one only on their yes. The preflight is a report for the user, not a to-do list for you.
918
918
  7. **The link is the game's page, and there are two versions.** After every preview give the user \`<dashboard>/draft/<slug>\` \u2014 \`dashboardOrigins[0]\` and \`slug\` from \`.genex/project.json\` \u2014 never localhost, a file path or the bare play origin. \`preview\` updates the draft and never touches what players are on; the first release is \`npx genex publish\` (it lists the game); after that "publish it", "update it" and "yes" all mean \`npx genex promote\` \u2014 the exact draft build, no rebuild. Ask once per round of work, in one line, and keep working while you wait.`;
919
+ var TOOLS_CONTRACT_LLM_OFFER = `A model running while people play is built into the Genex platform \u2014 the player pays, with Genex coins or their own Claude/ChatGPT subscription, and approves it on a Genex sheet; your game just calls \`generate()\`. Want it that way?`;
920
+ function toolsContractLlmLaw(n, hosted) {
921
+ const onYes = hosted ? "On a yes, load `$genex-tool-llm` and then `$genex-llm-in-games`, which owns the build." : "On a yes, load `$genex-tool-llm`, which owns the rest: it checks the lane with `npx genex llm models` first, then runs `npx genex init --convert` (a hosted game is a static browser build \u2014 a local server holding a key can never ship).";
922
+ return `${n}. **A model running while people PLAY is a platform feature \u2014 never something you wire onto the user's own meter.** When a request implies one (NPCs that talk or decide in their own words, content written from what the player types, a prompt box in the game, "let the player pick a model"), recognise it, say in ONE line: "${TOOLS_CONTRACT_LLM_OFFER}" \u2014 then ASK and wait. ${onYes} On a no, build the authored version instead. Either way, never ship a game that calls a model on a key or an account of the user's own: every visitor would spend their money with nobody approving it, and the credential is readable in the bundle. Player-funded generation is text and JSON only \u2014 3D, images, video and audio stay the asset lanes above, on the user's meter.`;
923
+ }
919
924
  function toolsContractBlock(opts) {
920
925
  return `${CONTRACT_BEGIN}
921
926
  ${TOOLS_CONTRACT_HEAD}${opts.hosted ? " This game is also hosted on Genex: `preview` \xB7 `promote` \xB7 `publish` \xB7 `rollback` \xB7 `rename` work here (the `genex-tool-publish` card owns them)." : ""}
922
927
 
923
928
  ${TOOLS_CONTRACT_LAWS_1_TO_4}
924
929
  ${opts.hosted ? TOOLS_CONTRACT_HOSTED_LAWS : TOOLS_CONTRACT_OFFER}
930
+ ${toolsContractLlmLaw(opts.hosted ? 8 : 6, opts.hosted)}
925
931
  ${CONTRACT_END}
926
932
  `;
927
933
  }
@@ -1227,13 +1233,18 @@ async function hasGenexSkills(skillsDir) {
1227
1233
  }
1228
1234
  }
1229
1235
  var TOOL_SKILL_PREFIX = "genex-tool-";
1230
- var HOSTED_TOOL_SKILLS = ["genex-tool-publish", "genex-threejs-embed-auth"];
1236
+ var HOSTED_TOOL_SKILLS = [
1237
+ "genex-tool-publish",
1238
+ "genex-threejs-embed-auth",
1239
+ "genex-llm-in-games"
1240
+ ];
1231
1241
  var REMIX_EXCLUDED_SKILLS = [
1232
1242
  "genex-game-director",
1233
1243
  "genex-getting-started",
1234
1244
  "genex-tool-audio",
1235
1245
  "genex-tool-character",
1236
1246
  "genex-tool-image",
1247
+ "genex-tool-llm",
1237
1248
  "genex-tool-model",
1238
1249
  "genex-tool-publish",
1239
1250
  "genex-tool-texture",
@@ -23278,8 +23289,13 @@ import fs33 from "fs/promises";
23278
23289
  import path34 from "path";
23279
23290
  import { randomUUID as randomUUID2 } from "crypto";
23280
23291
  var SUBS3 = ["models", "bench", "price"];
23281
- var LLM_BENCH_APPROVAL_REQUIRED = "STOP: `genex llm bench` runs the real model and spends the player's COIN \u2014 a wallet this build's asset allowance cannot see. Re-run with --max-coins <n> --user-approved only after they agreed to the number.";
23292
+ var LLM_BENCH_APPROVAL_REQUIRED = "STOP: `genex llm bench` runs the real model and spends YOUR OWN coin \u2014 your wallet, not a player's, and one this build's asset allowance cannot see. Re-run with --max-coins <n> --user-approved only after the person at the keyboard agreed to the number.";
23282
23293
  var LLM_LANE_OFF_LINE = "The runtime LLM lane is off on this stand \u2014 in-game generate() answers 404 here, and nothing can be benchmarked.";
23294
+ var LLM_TOOLS_CONVERT_HINT = [
23295
+ "In-game model calls need a hosted game. This folder is a Genex Tools workspace, so nothing here",
23296
+ "can call a model at play time yet \u2014 and the player, not you, is who pays for one that does.",
23297
+ "Ask the user first; on their yes run `npx genex init --convert`, then load the genex-tool-llm card."
23298
+ ];
23283
23299
  var LLM_BENCH_FILE = ".genex/llm-bench.json";
23284
23300
  var DEFAULT_BENCH_SAMPLES = 3;
23285
23301
  var DEFAULT_SAMPLE_TIMEOUT_SEC = 180;
@@ -23345,6 +23361,8 @@ async function runLlm(opts = {}) {
23345
23361
  await reportSavedPrice(cwd, opts, log);
23346
23362
  return;
23347
23363
  }
23364
+ const ws = await resolveWorkspace(cwd);
23365
+ const toolsOnly = ws.mode === "tools" && !ws.hosted;
23348
23366
  if (sub === "bench") {
23349
23367
  if (opts.maxCoins === void 0 || opts.userApproved !== true) {
23350
23368
  fail4("llm bench", LLM_BENCH_APPROVAL_REQUIRED);
@@ -23362,10 +23380,13 @@ async function runLlm(opts = {}) {
23362
23380
  fail4("llm bench", "Pass --json-output or --text, not both.");
23363
23381
  return;
23364
23382
  }
23365
- const ws = await resolveWorkspace(cwd);
23366
- if (ws.mode === "tools" && !ws.hosted) {
23383
+ if (toolsOnly) {
23367
23384
  if (opts.json) writeJsonLine({ command: "llm bench", status: "failed", error: "`genex llm bench` is not part of Genex Tools." });
23368
- else reportPlatformRefused(log, "llm bench");
23385
+ else {
23386
+ reportPlatformRefused(log, "llm bench");
23387
+ log.plain(` ${c.cyan("\u2192")} A model that runs while people PLAY is a platform feature: the player pays and approves it.`);
23388
+ log.plain(` Ask the user, and on their yes: ${c.cyan("npx genex init --convert")} \u2014 then the ${c.cyan("genex-tool-llm")} card.`);
23389
+ }
23369
23390
  process.exitCode = 1;
23370
23391
  return;
23371
23392
  }
@@ -23408,12 +23429,12 @@ async function runLlm(opts = {}) {
23408
23429
  await writeUserToken(token, opts.envPath, apiUrl);
23409
23430
  }
23410
23431
  if (sub === "models") {
23411
- await reportModels(apiUrl, token, opts, log);
23432
+ await reportModels(apiUrl, token, opts, log, toolsOnly);
23412
23433
  return;
23413
23434
  }
23414
23435
  await runBench({ apiUrl, token, projectId: meta.id, cwd, opts, log });
23415
23436
  }
23416
- async function reportModels(apiUrl, token, opts, log) {
23437
+ async function reportModels(apiUrl, token, opts, log, toolsOnly = false) {
23417
23438
  const probe = await apiFetch(`${apiUrl}/api/runtime/models`, {
23418
23439
  headers: { Authorization: `Bearer ${token}` }
23419
23440
  }).catch(() => null);
@@ -23435,7 +23456,8 @@ async function reportModels(apiUrl, token, opts, log) {
23435
23456
  enabled: null,
23436
23457
  models: null,
23437
23458
  externalProviders: null,
23438
- recommendedDeclaredHeadroomBps: null
23459
+ recommendedDeclaredHeadroomBps: null,
23460
+ convertHint: toolsOnly ? LLM_TOOLS_CONVERT_HINT.join(" ") : null
23439
23461
  });
23440
23462
  }
23441
23463
  process.exitCode = 1;
@@ -23450,22 +23472,33 @@ async function reportModels(apiUrl, token, opts, log) {
23450
23472
  enabled: lane.state === "live",
23451
23473
  models: lane.models,
23452
23474
  externalProviders: lane.externalProviders,
23453
- recommendedDeclaredHeadroomBps: lane.recommendedDeclaredHeadroomBps
23475
+ recommendedDeclaredHeadroomBps: lane.recommendedDeclaredHeadroomBps,
23476
+ // Always present, `null` off a tools workspace — the same
23477
+ // every-key-always-present rule the other keys follow, so a reader can
23478
+ // tell "no hint" from "a CLI that predates the hint".
23479
+ convertHint: toolsOnly ? LLM_TOOLS_CONVERT_HINT.join(" ") : null
23454
23480
  });
23455
23481
  if (lane.state === "unknown") process.exitCode = 1;
23456
23482
  return;
23457
23483
  }
23484
+ const printConvertHint = () => {
23485
+ if (!toolsOnly) return;
23486
+ log.plain("");
23487
+ for (const line of LLM_TOOLS_CONVERT_HINT) log.dim(` ${line}`);
23488
+ };
23458
23489
  log.plain(c.bold("genex llm models"));
23459
23490
  log.dim(` ${apiUrl}`);
23460
23491
  log.plain("");
23461
23492
  if (lane.state === "off") {
23462
23493
  log.plain(` ${LLM_LANE_OFF_LINE}`);
23463
23494
  log.dim(" Build the feature without an in-game model, or ask the operator to turn the lane on.");
23495
+ printConvertHint();
23464
23496
  return;
23465
23497
  }
23466
23498
  if (lane.state === "unavailable") {
23467
23499
  log.warn(" This stand has the lane on but cannot serve its model list.");
23468
23500
  log.dim(" That is a server configuration problem, not yours \u2014 nothing here can be benchmarked yet.");
23501
+ printConvertHint();
23469
23502
  return;
23470
23503
  }
23471
23504
  if (lane.state === "unknown" || !lane.models) {
@@ -23490,6 +23523,10 @@ async function reportModels(apiUrl, token, opts, log) {
23490
23523
  } else {
23491
23524
  log.dim(` Recommended headroom over benchmarked coins on this stand: ${lane.recommendedDeclaredHeadroomBps} bps.`);
23492
23525
  }
23526
+ if (toolsOnly) {
23527
+ printConvertHint();
23528
+ return;
23529
+ }
23493
23530
  log.dim(" Those per-million rates are the PROVIDER's; what your game is charged is the platform");
23494
23531
  log.dim(` tariff on top, which only a real call reveals: ${c.cyan('genex llm bench "<prompt>" --max-coins <n> --user-approved')}`);
23495
23532
  }
@@ -23935,7 +23972,7 @@ async function runDoctor(opts = {}) {
23935
23972
  }
23936
23973
  rows.push(termsRow(authState, legal));
23937
23974
  rows.push(creditsRow(authState, credits));
23938
- rows.push(runtimeRow(authState, runtime));
23975
+ rows.push(runtimeRow(authState, runtime, mode === "tools"));
23939
23976
  if (opts.json) {
23940
23977
  process.stdout.write(
23941
23978
  JSON.stringify(
@@ -24096,15 +24133,16 @@ function creditsRow(state, info) {
24096
24133
  fix: info.balance <= 0 ? "Out of credits \u2014 generation is refused until the refill or a top-up." : void 0
24097
24134
  };
24098
24135
  }
24099
- function runtimeRow(state, lane) {
24136
+ function runtimeRow(state, lane, toolsOnly = false) {
24100
24137
  if (state !== "ok") return { label: "Runtime", value: "\u2014 (sign in first)" };
24138
+ const convertFix = "In-game calls need a hosted game, and the PLAYER pays for them \u2014 ask the user, then `npx genex init --convert`; the genex-tool-llm card has the offer to make.";
24101
24139
  switch (lane?.state) {
24102
24140
  case "live": {
24103
24141
  const n = lane.models?.length ?? 0;
24104
24142
  return {
24105
24143
  label: "Runtime",
24106
24144
  value: `in-game LLM lane live \xB7 ${n} model${n === 1 ? "" : "s"}`,
24107
- fix: 'Price a call before a game declares one: `npx genex llm bench "<prompt>" --max-coins <n> --user-approved`.'
24145
+ fix: toolsOnly ? convertFix : 'Price a call before a game declares one: `npx genex llm bench "<prompt>" --max-coins <n> --user-approved`.'
24108
24146
  };
24109
24147
  }
24110
24148
  case "off":
@@ -26729,8 +26767,8 @@ ${c.bold("Usage")}
26729
26767
  Run the real model <n> times through the development
26730
26768
  lane and report what each attempt was CHARGED. Refused
26731
26769
  without both flags, before any network call: this spends
26732
- COIN, a wallet the build's asset allowance cannot see.
26733
- Needs a folder linked to a game you own.
26770
+ YOUR OWN COIN, a wallet the build's asset allowance
26771
+ cannot see. Needs a folder linked to a game you own.
26734
26772
  genex llm price [--json] The recommendation from the last bench in this folder.
26735
26773
  Reads a file; spends nothing.
26736
26774
 
@@ -26739,8 +26777,9 @@ ${c.bold("Bench options")}
26739
26777
  --samples <n> How many real attempts (default ${DEFAULT_BENCH_SAMPLES}). More samples, better p95.
26740
26778
  --schema <file> A JSON file holding the output schema; implies JSON output.
26741
26779
  --json-output Ask for JSON without a schema. --text Ask for text (the default).
26742
- --max-coins <n> The per-attempt ceiling the player approved. The run's worst case is
26743
- that number times --samples, and it is printed before anything starts.
26780
+ --max-coins <n> The per-attempt ceiling YOU approved \u2014 a bench runs on your own coin,
26781
+ never a player's. The run's worst case is that number times --samples,
26782
+ and it is printed before anything starts.
26744
26783
  --timeout <seconds> Bounds the wait for each attempt.
26745
26784
  --json One machine-readable object; every key is always present, null when
26746
26785
  the CLI could not learn it.
@@ -26749,10 +26788,12 @@ ${c.bold("Why a benchmark and not an estimate")}
26749
26788
  \`estimateCoins\` is a PRICE, not a guess: declaring it charges it, in full, even when the
26750
26789
  attempt fails, is cancelled, or stops at its budget. And three layers sit between the
26751
26790
  provider's rate card and that number \u2014 the provider's cost, the platform's tariff (already
26752
- inside every charged coin), and your headroom. Adding a margin to the provider's rate
26753
- silently drops the tariff and under-prices every call, so the recommendation is built from
26754
- CHARGED COINS and the headroom comes from the server. A stand that serves no headroom gets
26755
- no recommendation, because the multiplier is not the CLI's to invent.
26791
+ inside every charged coin), and your headroom. The bench itself is billed to YOU, on the
26792
+ development lane; the player-funded lane is what the published game uses. Adding a margin
26793
+ to the provider's rate silently drops the tariff and under-prices every call, so the
26794
+ recommendation is built from CHARGED COINS and the headroom comes from the server. A stand
26795
+ that serves no headroom gets no recommendation, because the multiplier is not the CLI's to
26796
+ invent.
26756
26797
 
26757
26798
  Results land in ${LLM_BENCH_FILE} \u2014 gitignored, machine-local, re-read by \`genex llm price\`.
26758
26799
  `,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.35.0-dev.736",
3
+ "version": "1.35.0-dev.737",
4
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": {
@@ -243,11 +243,15 @@ Nothing to fix in the game: ship the fallback and say so.
243
243
  **Nothing opens when the player clicks** — an `await` ran before `generate()`
244
244
  and the gesture was lost. Move the call to the first line of the handler.
245
245
 
246
- **`guest_no_wallet`** — guests play but hold no wallet. Show the feature as
247
- sign-in-to-use rather than hiding it (`$genex-threejs-embed-auth`).
248
-
249
- **`staging_no_generation`** — a `genex preview` build cannot spend real coin.
250
- Check the layout on the draft; check the call itself after `genex promote`.
246
+ **`player_wallet_required`** — ONE code for the two early dead ends: the lane
247
+ refuses a guest and a PREVIEW build on the same line. `waitForPlayer()` tells
248
+ them apart. `guest: true` — guests play but hold no wallet, so show the feature
249
+ as sign-in-to-use rather than hiding it (`$genex-threejs-embed-auth`). Signed
250
+ in and still refused — this is a `genex preview` draft, which never spends:
251
+ check the layout there, and the call itself only after `genex promote`. The
252
+ SDK's stock sentence for this code is "Sign in to Genex to use this", which is
253
+ right for the guest and wrong on a draft, so write the in-fiction line per
254
+ cause rather than showing it for both.
251
255
 
252
256
  **`grant_price_unreasonable`** — the declared per-call price is far above what
253
257
  that prompt can cost on that model. Re-benchmark and declare what it prints.
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: genex-tool-llm
3
+ description: A language model running while somebody PLAYS the finished game — an NPC that answers in its own words, a quest written from what the player typed, a prompt box in the game. On Genex the player pays and approves it, so this is a platform feature, never a key on the author's meter. Read this the moment a request implies a model at play time, before building anything.
4
+ ---
5
+
6
+ # Genex Tools · Models at play time
7
+
8
+ This card is about a model that runs **while somebody plays the finished game**
9
+ — not about generating art while you build it. "Make me a picture, a model, a
10
+ voice line" is the asset belt: use the lane card for it and stay here only if a
11
+ model has to run for the player.
12
+
13
+ ## What the platform does
14
+
15
+ A game hosted on Genex can call a language model from inside the running game
16
+ and get back text or JSON. **The player pays and the player approves**: coins
17
+ from their Genex wallet, or their own Claude / ChatGPT subscription, chosen on
18
+ an approval sheet Genex draws that the game cannot render, skin or bypass.
19
+ Either one call at a time, or one standing budget the player approves once and
20
+ the game then spends against without another popup. The game holds no provider
21
+ key, sees no credential, and never talks to a model vendor.
22
+
23
+ That is the whole reason this is a platform feature and not something you wire
24
+ up here: an unhosted folder has nowhere to put a key that is not the author's
25
+ own.
26
+
27
+ ## Recognise the request
28
+
29
+ Any of these means a model at PLAY time, however it is phrased:
30
+
31
+ - NPCs that talk, answer, argue, or decide in their own words
32
+ - anything written from what the player types — a name, a wish, a command, a question
33
+ - a prompt box, a chat panel, an "ask the oracle" widget inside the game
34
+ - quests, items, dialogue, or levels generated per save or per run
35
+ - "let the player choose a model", "use my API key in the game", "hook a chatbot into it"
36
+ - a judge, a grader, or a referee that reads free-form player input
37
+
38
+ ## Offer it in one line, then ASK
39
+
40
+ Say this and stop:
41
+
42
+ > A model running while people play is built into the Genex platform — the
43
+ > player pays, with Genex coins or their own Claude/ChatGPT subscription, and
44
+ > approves it on a Genex sheet; your game just calls `generate()`. Want it
45
+ > that way?
46
+
47
+ Wait for the answer. Do not start building either version first, and do not
48
+ expand the offer into a pitch — one line, one question.
49
+
50
+ ## On a yes
51
+
52
+ 1. `npx genex llm models` — whether this stand serves the lane at all, and
53
+ which models. It answers in this folder as it is, before anything is
54
+ converted, so it comes first: if it says the lane is off, in-game calls
55
+ answer 404 here — tell the user so plainly, build the graceful fallback, and
56
+ do not convert a folder for a feature the stand does not serve.
57
+ 2. `npx genex init --convert` — it connects this folder to a hosted Genex game
58
+ in place. The code, the files and this toolkit stay exactly as they are, and
59
+ generations still land in `./assets`. It is the user's yes that runs it, so
60
+ ask before you do if you have not already.
61
+ 3. Load `$genex-llm-in-games` — it arrives with the conversion and owns the
62
+ build: the SDK surface, one-time calls versus a standing budget, measuring
63
+ the price before the game declares it, and honest handling of every refusal.
64
+
65
+ **The game has to be a static browser build.** A hosted game is files served
66
+ from the edge; there is no server of yours inside it. So the familiar pattern —
67
+ a small local Express/Flask app that holds a key and proxies the model — works
68
+ on your machine and can never ship. Calling `generate()` from the browser is
69
+ the shipping shape of that idea.
70
+
71
+ ## On a no
72
+
73
+ Then build nothing that calls a model at play time. Not a key in `.env`, not a
74
+ local proxy, not the author's own account behind a fetch. A shipped game
75
+ carrying the author's credential means every visitor spends the author's money,
76
+ with nobody approving anything and no limit on it — and the credential is
77
+ readable in the bundle. Say that in one plain sentence, then build the authored
78
+ version instead: a written dialogue tree, a table of lines, a rule-based
79
+ director. Those are not consolation prizes; they are what most good games use.
80
+
81
+ ## The honest boundary — text and JSON only
82
+
83
+ Player-funded generation returns **text or JSON**. Nothing else.
84
+
85
+ - **"The player types anything and gets a 3D model, paid by them"** is not a
86
+ thing on this platform. Say so plainly instead of half-building it.
87
+ - 3D models, images, textures, video, music, voice and characters are the ASSET
88
+ lanes of this toolkit: you generate them while you build, on the user's own
89
+ meter, and they download into `./assets` and ship inside the game. What is
90
+ live and what it costs: `npx genex doctor`.
91
+ - The shape that does work is **JSON parameters, then render**: the model
92
+ returns a structured description and the game builds it from assets and code
93
+ you already shipped — a creature assembled from parts you generated, a room
94
+ laid out from a list of prefab ids, a palette, a stat block, a line delivered
95
+ from pre-generated voice clips. Offer that when somebody asks for the
96
+ impossible version.
@@ -8,6 +8,9 @@ description: How Genex Tools works across every lane — `npx genex doctor` to c
8
8
  The rules that apply to every lane. The per-lane cards are
9
9
  `$genex-tool-model`, `$genex-tool-image`, `$genex-tool-video`,
10
10
  `$genex-tool-texture`, `$genex-tool-audio`, `$genex-tool-character`.
11
+ `$genex-tool-llm` is the odd one out: it is not a generation lane at all but
12
+ the door for a model that runs while somebody PLAYS the finished game — read it
13
+ the moment a request implies one.
11
14
 
12
15
  ## Check before you promise
13
16
 
@@ -92,8 +95,10 @@ tiling floor and three paid assets nobody loaded.
92
95
 
93
96
  Hosting, publishing, multiplayer, remixing and custom domains are the Genex
94
97
  platform, not this toolkit — those commands are refused in this folder by
95
- design, and the refusal says where they live. This workspace generates assets
96
- for a game you build and ship yourself. When the user wants that game live on
97
- Genex with its own URL, the AGENTS.md rules say how to offer it; on a yes the
98
- folder is connected to a hosted game in place — same cards, same rules — and
99
- the `$genex-tool-publish` card arrives with the publishing commands.
98
+ design, and the refusal says where they live. So is a model that runs while
99
+ somebody plays the finished game: that one is player-funded and needs a hosted
100
+ game, and `$genex-tool-llm` owns how to offer it. This workspace generates
101
+ assets for a game you build and ship yourself. When the user wants that game
102
+ live on Genex with its own URL, the AGENTS.md rules say how to offer it; on a
103
+ yes the folder is connected to a hosted game in place — same cards, same rules
104
+ — and the `$genex-tool-publish` card arrives with the publishing commands.