Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/skill-parity/capability-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
],
"evidence": [
"payload merge defaults < --data < flags with explicit false/0 preserved; tests T-028; surface.test.ts unchanged",
"tests/codex-review-round1.test.ts R04/F08"
"tests/codex-review-round1.test.ts R04/F08",
"0.4.3: smart-topology --ai-model meshy-t2|meshy-t2.5 on image-to-3d and text-to-3d preview, per-model target_polycount range on the merged payload, meshy-t2.5 polycount default left to the API (decisions D-078); tests/api-alignment-features.test.ts, tests/surface.test.ts"
],
"intentional_differences": [],
"external_verification": "live_passed",
Expand Down
29 changes: 28 additions & 1 deletion docs/skill-parity/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ behind it, and what a reviewer should check. IDs are stable; append, do not renu

- `engines.node` was raised from `>=20` to `>=24` in `7edf056` ("chore: upgrade
Node and dependencies") as a side effect of a dependency bump, not because any
code needed Node 24. Audited on 2026-09-22: the only runtime gate in the tree
code needed Node 24. Checked on 2026-09-22: the only runtime gate in the tree
was `REQUIRED_NODE_MAJOR = 24` in `doctor.ts`; `@types/node@^22` typechecks
clean and the full suite is 563/563 on Node 22.22.0. The real floor is the
strictest dependency, `commander@15` at `>=22.12.0`.
Expand Down Expand Up @@ -1075,3 +1075,30 @@ behind it, and what a reviewer should check. IDs are stable; append, do not renu
call. It is now kind `action` (a one-off API call that changes state) with
its endpoint, and `counts` gains `action`. The entry keeps its keys and its
place at the end of `items`.

## D-078 `--ai-model` picks the Smart Topology model: meshy-t2 or meshy-t2.5

- Smart topology now has two models, so "the mode is the model" no longer
holds for it. image-to-3d and text-to-3d preview take `--ai-model
meshy-t2|meshy-t2.5`, with `--model-type smart-topology` only. Standard mode
keeps no model flag (other models stay reachable through `--data`), and
multi-image-to-3d gets none: the API offers no Smart Topology there.
- Without `--ai-model` nothing changes: the request carries no `ai_model` and
the API picks meshy-t2.
- `target_polycount` is checked per model before the request: meshy-t2
100–15,000, meshy-t2.5 100–30,000. image-to-3d had no local range check
before.
- Polycount default: meshy-t2 keeps the CLI's 10,000 (D-076). meshy-t2.5 sends
none and gets the API's 15,000. The 10,000 pin exists because meshy-t2's own
default (4,000) is too low for rigging; meshy-t2.5's is not, and pinning
10,000 would quietly cap a model picked for denser meshes. The default
follows `--ai-model`; a model set only through `--data` gets the meshy-t2
default, which meshy-t2.5 also accepts.
- The checks run on the merged payload, so `--data` meets the same rules. A
Smart Topology `ai_model` without `model_type: "smart-topology"`,
`topology: "quad"` with smart-topology (its output is triangle-only; chain
`remesh --topology quad` for quads) and an out-of-range polycount are usage
errors (exit 2).
- Credits are meshy-t2's: 5 for a mesh only; image-to-3d with 2K or 4K textures
15, with 8K textures 20. A text-to-3d refine of a meshy-t2.5 preview works
like one of a meshy-t2 preview.
8 changes: 8 additions & 0 deletions docs/skill-parity/endpoint-contracts.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
{ "id": "creative-lab.fidget-pixel.build", "commandPath": ["creative-lab", "fidget-pixel", "build"], "base": "creative-lab", "relativePath": "/fidget-pixel/v1/build", "legacyEndpoint": "/openapi/creative-lab/fidget-pixel/v1/build", "supports": { "create": true, "get": true, "list": true, "delete": true, "stream": true }, "mediaFields": [], "taskTypes": ["creative-lab-fidget-pixel-build"], "billing": { "create": "may-charge" } },
{ "id": "creative-lab.fidget-collapsible", "commandPath": ["creative-lab", "fidget-collapsible"], "base": "creative-lab", "relativePath": "/fidget-collapsible/v1", "legacyEndpoint": "/openapi/creative-lab/fidget-collapsible/v1", "supports": { "create": true, "get": true, "list": true, "delete": true, "stream": true }, "mediaFields": [ { "path": "image_url", "kind": "image", "many": false, "formats": ["jpg", "jpeg", "png", "webp"] } ], "taskTypes": ["creative-lab-fidget-collapsible"], "billing": { "create": "may-charge" } }
],
"smart_topology": {
"applies_to": ["image-to-3d", "text-to-3d (preview)"],
"request": { "model_type": "smart-topology (required for these models)", "ai_model": { "enum": ["meshy-t2", "meshy-t2.5"], "default": "omitted: the API picks meshy-t2", "cli_flag": "--ai-model" }, "topology": "triangle only; quad is refused" },
"target_polycount": { "meshy-t2": { "range": "[100,15000]", "api_default": 4000, "cli_default": 10000 }, "meshy-t2.5": { "range": "[100,30000]", "api_default": 15000, "cli_default": "omitted (the API's 15000)" } },
"credits": "both models: 5 mesh only; image-to-3d with 2K or 4K textures 15, with 8K textures 20",
"refine": "a text-to-3d refine of a meshy-t2.5 preview works like one of a meshy-t2 preview",
"decision": "D-078"
},
"creative_lab_products": {
"figure": {
"prototype": {
Expand Down
12 changes: 11 additions & 1 deletion docs/skill-parity/migration-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,19 @@ Each difference names the legacy behaviour, the CLI behaviour, and the evidence.
| `animation-catalog list` | anonymous, local search (name / key / sub-category); result had `fetched`, `total`, `authenticated`, `search_scope: "local"`; items had `rig_type` / `is_free` / `is_default` | needs a key; server-side filters (`--search` matches name / key, `--sub-category` filters); result has `count`, `filters`, `search_scope: "server"`; items as the API returns them | D-073 |
| `balance` | `balance` only | also `monthly_credits`, `permanent_credits`, `monthly_resets_at` | D-076 |

### 3.9 Changes in 0.4.3 (visible in both schemas)

| Behaviour | 0.4.2 and earlier | 0.4.3 | Decision |
| --- | --- | --- | --- |
| `--ai-model` on image-to-3d and text-to-3d preview | no flag (`--data` only) | `meshy-t2` \| `meshy-t2.5`, with `--model-type smart-topology` only; omitted, the API picks meshy-t2 as before | D-078 |
| smart-topology `--target-polycount` | text-to-3d checked 100–15,000; image-to-3d sent any value | checked per model on both: meshy-t2 100–15,000, meshy-t2.5 100–30,000 (exit 2 outside) | D-078 |
| smart-topology polycount default with `--ai-model meshy-t2.5` | — | none sent (the API's 15,000); meshy-t2 still gets 10,000 | D-078 |
| a Smart Topology `ai_model` without smart-topology, or smart-topology with `topology: "quad"` (through `--data`) | sent, refused by the API | refused locally (exit 2) | D-078 |

New global flags: `--output-schema`, `--api-key-file`, `--workspace`, `--no-update-check`,
`--base-url-creative-lab`. New per-command flags on task verbs: `--save-json`,
`--include-raw`, `--project`, `--stage`, `--operation-id`, `--stop-after-first` (make),
`--idle-timeout` (stream). New commands: `uv-unwrap`, `creative-lab`, `animation-catalog`,
`showcases`, `download`, `project`, `inspect`, `mesh`, `slicer`, `doctor`; in 0.4.2
`auto-split` and `usage`.
`auto-split` and `usage`. In 0.4.3: `--ai-model` on image-to-3d and text-to-3d
(smart-topology).
6 changes: 5 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,12 @@ meshy-cli image-to-3d create --image-url https://example.com/cat.png --geometry-
# text → standalone motion clip (Prime/FBX by default; Swift produces BVH)
meshy-cli text-to-motion create --prompt "a character waving" --duration 3

# smart topology: component-aware low-poly with a native polycount
# smart topology: component-aware low-poly with a native polycount (triangles only).
# meshy-t2 (the API's choice when --ai-model is omitted): 100-15000, CLI default 10000;
# meshy-t2.5: 100-30000, default the API's 15000. Credits for either: 5 mesh only,
# 15 textured at 2k/4k, 20 at 8k. A meshy-t2.5 preview refines like any other.
meshy-cli image-to-3d create --image-url cat.png --model-type smart-topology --target-polycount 10000
meshy-cli image-to-3d create --image-url cat.png --model-type smart-topology --ai-model meshy-t2.5 --target-polycount 25000
meshy-cli text-to-3d create --mode preview --prompt "a toy robot" --model-type smart-topology --target-polycount 8000

# printability with a print size: also checks (analyze) or thickens (repair) thin walls
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "meshy-cli",
"version": "0.4.2",
"version": "0.4.3",
"description": "Official command-line interface for the Meshy AI API — text-to-3D, image-to-3D, text-to-motion, remesh, UV unwrap, rigging, animate, retexture, 2D images, printability, auto split, multi-color print, Creative Lab, balance and usage — plus selective downloads, project folders, face checks, OBJ print preparation and slicer launch.",
"license": "MIT",
"type": "module",
Expand Down
5 changes: 3 additions & 2 deletions skills/meshy-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,9 @@ These are API rules, not preferences — ignoring them produces failed tasks:
`--geometry-resolution 2k|4k` runs the Ultra geometry pass (+5 credits;
text-to-3d: preview only; multi-image: `2k` only; not with smart-topology);
`--ultra-mode` on image-to-3d is its deprecated spelling of `2k`.
`--model-type smart-topology` (meshy-t2, text-to-3d and image-to-3d) has a
native `--target-polycount` (default 10000). `retexture`'s
`--model-type smart-topology` (text-to-3d and image-to-3d) has a native
`--target-polycount`: meshy-t2 100-15000 (default 10000); `--ai-model
meshy-t2.5` up to 30000 (default 15000). `retexture`'s
`--multiview-image-urls` needs Meshy 7 — it takes 1-4 views of **the same
object**, not style references.
- Printing: `analyze-`/`repair-printability` take `--printer-type
Expand Down
38 changes: 24 additions & 14 deletions src/cmd/image-to-3d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* image-to-3d — https://docs.meshy.ai/en/api/image-to-3d
*
* The mode IS the model — there is no model flag:
* The mode picks the model:
* - standard → meshy-7.1 (server default "latest") — full-detail
* generation; polycount is remesh's job afterwards.
* - smart-topology → meshy-t2 (server default) — component-aware low-poly
* with a native --target-polycount; the budget pick for game-ready
* geometry.
* - smart-topology → meshy-t2 (server default), or meshy-t2.5 with
* --ai-model — component-aware low-poly with a native
* --target-polycount; the budget pick for game-ready geometry.
*
* The Ultra geometry pass (--geometry-resolution 2k|4k, or the deprecated
* --ultra-mode) rides on standard only: it needs meshy-7.1, so pairing it with
Expand All @@ -17,6 +17,7 @@ import { Option } from "commander";
import { parseBool, parseCsv, parseInt10 } from "../internal/flags.js";
import { UsageError } from "../internal/errors.js";
import { buildResourceCommand, type ResourceCommandSpec } from "../internal/task-command.js";
import { checkSmartTopology, defaultPolycount, SMART_TOPOLOGY_MODELS } from "../internal/smart-topology.js";

const spec: ResourceCommandSpec = {
name: "image-to-3d",
Expand All @@ -38,12 +39,18 @@ const spec: ResourceCommandSpec = {
.addOption(
new Option(
"--model-type <kind>",
"standard (default; meshy-7.1 full-detail generation) | smart-topology (meshy-t2 component-aware low-poly: clean topology, separated parts)",
"standard (default; meshy-7.1 full-detail generation) | smart-topology (component-aware low-poly: clean topology, separated parts; meshy-t2, or --ai-model meshy-t2.5)",
).choices(["standard", "smart-topology"]),
)
.addOption(
new Option(
"--ai-model <model>",
"smart-topology only: meshy-t2 (the API's choice when omitted) | meshy-t2.5 (polycount up to 30000)",
).choices([...SMART_TOPOLOGY_MODELS]),
)
.option(
"--target-polycount <n>",
"smart-topology only: target triangle count, 100-15000 (default: 10000). For standard outputs use remesh",
"smart-topology only: target triangle count — meshy-t2 100-15000 (default: 10000), meshy-t2.5 100-30000 (default: the API's 15000). For standard outputs use remesh",
parseInt10,
)
.addOption(
Expand Down Expand Up @@ -105,11 +112,11 @@ const spec: ResourceCommandSpec = {
// The Ultra pass needs meshy-7.1, which only standard mode resolves to.
if (opts.ultraMode === true && smart) {
throw new UsageError(
"--ultra-mode applies to --model-type standard only (it needs meshy-7.1; smart-topology runs meshy-t2)",
"--ultra-mode applies to --model-type standard only (it needs meshy-7.1)",
);
}
if (smart && opts.geometryResolution !== undefined && opts.geometryResolution !== "standard") {
throw new UsageError("--geometry-resolution 2k/4k applies to --model-type standard only (it needs meshy-7.1; smart-topology runs meshy-t2)");
throw new UsageError("--geometry-resolution 2k/4k applies to --model-type standard only (it needs meshy-7.1)");
}
if (opts.ultraMode !== undefined && opts.geometryResolution !== undefined) {
throw new UsageError("--ultra-mode is the deprecated spelling of --geometry-resolution 2k; pass only --geometry-resolution");
Expand All @@ -132,6 +139,7 @@ const spec: ResourceCommandSpec = {
image_url: opts.imageUrl,
input_task_id: opts.inputTaskId,
model_type: opts.modelType,
ai_model: opts.aiModel,
target_polycount: opts.targetPolycount,
ultra_mode: opts.ultraMode,
geometry_resolution: opts.geometryResolution,
Expand All @@ -151,26 +159,28 @@ const spec: ResourceCommandSpec = {
// texturing is its own step, and the API rejects texture knobs on
// untextured runs, so they ride only when texturing is on — then the
// game-ready defaults apply: full PBR map set, 4k base color. The
// model is not a choice — it follows the mode (standard → meshy-7.1,
// smart-topology → meshy-t2, both server defaults). Smart topology
// gets the rigging-friendly 10k polycount. The Ultra pass stays off
// model follows the mode (standard → meshy-7.1, smart-topology →
// meshy-t2, both server defaults) unless --ai-model picks meshy-t2.5.
// meshy-t2 gets the rigging-friendly 10k polycount; meshy-t2.5 keeps
// the API's 15k (D-078). The Ultra pass stays off
// unless asked for: it bills extra. GLB-only output — omitting target_formats
// makes the API produce every format.
const texturing = opts.shouldTexture === true;
return {
should_texture: false,
enable_pbr: texturing ? true : undefined,
texture_resolution: texturing ? "4k" : undefined,
target_polycount: opts.modelType === "smart-topology" ? 10_000 : undefined,
target_polycount: defaultPolycount(opts),
target_formats: ["glb"],
};
},
validatePayload(payload) {
// meshy-t2 has neither knob; v1 answers 400 for either (it does not ignore them).
checkSmartTopology(payload);
// Smart topology has neither knob; v1 answers 400 for either (it does not ignore them).
if (payload.model_type === "smart-topology") {
for (const [flag, key] of [["--remove-lighting", "remove_lighting"], ["--image-enhancement", "image_enhancement"]] as const) {
if (payload[key] !== undefined) {
throw new UsageError(`${flag} applies to --model-type standard only (smart-topology runs meshy-t2, which the API refuses it for)`);
throw new UsageError(`${flag} applies to --model-type standard only (the API refuses it for smart-topology)`);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/multi-image-to-3d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* multi-image-to-3d — https://docs.meshy.ai/en/api/multi-image-to-3d
*
* No model flag, same as the other 3D generation commands: the API default
* "latest" is Meshy 7.1. geometry_resolution "2k" runs the Ultra pass (+5
* No model flag (the Smart Topology models are not offered here): the API
* default "latest" is Meshy 7.1. geometry_resolution "2k" runs the Ultra pass (+5
* credits; the same geometry setting as the web app's Ultra 2K) and needs
* meshy-7.1 / latest; the deprecated ultra_mode boolean is its old spelling.
* "4k" is single-image only — the API refuses it here. Only the enum is
Expand Down
Loading
Loading