@genex-ai/cli-demo 1.34.3-dev.705 → 1.34.4-dev.706

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/README.md CHANGED
@@ -18,6 +18,7 @@ genex texture "<prompt>" # generate a texture → prints an asset URL
18
18
  genex image "<prompt>" # generate an image → prints an asset URL
19
19
  genex video "<prompt>" # generate a video → prints an asset URL
20
20
  genex character "<brief>" # generate 3 neutral-A-pose concept candidates
21
+ genex character --image ./front.jpg --image ./back.jpg # Meshy from your own views
21
22
  genex character preview <concept-id> --candidate 1 --user-approved
22
23
  genex character finalize <preview-id> --user-approved --approve-remesh 10000
23
24
  genex character animate <id> --action <action-id> # add a same-rig Meshy action
@@ -164,6 +165,20 @@ one call (`image-main` + `image-alt-N` URLs) — pick the best instead of re-rol
164
165
 
165
166
  ## Meshy characters and animations
166
167
 
168
+ Use your own reference images directly with Meshy:
169
+
170
+ ```bash
171
+ genex character --image ./front.jpg --image ./back.jpg --texture 8k --polycount 100000 --pose t-pose --no-wait
172
+ ```
173
+
174
+ Repeat `--image` for 1-4 PNG/JPEG views of the same character, one view per
175
+ image. Local files may total up to 4 MB; Genex asset URLs are also accepted.
176
+ This starts a one-shot rigged character, skipping concept generation. An
177
+ optional brief labels the ledger entry; Meshy's image endpoints do not use it
178
+ as text conditioning. `creature --image` works with the same references and
179
+ keeps creature defaults (no controller pack). Do not combine `--image` with
180
+ `--direct-text`, guided subcommands or their approval flags.
181
+
167
182
  The default Meshy lane is an approval-gated, three-command workflow through the
168
183
  same authenticated credit, BullMQ/Redis, and R2 generation pipeline:
169
184
 
@@ -187,8 +202,9 @@ horizontal root/hip translation may be normalized for Rapier ownership.
187
202
  `finalize`; requested actions run only after the controller pack succeeds.
188
203
  `--height` and `--no-wait` carry through the approval flow. The guided lane
189
204
  always installs neutral-v3. `genex character "<prompt>" --direct-text` is the
190
- explicit compatibility path; only that path accepts `--polycount` or
191
- `--no-controller-pack`, and its default polycount is also 10,000.
205
+ explicit text-only compatibility path. Both one-shot paths (`--direct-text`
206
+ and `--image`) accept `--polycount` and `--no-controller-pack`, with a default
207
+ polycount of 10,000.
192
208
 
193
209
  Search before spending:
194
210
 
package/dist/index.js CHANGED
@@ -8748,6 +8748,14 @@ function buildGenOptions(kind, opts) {
8748
8748
  }
8749
8749
  async function runGenerate(kind, opts) {
8750
8750
  const log = createLogger({ quiet: opts.quiet || opts.json });
8751
+ if (kind === "model" && opts.imageUrls !== void 0) {
8752
+ if (opts.imageUrls.length > 1) {
8753
+ log.error("genex model accepts one --image reference. Use character --image for multiple character views.");
8754
+ process.exitCode = 1;
8755
+ return;
8756
+ }
8757
+ opts = { ...opts, imageUrl: opts.imageUrls[0] ?? opts.imageUrl };
8758
+ }
8751
8759
  let typedPrompt = opts.prompt?.trim();
8752
8760
  if (!typedPrompt && kind === "model" && opts.imageUrl) {
8753
8761
  const ref = opts.imageUrl.startsWith("data:") ? "local image" : opts.imageUrl;
@@ -20079,6 +20087,7 @@ async function resolveAdoptTarget(selector) {
20079
20087
  return { ok: true, characterId, from: file };
20080
20088
  }
20081
20089
  async function runCharacterAdopt(opts) {
20090
+ if (refuseSubcommandImages(opts)) return;
20082
20091
  const log = createLogger({ quiet: opts.quiet || opts.json });
20083
20092
  const target = await resolveAdoptTarget(opts.characterId);
20084
20093
  if (!target.ok) {
@@ -20139,6 +20148,7 @@ async function runCharacterAdopt(opts) {
20139
20148
  });
20140
20149
  }
20141
20150
  async function runCharacterImport(opts) {
20151
+ if (refuseSubcommandImages(opts)) return;
20142
20152
  const log = createLogger({ quiet: opts.quiet || opts.json });
20143
20153
  const filePath = opts.importPath?.trim();
20144
20154
  if (!filePath) {
@@ -20215,17 +20225,67 @@ async function runCharacterImport(opts) {
20215
20225
  });
20216
20226
  }
20217
20227
  async function runCharacter(opts) {
20228
+ const references = opts.imageUrls ?? (opts.imageUrl === void 0 ? [] : [opts.imageUrl]);
20229
+ if (references.length > 0) {
20230
+ if (opts.directText) {
20231
+ fail2(opts, "--image cannot be combined with --direct-text. Omit --direct-text to build from the supplied references.");
20232
+ return;
20233
+ }
20234
+ if (opts.candidate !== void 0 || opts.userApproved !== void 0 || opts.approveRemesh !== void 0) {
20235
+ fail2(opts, "--image starts a one-shot character. --candidate / --user-approved / --approve-remesh belong to the guided preview/finalize flow; use --polycount for the reference character's face budget.");
20236
+ return;
20237
+ }
20238
+ const prepared = await prepareCharacterImages(references);
20239
+ if (!prepared.ok) {
20240
+ fail2(opts, prepared.error);
20241
+ return;
20242
+ }
20243
+ await runOneShotCharacter(opts, prepared.imageUrls);
20244
+ return;
20245
+ }
20218
20246
  if (opts.directText) {
20219
- await runDirectTextCharacter(opts);
20247
+ await runOneShotCharacter(opts);
20220
20248
  return;
20221
20249
  }
20222
20250
  await runCharacterConcept(opts);
20223
20251
  }
20252
+ async function prepareCharacterImages(references) {
20253
+ if (references.length > 4) return { ok: false, error: "--image accepts 1-4 references to the same character, one view per image." };
20254
+ const imageUrls = [];
20255
+ let inlineBytes = 0;
20256
+ for (const reference of references) {
20257
+ let image = reference.trim();
20258
+ if (!image) return { ok: false, error: "--image needs a PNG/JPEG file or asset URL." };
20259
+ if (!/^(https?:\/\/|data:)/i.test(image)) {
20260
+ if (!/\.(png|jpe?g)$/i.test(image)) return { ok: false, error: "Character --image references must be PNG or JPEG files." };
20261
+ const inlined = await inlineLocalImage(image, "--image");
20262
+ if (!inlined.ok) return inlined;
20263
+ image = inlined.dataUri;
20264
+ }
20265
+ if (/^data:/i.test(image)) {
20266
+ const match = /^data:image\/(png|jpeg);base64,([A-Za-z0-9+/]+={0,2})$/.exec(image);
20267
+ if (!match || match[2].length % 4 !== 0) return { ok: false, error: "Character --image data must be a base64 PNG or JPEG." };
20268
+ const bytes = Buffer.from(match[2], "base64");
20269
+ const isPng2 = bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
20270
+ const isJpeg = bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
20271
+ if (!(match[1] === "png" ? isPng2 : isJpeg)) return { ok: false, error: "Character --image contents must match their PNG/JPEG format." };
20272
+ inlineBytes += bytes.length;
20273
+ if (inlineBytes > 4 * 1024 * 1024) return { ok: false, error: "--image references exceed the combined 4 MB inline limit. Compress them or use Genex asset URLs." };
20274
+ }
20275
+ imageUrls.push(image);
20276
+ }
20277
+ return { ok: true, imageUrls };
20278
+ }
20224
20279
  function fail2(opts, message, kind = "character") {
20225
20280
  if (opts.json) writeJson({ kind, status: "failed", error: message });
20226
20281
  else createLogger({ quiet: opts.quiet }).error(message);
20227
20282
  process.exitCode = 1;
20228
20283
  }
20284
+ function refuseSubcommandImages(opts) {
20285
+ if (opts.imageUrl === void 0 && !opts.imageUrls?.length) return false;
20286
+ fail2(opts, "--image starts a new reference character; it cannot be combined with a character subcommand.");
20287
+ return true;
20288
+ }
20229
20289
  function requestedBuild(opts, actionIds) {
20230
20290
  return {
20231
20291
  actionIds,
@@ -20310,15 +20370,15 @@ async function runCharacterConcept(opts) {
20310
20370
  return;
20311
20371
  }
20312
20372
  if (opts.polycount !== void 0) {
20313
- fail2(opts, "--polycount is only available with --direct-text. The approval flow takes its face budget on `character finalize --approve-remesh <faces>`.", "character_concept");
20373
+ fail2(opts, "--polycount is only available with --direct-text or --image. The approval flow takes its face budget on `character finalize --approve-remesh <faces>`.", "character_concept");
20314
20374
  return;
20315
20375
  }
20316
20376
  if (opts.texture !== void 0 || opts.ultra === false || opts.pose !== void 0) {
20317
- fail2(opts, "--texture / --no-ultra / --pose apply to the paid mesh call: pass them to `character preview` (guided) or `character --direct-text` / `creature` (one shot). Concepts are images.", "character_concept");
20377
+ fail2(opts, "--texture / --no-ultra / --pose apply to the paid mesh call: pass them to `character preview` (guided) or `character --direct-text` / `character --image` / `creature` (one shot). Concepts are images.", "character_concept");
20318
20378
  return;
20319
20379
  }
20320
20380
  if (opts.controllerPack === false) {
20321
- fail2(opts, "--no-controller-pack is only available with --direct-text. The guided Meshy parity flow always installs neutral-v3.", "character_concept");
20381
+ fail2(opts, "--no-controller-pack is only available with --direct-text or --image. The guided Meshy parity flow always installs neutral-v3.", "character_concept");
20322
20382
  return;
20323
20383
  }
20324
20384
  const resolved = resolveActionSelectors(opts.actions ?? []);
@@ -20363,13 +20423,26 @@ async function runCharacterConcept(opts) {
20363
20423
  })
20364
20424
  });
20365
20425
  }
20366
- async function runDirectTextCharacter(opts) {
20426
+ async function runOneShotCharacter(opts, imageUrls) {
20367
20427
  const log = createLogger({ quiet: opts.quiet || opts.json });
20368
- const prompt = opts.prompt?.trim();
20428
+ const prompt = opts.prompt?.trim() || (imageUrls ? "Character from supplied reference images" : void 0);
20369
20429
  if (!prompt) {
20370
20430
  fail2(opts, 'Missing prompt. Usage: genex character "<prompt>" --direct-text.');
20371
20431
  return;
20372
20432
  }
20433
+ const badTexture = badCharacterTexture(opts);
20434
+ if (badTexture) {
20435
+ fail2(opts, badTexture);
20436
+ return;
20437
+ }
20438
+ if (opts.polycount !== void 0 && (!Number.isInteger(opts.polycount) || opts.polycount < REMESH_TARGET_MIN || opts.polycount > REMESH_TARGET_MAX)) {
20439
+ fail2(opts, `--polycount must be an integer between ${REMESH_TARGET_MIN} and ${REMESH_TARGET_MAX}.`);
20440
+ return;
20441
+ }
20442
+ if (opts.pose !== void 0 && !["a-pose", "t-pose"].includes(opts.pose)) {
20443
+ fail2(opts, "--pose must be a-pose or t-pose.");
20444
+ return;
20445
+ }
20373
20446
  const resolved = resolveActionSelectors(opts.actions ?? []);
20374
20447
  if (!resolved.ok) {
20375
20448
  showAmbiguity(resolved.selector, resolved.candidates, log, opts.json);
@@ -20381,16 +20454,26 @@ async function runDirectTextCharacter(opts) {
20381
20454
  return;
20382
20455
  }
20383
20456
  const controllerPack = opts.controllerPack !== false;
20384
- const price = await quote(`${ctx.apiUrl}/api/characters/quote`, ctx.token, {
20457
+ const generationOptions = {
20385
20458
  actionIds: resolved.actionIds,
20386
- controllerPack
20387
- });
20459
+ controllerPack,
20460
+ ...opts.height === void 0 ? {} : { heightMeters: opts.height },
20461
+ targetPolycount: opts.polycount ?? 1e4,
20462
+ ...meshyQualityOptions(opts),
20463
+ ...opts.pose === void 0 ? {} : { poseMode: opts.pose },
20464
+ ...imageUrls === void 0 ? {} : { imageUrls }
20465
+ };
20466
+ const price = await quote(`${ctx.apiUrl}/api/characters/quote`, ctx.token, generationOptions);
20388
20467
  if (!price) {
20389
20468
  process.exitCode = 1;
20390
20469
  return;
20391
20470
  }
20471
+ if (imageUrls && price.referenceImageCount !== imageUrls.length) {
20472
+ fail2(opts, "This server did not confirm support for all character reference images. No generation was started. Update the Genex API to a version supporting character --image, then run this command.");
20473
+ return;
20474
+ }
20392
20475
  if (!opts.json) {
20393
- log.plain(c.bold("Direct-text character quote"));
20476
+ log.plain(c.bold(imageUrls ? "Reference character quote" : "Direct-text character quote"));
20394
20477
  log.plain(` ${price.credits} Genex credits = base ${price.baseCredits} + actions ${price.animationCredits}`);
20395
20478
  if (price.controllerPackKey) {
20396
20479
  const version = price.controllerPackVersion === void 0 ? "" : ` v${price.controllerPackVersion}`;
@@ -20408,14 +20491,7 @@ async function runDirectTextCharacter(opts) {
20408
20491
  token: ctx.token,
20409
20492
  apiUrl: ctx.apiUrl,
20410
20493
  quotedCredits: price.credits,
20411
- generationOptions: {
20412
- actionIds: resolved.actionIds,
20413
- controllerPack,
20414
- ...opts.height === void 0 ? {} : { heightMeters: opts.height },
20415
- targetPolycount: opts.polycount ?? 1e4,
20416
- ...meshyQualityOptions(opts),
20417
- ...opts.pose === void 0 ? {} : { poseMode: opts.pose }
20418
- }
20494
+ generationOptions
20419
20495
  });
20420
20496
  }
20421
20497
  var CHARACTER_TEXTURE_SIZES = ["2k", "4k", "8k"];
@@ -20433,6 +20509,7 @@ function badCharacterTexture(opts) {
20433
20509
  return `--texture ${opts.texture} is a \`genex model\` tier. A character takes a texture SIZE: ${CHARACTER_TEXTURE_SIZES.join("|")} (default 4k).`;
20434
20510
  }
20435
20511
  async function runCharacterPreview(opts) {
20512
+ if (refuseSubcommandImages(opts)) return;
20436
20513
  const log = createLogger({ quiet: opts.quiet || opts.json });
20437
20514
  const conceptId = opts.conceptId?.trim();
20438
20515
  if (!conceptId) {
@@ -20486,6 +20563,7 @@ async function runCharacterPreview(opts) {
20486
20563
  });
20487
20564
  }
20488
20565
  async function runCharacterFinalize(opts) {
20566
+ if (refuseSubcommandImages(opts)) return;
20489
20567
  const log = createLogger({ quiet: opts.quiet || opts.json });
20490
20568
  const previewId = opts.previewId?.trim();
20491
20569
  if (!previewId) {
@@ -20513,7 +20591,7 @@ async function runCharacterFinalize(opts) {
20513
20591
  return;
20514
20592
  }
20515
20593
  if (opts.controllerPack === false) {
20516
- fail2(opts, "--no-controller-pack is only available with --direct-text. Guided finalization requires neutral-v3.");
20594
+ fail2(opts, "--no-controller-pack is only available with --direct-text or --image. Guided finalization requires neutral-v3.");
20517
20595
  return;
20518
20596
  }
20519
20597
  const resolved = resolveActionSelectors(opts.actions ?? []);
@@ -20583,6 +20661,7 @@ async function runCharacterFinalize(opts) {
20583
20661
  });
20584
20662
  }
20585
20663
  async function runCharacterAnimate(opts) {
20664
+ if (refuseSubcommandImages(opts)) return;
20586
20665
  const log = createLogger({ quiet: opts.quiet || opts.json });
20587
20666
  const characterId = opts.characterId?.trim();
20588
20667
  if (!characterId) {
@@ -23803,6 +23882,7 @@ ${c.bold("Usage")}
23803
23882
  genex image "<prompt>" [options] Generate an image (PNG); prints a public asset URL.
23804
23883
  genex video "<prompt>" [options] Generate a video (mp4); prints a public asset URL.
23805
23884
  genex character "<brief>" Generate 3 character concepts for user selection.
23885
+ genex character --image <path|url> Build directly from 1-4 supplied views (repeat --image).
23806
23886
  genex character preview <concept-id>
23807
23887
  Generate an unremeshed Meshy 7 preview of one
23808
23888
  explicitly user-approved concept candidate.
@@ -24050,6 +24130,10 @@ ${c.bold("Options for `list`")}
24050
24130
  --auth-url <url> Override the auth site (used only if sign-in is needed).
24051
24131
 
24052
24132
  ${c.bold("Options for `character` / `animations search`")}
24133
+ --image <path|url> Build from supplied PNG/JPEG references; repeat up to 4 times.
24134
+ One view per image, same character; local files total \u22644 MB,
24135
+ or Genex asset URLs. Brief optional (ledger label only).
24136
+ Starts one shot; cannot combine with --direct-text or guided flags.
24053
24137
  --animation <id|q> (character/finalize) add an action; repeatable. Ambiguous queries print
24054
24138
  ranked action IDs instead of choosing silently.
24055
24139
  --candidate <1|2|3> (character preview) selected concept candidate.
@@ -24060,15 +24144,15 @@ ${c.bold("Options for `character` / `animations search`")}
24060
24144
  hero). The number IS the approval.
24061
24145
  --direct-text One-shot path: skip concept/preview approval and generate
24062
24146
  directly from text (defaults to a 10,000-face target).
24063
- --no-controller-pack (character --direct-text only) skip the validated locomotion pack.
24147
+ --no-controller-pack (character one shot) skip the validated locomotion pack.
24064
24148
  --height <meters> (character) target character height, 0.5-3 meters.
24065
- --polycount <count> (character --direct-text / creature) target 10000-100000 polygons.
24149
+ --polycount <count> (character one shot / creature) target 10000-100000 polygons.
24066
24150
  --texture <res> (character/creature/preview) texture size 2k | 4k (default) | 8k
24067
24151
  (+5 credits). Meshy 7 on every lane.
24068
24152
  --no-ultra (character/creature/preview) skip Meshy Ultra (\u22125 credits, less
24069
24153
  surface detail).
24070
24154
  --pose <a-pose|t-pose>
24071
- (character --direct-text / creature) preferred rest pose; the
24155
+ (character one shot / creature) preferred rest pose; the
24072
24156
  other stays the QA fallback.
24073
24157
  --no-fingers (character import) skip finger joints in the Uthana rig.
24074
24158
  --no-wait (character/preview/finalize/animate) enqueue and return a generation id.
@@ -24141,6 +24225,7 @@ ${c.bold("Examples")}
24141
24225
  genex character preview <concept-id> --candidate 2 --user-approved
24142
24226
  genex character finalize <preview-id> --user-approved --approve-remesh 10000 --animation 466
24143
24227
  genex character "stylized sci-fi courier" --direct-text --no-wait
24228
+ genex character --image ./front.jpg --image ./back.jpg --texture 8k --polycount 100000 --no-wait
24144
24229
  genex character animate <character-id> --action <action-id>
24145
24230
  genex character animate <character-id> "overhead slam" "parry and recover" --no-wait
24146
24231
  genex character animate <character-id> --locomotion --no-wait
@@ -24193,6 +24278,7 @@ ${c.bold("Characters")}
24193
24278
  The rigged, game-ready character. ${cr("character_finalize")}
24194
24279
  genex character "<brief>" --direct-text
24195
24280
  One shot, no approval steps. ${cr("character")}
24281
+ genex character --image <path|url> One shot from 1-4 supplied PNG/JPEG views.
24196
24282
  genex creature "<desc>" A rigged enemy (biped-shaped bodies). ${String(CREATURE_TYPICAL_CREDITS).padStart(3)} cr
24197
24283
  genex character import <file.glb> Your own humanoid mesh (or a genex model), ${cr("character_import")}
24198
24284
  auto-rigged by Uthana; the upload is free.
@@ -24253,6 +24339,11 @@ ${c.bold("Make one")}
24253
24339
  genex character finalize <preview-id> --user-approved --approve-remesh <faces>
24254
24340
  The rigged, game-ready character (step 3).
24255
24341
  genex character "<brief>" --direct-text One shot, no approval steps.
24342
+ genex character --image <path|url> [--image \u2026] One shot from 1-4 supplied PNG/JPEG views.
24343
+ Local files total \u22644 MB or Genex asset URLs.
24344
+ One view per image, same character. Optional
24345
+ brief is a ledger label, not text conditioning.
24346
+ Do not combine with --direct-text or guided flags.
24256
24347
  genex creature "<desc>" The one-shot lane with enemy defaults (no controller pack).
24257
24348
  genex character import <file.glb> [--height <m>] [--no-fingers]
24258
24349
  Your own humanoid mesh \u2014 or a \`genex model\` GLB \u2014
@@ -24748,6 +24839,22 @@ function parseArgs(argv) {
24748
24839
  }
24749
24840
  }
24750
24841
  }
24842
+ const imageCount = parsed.options.imageUrls?.length ?? 0;
24843
+ if (imageCount > 0) {
24844
+ if (parsed.command === "model" && imageCount > 1) {
24845
+ parsed.error = "genex model accepts one --image reference. Use character --image for multiple character views.";
24846
+ } else if (parsed.command === "character" || parsed.command === "creature") {
24847
+ if (["preview", "finalize", "import", "animate", "motions", "adopt"].includes(parsed.options.name ?? "")) {
24848
+ parsed.error = "--image starts a new reference character; it cannot be combined with a character subcommand.";
24849
+ } else if (imageCount > 4) {
24850
+ parsed.error = "--image accepts 1-4 references to the same character, one view per image.";
24851
+ } else if (parsed.options.directText) {
24852
+ parsed.error = "--image cannot be combined with --direct-text. Omit --direct-text to build from the supplied references.";
24853
+ } else if (parsed.options.candidate !== void 0 || parsed.options.userApproved !== void 0 || parsed.options.approveRemesh !== void 0) {
24854
+ parsed.error = "--image cannot be combined with guided --candidate / --user-approved / --approve-remesh flags. Use --polycount for the reference character's face budget.";
24855
+ }
24856
+ }
24857
+ }
24751
24858
  return parsed;
24752
24859
  }
24753
24860
  function applyValueFlag(options, flag, value) {
@@ -25025,7 +25132,9 @@ function applyValueFlag(options, flag, value) {
25025
25132
  options.refs = [...options.refs ?? [], value];
25026
25133
  break;
25027
25134
  case "--image":
25135
+ if (!value.trim() || value.startsWith("--")) throw new Error("Missing value for --image");
25028
25136
  options.imageUrl = value;
25137
+ (options.imageUrls ??= []).push(value);
25029
25138
  break;
25030
25139
  case "--texture":
25031
25140
  if (!TEXTURE_FLAG_VALUES.includes(value)) {
@@ -25267,7 +25376,7 @@ async function main() {
25267
25376
  await runCharacter({
25268
25377
  ...parsed.options,
25269
25378
  prompt: parsed.options.name,
25270
- directText: true,
25379
+ directText: parsed.options.imageUrls?.length ? parsed.options.directText : true,
25271
25380
  controllerPack: false
25272
25381
  });
25273
25382
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.34.3-dev.705",
3
+ "version": "1.34.4-dev.706",
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": {
@@ -52,6 +52,24 @@ no code to rewrite, which is exactly why there is no excuse to skip it.
52
52
  selection, then an explicit approval of the separate 10,000-face
53
53
  remesh. Never compress these two stops for a user-initiated ask.
54
54
 
55
+ ## Build from supplied reference images
56
+
57
+ When the user supplies a character reference to reproduce, use it directly:
58
+
59
+ ```bash
60
+ npx genex character --image ./front.jpg --image ./back.jpg --texture 8k --polycount 100000 --pose t-pose --no-wait
61
+ ```
62
+
63
+ Repeat `--image` for 1-4 PNG/JPEG views of the same character, one view per
64
+ image. Split a contact sheet into individual views first. Local files total
65
+ at most 4 MB; Genex asset URLs also work. Meshy uses these images directly,
66
+ skipping concept generation, then remeshes and rigs the result. An optional
67
+ brief is only a ledger label, not text conditioning. Do not combine with
68
+ `--direct-text`, guided subcommands or their approval flags. One-shot quality,
69
+ pose, face-budget and controller options apply; `creature --image` omits the
70
+ controller pack by default. The following guided flow is for a new look from
71
+ text, when no supplied images already define the character.
72
+
55
73
  ## Generate a controller-ready character
56
74
 
57
75
  Before generating a Meshy character, discuss two or three visual directions.
@@ -172,8 +190,8 @@ npx genex wait <generation-id>
172
190
  ```
173
191
 
174
192
  `--animation <id-or-query>` is repeatable on `finalize`.
175
- `--no-controller-pack` is available only with the explicit `--direct-text`
176
- compatibility path. The guided parity workflow always installs neutral-v3.
193
+ `--no-controller-pack` is available with the one-shot `--direct-text` and
194
+ `--image` paths. The guided parity workflow always installs neutral-v3.
177
195
  `--no-wait` returns a generation id for `genex wait`; it does not create a
178
196
  second paid request. In the default lane, `--user-approved` and
179
197
  `--approve-remesh <faces>` record the pick you made and announced after showing
@@ -12,9 +12,26 @@ For a prop, vehicle or building, use `$genex-tool-model`. For a non-humanoid
12
12
  body plan (quadruped, flier, serpent) use that skill's `model rig` lane — this
13
13
  one produces biped-shaped bodies.
14
14
 
15
+ ## From the user's reference images
16
+
17
+ When the user supplies a character reference, send those images to Meshy:
18
+
19
+ ```bash
20
+ npx genex character --image ./front.jpg --image ./back.jpg --texture 8k --polycount 100000 --pose t-pose --no-wait
21
+ ```
22
+
23
+ `--image` is repeatable, 1-4 PNG/JPEG views of the same character, one view per
24
+ image. Local files must total at most 4 MB; Genex asset URLs also work. Split a
25
+ contact sheet into individual views first. This is one shot: it skips concept
26
+ generation and rigs the reference-derived mesh. An optional brief is only a
27
+ ledger label, not text conditioning. Do not add `--direct-text`, guided
28
+ subcommands or their approval flags. Ultra is on by default; select texture
29
+ size and face budget for the requested quality. `creature --image` uses this
30
+ same route without the player controller pack.
31
+
15
32
  ## The guided flow (three steps, one approval each)
16
33
 
17
- Use this when the character's LOOK matters and a person is choosing it:
34
+ Use this when creating a new look from text and a person is choosing it:
18
35
 
19
36
  ```bash
20
37
  npx genex character "stylized sci-fi courier, practical layered clothing"