unity-agentic-tools 0.3.2 → 0.3.3

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/cli.js CHANGED
@@ -2299,7 +2299,7 @@ var require_package = __commonJS((exports2, module2) => {
2299
2299
  module2.exports = {
2300
2300
  name: "unity-agentic-tools",
2301
2301
  packageManager: "bun@latest",
2302
- version: "0.3.2",
2302
+ version: "0.3.3",
2303
2303
  description: "Fast, token-efficient Unity YAML parser for AI agents",
2304
2304
  exports: {
2305
2305
  ".": {
@@ -3358,188 +3358,61 @@ function resolve_enum_fields(fields, project_path) {
3358
3358
  }
3359
3359
  } catch {}
3360
3360
  }
3361
-
3362
- // src/editor/yaml-fields.ts
3363
- var PRIMITIVE_DEFAULTS = {
3364
- int: "0",
3365
- float: "0",
3366
- double: "0",
3367
- bool: "0",
3368
- string: "",
3369
- byte: "0",
3370
- sbyte: "0",
3371
- short: "0",
3372
- ushort: "0",
3373
- uint: "0",
3374
- long: "0",
3375
- ulong: "0",
3376
- char: "0",
3377
- Int32: "0",
3378
- Single: "0",
3379
- Double: "0",
3380
- Boolean: "0",
3381
- String: "",
3382
- Byte: "0",
3383
- SByte: "0",
3384
- Int16: "0",
3385
- UInt16: "0",
3386
- UInt32: "0",
3387
- Int64: "0",
3388
- UInt64: "0",
3389
- Char: "0"
3390
- };
3391
- var STRUCT_DEFAULTS = {
3392
- Vector2: "{x: 0, y: 0}",
3393
- Vector3: "{x: 0, y: 0, z: 0}",
3394
- Vector4: "{x: 0, y: 0, z: 0, w: 0}",
3395
- Vector2Int: "{x: 0, y: 0}",
3396
- Vector3Int: "{x: 0, y: 0, z: 0}",
3397
- Quaternion: "{x: 0, y: 0, z: 0, w: 1}",
3398
- Color: "{r: 0, g: 0, b: 0, a: 0}",
3399
- Color32: "{r: 0, g: 0, b: 0, a: 0}",
3400
- Rect: `serializedVersion: 2
3401
- x: 0
3402
- y: 0
3403
- width: 0
3404
- height: 0`,
3405
- RectInt: "{x: 0, y: 0, width: 0, height: 0}",
3406
- RectOffset: "{m_Left: 0, m_Right: 0, m_Top: 0, m_Bottom: 0}",
3407
- Matrix4x4: "{e00: 1, e01: 0, e02: 0, e03: 0, e10: 0, e11: 1, e12: 0, e13: 0, e20: 0, e21: 0, e22: 1, e23: 0, e30: 0, e31: 0, e32: 0, e33: 1}",
3408
- LayerMask: `serializedVersion: 2
3409
- m_Bits: 0`
3410
- };
3411
- var VERSION_GATED_STRUCT_DEFAULTS = {
3412
- Hash128: { value: `serializedVersion: 2
3413
- Hash: 00000000000000000000000000000000`, min_major: 2021, min_minor: 1 },
3414
- RenderingLayerMask: { value: `serializedVersion: 2
3415
- m_Bits: 0`, min_major: 6000, min_minor: 0 }
3416
- };
3417
- var BLOCK_STRUCT_DEFAULTS = {
3418
- Bounds: `m_Center: {x: 0, y: 0, z: 0}
3419
- m_Extent: {x: 0, y: 0, z: 0}`,
3420
- BoundsInt: `m_Position: {x: 0, y: 0, z: 0}
3421
- m_Size: {x: 0, y: 0, z: 0}`
3422
- };
3423
- var OBJECT_REF_TYPES = new Set([
3424
- "GameObject",
3425
- "Transform",
3426
- "RectTransform",
3427
- "Material",
3428
- "Texture",
3429
- "Texture2D",
3430
- "Texture3D",
3431
- "RenderTexture",
3432
- "Sprite",
3433
- "AudioClip",
3434
- "VideoClip",
3435
- "Mesh",
3436
- "Shader",
3437
- "ComputeShader",
3438
- "AnimationClip",
3439
- "AnimatorController",
3440
- "RuntimeAnimatorController",
3441
- "PhysicMaterial",
3442
- "PhysicsMaterial",
3443
- "PhysicsMaterial2D",
3444
- "Font",
3445
- "TMP_FontAsset",
3446
- "Object",
3447
- "Component",
3448
- "Behaviour",
3449
- "MonoBehaviour",
3450
- "ScriptableObject",
3451
- "Rigidbody",
3452
- "Rigidbody2D",
3453
- "Collider",
3454
- "Collider2D",
3455
- "Camera",
3456
- "Light",
3457
- "Canvas",
3458
- "CanvasGroup",
3459
- "EventSystem",
3460
- "AudioSource",
3461
- "ParticleSystem",
3462
- "TextAsset",
3463
- "TerrainData"
3464
- ]);
3465
- function version_at_least(version, min_major, min_minor) {
3466
- if (!version)
3467
- return false;
3468
- if (version.major > min_major)
3469
- return true;
3470
- if (version.major === min_major)
3471
- return version.minor >= min_minor;
3472
- return false;
3473
- }
3474
- function yaml_default_for_type(csharp_type, version) {
3475
- if (csharp_type.endsWith("?")) {
3476
- return null;
3477
- }
3478
- if (csharp_type in PRIMITIVE_DEFAULTS) {
3479
- return PRIMITIVE_DEFAULTS[csharp_type];
3480
- }
3481
- if (csharp_type in STRUCT_DEFAULTS) {
3482
- return STRUCT_DEFAULTS[csharp_type];
3483
- }
3484
- if (csharp_type in VERSION_GATED_STRUCT_DEFAULTS) {
3485
- const gated = VERSION_GATED_STRUCT_DEFAULTS[csharp_type];
3486
- if (version_at_least(version, gated.min_major, gated.min_minor)) {
3487
- return gated.value;
3488
- }
3489
- return null;
3490
- }
3491
- if (csharp_type in BLOCK_STRUCT_DEFAULTS) {
3492
- return BLOCK_STRUCT_DEFAULTS[csharp_type];
3493
- }
3494
- if (csharp_type.endsWith("[]")) {
3495
- return "[]";
3496
- }
3497
- if (csharp_type.startsWith("List<") && csharp_type.endsWith(">")) {
3498
- return "[]";
3499
- }
3500
- if (OBJECT_REF_TYPES.has(csharp_type)) {
3501
- return "{fileID: 0}";
3502
- }
3503
- return "{fileID: 0}";
3504
- }
3505
- function generate_field_yaml(fields, version, indent = " ") {
3506
- const lines = [];
3507
- for (const field of fields) {
3508
- if (field.hasSerializeReference) {
3509
- const t = field.typeName;
3510
- if (t.endsWith("[]") || t.startsWith("List<") && t.endsWith(">")) {
3511
- lines.push(`${indent}${field.name}: []`);
3512
- } else {
3513
- lines.push(`${indent}${field.name}:`);
3514
- lines.push(`${indent} rid: 0`);
3361
+ function build_type_lookup(project_path) {
3362
+ let extractFn = null;
3363
+ try {
3364
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
3365
+ extractFn = getNativeExtractSerializedFields2();
3366
+ } catch {}
3367
+ if (!extractFn)
3368
+ return () => null;
3369
+ const assetsDir = path.join(project_path, "Assets");
3370
+ const csIndex = new Map;
3371
+ try {
3372
+ const { readdirSync: readdirSync3 } = require("fs");
3373
+ const entries = readdirSync3(assetsDir, { recursive: true, withFileTypes: false });
3374
+ for (const entry of entries) {
3375
+ if (typeof entry === "string" && entry.endsWith(".cs")) {
3376
+ const basename3 = entry.substring(entry.lastIndexOf("/") + 1).replace(/\.cs$/, "");
3377
+ if (!csIndex.has(basename3)) {
3378
+ csIndex.set(basename3, path.join(assetsDir, entry));
3379
+ }
3515
3380
  }
3516
- continue;
3517
3381
  }
3518
- const default_value = yaml_default_for_type(field.typeName, version);
3519
- if (default_value === null) {
3520
- continue;
3382
+ } catch {}
3383
+ if (csIndex.size === 0)
3384
+ return () => null;
3385
+ const cache = new Map;
3386
+ return (typeName) => {
3387
+ if (cache.has(typeName))
3388
+ return cache.get(typeName) ?? null;
3389
+ const shortName = typeName.includes(".") ? typeName.substring(typeName.lastIndexOf(".") + 1) : typeName;
3390
+ const csPath = csIndex.get(shortName);
3391
+ if (!csPath || !import_fs6.existsSync(csPath)) {
3392
+ cache.set(typeName, null);
3393
+ return null;
3521
3394
  }
3522
- if (default_value.includes(`
3523
- `)) {
3524
- lines.push(`${indent}${field.name}:`);
3525
- for (const sub_line of default_value.split(`
3526
- `)) {
3527
- lines.push(`${indent}${sub_line}`);
3395
+ try {
3396
+ const typeInfos = extractFn(csPath);
3397
+ if (!typeInfos || typeInfos.length === 0) {
3398
+ cache.set(typeName, null);
3399
+ return null;
3528
3400
  }
3529
- } else {
3530
- lines.push(`${indent}${field.name}: ${default_value}`);
3401
+ const match = typeInfos.find((t) => t.name === shortName);
3402
+ if (match && match.fields && match.fields.length > 0) {
3403
+ resolve_enum_fields(match.fields, project_path);
3404
+ cache.set(typeName, match.fields);
3405
+ return match.fields;
3406
+ }
3407
+ cache.set(typeName, null);
3408
+ return null;
3409
+ } catch {
3410
+ cache.set(typeName, null);
3411
+ return null;
3531
3412
  }
3532
- }
3533
- return lines.length > 0 ? `
3534
- ` + lines.join(`
3535
- `) + `
3536
- ` : `
3537
- `;
3413
+ };
3538
3414
  }
3539
3415
 
3540
- // src/editor/unity-document.ts
3541
- var import_fs7 = require("fs");
3542
-
3543
3416
  // src/editor/unity-block.ts
3544
3417
  var HEADER_PATTERN = /^--- !u!(\d+) &(-?\d+)(\s+stripped)?/;
3545
3418
  function parse_header(raw) {
@@ -3635,10 +3508,36 @@ class UnityBlock {
3635
3508
  const old_raw = this._raw;
3636
3509
  const effective_ref = object_reference && object_reference !== "{fileID: 0}" ? object_reference : undefined;
3637
3510
  if (!path2.includes(".") && !path2.includes("Array")) {
3638
- const prop_pattern = new RegExp(`(^\\s*${escape_regex(path2)}:\\s*)(.*)$`, "m");
3639
- if (prop_pattern.test(this._raw)) {
3511
+ const prop_pattern = new RegExp(`(^\\s*${escape_regex(path2)}:[ \\t]*)(.*)$`, "m");
3512
+ const match = this._raw.match(prop_pattern);
3513
+ if (match && match.index !== undefined) {
3640
3514
  const replacement_value = effective_ref ?? yaml_quote_if_needed(value);
3641
- this._raw = this._raw.replace(prop_pattern, (_match, prefix) => prefix + replacement_value);
3515
+ const current_inline = match[2];
3516
+ if (current_inline.trim() === "") {
3517
+ const key_indent = (match[0].match(/^[ \t]*/)?.[0] || "").length;
3518
+ const after = match.index + match[0].length;
3519
+ let end = after;
3520
+ const rest = this._raw.slice(after);
3521
+ const rest_lines = rest.split(`
3522
+ `);
3523
+ for (let i = 1;i < rest_lines.length; i++) {
3524
+ const line = rest_lines[i];
3525
+ if (line.trim() === "") {
3526
+ end += 1 + line.length;
3527
+ continue;
3528
+ }
3529
+ const line_indent = (line.match(/^[ \t]*/)?.[0] || "").length;
3530
+ if (line_indent > key_indent) {
3531
+ end += 1 + line.length;
3532
+ } else {
3533
+ break;
3534
+ }
3535
+ }
3536
+ const prefix = match[1].trimEnd() + " ";
3537
+ this._raw = this._raw.slice(0, match.index) + prefix + replacement_value + this._raw.slice(end);
3538
+ } else {
3539
+ this._raw = this._raw.replace(prop_pattern, (_match, prefix) => prefix + replacement_value);
3540
+ }
3642
3541
  } else {
3643
3542
  const insert_value = effective_ref ?? yaml_quote_if_needed(value);
3644
3543
  this._insert_property(path2, insert_value);
@@ -3718,6 +3617,11 @@ class UnityBlock {
3718
3617
  if (parent_pattern.test(this._raw)) {
3719
3618
  return;
3720
3619
  }
3620
+ const alt = parent.startsWith("m_") ? parent.slice(2) : "m_" + parent;
3621
+ const alt_pattern = new RegExp(`^[ \\t]*${escape_regex(alt)}:`, "m");
3622
+ if (alt_pattern.test(this._raw)) {
3623
+ return;
3624
+ }
3721
3625
  const struct_block = ` ${parent}:
3722
3626
  ${child}: ${value}`;
3723
3627
  const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
@@ -4020,81 +3924,359 @@ ${element_indent2}- ${value}`);
4020
3924
  }
4021
3925
  return null;
4022
3926
  }
4023
- _resolve_block_style_path(segments, value) {
4024
- const lines = this._raw.split(`
4025
- `);
4026
- let search_start = 0;
4027
- let search_end = lines.length;
4028
- for (let seg = 0;seg < segments.length - 1; seg++) {
4029
- const segment = segments[seg];
4030
- let parent_idx = -1;
4031
- let parent_indent = -1;
4032
- for (let i = search_start;i < search_end; i++) {
4033
- const match = lines[i].match(new RegExp(`^(\\s*)${escape_regex(segment)}:\\s*(.*)$`));
4034
- if (match) {
4035
- const trailing = match[2].trim();
4036
- if (trailing === "") {
4037
- parent_idx = i;
4038
- parent_indent = match[1].length;
4039
- break;
4040
- }
4041
- for (let j = i + 1;j < search_end; j++) {
4042
- if (lines[j].trim() === "")
4043
- continue;
4044
- const nextLeading = lines[j].match(/^(\s*)/);
4045
- if (nextLeading && nextLeading[1].length > match[1].length) {
4046
- parent_idx = i;
4047
- parent_indent = match[1].length;
4048
- }
4049
- break;
4050
- }
4051
- if (parent_idx !== -1)
4052
- break;
4053
- }
3927
+ _resolve_block_style_path(segments, value) {
3928
+ const lines = this._raw.split(`
3929
+ `);
3930
+ let search_start = 0;
3931
+ let search_end = lines.length;
3932
+ for (let seg = 0;seg < segments.length - 1; seg++) {
3933
+ const segment = segments[seg];
3934
+ let parent_idx = -1;
3935
+ let parent_indent = -1;
3936
+ for (let i = search_start;i < search_end; i++) {
3937
+ const match = lines[i].match(new RegExp(`^(\\s*)${escape_regex(segment)}:\\s*(.*)$`));
3938
+ if (match) {
3939
+ const trailing = match[2].trim();
3940
+ if (trailing === "") {
3941
+ parent_idx = i;
3942
+ parent_indent = match[1].length;
3943
+ break;
3944
+ }
3945
+ for (let j = i + 1;j < search_end; j++) {
3946
+ if (lines[j].trim() === "")
3947
+ continue;
3948
+ const nextLeading = lines[j].match(/^(\s*)/);
3949
+ if (nextLeading && nextLeading[1].length > match[1].length) {
3950
+ parent_idx = i;
3951
+ parent_indent = match[1].length;
3952
+ }
3953
+ break;
3954
+ }
3955
+ if (parent_idx !== -1)
3956
+ break;
3957
+ }
3958
+ }
3959
+ if (parent_idx === -1)
3960
+ return null;
3961
+ let child_indent = -1;
3962
+ for (let i = parent_idx + 1;i < search_end; i++) {
3963
+ if (lines[i].trim() === "")
3964
+ continue;
3965
+ const leading_spaces = lines[i].match(/^(\s*)/);
3966
+ if (leading_spaces) {
3967
+ const indent = leading_spaces[1].length;
3968
+ if (indent > parent_indent) {
3969
+ child_indent = indent;
3970
+ break;
3971
+ }
3972
+ }
3973
+ break;
3974
+ }
3975
+ if (child_indent === -1)
3976
+ return null;
3977
+ search_start = parent_idx + 1;
3978
+ for (let i = parent_idx + 1;i < search_end; i++) {
3979
+ if (lines[i].trim() === "")
3980
+ continue;
3981
+ const leading_spaces = lines[i].match(/^(\s*)/);
3982
+ if (leading_spaces && leading_spaces[1].length < child_indent) {
3983
+ search_end = i;
3984
+ break;
3985
+ }
3986
+ }
3987
+ }
3988
+ const final_prop = segments[segments.length - 1];
3989
+ for (let i = search_start;i < search_end; i++) {
3990
+ const match = lines[i].match(new RegExp(`^(\\s*${escape_regex(final_prop)}:\\s*).+$`));
3991
+ if (match) {
3992
+ lines[i] = match[1] + value;
3993
+ return lines.join(`
3994
+ `);
3995
+ }
3996
+ }
3997
+ return null;
3998
+ }
3999
+ }
4000
+
4001
+ // src/editor/yaml-fields.ts
4002
+ var PRIMITIVE_DEFAULTS = {
4003
+ int: "0",
4004
+ float: "0",
4005
+ double: "0",
4006
+ bool: "0",
4007
+ string: "",
4008
+ byte: "0",
4009
+ sbyte: "0",
4010
+ short: "0",
4011
+ ushort: "0",
4012
+ uint: "0",
4013
+ long: "0",
4014
+ ulong: "0",
4015
+ char: "0",
4016
+ Int32: "0",
4017
+ Single: "0",
4018
+ Double: "0",
4019
+ Boolean: "0",
4020
+ String: "",
4021
+ Byte: "0",
4022
+ SByte: "0",
4023
+ Int16: "0",
4024
+ UInt16: "0",
4025
+ UInt32: "0",
4026
+ Int64: "0",
4027
+ UInt64: "0",
4028
+ Char: "0"
4029
+ };
4030
+ var STRUCT_DEFAULTS = {
4031
+ Vector2: "{x: 0, y: 0}",
4032
+ Vector3: "{x: 0, y: 0, z: 0}",
4033
+ Vector4: "{x: 0, y: 0, z: 0, w: 0}",
4034
+ Vector2Int: "{x: 0, y: 0}",
4035
+ Vector3Int: "{x: 0, y: 0, z: 0}",
4036
+ Quaternion: "{x: 0, y: 0, z: 0, w: 1}",
4037
+ Color: "{r: 0, g: 0, b: 0, a: 0}",
4038
+ Color32: "{r: 0, g: 0, b: 0, a: 0}",
4039
+ Rect: `serializedVersion: 2
4040
+ x: 0
4041
+ y: 0
4042
+ width: 0
4043
+ height: 0`,
4044
+ RectInt: "{x: 0, y: 0, width: 0, height: 0}",
4045
+ RectOffset: "{m_Left: 0, m_Right: 0, m_Top: 0, m_Bottom: 0}",
4046
+ Matrix4x4: "{e00: 1, e01: 0, e02: 0, e03: 0, e10: 0, e11: 1, e12: 0, e13: 0, e20: 0, e21: 0, e22: 1, e23: 0, e30: 0, e31: 0, e32: 0, e33: 1}",
4047
+ LayerMask: `serializedVersion: 2
4048
+ m_Bits: 0`
4049
+ };
4050
+ var VERSION_GATED_STRUCT_DEFAULTS = {
4051
+ Hash128: { value: `serializedVersion: 2
4052
+ Hash: 00000000000000000000000000000000`, min_major: 2021, min_minor: 1 },
4053
+ RenderingLayerMask: { value: `serializedVersion: 2
4054
+ m_Bits: 0`, min_major: 6000, min_minor: 0 }
4055
+ };
4056
+ var BLOCK_STRUCT_DEFAULTS = {
4057
+ Bounds: `m_Center: {x: 0, y: 0, z: 0}
4058
+ m_Extent: {x: 0, y: 0, z: 0}`,
4059
+ BoundsInt: `m_Position: {x: 0, y: 0, z: 0}
4060
+ m_Size: {x: 0, y: 0, z: 0}`
4061
+ };
4062
+ var OBJECT_REF_TYPES = new Set([
4063
+ "GameObject",
4064
+ "Transform",
4065
+ "RectTransform",
4066
+ "Material",
4067
+ "Texture",
4068
+ "Texture2D",
4069
+ "Texture3D",
4070
+ "RenderTexture",
4071
+ "Sprite",
4072
+ "AudioClip",
4073
+ "VideoClip",
4074
+ "Mesh",
4075
+ "Shader",
4076
+ "ComputeShader",
4077
+ "AnimationClip",
4078
+ "AnimatorController",
4079
+ "RuntimeAnimatorController",
4080
+ "PhysicMaterial",
4081
+ "PhysicsMaterial",
4082
+ "PhysicsMaterial2D",
4083
+ "Font",
4084
+ "TMP_FontAsset",
4085
+ "Object",
4086
+ "Component",
4087
+ "Behaviour",
4088
+ "MonoBehaviour",
4089
+ "ScriptableObject",
4090
+ "Rigidbody",
4091
+ "Rigidbody2D",
4092
+ "Collider",
4093
+ "Collider2D",
4094
+ "Camera",
4095
+ "Light",
4096
+ "Canvas",
4097
+ "CanvasGroup",
4098
+ "EventSystem",
4099
+ "AudioSource",
4100
+ "ParticleSystem",
4101
+ "TextAsset",
4102
+ "TerrainData"
4103
+ ]);
4104
+ function version_at_least(version, min_major, min_minor) {
4105
+ if (!version)
4106
+ return false;
4107
+ if (version.major > min_major)
4108
+ return true;
4109
+ if (version.major === min_major)
4110
+ return version.minor >= min_minor;
4111
+ return false;
4112
+ }
4113
+ function yaml_default_for_type(csharp_type, version) {
4114
+ if (csharp_type.endsWith("?")) {
4115
+ return null;
4116
+ }
4117
+ if (csharp_type in PRIMITIVE_DEFAULTS) {
4118
+ return PRIMITIVE_DEFAULTS[csharp_type];
4119
+ }
4120
+ if (csharp_type in STRUCT_DEFAULTS) {
4121
+ return STRUCT_DEFAULTS[csharp_type];
4122
+ }
4123
+ if (csharp_type in VERSION_GATED_STRUCT_DEFAULTS) {
4124
+ const gated = VERSION_GATED_STRUCT_DEFAULTS[csharp_type];
4125
+ if (version_at_least(version, gated.min_major, gated.min_minor)) {
4126
+ return gated.value;
4127
+ }
4128
+ return null;
4129
+ }
4130
+ if (csharp_type in BLOCK_STRUCT_DEFAULTS) {
4131
+ return BLOCK_STRUCT_DEFAULTS[csharp_type];
4132
+ }
4133
+ if (csharp_type.endsWith("[]")) {
4134
+ return "[]";
4135
+ }
4136
+ if (csharp_type.startsWith("List<") && csharp_type.endsWith(">")) {
4137
+ return "[]";
4138
+ }
4139
+ if (OBJECT_REF_TYPES.has(csharp_type)) {
4140
+ return "{fileID: 0}";
4141
+ }
4142
+ return "{fileID: 0}";
4143
+ }
4144
+ function generate_field_yaml(fields, version, indent = " ", type_lookup) {
4145
+ const lines = [];
4146
+ for (const field of fields) {
4147
+ if (field.hasSerializeReference) {
4148
+ const t = field.typeName;
4149
+ if (t.endsWith("[]") || t.startsWith("List<") && t.endsWith(">")) {
4150
+ lines.push(`${indent}${field.name}: []`);
4151
+ } else {
4152
+ lines.push(`${indent}${field.name}:`);
4153
+ lines.push(`${indent} rid: 0`);
4054
4154
  }
4055
- if (parent_idx === -1)
4056
- return null;
4057
- let child_indent = -1;
4058
- for (let i = parent_idx + 1;i < search_end; i++) {
4059
- if (lines[i].trim() === "")
4060
- continue;
4061
- const leading_spaces = lines[i].match(/^(\s*)/);
4062
- if (leading_spaces) {
4063
- const indent = leading_spaces[1].length;
4064
- if (indent > parent_indent) {
4065
- child_indent = indent;
4066
- break;
4067
- }
4155
+ continue;
4156
+ }
4157
+ const default_value = yaml_default_for_type(field.typeName, version);
4158
+ if (default_value === null) {
4159
+ continue;
4160
+ }
4161
+ if (default_value === "{fileID: 0}" && type_lookup) {
4162
+ const struct_fields = type_lookup(field.typeName);
4163
+ if (struct_fields && struct_fields.length > 0) {
4164
+ lines.push(`${indent}${field.name}:`);
4165
+ const nested = generate_field_yaml(struct_fields, version, indent + " ", type_lookup);
4166
+ const nested_lines = nested.slice(1).replace(/\n$/, "").split(`
4167
+ `);
4168
+ for (const nl of nested_lines) {
4169
+ lines.push(nl);
4068
4170
  }
4069
- break;
4171
+ continue;
4070
4172
  }
4071
- if (child_indent === -1)
4072
- return null;
4073
- search_start = parent_idx + 1;
4074
- for (let i = parent_idx + 1;i < search_end; i++) {
4075
- if (lines[i].trim() === "")
4076
- continue;
4077
- const leading_spaces = lines[i].match(/^(\s*)/);
4078
- if (leading_spaces && leading_spaces[1].length < child_indent) {
4079
- search_end = i;
4080
- break;
4081
- }
4173
+ }
4174
+ if (default_value.includes(`
4175
+ `)) {
4176
+ lines.push(`${indent}${field.name}:`);
4177
+ for (const sub_line of default_value.split(`
4178
+ `)) {
4179
+ lines.push(`${indent}${sub_line}`);
4082
4180
  }
4181
+ } else {
4182
+ lines.push(`${indent}${field.name}: ${default_value}`);
4083
4183
  }
4084
- const final_prop = segments[segments.length - 1];
4085
- for (let i = search_start;i < search_end; i++) {
4086
- const match = lines[i].match(new RegExp(`^(\\s*${escape_regex(final_prop)}:\\s*).+$`));
4087
- if (match) {
4088
- lines[i] = match[1] + value;
4089
- return lines.join(`
4090
- `);
4184
+ }
4185
+ return lines.length > 0 ? `
4186
+ ` + lines.join(`
4187
+ `) + `
4188
+ ` : `
4189
+ `;
4190
+ }
4191
+ function json_value_to_yaml_lines(value, indent = " ") {
4192
+ if (value === null || value === undefined) {
4193
+ return [""];
4194
+ }
4195
+ if (typeof value === "string") {
4196
+ return [yaml_quote_if_needed(value)];
4197
+ }
4198
+ if (typeof value === "number" || typeof value === "boolean") {
4199
+ return [String(value)];
4200
+ }
4201
+ if (Array.isArray(value)) {
4202
+ if (value.length === 0)
4203
+ return ["[]"];
4204
+ const lines = [];
4205
+ for (const item of value) {
4206
+ if (is_scalar(item) || is_empty_collection(item)) {
4207
+ const scalar = json_value_to_yaml_lines(item);
4208
+ lines.push(`${indent}- ${scalar[0]}`);
4209
+ } else if (is_flow_mappable(item)) {
4210
+ lines.push(`${indent}- ${to_flow_mapping(item)}`);
4211
+ } else {
4212
+ const child_lines = json_value_to_yaml_lines(item, indent + " ");
4213
+ lines.push(`${indent}- ${child_lines[0].trimStart()}`);
4214
+ for (let i = 1;i < child_lines.length; i++) {
4215
+ lines.push(child_lines[i]);
4216
+ }
4217
+ }
4218
+ }
4219
+ return lines;
4220
+ }
4221
+ if (typeof value === "object") {
4222
+ const entries = Object.entries(value);
4223
+ if (entries.length === 0)
4224
+ return ["{}"];
4225
+ const lines = [];
4226
+ for (const [key, val] of entries) {
4227
+ if (is_scalar(val) || is_empty_collection(val)) {
4228
+ const scalar = json_value_to_yaml_lines(val);
4229
+ lines.push(`${indent}${key}: ${scalar[0]}`);
4230
+ } else if (is_flow_mappable(val)) {
4231
+ lines.push(`${indent}${key}: ${to_flow_mapping(val)}`);
4232
+ } else {
4233
+ const child_indent = Array.isArray(val) ? indent : indent + " ";
4234
+ lines.push(`${indent}${key}:`);
4235
+ const child_lines = json_value_to_yaml_lines(val, child_indent);
4236
+ for (const cl of child_lines) {
4237
+ lines.push(cl);
4238
+ }
4091
4239
  }
4092
4240
  }
4093
- return null;
4241
+ return lines;
4094
4242
  }
4243
+ return [String(value)];
4244
+ }
4245
+ function is_scalar(value) {
4246
+ return value === null || value === undefined || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
4247
+ }
4248
+ function is_empty_collection(value) {
4249
+ if (Array.isArray(value))
4250
+ return value.length === 0;
4251
+ if (typeof value === "object" && value !== null)
4252
+ return Object.keys(value).length === 0;
4253
+ return false;
4254
+ }
4255
+ function is_flow_mappable(value) {
4256
+ if (typeof value !== "object" || value === null || Array.isArray(value))
4257
+ return false;
4258
+ const entries = Object.entries(value);
4259
+ if (entries.length === 0)
4260
+ return false;
4261
+ return entries.every(([, v]) => is_scalar(v) || is_empty_collection(v));
4262
+ }
4263
+ function to_flow_mapping(obj) {
4264
+ const parts = Object.entries(obj).map(([k, v]) => {
4265
+ if (v === null || v === undefined)
4266
+ return `${k}: `;
4267
+ if (typeof v === "string")
4268
+ return `${k}: ${yaml_quote_if_needed(v)}`;
4269
+ if (Array.isArray(v) && v.length === 0)
4270
+ return `${k}: []`;
4271
+ if (typeof v === "object" && v !== null && Object.keys(v).length === 0)
4272
+ return `${k}: {}`;
4273
+ return `${k}: ${String(v)}`;
4274
+ });
4275
+ return `{${parts.join(", ")}}`;
4095
4276
  }
4096
4277
 
4097
4278
  // src/editor/unity-document.ts
4279
+ var import_fs7 = require("fs");
4098
4280
  class UnityDocument {
4099
4281
  file_path;
4100
4282
  _header;
@@ -5085,8 +5267,8 @@ function addComponentToGameObject(doc, gameObjectId, componentId) {
5085
5267
  `);
5086
5268
  goBlock.replace_raw(raw);
5087
5269
  }
5088
- function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000) {
5089
- const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version) : `
5270
+ function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000, type_lookup) {
5271
+ const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version, " ", type_lookup) : `
5090
5272
  `;
5091
5273
  const has_serialize_ref = fields?.some((f) => f.hasSerializeReference) ?? false;
5092
5274
  const references_section = has_serialize_ref ? ` references:
@@ -5744,6 +5926,170 @@ PrefabImporter:
5744
5926
  prefab_instance_id: prefabInstanceId
5745
5927
  };
5746
5928
  }
5929
+ function createPrefabInstance(options) {
5930
+ const { scene_path, prefab_path, name, parent, position } = options;
5931
+ const pathError = validate_file_path(scene_path, "write");
5932
+ if (pathError) {
5933
+ return { success: false, file_path: scene_path, error: pathError };
5934
+ }
5935
+ if (!scene_path.endsWith(".unity")) {
5936
+ return { success: false, file_path: scene_path, error: "Scene path must be a .unity file" };
5937
+ }
5938
+ if (!prefab_path.endsWith(".prefab")) {
5939
+ return { success: false, file_path: scene_path, error: "Prefab path must be a .prefab file" };
5940
+ }
5941
+ if (!import_fs8.existsSync(scene_path)) {
5942
+ return { success: false, file_path: scene_path, error: `Scene file not found: ${scene_path}` };
5943
+ }
5944
+ if (!import_fs8.existsSync(prefab_path)) {
5945
+ return { success: false, file_path: scene_path, error: `Prefab file not found: ${prefab_path}` };
5946
+ }
5947
+ const metaPath = prefab_path + ".meta";
5948
+ const sourceGuid = extractGuidFromMeta(metaPath);
5949
+ if (!sourceGuid) {
5950
+ return { success: false, file_path: scene_path, error: `Could not find or read .meta file for prefab: ${metaPath}` };
5951
+ }
5952
+ let prefabDoc;
5953
+ try {
5954
+ prefabDoc = UnityDocument.from_file(prefab_path);
5955
+ } catch (err) {
5956
+ return { success: false, file_path: scene_path, error: `Failed to read prefab: ${err instanceof Error ? err.message : String(err)}` };
5957
+ }
5958
+ const rootInfo = prefabDoc.find_prefab_root();
5959
+ if (!rootInfo) {
5960
+ return { success: false, file_path: scene_path, error: "Could not find root GameObject in source prefab" };
5961
+ }
5962
+ let doc;
5963
+ try {
5964
+ doc = UnityDocument.from_file(scene_path);
5965
+ } catch (err) {
5966
+ return { success: false, file_path: scene_path, error: `Failed to read scene: ${err instanceof Error ? err.message : String(err)}` };
5967
+ }
5968
+ let parentTransformIdStr = "0";
5969
+ if (parent !== undefined) {
5970
+ if (typeof parent === "number") {
5971
+ const parentIdStr = String(parent);
5972
+ const parentBlock = doc.find_by_file_id(parentIdStr);
5973
+ if (!parentBlock) {
5974
+ return { success: false, file_path: scene_path, error: `Parent with fileID ${parent} not found. Provide a GameObject or Transform fileID.` };
5975
+ }
5976
+ if (parentBlock.is_stripped) {
5977
+ return { success: false, file_path: scene_path, error: `Parent with fileID ${parent} is a stripped block (inside a PrefabInstance). Cannot parent to stripped transforms.` };
5978
+ }
5979
+ if (parentBlock.class_id === 4) {
5980
+ parentTransformIdStr = parentIdStr;
5981
+ } else if (parentBlock.class_id === 1) {
5982
+ const compMatch = parentBlock.raw.match(/m_Component:\s*\n\s*-\s*component:\s*\{fileID:\s*(\d+)\}/);
5983
+ if (compMatch) {
5984
+ parentTransformIdStr = compMatch[1];
5985
+ } else {
5986
+ return { success: false, file_path: scene_path, error: `Parent with fileID ${parent} has no Transform component.` };
5987
+ }
5988
+ } else {
5989
+ return { success: false, file_path: scene_path, error: `Parent with fileID ${parent} is not a GameObject or Transform.` };
5990
+ }
5991
+ } else {
5992
+ const foundResult = findTransformIdByName(doc, parent);
5993
+ if (foundResult === null) {
5994
+ return { success: false, file_path: scene_path, error: `Parent GameObject "${parent}" not found` };
5995
+ }
5996
+ if (typeof foundResult === "object") {
5997
+ return { success: false, file_path: scene_path, error: foundResult.error };
5998
+ }
5999
+ parentTransformIdStr = foundResult;
6000
+ }
6001
+ }
6002
+ const prefabInstanceId = doc.generate_file_id();
6003
+ const strippedGoId = doc.generate_file_id();
6004
+ const strippedTransformId = doc.generate_file_id();
6005
+ const instanceName = name || path3.basename(prefab_path, ".prefab");
6006
+ const pos = position ?? { x: 0, y: 0, z: 0 };
6007
+ const rootGoFileId = rootInfo.game_object.file_id;
6008
+ const rootTransformFileId = rootInfo.transform.file_id;
6009
+ const yaml = `--- !u!1 &${strippedGoId} stripped
6010
+ GameObject:
6011
+ m_CorrespondingSourceObject: {fileID: ${rootGoFileId}, guid: ${sourceGuid}, type: 3}
6012
+ m_PrefabInstance: {fileID: ${prefabInstanceId}}
6013
+ m_PrefabAsset: {fileID: 0}
6014
+ --- !u!4 &${strippedTransformId} stripped
6015
+ Transform:
6016
+ m_CorrespondingSourceObject: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6017
+ m_PrefabInstance: {fileID: ${prefabInstanceId}}
6018
+ m_PrefabAsset: {fileID: 0}
6019
+ --- !u!1001 &${prefabInstanceId}
6020
+ PrefabInstance:
6021
+ m_ObjectHideFlags: 0
6022
+ serializedVersion: 2
6023
+ m_Modification:
6024
+ m_TransformParent: {fileID: ${parentTransformIdStr}}
6025
+ m_Modifications:
6026
+ - target: {fileID: ${rootGoFileId}, guid: ${sourceGuid}, type: 3}
6027
+ propertyPath: m_Name
6028
+ value: ${instanceName}
6029
+ objectReference: {fileID: 0}
6030
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6031
+ propertyPath: m_LocalPosition.x
6032
+ value: ${pos.x}
6033
+ objectReference: {fileID: 0}
6034
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6035
+ propertyPath: m_LocalPosition.y
6036
+ value: ${pos.y}
6037
+ objectReference: {fileID: 0}
6038
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6039
+ propertyPath: m_LocalPosition.z
6040
+ value: ${pos.z}
6041
+ objectReference: {fileID: 0}
6042
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6043
+ propertyPath: m_LocalRotation.w
6044
+ value: 1
6045
+ objectReference: {fileID: 0}
6046
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6047
+ propertyPath: m_LocalRotation.x
6048
+ value: 0
6049
+ objectReference: {fileID: 0}
6050
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6051
+ propertyPath: m_LocalRotation.y
6052
+ value: 0
6053
+ objectReference: {fileID: 0}
6054
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6055
+ propertyPath: m_LocalRotation.z
6056
+ value: 0
6057
+ objectReference: {fileID: 0}
6058
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6059
+ propertyPath: m_LocalEulerAnglesHint.x
6060
+ value: 0
6061
+ objectReference: {fileID: 0}
6062
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6063
+ propertyPath: m_LocalEulerAnglesHint.y
6064
+ value: 0
6065
+ objectReference: {fileID: 0}
6066
+ - target: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
6067
+ propertyPath: m_LocalEulerAnglesHint.z
6068
+ value: 0
6069
+ objectReference: {fileID: 0}
6070
+ m_RemovedComponents: []
6071
+ m_RemovedGameObjects: []
6072
+ m_AddedGameObjects: []
6073
+ m_AddedComponents: []
6074
+ m_SourcePrefab: {fileID: 100100000, guid: ${sourceGuid}, type: 3}
6075
+ `;
6076
+ doc.append_raw(yaml);
6077
+ if (parentTransformIdStr !== "0") {
6078
+ doc.add_child_to_parent(parentTransformIdStr, strippedTransformId);
6079
+ }
6080
+ try {
6081
+ doc.save();
6082
+ } catch (err) {
6083
+ return { success: false, file_path: scene_path, error: `Failed to save scene: ${err instanceof Error ? err.message : String(err)}` };
6084
+ }
6085
+ return {
6086
+ success: true,
6087
+ file_path: scene_path,
6088
+ prefab_instance_id: prefabInstanceId,
6089
+ game_object_id: strippedGoId,
6090
+ transform_id: strippedTransformId
6091
+ };
6092
+ }
5747
6093
  function createScriptableObject(options) {
5748
6094
  const { output_path, script } = options;
5749
6095
  const project_path = options.project_path || find_unity_project_root(path3.dirname(output_path)) || undefined;
@@ -5789,7 +6135,8 @@ function createScriptableObject(options) {
5789
6135
  } catch {}
5790
6136
  }
5791
6137
  const baseName = path3.basename(output_path, ".asset");
5792
- const field_yaml = resolved.fields && resolved.fields.length > 0 ? generate_field_yaml(resolved.fields, version) : `
6138
+ const type_lookup = project_path ? build_type_lookup(project_path) : undefined;
6139
+ const field_yaml = resolved.fields && resolved.fields.length > 0 ? generate_field_yaml(resolved.fields, version, " ", type_lookup) : `
5793
6140
  `;
5794
6141
  let assetYaml = `%YAML 1.1
5795
6142
  %TAG !u! tag:unity3d.com,2011:
@@ -5809,8 +6156,58 @@ MonoBehaviour:
5809
6156
  const doc = UnityDocument.from_string(assetYaml);
5810
6157
  const block = doc.blocks[0];
5811
6158
  if (block) {
6159
+ const complex_entries = [];
5812
6160
  for (const [key, val] of Object.entries(options.initial_values)) {
5813
- block.set_property(key, val);
6161
+ if (val === null || val === undefined || typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
6162
+ block.set_property(key, String(val ?? ""));
6163
+ } else if (Array.isArray(val) && val.length === 0) {
6164
+ block.set_property(key, "[]");
6165
+ } else if (typeof val === "object" && val !== null && !Array.isArray(val) && Object.keys(val).length === 0) {
6166
+ block.set_property(key, "{}");
6167
+ } else {
6168
+ complex_entries.push([key, val]);
6169
+ }
6170
+ }
6171
+ if (complex_entries.length > 0) {
6172
+ let raw = block.raw;
6173
+ for (const [key, val] of complex_entries) {
6174
+ const yaml_lines = json_value_to_yaml_lines(val, " ");
6175
+ const key_pattern = new RegExp(`^([ \\t]*${key}:)[^\\n]*$`, "m");
6176
+ const key_match = raw.match(key_pattern);
6177
+ if (key_match && key_match.index !== undefined) {
6178
+ const key_indent = key_match[0].match(/^[ \t]*/)?.[0].length ?? 0;
6179
+ const after_key = key_match.index + key_match[0].length;
6180
+ let end = after_key;
6181
+ const remaining = raw.slice(after_key);
6182
+ const remaining_lines = remaining.split(`
6183
+ `);
6184
+ for (let i = 1;i < remaining_lines.length; i++) {
6185
+ const line = remaining_lines[i];
6186
+ if (line.trim() === "") {
6187
+ end += 1 + line.length;
6188
+ continue;
6189
+ }
6190
+ const line_indent = line.match(/^[ \t]*/)?.[0].length ?? 0;
6191
+ if (line_indent > key_indent) {
6192
+ end += 1 + line.length;
6193
+ } else {
6194
+ break;
6195
+ }
6196
+ }
6197
+ const block_yaml = ` ${key}:
6198
+ ${yaml_lines.join(`
6199
+ `)}`;
6200
+ raw = raw.slice(0, key_match.index) + block_yaml + raw.slice(end);
6201
+ } else {
6202
+ const block_yaml = ` ${key}:
6203
+ ${yaml_lines.join(`
6204
+ `)}`;
6205
+ raw = raw.trimEnd() + `
6206
+ ` + block_yaml + `
6207
+ `;
6208
+ }
6209
+ }
6210
+ block.replace_raw(raw);
5814
6211
  }
5815
6212
  assetYaml = doc.serialize();
5816
6213
  }
@@ -6083,7 +6480,8 @@ function addComponent(options) {
6083
6480
  if (resolved.path?.endsWith(".dll") && resolved.class_name) {
6084
6481
  scriptFileId = compute_dll_script_file_id(resolved.namespace ?? "", resolved.class_name);
6085
6482
  }
6086
- componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version, scriptFileId);
6483
+ const type_lookup_comp = project_path ? build_type_lookup(project_path) : undefined;
6484
+ componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version, scriptFileId, type_lookup_comp);
6087
6485
  scriptGuid = resolved.guid;
6088
6486
  scriptPath = resolved.path || undefined;
6089
6487
  extractionError = resolved.extraction_error;
@@ -8150,11 +8548,15 @@ function applyManagedReference(doc, block, file_path, field_path, typeInfo, appe
8150
8548
  const refEntry = ` - rid: ${nextRid}
8151
8549
  type: {class: ${typeInfo.class_name}, ns: ${typeInfo.namespace}, asm: ${typeInfo.assembly}}
8152
8550
  data: ${dataBlock}`;
8153
- const refIdsPattern = /(\s*RefIds:\s*\n)/;
8551
+ const refIdsBlockPattern = /(\s*RefIds:\s*\n)/;
8552
+ const refIdsEmptyPattern = /(\s*RefIds:)[ \t]*\[\]/;
8154
8553
  const referencesPattern = /(\s*references:\s*\n\s*version:\s*\d+\s*\n)/;
8155
- if (refIdsPattern.test(raw)) {
8156
- raw = raw.replace(refIdsPattern, `$1${refEntry}
8554
+ if (refIdsBlockPattern.test(raw)) {
8555
+ raw = raw.replace(refIdsBlockPattern, `$1${refEntry}
8157
8556
  `);
8557
+ } else if (refIdsEmptyPattern.test(raw)) {
8558
+ raw = raw.replace(refIdsEmptyPattern, `$1
8559
+ ${refEntry}`);
8158
8560
  } else if (referencesPattern.test(raw)) {
8159
8561
  raw = raw.replace(referencesPattern, `$1 RefIds:
8160
8562
  ${refEntry}
@@ -8167,8 +8569,23 @@ ${refEntry}
8167
8569
  ${refEntry}
8168
8570
  `;
8169
8571
  }
8170
- if (!append) {
8171
- const fieldRidPattern = new RegExp(`(${field_path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*\\n\\s*rid:[ \\t]*)\\d+`, "m");
8572
+ const escaped_field = field_path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8573
+ if (append) {
8574
+ const fieldLineMatch = raw.match(new RegExp(`^([ \\t]*)${escaped_field}:`, "m"));
8575
+ const fieldIndent = fieldLineMatch ? fieldLineMatch[1] : " ";
8576
+ const ridEntry = `
8577
+ ${fieldIndent} - rid: ${nextRid}`;
8578
+ const emptyArrayPattern = new RegExp(`(${escaped_field}:)[ \\t]*\\[\\]`, "m");
8579
+ if (emptyArrayPattern.test(raw)) {
8580
+ raw = raw.replace(emptyArrayPattern, `$1${ridEntry}`);
8581
+ } else {
8582
+ const lastRidPattern = new RegExp(`(^[ \\t]*${escaped_field}:[\\s\\S]*?- rid: \\d+)`, "m");
8583
+ if (lastRidPattern.test(raw)) {
8584
+ raw = raw.replace(lastRidPattern, `$1${ridEntry}`);
8585
+ }
8586
+ }
8587
+ } else {
8588
+ const fieldRidPattern = new RegExp(`(${escaped_field}:\\s*\\n\\s*rid:[ \\t]*)\\d+`, "m");
8172
8589
  if (fieldRidPattern.test(raw)) {
8173
8590
  raw = raw.replace(fieldRidPattern, `$1${nextRid}`);
8174
8591
  }
@@ -9297,6 +9714,36 @@ function build_create_command() {
9297
9714
  if (!result.success)
9298
9715
  process.exitCode = 1;
9299
9716
  });
9717
+ cmd.command("prefab-instance <scene_file> <prefab_path>").description("Instantiate a prefab into a scene file").option("-n, --name <name>", "Instance name (defaults to prefab filename)").option("-p, --parent <name|id>", "Parent GameObject name or Transform fileID").option("--position <x,y,z>", "Local position (default: 0,0,0)").option("-j, --json", "Output as JSON").action((scene_file, prefab_path_arg, options) => {
9718
+ let position;
9719
+ if (options.position) {
9720
+ const parts = options.position.split(",").map(Number);
9721
+ if (parts.length !== 3 || parts.some(isNaN)) {
9722
+ console.log(JSON.stringify({
9723
+ success: false,
9724
+ error: "--position must be three comma-separated numbers, e.g. 1,2,3"
9725
+ }, null, 2));
9726
+ process.exitCode = 1;
9727
+ return;
9728
+ }
9729
+ position = { x: parts[0], y: parts[1], z: parts[2] };
9730
+ }
9731
+ let parent;
9732
+ if (options.parent) {
9733
+ const asNumber = parseInt(options.parent, 10);
9734
+ parent = isNaN(asNumber) ? options.parent : asNumber;
9735
+ }
9736
+ const result = createPrefabInstance({
9737
+ scene_path: scene_file,
9738
+ prefab_path: prefab_path_arg,
9739
+ name: options.name,
9740
+ parent,
9741
+ position
9742
+ });
9743
+ console.log(JSON.stringify(result, null, 2));
9744
+ if (!result.success)
9745
+ process.exitCode = 1;
9746
+ });
9300
9747
  cmd.command("scriptable-object <output_path> <script>").description("Create a new ScriptableObject .asset file").option("-p, --project <path>", "Unity project path (for script GUID lookup)").option("--set <json>", `Initial field values as JSON object (e.g. '{"damage": "10", "targetScope": "1"}')`).option("-j, --json", "Output as JSON").action((output_path, script, options) => {
9301
9748
  let initial_values;
9302
9749
  if (options.set) {