@forgeax/engine-gltf 0.1.27 → 0.1.29

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
@@ -51,7 +51,7 @@ reflection, Pack refs, Catalog/load, and runtime sampling.
51
51
  > structured `material-tangent-required` error. The importer persists generated
52
52
  > tangents in `MeshAsset.attributes.tangent` and never writes an identity guess.
53
53
 
54
- > Runtime glTF 2.0 importer (Tier-C subset). Pure-function pipeline `parseGlb` / `parseGltf` / `toAssetPack` consumed by build-time CLI plugin bin `forgeax-engine-remote-gltf` (resolved via PATH-prefix scan for `forgeax-engine-remote-`) writing `<source>.meta.json` (external-asset-package; dispatch on top-level `importer: 'gltf'`); runtime spawn happens via the existing `loadByGuid<SceneAsset>` plus `world.instantiateScene` 4-step recipe (no `loadGltf(url)` parallel API).
54
+ > Runtime glTF 2.0 importer (Tier-C subset). Pure-function pipeline `parseGlb` / `parseGltf` / `toAssetPack` is selected by the unified `forgeax asset import` producer, which writes `<source>.meta.json` (external-asset-package; dispatch on top-level `importer: 'gltf'`); runtime spawn happens via the existing `loadByGuid<SceneAsset>` plus `world.instantiateScene` 4-step recipe (no `loadGltf(url)` parallel API).
55
55
 
56
56
  > [!IMPORTANT]
57
57
  > `toAssetPack` and `reimportReuseMeta` return `Result` values. The producer derives semantic `sourceKey` values before GUID reuse; duplicate or ambiguous identities return `duplicate-source-key` / `ambiguous-source-key` and the CLI leaves the previous sidecar untouched. `sourceIndex` is a locator only, never a generated identity.
@@ -228,7 +228,7 @@ When `ImporterRegistry` + `runImport` consumes a glTF source, malformed base64 i
228
228
  > Two submodules: `parse-skin.ts` (skin index dedupe via UUIDv5 + IBM decoding + jointPath derivation) + `parse-animation.ts` (LINEAR/STEP samplers, CUBICSPLINE/morph fail-fast). Called inside `parseGltfWithBin` -> `toAssetPack` which extends sub-asset output from 3 kinds (mesh/material/scene) to 6 (+ skeleton + skin + animation-clip).
229
229
 
230
230
  - **Limitations**: CUBICSPLINE interpolation not supported; morph weight animation not supported; jointPath resolution uses leaf-name first-match (same-name sibling is warn-only).
231
- - **BindPose AABB** derived at importer time (per skinned mesh-primitive, static BindPose) and written to mesh asset metadata for frustum cull; dynamic AABB deferred to OOS-skin-dyn-bounds.
231
+ - **Animated skin bounds** are producer-authored facts. The primary source is `skins[].extras.forgeax.conservativeAnimatedBounds`; `importSettings.conservativeAnimatedBounds[sourceIndex]` is accepted only when source extras do not provide bounds. The importer publishes the six-float bounds on `SkeletonAsset`. If both sources are absent, bounds remain absent and Render keeps the skin on the `cpu-deformation` lane; no BindPose AABB is derived as an animation-bound fallback.
232
232
 
233
233
  ## 4-step runtime recipe (apps/hello/gltf, M5)
234
234
 
@@ -248,19 +248,21 @@ const root = engine.assets.instantiate(sceneResult.value, world);
248
248
  // equivalent to world.instantiateScene(handle).
249
249
  ```
250
250
 
251
- ## CLI plugin — `forgeax-engine-remote-gltf`
251
+ ## Unified asset producer
252
252
 
253
- The build-time CLI subcommand `import` ships as a standalone plugin bin `forgeax-engine-remote-gltf` (entry `dist/cli-gltf.mjs`) declared in this package's `package.json#bin`, discovered via PATH-prefix scan for `forgeax-engine-remote-`.
253
+ The glTF producer is selected by the unified `asset import` command. DevKit
254
+ loads this package's producer only when a `.gltf` or `.glb` source is passed;
255
+ the package no longer publishes an independent executable.
254
256
 
255
257
  | Subcommand | Description | Exit code |
256
258
  |:--|:--|:--|
257
- | `forgeax-engine-remote-gltf import <path>` | Parse `.gltf` / `.glb`; write sidecar `<source>.meta.json` (top-level `importer: 'gltf'`) next to source; UUIDv7 GUIDs assigned per sub-asset in document order | 0 success / 1 `GltfError` |
258
- | `forgeax-engine-remote-gltf import <path> --check` | Dry-run mode no sidecar write; surfaces `gltf-meta-missing` route b (cf. importer route a) | 0 if sidecar already present / 1 if missing |
259
+ | `forgeax asset import <path> --root <project>` | Parse `.gltf` / `.glb`; write sidecar `<source>.meta.json` (top-level `importer: 'gltf'`) next to source; UUIDv7 GUIDs assigned per sub-asset in document order | 0 success / 1 `GltfError` |
260
+ | `forgeax asset import <path> --dry-run --root <project>` | Validate the producer path without writing a sidecar | 0 if valid / 1 if missing or invalid |
259
261
 
260
262
  ```bash
261
- # Direct invocation (after pnpm -F @forgeax/engine-gltf build)
262
- forgeax-engine-remote-gltf import apps/hello/gltf/assets/box.glb
263
- forgeax-engine-remote-gltf import apps/hello/gltf/assets/ --check
263
+ # Unified invocation
264
+ forgeax asset import apps/hello/gltf/assets/box.glb --root ./game --json
265
+ forgeax asset import apps/hello/gltf/assets/ --dry-run --root ./game --json
264
266
  ```
265
267
 
266
268
  ### End-to-end source-to-Pack route
@@ -270,14 +272,14 @@ Pack/DDC publication path. Keep these steps together when adding a glTF LOD:
270
272
 
271
273
  ```mermaid
272
274
  flowchart LR
273
- source["source .gltf/.glb"] --> cli["remote-gltf import"]
275
+ source["source .gltf/.glb"] --> cli["forgeax asset import"]
274
276
  cli --> meta["source.meta.json"]
275
277
  meta --> registry["ImporterRegistry + gltfImporter"]
276
278
  registry --> runner["runImport"]
277
279
  runner --> pack["Pack/DDC + Catalog"]
278
280
  ```
279
281
 
280
- 1. Run `forgeax-engine-remote-gltf import <source>` to create or refresh the
282
+ 1. Run `forgeax asset import <source> --root <project>` to create or refresh the
281
283
  adjacent `<source>.meta.json` and its stable sub-asset GUIDs.
282
284
  2. Register `gltfImporter` with the build-time `ImporterRegistry` and pass the
283
285
  sidecar to `runImport`; this validates the declared LOD GUID closure and
package/dist/bridge.d.ts CHANGED
@@ -53,6 +53,13 @@ export interface GltfBridgeContext {
53
53
  * matching SkinAsset.jointPaths against the spawn subtree to fill Skin.joints[].
54
54
  */
55
55
  readonly skeletonGuidBySkinIndex?: ReadonlyMap<number, string>;
56
+ /**
57
+ * glTF node index -> a live renderer-owned instance collection identity.
58
+ * The bridge is intentionally pure and cannot mint collection ids or copy
59
+ * matrix bytes into ECS. Runtime consumers create collections through their
60
+ * Renderer first, then provide this map when lowering the SceneAsset.
61
+ */
62
+ readonly instanceCollectionIdsByNodeIndex?: ReadonlyMap<number, number>;
56
63
  }
57
64
  /**
58
65
  * Convert a parsed GltfDoc into a SceneAsset POD. Caller supplies registry
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EAEN,aAAa,EACb,aAAa,EAEb,SAAS,EAIT,MAAM,EACN,UAAU,EAIX,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAA4B,KAAK,SAAS,EAAe,MAAM,aAAa,CAAC;AACpF,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,UAAU,EACV,UAAU,EAGX,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,SAAS,GAAE;IACT,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD,GACL,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CA0V9B;AAED,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzE,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjF;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChE;AA+DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,GAAG,UAAU,CA8NpF;AAED,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAEtC,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAuKD,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAsC7B;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,UAAU,EAChB,KAAK,SAAc,GAClB,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAoD7B;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,qBAAqB,GAAG,aAAa,CA2H/F"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EAEN,aAAa,EACb,aAAa,EAEb,SAAS,EAIT,MAAM,EACN,UAAU,EAGX,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAA4B,KAAK,SAAS,EAAe,MAAM,aAAa,CAAC;AACpF,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,UAAU,EACV,UAAU,EAGX,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,SAAS,GAAE;IACT,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD,GACL,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CA0V9B;AAED,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzE,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjF;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/D;;;;;OAKG;IACH,QAAQ,CAAC,gCAAgC,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzE;AA+DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,GAAG,UAAU,CAgPpF;AAED,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAEtC,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAuKD,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAsC7B;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,UAAU,EAChB,KAAK,SAAc,GAClB,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAoD7B;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,qBAAqB,GAAG,aAAa,CA2H/F"}
@@ -1 +1 @@
1
- {"version":3,"file":"cli-gltf.d.ts","sourceRoot":"","sources":["../src/cli-gltf.ts"],"names":[],"mappings":";AAgCA,UAAU,QAAQ;IAChB,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAkCD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAe/E"}
1
+ {"version":3,"file":"cli-gltf.d.ts","sourceRoot":"","sources":["../src/cli-gltf.ts"],"names":[],"mappings":";AA8BA,UAAU,QAAQ;IAChB,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAkCD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAe/E"}
package/dist/cli-gltf.mjs CHANGED
@@ -3,7 +3,7 @@ import { realpath, stat, readFile, writeFile, readdir } from 'fs/promises';
3
3
  import { resolve, dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { parseArgs } from 'util';
6
- import { err, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ok, ImportError, IMPORT_ERROR_HINTS } from '@forgeax/engine-types';
6
+ import { err, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ok, ImportError, IMPORT_ERROR_HINTS, parseConservativeAnimatedBounds } from '@forgeax/engine-types';
7
7
  import { vec3, quat, mat4 } from '@forgeax/engine-math';
8
8
  import { deriveDefaultLodScreenCoverages, reconcileMeshLodMeta } from '@forgeax/engine-import';
9
9
  import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
@@ -14,7 +14,7 @@ var GLTF_MESHOPT_FILTERS = ["NONE", "OCTAHEDRAL", "QUATERNION", "EXPONENTIAL"];
14
14
  var gltfErrorPolicy = {
15
15
  "gltf-malformed-header": {
16
16
  expected: "GLB 12-byte header (magic 0x46546C67 + version=2 + length) plus mandatory JSON chunk",
17
- hint: "verify .glb is not truncated; rerun: forgeax-engine-remote-gltf import <path>"
17
+ hint: "verify .glb is not truncated; rerun: forgeax asset import <path> --root <project> --json"
18
18
  },
19
19
  "gltf-version-unsupported": {
20
20
  expected: 'asset.version === "2.0"',
@@ -50,7 +50,7 @@ var gltfErrorPolicy = {
50
50
  },
51
51
  "gltf-meta-missing": {
52
52
  expected: "sidecar <source>.meta.json (importer: 'gltf') present in same directory",
53
- hint: "run: forgeax-engine-remote-gltf import <path>"
53
+ hint: "run: forgeax asset import <path> --root <project> --json"
54
54
  },
55
55
  "gltf-instancing-count-mismatch": {
56
56
  expected: "all instance attribute accessors share the same count",
@@ -78,7 +78,7 @@ var gltfErrorPolicy = {
78
78
  },
79
79
  "gltf-image-extract-failed": {
80
80
  expected: "image bytes extractable from bufferView / data-URI / external URI without corruption",
81
- hint: "verify the bufferView byte range / data: URI base64 / external URI sibling file is intact next to the .gltf source; rerun: forgeax-engine-remote-gltf import <path>"
81
+ hint: "verify the bufferView byte range / data: URI base64 / external URI sibling file is intact next to the .gltf source; rerun: forgeax asset import <path> --root <project> --json"
82
82
  },
83
83
  "gltf-skin-attr-asymmetric": {
84
84
  expected: "mesh primitive declares JOINTS_0 and WEIGHTS_0 symmetrically (both present or both absent)",
@@ -1320,8 +1320,6 @@ function parseGlbChunks(buffer, filePath) {
1320
1320
  ...binChunk === void 0 ? {} : { binChunk }
1321
1321
  });
1322
1322
  }
1323
-
1324
- // src/parse-skin.ts
1325
1323
  var MAX_JOINTS = 256;
1326
1324
  var SKIN_ACCESSOR_TYPES = ["MAT4"];
1327
1325
  function identityMat4() {
@@ -1402,10 +1400,14 @@ function parseSkin(skinsJson, nodesJson, accessors, bufferViews, buffers) {
1402
1400
  if (!pathResult.ok) return err(pathResult.error);
1403
1401
  jointPaths.push(pathResult.value.join("/"));
1404
1402
  }
1403
+ const bounds = parseConservativeAnimatedBounds(
1404
+ skin.extras?.forgeax?.conservativeAnimatedBounds
1405
+ );
1405
1406
  records.push({
1406
1407
  jointCount: joints.length,
1407
1408
  inverseBindMatrices: ibm,
1408
- jointPaths
1409
+ jointPaths,
1410
+ ...bounds === void 0 ? {} : { bounds }
1409
1411
  });
1410
1412
  }
1411
1413
  return ok(records);
@@ -2612,14 +2614,14 @@ function emitError(ctx, err3) {
2612
2614
  }
2613
2615
  function helpBody() {
2614
2616
  return [
2615
- "forgeax-engine-remote-gltf \u2014 glTF / GLB sidecar importer (produces texture/mesh/material/scene sub-assets)",
2617
+ "forgeax asset import \u2014 glTF / GLB sidecar importer (internal producer)",
2616
2618
  "",
2617
2619
  "Usage:",
2618
- " forgeax-engine-remote-gltf import <path.gltf|path.glb>",
2619
- " forgeax-engine-remote-gltf import --check <dir>",
2620
+ " forgeax asset import <path.gltf|path.glb> --root <project>",
2621
+ " forgeax asset import <dir> --dry-run --root <project>",
2620
2622
  "",
2621
- "Produces a sibling <source>.meta.json sidecar with sub-asset entries",
2622
- "for every glTF asset category: texture, mesh, material, scene.",
2623
+ "produces texture, mesh, material, and scene sub-asset entries in a sibling",
2624
+ "<source>.meta.json sidecar.",
2623
2625
  ""
2624
2626
  ].join("\n");
2625
2627
  }
@@ -2633,7 +2635,7 @@ async function runCliGltf(rest, ctx) {
2633
2635
  return emitError(ctx, {
2634
2636
  code: "unknown-subcommand",
2635
2637
  expected: "subcommand 'import'",
2636
- hint: "run 'forgeax-engine-remote-gltf --help' for usage",
2638
+ hint: "run 'forgeax help asset import' for usage",
2637
2639
  detail: { subcommand: sub }
2638
2640
  });
2639
2641
  }
@@ -2655,8 +2657,8 @@ async function runImport(rest, ctx) {
2655
2657
  const message = e instanceof Error ? e.message : String(e);
2656
2658
  return emitError(ctx, {
2657
2659
  code: "cli-parse-error",
2658
- expected: "forgeax-engine-remote-gltf import [--check] <path>",
2659
- hint: "run 'forgeax-engine-remote-gltf --help' for usage",
2660
+ expected: "forgeax asset import [--dry-run] <path> --root <project>",
2661
+ hint: "run 'forgeax help asset import' for usage",
2660
2662
  detail: { message }
2661
2663
  });
2662
2664
  }
@@ -2664,7 +2666,7 @@ async function runImport(rest, ctx) {
2664
2666
  if (target === void 0) {
2665
2667
  return emitError(ctx, {
2666
2668
  code: "cli-parse-error",
2667
- expected: check ? "forgeax-engine-remote-gltf import --check <dir>" : "forgeax-engine-remote-gltf import <path.gltf|path.glb>",
2669
+ expected: check ? "forgeax asset import <dir> --dry-run --root <project>" : "forgeax asset import <path.gltf|path.glb> --root <project>",
2668
2670
  hint: "pass a positional <gltf-or-glb> argument; with --check pass a directory"
2669
2671
  });
2670
2672
  }
@@ -2730,7 +2732,7 @@ async function runCheck(target, ctx) {
2730
2732
  const message = e instanceof Error ? e.message : String(e);
2731
2733
  return emitError(ctx, {
2732
2734
  code: "cli-parse-error",
2733
- expected: "forgeax-engine-remote-gltf import --check <dir>",
2735
+ expected: "forgeax asset import <dir> --dry-run --root <project>",
2734
2736
  hint: "pass a directory that exists and is readable",
2735
2737
  detail: { path: target, message }
2736
2738
  });