unity-agentic-tools 0.3.1 → 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
@@ -2170,6 +2170,7 @@ __export(exports_scanner, {
2170
2170
  getNativeExtractCsharpTypes: () => getNativeExtractCsharpTypes,
2171
2171
  getNativeBuildTypeRegistry: () => getNativeBuildTypeRegistry,
2172
2172
  getNativeBuildPackageGuidCache: () => getNativeBuildPackageGuidCache,
2173
+ getNativeBuildLocalPackageGuidCache: () => getNativeBuildLocalPackageGuidCache,
2173
2174
  getNativeBuildGuidCache: () => getNativeBuildGuidCache,
2174
2175
  UnityScanner: () => UnityScanner
2175
2176
  });
@@ -2250,13 +2251,16 @@ function getNativeExtractDllTypes() {
2250
2251
  function getNativeBuildPackageGuidCache() {
2251
2252
  return nativeBuildPackageGuidCache;
2252
2253
  }
2254
+ function getNativeBuildLocalPackageGuidCache() {
2255
+ return nativeBuildLocalPackageGuidCache;
2256
+ }
2253
2257
  function getNativeExtractSerializedFields() {
2254
2258
  return nativeExtractSerializedFields;
2255
2259
  }
2256
2260
  function getNativeExtractDllFields() {
2257
2261
  return nativeExtractDllFields;
2258
2262
  }
2259
- var import_module, import_fs, import_path, __dirname = "/home/runner/work/unity-agentic-tools/unity-agentic-tools/unity-agentic-tools/src", RustScanner = null, nativeModuleError = null, nativeWalkProjectFiles = null, nativeGrepProject = null, nativeBuildGuidCache = null, nativeExtractCsharpTypes = null, nativeBuildTypeRegistry = null, nativeExtractDllTypes = null, nativeBuildPackageGuidCache = null, nativeExtractSerializedFields = null, nativeExtractDllFields = null;
2263
+ var import_module, import_fs, import_path, __dirname = "/home/runner/work/unity-agentic-tools/unity-agentic-tools/unity-agentic-tools/src", RustScanner = null, nativeModuleError = null, nativeWalkProjectFiles = null, nativeGrepProject = null, nativeBuildGuidCache = null, nativeExtractCsharpTypes = null, nativeBuildTypeRegistry = null, nativeExtractDllTypes = null, nativeBuildPackageGuidCache = null, nativeBuildLocalPackageGuidCache = null, nativeExtractSerializedFields = null, nativeExtractDllFields = null;
2260
2264
  var init_scanner = __esm(() => {
2261
2265
  import_module = require("module");
2262
2266
  import_fs = require("fs");
@@ -2280,6 +2284,7 @@ var init_scanner = __esm(() => {
2280
2284
  nativeBuildTypeRegistry = rustModule.buildTypeRegistry || null;
2281
2285
  nativeExtractDllTypes = rustModule.extractDllTypes || null;
2282
2286
  nativeBuildPackageGuidCache = rustModule.buildPackageGuidCache || null;
2287
+ nativeBuildLocalPackageGuidCache = rustModule.buildLocalPackageGuidCache || null;
2283
2288
  nativeExtractSerializedFields = rustModule.extractSerializedFields || null;
2284
2289
  nativeExtractDllFields = rustModule.extractDllFields || null;
2285
2290
  } catch (err) {
@@ -2294,7 +2299,7 @@ var require_package = __commonJS((exports2, module2) => {
2294
2299
  module2.exports = {
2295
2300
  name: "unity-agentic-tools",
2296
2301
  packageManager: "bun@latest",
2297
- version: "0.3.1",
2302
+ version: "0.3.3",
2298
2303
  description: "Fast, token-efficient Unity YAML parser for AI agents",
2299
2304
  exports: {
2300
2305
  ".": {
@@ -2386,6 +2391,7 @@ var CONFIG_DIR = ".unity-agentic";
2386
2391
  var CONFIG_FILE = "config.json";
2387
2392
  var GUID_CACHE_FILE = "guid-cache.json";
2388
2393
  var PACKAGE_CACHE_FILE = "package-cache.json";
2394
+ var LOCAL_PACKAGE_CACHE_FILE = "local-package-cache.json";
2389
2395
  var TYPE_REGISTRY_FILE = "type-registry.json";
2390
2396
  var DOC_INDEX_FILE = "doc-index.json";
2391
2397
  function setup(options = {}) {
@@ -2429,6 +2435,17 @@ function setup(options = {}) {
2429
2435
  packageGuidCount = Object.keys(packageCache).length;
2430
2436
  } catch {}
2431
2437
  }
2438
+ const nativeLocalPkgBuild = getNativeBuildLocalPackageGuidCache();
2439
+ if (nativeLocalPkgBuild) {
2440
+ try {
2441
+ const localPkgCache = nativeLocalPkgBuild(projectPath);
2442
+ const localPkgCount = Object.keys(localPkgCache).length;
2443
+ if (localPkgCount > 0) {
2444
+ const localPkgCachePath = import_path2.join(configPath, LOCAL_PACKAGE_CACHE_FILE);
2445
+ import_fs2.writeFileSync(localPkgCachePath, JSON.stringify(localPkgCache, null, 2));
2446
+ }
2447
+ } catch {}
2448
+ }
2432
2449
  let typeRegistryCreated = false;
2433
2450
  let typeCount;
2434
2451
  const nativeRegistryBuild = getNativeBuildTypeRegistry();
@@ -2578,7 +2595,7 @@ function removeDirectoryRecursive(dir) {
2578
2595
  }
2579
2596
 
2580
2597
  // src/cmd-create.ts
2581
- var import_fs15 = require("fs");
2598
+ var import_fs16 = require("fs");
2582
2599
  var import_path7 = require("path");
2583
2600
  var import_crypto2 = require("crypto");
2584
2601
 
@@ -2622,31 +2639,44 @@ function atomicWrite(filePath, content) {
2622
2639
  };
2623
2640
  }
2624
2641
  }
2625
- const tmpPath = `${filePath}.tmp`;
2642
+ const suffix = `${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
2643
+ const tmpPath = `${filePath}.${suffix}.tmp`;
2644
+ const bakPath = `${filePath}.${suffix}.bak`;
2645
+ let bakCreated = false;
2626
2646
  try {
2627
2647
  import_fs4.writeFileSync(tmpPath, content, "utf-8");
2628
- if (import_fs4.existsSync(filePath)) {
2629
- import_fs4.renameSync(filePath, `${filePath}.bak`);
2630
- }
2631
- import_fs4.renameSync(tmpPath, filePath);
2632
2648
  try {
2633
- if (import_fs4.existsSync(`${filePath}.bak`)) {
2634
- import_fs4.unlinkSync(`${filePath}.bak`);
2649
+ import_fs4.renameSync(filePath, bakPath);
2650
+ bakCreated = true;
2651
+ } catch (err) {
2652
+ if (is_enoent(err)) {
2653
+ bakCreated = false;
2654
+ } else {
2655
+ throw err;
2635
2656
  }
2636
- } catch {}
2657
+ }
2658
+ import_fs4.renameSync(tmpPath, filePath);
2659
+ if (bakCreated) {
2660
+ try {
2661
+ import_fs4.unlinkSync(bakPath);
2662
+ } catch {}
2663
+ }
2637
2664
  return {
2638
2665
  success: true,
2639
2666
  file_path: filePath,
2640
2667
  bytes_written: Buffer.byteLength(content, "utf-8")
2641
2668
  };
2642
2669
  } catch (error) {
2643
- if (import_fs4.existsSync(`${filePath}.bak`)) {
2670
+ if (bakCreated) {
2644
2671
  try {
2645
- import_fs4.renameSync(`${filePath}.bak`, filePath);
2672
+ import_fs4.renameSync(bakPath, filePath);
2646
2673
  } catch (restoreError) {
2647
2674
  console.error("Failed to restore backup:", restoreError);
2648
2675
  }
2649
2676
  }
2677
+ try {
2678
+ import_fs4.unlinkSync(tmpPath);
2679
+ } catch {}
2650
2680
  return {
2651
2681
  success: false,
2652
2682
  file_path: filePath,
@@ -2654,6 +2684,12 @@ function atomicWrite(filePath, content) {
2654
2684
  };
2655
2685
  }
2656
2686
  }
2687
+ function is_enoent(err) {
2688
+ return err instanceof Error && "code" in err && err.code === "ENOENT";
2689
+ }
2690
+ function normalize_property_path(path) {
2691
+ return path.replace(/\.Array\.data\.(\d+)/g, ".Array.data[$1]");
2692
+ }
2657
2693
  function is_match_all(pattern) {
2658
2694
  return pattern === "*" || pattern === "." || pattern === "**" || pattern === ".*";
2659
2695
  }
@@ -3032,6 +3068,31 @@ function extractGuidFromMeta(metaPath) {
3032
3068
  return null;
3033
3069
  }
3034
3070
  }
3071
+ function resolve_source_prefab(doc, file_path, project_path) {
3072
+ const pi_blocks = doc.find_by_class_id(1001);
3073
+ if (pi_blocks.length === 0)
3074
+ return null;
3075
+ const pi_block = pi_blocks[0];
3076
+ const source_match = pi_block.raw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
3077
+ if (!source_match)
3078
+ return null;
3079
+ const source_guid = source_match[1];
3080
+ const resolved_project = project_path || find_unity_project_root(path.dirname(file_path));
3081
+ if (!resolved_project)
3082
+ return null;
3083
+ const cache = load_guid_cache_for_file(file_path, resolved_project);
3084
+ if (!cache)
3085
+ return null;
3086
+ const source_path = cache.resolve_absolute(source_guid);
3087
+ if (!source_path || !import_fs6.existsSync(source_path))
3088
+ return null;
3089
+ return {
3090
+ source_guid,
3091
+ source_path,
3092
+ prefab_instance_id: pi_block.file_id,
3093
+ prefab_instance_block: pi_block
3094
+ };
3095
+ }
3035
3096
  function resolveScriptGuid(script, projectPath) {
3036
3097
  if (/^[a-f0-9]{32}$/i.test(script)) {
3037
3098
  return { guid: script.toLowerCase(), path: null };
@@ -3081,27 +3142,122 @@ function resolveScriptGuid(script, projectPath) {
3081
3142
  return false;
3082
3143
  return true;
3083
3144
  });
3084
- if (matches.length === 1 && matches[0].guid) {
3085
- return { guid: matches[0].guid, path: matches[0].filePath };
3145
+ if (matches.length === 1) {
3146
+ if (matches[0].guid) {
3147
+ return { guid: matches[0].guid, path: matches[0].filePath };
3148
+ }
3149
+ if (matches[0].filePath && projectPath) {
3150
+ const fullFilePath = path.isAbsolute(matches[0].filePath) ? matches[0].filePath : path.join(projectPath, matches[0].filePath);
3151
+ const metaPath = fullFilePath + ".meta";
3152
+ if (import_fs6.existsSync(metaPath)) {
3153
+ const guid = extractGuidFromMeta(metaPath);
3154
+ if (guid)
3155
+ return { guid, path: matches[0].filePath };
3156
+ }
3157
+ if (matches[0].filePath.startsWith("Packages/")) {
3158
+ const pkgPath = path.join(projectPath, matches[0].filePath);
3159
+ const pkgMetaPath = pkgPath + ".meta";
3160
+ if (import_fs6.existsSync(pkgMetaPath)) {
3161
+ const guid = extractGuidFromMeta(pkgMetaPath);
3162
+ if (guid)
3163
+ return { guid, path: matches[0].filePath };
3164
+ }
3165
+ }
3166
+ if (matches[0].filePath?.endsWith(".dll")) {
3167
+ const dllBasename = path.basename(matches[0].filePath).toLowerCase();
3168
+ for (const cacheName of ["package-cache.json", "local-package-cache.json"]) {
3169
+ const cachePath = path.join(projectPath, ".unity-agentic", cacheName);
3170
+ if (!import_fs6.existsSync(cachePath))
3171
+ continue;
3172
+ try {
3173
+ const cache = JSON.parse(import_fs6.readFileSync(cachePath, "utf-8"));
3174
+ for (const [guid, assetPath] of Object.entries(cache)) {
3175
+ if (assetPath.toLowerCase().endsWith(".dll") && path.basename(assetPath).toLowerCase() === dllBasename) {
3176
+ return { guid, path: assetPath };
3177
+ }
3178
+ }
3179
+ } catch {}
3180
+ }
3181
+ }
3182
+ }
3086
3183
  }
3087
3184
  if (matches.length > 1) {
3088
3185
  const withGuid = matches.filter((m) => m.guid);
3089
3186
  if (withGuid.length === 1) {
3090
3187
  return { guid: withGuid[0].guid, path: withGuid[0].filePath };
3091
3188
  }
3189
+ const resolvedFromMeta = matches.filter((m) => !m.guid && m.filePath).map((m) => {
3190
+ const fullPath = path.isAbsolute(m.filePath) ? m.filePath : path.join(projectPath, m.filePath);
3191
+ let guid = import_fs6.existsSync(fullPath + ".meta") ? extractGuidFromMeta(fullPath + ".meta") : null;
3192
+ if (!guid && m.filePath.startsWith("Packages/")) {
3193
+ const pkgPath = path.join(projectPath, m.filePath);
3194
+ if (import_fs6.existsSync(pkgPath + ".meta")) {
3195
+ guid = extractGuidFromMeta(pkgPath + ".meta");
3196
+ }
3197
+ }
3198
+ if (!guid && m.filePath?.endsWith(".dll")) {
3199
+ const dllBase = path.basename(m.filePath).toLowerCase();
3200
+ for (const cn of ["package-cache.json", "local-package-cache.json"]) {
3201
+ const cp = path.join(projectPath, ".unity-agentic", cn);
3202
+ if (!import_fs6.existsSync(cp))
3203
+ continue;
3204
+ try {
3205
+ const c = JSON.parse(import_fs6.readFileSync(cp, "utf-8"));
3206
+ for (const [g, p] of Object.entries(c)) {
3207
+ if (p.toLowerCase().endsWith(".dll") && path.basename(p).toLowerCase() === dllBase) {
3208
+ guid = g;
3209
+ break;
3210
+ }
3211
+ }
3212
+ } catch {}
3213
+ if (guid)
3214
+ break;
3215
+ }
3216
+ }
3217
+ return guid ? { guid, path: m.filePath } : null;
3218
+ }).filter((r) => r !== null);
3219
+ const allResolved = [...withGuid.map((m) => ({ guid: m.guid, path: m.filePath })), ...resolvedFromMeta];
3220
+ if (allResolved.length === 1) {
3221
+ return allResolved[0];
3222
+ }
3223
+ if (allResolved.length > 1) {
3224
+ const pkgCachePaths = [
3225
+ path.join(projectPath, ".unity-agentic", "package-cache.json"),
3226
+ path.join(projectPath, ".unity-agentic", "local-package-cache.json")
3227
+ ];
3228
+ for (const cachePath of pkgCachePaths) {
3229
+ if (!import_fs6.existsSync(cachePath))
3230
+ continue;
3231
+ try {
3232
+ const cache = JSON.parse(import_fs6.readFileSync(cachePath, "utf-8"));
3233
+ const inCache = allResolved.filter((r) => (r.guid in cache));
3234
+ if (inCache.length === 1)
3235
+ return inCache[0];
3236
+ } catch {}
3237
+ }
3238
+ const paths = allResolved.map((m) => m.path).join(", ");
3239
+ throw new Error(`Ambiguous type "${script}": found ${allResolved.length} matches (${paths}). ` + `Use a qualified name (e.g., "Namespace.${targetName}") or provide the full path.`);
3240
+ }
3092
3241
  }
3093
3242
  } catch {}
3094
3243
  }
3095
- const packageCachePath = path.join(projectPath, ".unity-agentic", "package-cache.json");
3096
- if (import_fs6.existsSync(packageCachePath)) {
3244
+ const scriptNameLower = script.toLowerCase().replace(/\.cs$/, "");
3245
+ const dotIdx = scriptNameLower.lastIndexOf(".");
3246
+ const classNameOnly = dotIdx > 0 ? scriptNameLower.substring(dotIdx + 1) : null;
3247
+ const cachePaths = [
3248
+ path.join(projectPath, ".unity-agentic", "package-cache.json"),
3249
+ path.join(projectPath, ".unity-agentic", "local-package-cache.json")
3250
+ ];
3251
+ for (const cachePath of cachePaths) {
3252
+ if (!import_fs6.existsSync(cachePath))
3253
+ continue;
3097
3254
  try {
3098
- const packageCache = JSON.parse(import_fs6.readFileSync(packageCachePath, "utf-8"));
3099
- const scriptNameLower = script.toLowerCase().replace(/\.cs$/, "");
3100
- for (const [guid, assetPath] of Object.entries(packageCache)) {
3255
+ const cache = JSON.parse(import_fs6.readFileSync(cachePath, "utf-8"));
3256
+ for (const [guid, assetPath] of Object.entries(cache)) {
3101
3257
  if (!assetPath.endsWith(".cs"))
3102
3258
  continue;
3103
3259
  const fileName = path.basename(assetPath, ".cs").toLowerCase();
3104
- if (fileName === scriptNameLower) {
3260
+ if (fileName === scriptNameLower || classNameOnly && fileName === classNameOnly) {
3105
3261
  return { guid, path: assetPath };
3106
3262
  }
3107
3263
  }
@@ -3152,6 +3308,8 @@ function resolve_script_with_fields(script, project_path) {
3152
3308
  result.fields = chosen.fields;
3153
3309
  result.base_class = chosen.baseClass ?? undefined;
3154
3310
  result.kind = chosen.kind;
3311
+ result.namespace = chosen.namespace ?? undefined;
3312
+ result.class_name = chosen.name;
3155
3313
  }
3156
3314
  } else {
3157
3315
  result.extraction_error = "Native extractDllFields function not available";
@@ -3200,178 +3358,61 @@ function resolve_enum_fields(fields, project_path) {
3200
3358
  }
3201
3359
  } catch {}
3202
3360
  }
3203
-
3204
- // src/editor/yaml-fields.ts
3205
- var PRIMITIVE_DEFAULTS = {
3206
- int: "0",
3207
- float: "0",
3208
- double: "0",
3209
- bool: "0",
3210
- string: "",
3211
- byte: "0",
3212
- sbyte: "0",
3213
- short: "0",
3214
- ushort: "0",
3215
- uint: "0",
3216
- long: "0",
3217
- ulong: "0",
3218
- char: "0",
3219
- Int32: "0",
3220
- Single: "0",
3221
- Double: "0",
3222
- Boolean: "0",
3223
- String: "",
3224
- Byte: "0",
3225
- SByte: "0",
3226
- Int16: "0",
3227
- UInt16: "0",
3228
- UInt32: "0",
3229
- Int64: "0",
3230
- UInt64: "0",
3231
- Char: "0"
3232
- };
3233
- var STRUCT_DEFAULTS = {
3234
- Vector2: "{x: 0, y: 0}",
3235
- Vector3: "{x: 0, y: 0, z: 0}",
3236
- Vector4: "{x: 0, y: 0, z: 0, w: 0}",
3237
- Vector2Int: "{x: 0, y: 0}",
3238
- Vector3Int: "{x: 0, y: 0, z: 0}",
3239
- Quaternion: "{x: 0, y: 0, z: 0, w: 1}",
3240
- Color: "{r: 0, g: 0, b: 0, a: 0}",
3241
- Color32: "{r: 0, g: 0, b: 0, a: 0}",
3242
- Rect: `serializedVersion: 2
3243
- x: 0
3244
- y: 0
3245
- width: 0
3246
- height: 0`,
3247
- RectInt: "{x: 0, y: 0, width: 0, height: 0}",
3248
- RectOffset: "{m_Left: 0, m_Right: 0, m_Top: 0, m_Bottom: 0}",
3249
- 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}",
3250
- LayerMask: `serializedVersion: 2
3251
- m_Bits: 0`
3252
- };
3253
- var VERSION_GATED_STRUCT_DEFAULTS = {
3254
- Hash128: { value: `serializedVersion: 2
3255
- Hash: 00000000000000000000000000000000`, min_major: 2021, min_minor: 1 },
3256
- RenderingLayerMask: { value: `serializedVersion: 2
3257
- m_Bits: 0`, min_major: 6000, min_minor: 0 }
3258
- };
3259
- var BLOCK_STRUCT_DEFAULTS = {
3260
- Bounds: `m_Center: {x: 0, y: 0, z: 0}
3261
- m_Extent: {x: 0, y: 0, z: 0}`,
3262
- BoundsInt: `m_Position: {x: 0, y: 0, z: 0}
3263
- m_Size: {x: 0, y: 0, z: 0}`
3264
- };
3265
- var OBJECT_REF_TYPES = new Set([
3266
- "GameObject",
3267
- "Transform",
3268
- "RectTransform",
3269
- "Material",
3270
- "Texture",
3271
- "Texture2D",
3272
- "Texture3D",
3273
- "RenderTexture",
3274
- "Sprite",
3275
- "AudioClip",
3276
- "VideoClip",
3277
- "Mesh",
3278
- "Shader",
3279
- "ComputeShader",
3280
- "AnimationClip",
3281
- "AnimatorController",
3282
- "RuntimeAnimatorController",
3283
- "PhysicMaterial",
3284
- "PhysicsMaterial",
3285
- "PhysicsMaterial2D",
3286
- "Font",
3287
- "TMP_FontAsset",
3288
- "Object",
3289
- "Component",
3290
- "Behaviour",
3291
- "MonoBehaviour",
3292
- "ScriptableObject",
3293
- "Rigidbody",
3294
- "Rigidbody2D",
3295
- "Collider",
3296
- "Collider2D",
3297
- "Camera",
3298
- "Light",
3299
- "Canvas",
3300
- "CanvasGroup",
3301
- "EventSystem",
3302
- "AudioSource",
3303
- "ParticleSystem",
3304
- "TextAsset",
3305
- "TerrainData"
3306
- ]);
3307
- function version_at_least(version, min_major, min_minor) {
3308
- if (!version)
3309
- return false;
3310
- if (version.major > min_major)
3311
- return true;
3312
- if (version.major === min_major)
3313
- return version.minor >= min_minor;
3314
- return false;
3315
- }
3316
- function yaml_default_for_type(csharp_type, version) {
3317
- if (csharp_type.endsWith("?")) {
3318
- return null;
3319
- }
3320
- if (csharp_type in PRIMITIVE_DEFAULTS) {
3321
- return PRIMITIVE_DEFAULTS[csharp_type];
3322
- }
3323
- if (csharp_type in STRUCT_DEFAULTS) {
3324
- return STRUCT_DEFAULTS[csharp_type];
3325
- }
3326
- if (csharp_type in VERSION_GATED_STRUCT_DEFAULTS) {
3327
- const gated = VERSION_GATED_STRUCT_DEFAULTS[csharp_type];
3328
- if (version_at_least(version, gated.min_major, gated.min_minor)) {
3329
- return gated.value;
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
+ }
3380
+ }
3330
3381
  }
3331
- return null;
3332
- }
3333
- if (csharp_type in BLOCK_STRUCT_DEFAULTS) {
3334
- return BLOCK_STRUCT_DEFAULTS[csharp_type];
3335
- }
3336
- if (csharp_type.endsWith("[]")) {
3337
- return "[]";
3338
- }
3339
- if (csharp_type.startsWith("List<") && csharp_type.endsWith(">")) {
3340
- return "[]";
3341
- }
3342
- if (OBJECT_REF_TYPES.has(csharp_type)) {
3343
- return "{fileID: 0}";
3344
- }
3345
- return "{fileID: 0}";
3346
- }
3347
- function generate_field_yaml(fields, version, indent = " ") {
3348
- const lines = [];
3349
- for (const field of fields) {
3350
- const default_value = yaml_default_for_type(field.typeName, version);
3351
- if (default_value === null) {
3352
- 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;
3353
3394
  }
3354
- if (default_value.includes(`
3355
- `)) {
3356
- lines.push(`${indent}${field.name}:`);
3357
- for (const sub_line of default_value.split(`
3358
- `)) {
3359
- 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;
3360
3400
  }
3361
- } else {
3362
- 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;
3363
3412
  }
3364
- }
3365
- return lines.length > 0 ? `
3366
- ` + lines.join(`
3367
- `) + `
3368
- ` : `
3369
- `;
3413
+ };
3370
3414
  }
3371
3415
 
3372
- // src/editor/unity-document.ts
3373
- var import_fs7 = require("fs");
3374
-
3375
3416
  // src/editor/unity-block.ts
3376
3417
  var HEADER_PATTERN = /^--- !u!(\d+) &(-?\d+)(\s+stripped)?/;
3377
3418
  function parse_header(raw) {
@@ -3390,6 +3431,17 @@ function parse_header(raw) {
3390
3431
  function escape_regex(str) {
3391
3432
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3392
3433
  }
3434
+ function yaml_quote_if_needed(value) {
3435
+ if (value === "")
3436
+ return value;
3437
+ if (value.startsWith("{") && value.endsWith("}") || value.startsWith("[") && value.endsWith("]")) {
3438
+ return value;
3439
+ }
3440
+ if (value.includes(": ") || value.includes(" #") || /^[#\{\[*&!|>'"]/.test(value) || value !== value.trim()) {
3441
+ return `'${value.replace(/'/g, "''")}'`;
3442
+ }
3443
+ return value;
3444
+ }
3393
3445
 
3394
3446
  class UnityBlock {
3395
3447
  _raw;
@@ -3425,6 +3477,7 @@ class UnityBlock {
3425
3477
  return this._dirty;
3426
3478
  }
3427
3479
  get_property(path2) {
3480
+ path2 = normalize_property_path(path2);
3428
3481
  if (path2.includes("Array.data[")) {
3429
3482
  return this._get_array_element(path2);
3430
3483
  }
@@ -3451,13 +3504,43 @@ class UnityBlock {
3451
3504
  return match ? match[1].trim() : null;
3452
3505
  }
3453
3506
  set_property(path2, value, object_reference) {
3507
+ path2 = normalize_property_path(path2);
3454
3508
  const old_raw = this._raw;
3455
3509
  const effective_ref = object_reference && object_reference !== "{fileID: 0}" ? object_reference : undefined;
3456
3510
  if (!path2.includes(".") && !path2.includes("Array")) {
3457
- const prop_pattern = new RegExp(`(^\\s*${escape_regex(path2)}:\\s*)(.*)$`, "m");
3458
- if (prop_pattern.test(this._raw)) {
3459
- const replacement_value = effective_ref ?? value;
3460
- this._raw = this._raw.replace(prop_pattern, `$1${replacement_value}`);
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) {
3514
+ const replacement_value = effective_ref ?? yaml_quote_if_needed(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
+ }
3541
+ } else {
3542
+ const insert_value = effective_ref ?? yaml_quote_if_needed(value);
3543
+ this._insert_property(path2, insert_value);
3461
3544
  }
3462
3545
  return this._check_dirty(old_raw);
3463
3546
  }
@@ -3472,8 +3555,8 @@ class UnityBlock {
3472
3555
  const fields = inline_match[2];
3473
3556
  const field_pattern = new RegExp(`(${escape_regex(sub_field)}:\\s*)([^,}]+)`);
3474
3557
  if (field_pattern.test(fields)) {
3475
- const updated_fields = fields.replace(field_pattern, `$1${value}`);
3476
- this._raw = this._raw.replace(inline_pattern, `$1${updated_fields}$3`);
3558
+ const updated_fields = fields.replace(field_pattern, (_m, prefix) => prefix + value);
3559
+ this._raw = this._raw.replace(inline_pattern, (_m, g1, _g2, g3) => g1 + updated_fields + g3);
3477
3560
  inline_applied = true;
3478
3561
  }
3479
3562
  }
@@ -3482,6 +3565,8 @@ class UnityBlock {
3482
3565
  const block_result = this._resolve_block_style_path(parts, effective_value);
3483
3566
  if (block_result !== null) {
3484
3567
  this._raw = block_result;
3568
+ } else if (parts.length === 2) {
3569
+ this._insert_struct_property(parent_prop, sub_field, effective_value);
3485
3570
  }
3486
3571
  }
3487
3572
  return this._check_dirty(old_raw);
@@ -3499,7 +3584,7 @@ class UnityBlock {
3499
3584
  `).filter((l) => l.trim().startsWith("-"));
3500
3585
  if (index < lines.length) {
3501
3586
  const old_line = lines[index];
3502
- const new_line = old_line.replace(/-\s*.*/, `- ${ref_value}`);
3587
+ const new_line = old_line.replace(/-\s*.*/, () => `- ${ref_value}`);
3503
3588
  this._raw = this._raw.replace(old_line, new_line);
3504
3589
  }
3505
3590
  }
@@ -3508,6 +3593,49 @@ class UnityBlock {
3508
3593
  }
3509
3594
  return false;
3510
3595
  }
3596
+ _insert_property(path2, value) {
3597
+ const alt = path2.startsWith("m_") ? path2.slice(2) : "m_" + path2;
3598
+ const alt_pattern = new RegExp(`^\\s*${escape_regex(alt)}:`, "m");
3599
+ if (alt_pattern.test(this._raw)) {
3600
+ return;
3601
+ }
3602
+ const indented_line = ` ${path2}: ${value}`;
3603
+ const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
3604
+ const eci_match = this._raw.match(eci_pattern);
3605
+ if (eci_match && eci_match.index !== undefined) {
3606
+ const insert_after = eci_match.index + eci_match[0].length;
3607
+ this._raw = this._raw.slice(0, insert_after) + `
3608
+ ` + indented_line + this._raw.slice(insert_after);
3609
+ return;
3610
+ }
3611
+ this._raw = this._raw.trimEnd() + `
3612
+ ` + indented_line + `
3613
+ `;
3614
+ }
3615
+ _insert_struct_property(parent, child, value) {
3616
+ const parent_pattern = new RegExp(`^[ ]*${escape_regex(parent)}:`, "m");
3617
+ if (parent_pattern.test(this._raw)) {
3618
+ return;
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
+ }
3625
+ const struct_block = ` ${parent}:
3626
+ ${child}: ${value}`;
3627
+ const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
3628
+ const eci_match = this._raw.match(eci_pattern);
3629
+ if (eci_match && eci_match.index !== undefined) {
3630
+ const insert_after = eci_match.index + eci_match[0].length;
3631
+ this._raw = this._raw.slice(0, insert_after) + `
3632
+ ` + struct_block + this._raw.slice(insert_after);
3633
+ return;
3634
+ }
3635
+ this._raw = this._raw.trimEnd() + `
3636
+ ` + struct_block + `
3637
+ `;
3638
+ }
3511
3639
  has_property(path2) {
3512
3640
  return this.get_property(path2) !== null;
3513
3641
  }
@@ -3563,7 +3691,7 @@ class UnityBlock {
3563
3691
  if (empty_match) {
3564
3692
  const base_indent = empty_match[2];
3565
3693
  const element_indent2 = base_indent + " ";
3566
- this._raw = this._raw.replace(empty_pattern, `$1
3694
+ this._raw = this._raw.replace(empty_pattern, (_m, g1) => `${g1}
3567
3695
  ${element_indent2}- ${value}`);
3568
3696
  return this._check_dirty(old_raw);
3569
3697
  }
@@ -3794,83 +3922,361 @@ ${element_indent2}- ${value}`);
3794
3922
  return match[1].trim();
3795
3923
  }
3796
3924
  }
3797
- return null;
3798
- }
3799
- _resolve_block_style_path(segments, value) {
3800
- const lines = this._raw.split(`
3925
+ return null;
3926
+ }
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`);
4154
+ }
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(`
3801
4167
  `);
3802
- let search_start = 0;
3803
- let search_end = lines.length;
3804
- for (let seg = 0;seg < segments.length - 1; seg++) {
3805
- const segment = segments[seg];
3806
- let parent_idx = -1;
3807
- let parent_indent = -1;
3808
- for (let i = search_start;i < search_end; i++) {
3809
- const match = lines[i].match(new RegExp(`^(\\s*)${escape_regex(segment)}:\\s*(.*)$`));
3810
- if (match) {
3811
- const trailing = match[2].trim();
3812
- if (trailing === "") {
3813
- parent_idx = i;
3814
- parent_indent = match[1].length;
3815
- break;
3816
- }
3817
- for (let j = i + 1;j < search_end; j++) {
3818
- if (lines[j].trim() === "")
3819
- continue;
3820
- const nextLeading = lines[j].match(/^(\s*)/);
3821
- if (nextLeading && nextLeading[1].length > match[1].length) {
3822
- parent_idx = i;
3823
- parent_indent = match[1].length;
3824
- }
3825
- break;
3826
- }
3827
- if (parent_idx !== -1)
3828
- break;
3829
- }
3830
- }
3831
- if (parent_idx === -1)
3832
- return null;
3833
- let child_indent = -1;
3834
- for (let i = parent_idx + 1;i < search_end; i++) {
3835
- if (lines[i].trim() === "")
3836
- continue;
3837
- const leading_spaces = lines[i].match(/^(\s*)/);
3838
- if (leading_spaces) {
3839
- const indent = leading_spaces[1].length;
3840
- if (indent > parent_indent) {
3841
- child_indent = indent;
3842
- break;
3843
- }
4168
+ for (const nl of nested_lines) {
4169
+ lines.push(nl);
3844
4170
  }
3845
- break;
4171
+ continue;
3846
4172
  }
3847
- if (child_indent === -1)
3848
- return null;
3849
- search_start = parent_idx + 1;
3850
- for (let i = parent_idx + 1;i < search_end; i++) {
3851
- if (lines[i].trim() === "")
3852
- continue;
3853
- const leading_spaces = lines[i].match(/^(\s*)/);
3854
- if (leading_spaces && leading_spaces[1].length < child_indent) {
3855
- search_end = i;
3856
- break;
3857
- }
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}`);
3858
4180
  }
4181
+ } else {
4182
+ lines.push(`${indent}${field.name}: ${default_value}`);
3859
4183
  }
3860
- const final_prop = segments[segments.length - 1];
3861
- for (let i = search_start;i < search_end; i++) {
3862
- const match = lines[i].match(new RegExp(`^(\\s*${escape_regex(final_prop)}:\\s*).+$`));
3863
- if (match) {
3864
- lines[i] = match[1] + value;
3865
- return lines.join(`
3866
- `);
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
+ }
3867
4239
  }
3868
4240
  }
3869
- return null;
4241
+ return lines;
3870
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(", ")}}`;
3871
4276
  }
3872
4277
 
3873
4278
  // src/editor/unity-document.ts
4279
+ var import_fs7 = require("fs");
3874
4280
  class UnityDocument {
3875
4281
  file_path;
3876
4282
  _header;
@@ -4508,6 +4914,20 @@ function get_project_info(projectPath) {
4508
4914
  }
4509
4915
 
4510
4916
  // src/editor/create.ts
4917
+ function compute_dll_script_file_id(namespace, class_name) {
4918
+ const { createHash } = require("crypto");
4919
+ const full_name = namespace ? `${namespace}.${class_name}` : class_name;
4920
+ const name_bytes = Buffer.from(full_name, "utf-8");
4921
+ const input = Buffer.alloc(4 + name_bytes.length);
4922
+ input.writeInt32LE(114, 0);
4923
+ name_bytes.copy(input, 4);
4924
+ const hash = createHash("md4").update(input).digest();
4925
+ const a = hash.readInt32LE(0);
4926
+ const b = hash.readInt32LE(4);
4927
+ const c = hash.readInt32LE(8);
4928
+ const d = hash.readInt32LE(12);
4929
+ return a ^ b ^ c ^ d;
4930
+ }
4511
4931
  function get_layer_from_parent(doc, parentTransformId) {
4512
4932
  if (parentTransformId === "0")
4513
4933
  return 0;
@@ -4523,7 +4943,7 @@ function get_layer_from_parent(doc, parentTransformId) {
4523
4943
  const layerMatch = parentGo.raw.match(/m_Layer:\s*(\d+)/);
4524
4944
  return layerMatch ? parseInt(layerMatch[1], 10) : 0;
4525
4945
  }
4526
- function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = 0, rootOrder = 0, layer = 0) {
4946
+ function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
4527
4947
  return `--- !u!1 &${gameObjectId}
4528
4948
  GameObject:
4529
4949
  m_ObjectHideFlags: 0
@@ -4584,9 +5004,9 @@ function findStrippedRootTransform(doc, prefabInstanceId) {
4584
5004
  for (const block of doc.blocks) {
4585
5005
  if (block.class_id !== 4 || !block.is_stripped)
4586
5006
  continue;
4587
- const piMatch = block.raw.match(/m_PrefabInstance:\s*\{fileID:\s*(\d+)\}/);
5007
+ const piMatch = block.raw.match(/m_PrefabInstance:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
4588
5008
  if (piMatch && piMatch[1] === prefabInstanceId) {
4589
- const sourceMatch = block.raw.match(/m_CorrespondingSourceObject:\s*(\{[^}]+\})/);
5009
+ const sourceMatch = block.raw.match(/m_CorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
4590
5010
  if (sourceMatch) {
4591
5011
  return {
4592
5012
  transformId: block.file_id,
@@ -4597,6 +5017,66 @@ function findStrippedRootTransform(doc, prefabInstanceId) {
4597
5017
  }
4598
5018
  return null;
4599
5019
  }
5020
+ function find_game_object_in_variant(doc, name, file_path, project_path) {
5021
+ const pi_blocks = doc.find_by_class_id(1001);
5022
+ for (const pi_block of pi_blocks) {
5023
+ const mod_pattern = /- target:[ \t]*(\{[^}]+\})\s*propertyPath:[ \t]*m_Name\s*value:[ \t]*([^\n]*)/g;
5024
+ let mod_match;
5025
+ while ((mod_match = mod_pattern.exec(pi_block.raw)) !== null) {
5026
+ const target_ref = mod_match[1];
5027
+ const mod_value = mod_match[2].trim();
5028
+ if (mod_value !== name)
5029
+ continue;
5030
+ for (const block of doc.blocks) {
5031
+ if (block.class_id !== 1 || !block.is_stripped)
5032
+ continue;
5033
+ const source_match = block.raw.match(/m_CorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
5034
+ if (!source_match)
5035
+ continue;
5036
+ const normalized_source = source_match[1].replace(/\s+/g, " ");
5037
+ const normalized_target = target_ref.replace(/\s+/g, " ");
5038
+ if (normalized_source === normalized_target) {
5039
+ return {
5040
+ stripped_go_id: block.file_id,
5041
+ source_ref: target_ref,
5042
+ prefab_instance_id: pi_block.file_id
5043
+ };
5044
+ }
5045
+ }
5046
+ }
5047
+ }
5048
+ const source_info = resolve_source_prefab(doc, file_path, project_path);
5049
+ if (!source_info)
5050
+ return null;
5051
+ let source_doc;
5052
+ try {
5053
+ source_doc = UnityDocument.from_file(source_info.source_path);
5054
+ } catch {
5055
+ return null;
5056
+ }
5057
+ const source_gos = source_doc.find_game_objects_by_name(name);
5058
+ if (source_gos.length !== 1)
5059
+ return null;
5060
+ const source_go = source_gos[0];
5061
+ const source_go_file_id = source_go.file_id;
5062
+ const source_guid = source_info.source_guid;
5063
+ for (const block of doc.blocks) {
5064
+ if (block.class_id !== 1 || !block.is_stripped)
5065
+ continue;
5066
+ const source_match = block.raw.match(/m_CorrespondingSourceObject:[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)[^}]*guid:[ \t]*([a-f0-9]{32})/);
5067
+ if (!source_match)
5068
+ continue;
5069
+ if (source_match[1] === source_go_file_id && source_match[2] === source_guid) {
5070
+ const source_ref = `{fileID: ${source_go_file_id}, guid: ${source_guid}, type: 3}`;
5071
+ return {
5072
+ stripped_go_id: block.file_id,
5073
+ source_ref,
5074
+ prefab_instance_id: source_info.prefab_instance_id
5075
+ };
5076
+ }
5077
+ }
5078
+ return null;
5079
+ }
4600
5080
  function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
4601
5081
  const piBlock = doc.find_by_file_id(piId);
4602
5082
  if (!piBlock)
@@ -4606,13 +5086,34 @@ function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
4606
5086
  insertIndex: -1
4607
5087
  addedObject: {fileID: ${newGoId}}`;
4608
5088
  let raw = piBlock.raw;
4609
- const emptyPattern = /m_AddedGameObjects:\s*\[\]/;
5089
+ const emptyPattern = /m_AddedGameObjects:[ \t]*\[\]/;
4610
5090
  if (emptyPattern.test(raw)) {
4611
5091
  raw = raw.replace(emptyPattern, `m_AddedGameObjects:${entry}`);
4612
5092
  piBlock.replace_raw(raw);
4613
5093
  return;
4614
5094
  }
4615
- const existingPattern = /(m_AddedGameObjects:\s*\n(?:\s+-[\s\S]*?(?=\n\s+m_|$))*)/;
5095
+ const existingPattern = /(m_AddedGameObjects:[ \t]*\n(?:[ \t]+-[\s\S]*?(?=\n[ \t]+m_|$))*)/;
5096
+ if (existingPattern.test(raw)) {
5097
+ raw = raw.replace(existingPattern, `$1${entry}`);
5098
+ piBlock.replace_raw(raw);
5099
+ }
5100
+ }
5101
+ function appendToAddedComponents(doc, piId, targetSourceRef, newComponentId) {
5102
+ const piBlock = doc.find_by_file_id(piId);
5103
+ if (!piBlock)
5104
+ return;
5105
+ const entry = `
5106
+ - targetCorrespondingSourceObject: ${targetSourceRef}
5107
+ insertIndex: -1
5108
+ addedObject: {fileID: ${newComponentId}}`;
5109
+ let raw = piBlock.raw;
5110
+ const emptyPattern = /m_AddedComponents:[ \t]*\[\]/;
5111
+ if (emptyPattern.test(raw)) {
5112
+ raw = raw.replace(emptyPattern, `m_AddedComponents:${entry}`);
5113
+ piBlock.replace_raw(raw);
5114
+ return;
5115
+ }
5116
+ const existingPattern = /(m_AddedComponents:[ \t]*\n(?:[ \t]+-[\s\S]*?(?=\n[ \t]+m_|$))*)/;
4616
5117
  if (existingPattern.test(raw)) {
4617
5118
  raw = raw.replace(existingPattern, `$1${entry}`);
4618
5119
  piBlock.replace_raw(raw);
@@ -4635,11 +5136,31 @@ var COMPONENT_DEFAULTS = {
4635
5136
  m_Materials:
4636
5137
  - {fileID: 0}`,
4637
5138
  33: ` m_Mesh: {fileID: 0}`,
5139
+ 50: ` m_BodyType: 0
5140
+ m_Mass: 1
5141
+ m_LinearDrag: 0
5142
+ m_AngularDrag: 0.05
5143
+ m_GravityScale: 1
5144
+ m_Interpolate: 0
5145
+ m_SleepingMode: 1
5146
+ m_CollisionDetection: 0`,
4638
5147
  54: ` m_Mass: 1
4639
5148
  m_Drag: 0
4640
5149
  m_AngularDrag: 0.05
4641
5150
  m_UseGravity: 1
4642
5151
  m_IsKinematic: 0`,
5152
+ 58: ` m_IsTrigger: 0
5153
+ m_Material: {fileID: 0}
5154
+ m_Offset: {x: 0, y: 0}
5155
+ m_Radius: 0.5`,
5156
+ 60: ` m_IsTrigger: 0
5157
+ m_Material: {fileID: 0}
5158
+ m_Offset: {x: 0, y: 0}
5159
+ m_Points: []`,
5160
+ 61: ` m_IsTrigger: 0
5161
+ m_Material: {fileID: 0}
5162
+ m_Offset: {x: 0, y: 0}
5163
+ m_Size: {x: 1, y: 1}`,
4643
5164
  64: ` m_IsTrigger: 0
4644
5165
  m_Convex: 0
4645
5166
  m_CookingOptions: 30
@@ -4648,6 +5169,19 @@ var COMPONENT_DEFAULTS = {
4648
5169
  m_Material: {fileID: 0}
4649
5170
  m_Center: {x: 0, y: 0, z: 0}
4650
5171
  m_Size: {x: 1, y: 1, z: 1}`,
5172
+ 66: ` m_IsTrigger: 0
5173
+ m_Material: {fileID: 0}
5174
+ m_Offset: {x: 0, y: 0}
5175
+ m_GeometryType: 0`,
5176
+ 68: ` m_IsTrigger: 0
5177
+ m_Material: {fileID: 0}
5178
+ m_Offset: {x: 0, y: 0}
5179
+ m_Points: []`,
5180
+ 70: ` m_IsTrigger: 0
5181
+ m_Material: {fileID: 0}
5182
+ m_Offset: {x: 0, y: 0}
5183
+ m_Size: {x: 1, y: 1}
5184
+ m_Direction: 0`,
4651
5185
  82: ` m_PlayOnAwake: 1
4652
5186
  m_Volume: 1
4653
5187
  m_Pitch: 1
@@ -4733,9 +5267,14 @@ function addComponentToGameObject(doc, gameObjectId, componentId) {
4733
5267
  `);
4734
5268
  goBlock.replace_raw(raw);
4735
5269
  }
4736
- function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version) {
4737
- 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) : `
4738
5272
  `;
5273
+ const has_serialize_ref = fields?.some((f) => f.hasSerializeReference) ?? false;
5274
+ const references_section = has_serialize_ref ? ` references:
5275
+ version: 2
5276
+ RefIds: []
5277
+ ` : "";
4739
5278
  return `--- !u!114 &${componentId}
4740
5279
  MonoBehaviour:
4741
5280
  m_ObjectHideFlags: 0
@@ -4745,9 +5284,9 @@ MonoBehaviour:
4745
5284
  m_GameObject: {fileID: ${gameObjectId}}
4746
5285
  m_Enabled: 1
4747
5286
  m_EditorHideFlags: 0
4748
- m_Script: {fileID: 11500000, guid: ${scriptGuid}, type: 3}
5287
+ m_Script: {fileID: ${scriptFileId}, guid: ${scriptGuid}, type: 3}
4749
5288
  m_Name:
4750
- m_EditorClassIdentifier:${field_yaml}`;
5289
+ m_EditorClassIdentifier:${field_yaml}${references_section}`;
4751
5290
  }
4752
5291
  function createGameObject(options) {
4753
5292
  const { file_path, name, parent } = options;
@@ -4846,9 +5385,9 @@ function createGameObject(options) {
4846
5385
  const layer = get_layer_from_parent(doc, parentTransformIdStr);
4847
5386
  const gameObjectIdStr = doc.generate_file_id();
4848
5387
  const transformIdStr = doc.generate_file_id();
4849
- const gameObjectId = parseInt(gameObjectIdStr, 10);
4850
- const transformId = parseInt(transformIdStr, 10);
4851
- const parentTransformId = parseInt(parentTransformIdStr, 10);
5388
+ const gameObjectId = gameObjectIdStr;
5389
+ const transformId = transformIdStr;
5390
+ const parentTransformId = parentTransformIdStr;
4852
5391
  const newBlocks = createGameObjectYAML(gameObjectId, transformId, name.trim(), parentTransformId, rootOrder, layer);
4853
5392
  doc.append_raw(newBlocks);
4854
5393
  if (parentTransformIdStr !== "0" && !variantPiId) {
@@ -4870,7 +5409,7 @@ function createGameObject(options) {
4870
5409
  file_path,
4871
5410
  game_object_id: gameObjectId,
4872
5411
  transform_id: transformId,
4873
- prefab_instance_id: variantPiId ? parseInt(variantPiId, 10) : undefined
5412
+ prefab_instance_id: variantPiId || undefined
4874
5413
  };
4875
5414
  }
4876
5415
  function createScene(options) {
@@ -5316,20 +5855,165 @@ function createPrefabVariant(options) {
5316
5855
  const tempDoc = UnityDocument.from_string(`%YAML 1.1
5317
5856
  %TAG !u! tag:unity3d.com,2011:
5318
5857
  `);
5319
- const prefabInstanceId = parseInt(tempDoc.generate_file_id(), 10);
5320
- const strippedGoId = parseInt(tempDoc.generate_file_id(), 10);
5321
- const strippedTransformId = parseInt(tempDoc.generate_file_id(), 10);
5858
+ const prefabInstanceId = tempDoc.generate_file_id();
5859
+ const strippedGoId = tempDoc.generate_file_id();
5860
+ const strippedTransformId = tempDoc.generate_file_id();
5322
5861
  const finalName = variant_name || `${rootInfo.name} Variant`;
5323
5862
  const variantYaml = `%YAML 1.1
5324
5863
  %TAG !u! tag:unity3d.com,2011:
5325
5864
  --- !u!1 &${strippedGoId} stripped
5326
5865
  GameObject:
5327
- m_CorrespondingSourceObject: {fileID: ${rootInfo.game_object.file_id}, guid: ${sourceGuid}, type: 3}
5866
+ m_CorrespondingSourceObject: {fileID: ${rootInfo.game_object.file_id}, guid: ${sourceGuid}, type: 3}
5867
+ m_PrefabInstance: {fileID: ${prefabInstanceId}}
5868
+ m_PrefabAsset: {fileID: 0}
5869
+ --- !u!4 &${strippedTransformId} stripped
5870
+ Transform:
5871
+ m_CorrespondingSourceObject: {fileID: ${rootInfo.transform.file_id}, guid: ${sourceGuid}, type: 3}
5872
+ m_PrefabInstance: {fileID: ${prefabInstanceId}}
5873
+ m_PrefabAsset: {fileID: 0}
5874
+ --- !u!1001 &${prefabInstanceId}
5875
+ PrefabInstance:
5876
+ m_ObjectHideFlags: 0
5877
+ serializedVersion: 2
5878
+ m_Modification:
5879
+ m_TransformParent: {fileID: 0}
5880
+ m_Modifications:
5881
+ - target: {fileID: ${rootInfo.game_object.file_id}, guid: ${sourceGuid}, type: 3}
5882
+ propertyPath: m_Name
5883
+ value: ${finalName}
5884
+ objectReference: {fileID: 0}
5885
+ m_RemovedComponents: []
5886
+ m_RemovedGameObjects: []
5887
+ m_AddedGameObjects: []
5888
+ m_AddedComponents: []
5889
+ m_SourcePrefab: {fileID: 100100000, guid: ${sourceGuid}, type: 3}
5890
+ `;
5891
+ try {
5892
+ import_fs8.writeFileSync(output_path, variantYaml, "utf-8");
5893
+ } catch (err) {
5894
+ return {
5895
+ success: false,
5896
+ output_path,
5897
+ error: `Failed to write variant prefab: ${err instanceof Error ? err.message : String(err)}`
5898
+ };
5899
+ }
5900
+ const variantGuid = generateGuid();
5901
+ const variantMetaContent = `fileFormatVersion: 2
5902
+ guid: ${variantGuid}
5903
+ PrefabImporter:
5904
+ externalObjects: {}
5905
+ userData:
5906
+ assetBundleName:
5907
+ assetBundleVariant:
5908
+ `;
5909
+ try {
5910
+ import_fs8.writeFileSync(output_path + ".meta", variantMetaContent, "utf-8");
5911
+ } catch (err) {
5912
+ try {
5913
+ const fs2 = require("fs");
5914
+ fs2.unlinkSync(output_path);
5915
+ } catch {}
5916
+ return {
5917
+ success: false,
5918
+ output_path,
5919
+ error: `Failed to write .meta file: ${err instanceof Error ? err.message : String(err)}`
5920
+ };
5921
+ }
5922
+ return {
5923
+ success: true,
5924
+ output_path,
5925
+ source_guid: sourceGuid,
5926
+ prefab_instance_id: prefabInstanceId
5927
+ };
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}
5328
6012
  m_PrefabInstance: {fileID: ${prefabInstanceId}}
5329
6013
  m_PrefabAsset: {fileID: 0}
5330
6014
  --- !u!4 &${strippedTransformId} stripped
5331
6015
  Transform:
5332
- m_CorrespondingSourceObject: {fileID: ${rootInfo.transform.file_id}, guid: ${sourceGuid}, type: 3}
6016
+ m_CorrespondingSourceObject: {fileID: ${rootTransformFileId}, guid: ${sourceGuid}, type: 3}
5333
6017
  m_PrefabInstance: {fileID: ${prefabInstanceId}}
5334
6018
  m_PrefabAsset: {fileID: 0}
5335
6019
  --- !u!1001 &${prefabInstanceId}
@@ -5337,11 +6021,51 @@ PrefabInstance:
5337
6021
  m_ObjectHideFlags: 0
5338
6022
  serializedVersion: 2
5339
6023
  m_Modification:
5340
- m_TransformParent: {fileID: 0}
6024
+ m_TransformParent: {fileID: ${parentTransformIdStr}}
5341
6025
  m_Modifications:
5342
- - target: {fileID: ${rootInfo.game_object.file_id}, guid: ${sourceGuid}, type: 3}
6026
+ - target: {fileID: ${rootGoFileId}, guid: ${sourceGuid}, type: 3}
5343
6027
  propertyPath: m_Name
5344
- value: ${finalName}
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
5345
6069
  objectReference: {fileID: 0}
5346
6070
  m_RemovedComponents: []
5347
6071
  m_RemovedGameObjects: []
@@ -5349,42 +6073,21 @@ PrefabInstance:
5349
6073
  m_AddedComponents: []
5350
6074
  m_SourcePrefab: {fileID: 100100000, guid: ${sourceGuid}, type: 3}
5351
6075
  `;
5352
- try {
5353
- import_fs8.writeFileSync(output_path, variantYaml, "utf-8");
5354
- } catch (err) {
5355
- return {
5356
- success: false,
5357
- output_path,
5358
- error: `Failed to write variant prefab: ${err instanceof Error ? err.message : String(err)}`
5359
- };
6076
+ doc.append_raw(yaml);
6077
+ if (parentTransformIdStr !== "0") {
6078
+ doc.add_child_to_parent(parentTransformIdStr, strippedTransformId);
5360
6079
  }
5361
- const variantGuid = generateGuid();
5362
- const variantMetaContent = `fileFormatVersion: 2
5363
- guid: ${variantGuid}
5364
- PrefabImporter:
5365
- externalObjects: {}
5366
- userData:
5367
- assetBundleName:
5368
- assetBundleVariant:
5369
- `;
5370
6080
  try {
5371
- import_fs8.writeFileSync(output_path + ".meta", variantMetaContent, "utf-8");
6081
+ doc.save();
5372
6082
  } catch (err) {
5373
- try {
5374
- const fs2 = require("fs");
5375
- fs2.unlinkSync(output_path);
5376
- } catch {}
5377
- return {
5378
- success: false,
5379
- output_path,
5380
- error: `Failed to write .meta file: ${err instanceof Error ? err.message : String(err)}`
5381
- };
6083
+ return { success: false, file_path: scene_path, error: `Failed to save scene: ${err instanceof Error ? err.message : String(err)}` };
5382
6084
  }
5383
6085
  return {
5384
6086
  success: true,
5385
- output_path,
5386
- source_guid: sourceGuid,
5387
- prefab_instance_id: prefabInstanceId
6087
+ file_path: scene_path,
6088
+ prefab_instance_id: prefabInstanceId,
6089
+ game_object_id: strippedGoId,
6090
+ transform_id: strippedTransformId
5388
6091
  };
5389
6092
  }
5390
6093
  function createScriptableObject(options) {
@@ -5432,9 +6135,10 @@ function createScriptableObject(options) {
5432
6135
  } catch {}
5433
6136
  }
5434
6137
  const baseName = path3.basename(output_path, ".asset");
5435
- 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) : `
5436
6140
  `;
5437
- const assetYaml = `%YAML 1.1
6141
+ let assetYaml = `%YAML 1.1
5438
6142
  %TAG !u! tag:unity3d.com,2011:
5439
6143
  --- !u!114 &11400000
5440
6144
  MonoBehaviour:
@@ -5448,6 +6152,66 @@ MonoBehaviour:
5448
6152
  m_Script: {fileID: 11500000, guid: ${resolved.guid}, type: 3}
5449
6153
  m_Name: ${baseName}
5450
6154
  m_EditorClassIdentifier:${field_yaml}`;
6155
+ if (options.initial_values && Object.keys(options.initial_values).length > 0) {
6156
+ const doc = UnityDocument.from_string(assetYaml);
6157
+ const block = doc.blocks[0];
6158
+ if (block) {
6159
+ const complex_entries = [];
6160
+ for (const [key, val] of Object.entries(options.initial_values)) {
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);
6211
+ }
6212
+ assetYaml = doc.serialize();
6213
+ }
6214
+ }
5451
6215
  try {
5452
6216
  import_fs8.writeFileSync(output_path, assetYaml, "utf-8");
5453
6217
  } catch (err) {
@@ -5528,6 +6292,80 @@ MonoImporter:
5528
6292
  guid
5529
6293
  };
5530
6294
  }
6295
+ var COMPONENT_BASE_CLASSES = new Set([
6296
+ "MonoBehaviour",
6297
+ "NetworkBehaviour",
6298
+ "StateMachineBehaviour"
6299
+ ]);
6300
+ function is_valid_component_base(base_class, project_path, depth = 0) {
6301
+ if (depth > 5)
6302
+ return false;
6303
+ if (COMPONENT_BASE_CLASSES.has(base_class))
6304
+ return true;
6305
+ if (!project_path)
6306
+ return false;
6307
+ const registryPath = path3.join(project_path, ".unity-agentic", "type-registry.json");
6308
+ if (import_fs8.existsSync(registryPath)) {
6309
+ try {
6310
+ const registry = JSON.parse(import_fs8.readFileSync(registryPath, "utf-8"));
6311
+ const match = registry.find((t) => t.name === base_class && t.kind === "class");
6312
+ if (match?.filePath) {
6313
+ const fullPath = path3.isAbsolute(match.filePath) ? match.filePath : path3.join(project_path, match.filePath);
6314
+ if (fullPath.endsWith(".cs") && import_fs8.existsSync(fullPath)) {
6315
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
6316
+ const extract = getNativeExtractSerializedFields2();
6317
+ if (extract) {
6318
+ const typeInfos = extract(fullPath);
6319
+ const info = typeInfos?.find((t) => t.name === base_class);
6320
+ if (info?.baseClass) {
6321
+ return is_valid_component_base(info.baseClass, project_path, depth + 1);
6322
+ }
6323
+ }
6324
+ }
6325
+ if (!fullPath.endsWith(".cs") || !import_fs8.existsSync(fullPath)) {
6326
+ if (match.kind === "class") {
6327
+ return is_likely_component_base_name(base_class);
6328
+ }
6329
+ }
6330
+ }
6331
+ } catch {}
6332
+ }
6333
+ const cachePaths = [
6334
+ path3.join(project_path, ".unity-agentic", "package-cache.json"),
6335
+ path3.join(project_path, ".unity-agentic", "local-package-cache.json")
6336
+ ];
6337
+ for (const cachePath of cachePaths) {
6338
+ if (!import_fs8.existsSync(cachePath))
6339
+ continue;
6340
+ try {
6341
+ const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
6342
+ const baseClassLower = base_class.toLowerCase();
6343
+ for (const [, assetPath] of Object.entries(cache)) {
6344
+ if (!assetPath.endsWith(".cs"))
6345
+ continue;
6346
+ if (path3.basename(assetPath, ".cs").toLowerCase() !== baseClassLower)
6347
+ continue;
6348
+ const fullPath = path3.join(project_path, assetPath);
6349
+ if (!import_fs8.existsSync(fullPath))
6350
+ continue;
6351
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
6352
+ const extract = getNativeExtractSerializedFields2();
6353
+ if (!extract)
6354
+ continue;
6355
+ const typeInfos = extract(fullPath);
6356
+ const info = typeInfos?.find((t) => t.name === base_class);
6357
+ if (info?.baseClass) {
6358
+ return is_valid_component_base(info.baseClass, project_path, depth + 1);
6359
+ }
6360
+ }
6361
+ } catch {}
6362
+ }
6363
+ return false;
6364
+ }
6365
+ function is_likely_component_base_name(name) {
6366
+ const lower = name.toLowerCase();
6367
+ return lower.endsWith("behaviour") || lower.endsWith("behavior") || lower.includes("monobehaviour") || lower.includes("networkbehaviour");
6368
+ }
5531
6369
  function addComponent(options) {
5532
6370
  const { file_path, game_object_name, component_type } = options;
5533
6371
  const project_path = options.project_path || find_unity_project_root(path3.dirname(file_path)) || undefined;
@@ -5553,11 +6391,19 @@ function addComponent(options) {
5553
6391
  };
5554
6392
  }
5555
6393
  const goResult = doc.require_unique_game_object(game_object_name);
6394
+ let gameObjectIdStr;
6395
+ let variantInfo = null;
5556
6396
  if ("error" in goResult) {
5557
- return { success: false, file_path, error: goResult.error };
6397
+ const vResult = find_game_object_in_variant(doc, game_object_name, file_path, project_path);
6398
+ if (!vResult) {
6399
+ return { success: false, file_path, error: goResult.error };
6400
+ }
6401
+ gameObjectIdStr = vResult.stripped_go_id;
6402
+ variantInfo = { source_ref: vResult.source_ref, prefab_instance_id: vResult.prefab_instance_id };
6403
+ } else {
6404
+ gameObjectIdStr = goResult.file_id;
5558
6405
  }
5559
- const gameObjectIdStr = goResult.file_id;
5560
- const gameObjectId = parseInt(gameObjectIdStr, 10);
6406
+ const gameObjectId = gameObjectIdStr;
5561
6407
  const classId = get_class_id(component_type);
5562
6408
  let duplicateWarning;
5563
6409
  const goBlock = doc.find_by_file_id(gameObjectIdStr);
@@ -5572,7 +6418,7 @@ function addComponent(options) {
5572
6418
  }
5573
6419
  }
5574
6420
  const componentIdStr = doc.generate_file_id();
5575
- const componentId = parseInt(componentIdStr, 10);
6421
+ const componentId = componentIdStr;
5576
6422
  let componentYAML;
5577
6423
  let scriptGuid;
5578
6424
  let scriptPath;
@@ -5581,7 +6427,16 @@ function addComponent(options) {
5581
6427
  const componentName = UNITY_CLASS_IDS[classId] || component_type;
5582
6428
  componentYAML = createGenericComponentYAML(componentName, classId, componentId, gameObjectId);
5583
6429
  } else {
5584
- const resolved = resolve_script_with_fields(component_type, project_path);
6430
+ let resolved;
6431
+ try {
6432
+ resolved = resolve_script_with_fields(component_type, project_path);
6433
+ } catch (e) {
6434
+ return {
6435
+ success: false,
6436
+ file_path,
6437
+ error: e instanceof Error ? e.message : String(e)
6438
+ };
6439
+ }
5585
6440
  if (!resolved) {
5586
6441
  const hints = [];
5587
6442
  if (project_path) {
@@ -5608,11 +6463,11 @@ function addComponent(options) {
5608
6463
  error: `"${component_type}" is ${resolved.kind === "enum" ? "an enum" : "an interface"}, not a MonoBehaviour. Cannot add as a component.`
5609
6464
  };
5610
6465
  }
5611
- if (resolved.base_class && !["MonoBehaviour", "NetworkBehaviour", "StateMachineBehaviour"].includes(resolved.base_class)) {
6466
+ if (resolved.base_class && !COMPONENT_BASE_CLASSES.has(resolved.base_class) && !is_valid_component_base(resolved.base_class, project_path)) {
5612
6467
  return {
5613
6468
  success: false,
5614
6469
  file_path,
5615
- error: `"${component_type}" extends ${resolved.base_class}, not MonoBehaviour. Cannot add as a component.`
6470
+ error: `"${component_type}" extends ${resolved.base_class}, which does not inherit from MonoBehaviour. Cannot add as a component. If this is incorrect, ensure the type registry is up to date ("setup" command).`
5616
6471
  };
5617
6472
  }
5618
6473
  let version;
@@ -5621,12 +6476,21 @@ function addComponent(options) {
5621
6476
  version = read_project_version(project_path);
5622
6477
  } catch {}
5623
6478
  }
5624
- componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version);
6479
+ let scriptFileId = 11500000;
6480
+ if (resolved.path?.endsWith(".dll") && resolved.class_name) {
6481
+ scriptFileId = compute_dll_script_file_id(resolved.namespace ?? "", resolved.class_name);
6482
+ }
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);
5625
6485
  scriptGuid = resolved.guid;
5626
6486
  scriptPath = resolved.path || undefined;
5627
6487
  extractionError = resolved.extraction_error;
5628
6488
  }
5629
- addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
6489
+ if (variantInfo) {
6490
+ appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
6491
+ } else {
6492
+ addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
6493
+ }
5630
6494
  doc.append_raw(componentYAML);
5631
6495
  const saveResult = doc.save();
5632
6496
  if (!saveResult.success) {
@@ -5703,6 +6567,7 @@ function copyComponent(options) {
5703
6567
  }
5704
6568
  // src/editor/update.ts
5705
6569
  var import_fs10 = require("fs");
6570
+ var import_fs11 = require("fs");
5706
6571
 
5707
6572
  // src/settings.ts
5708
6573
  var import_fs9 = require("fs");
@@ -6309,6 +7174,14 @@ function edit_sorting_layer(options) {
6309
7174
  }
6310
7175
 
6311
7176
  // src/editor/update.ts
7177
+ function enrich_target_ref(targetRef, blockRaw) {
7178
+ if (/guid:/.test(targetRef))
7179
+ return targetRef;
7180
+ const sourceMatch = blockRaw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
7181
+ if (!sourceMatch)
7182
+ return null;
7183
+ return targetRef.replace(/\}$/, `, guid: ${sourceMatch[1]}, type: 3}`);
7184
+ }
6312
7185
  function eulerToQuaternion(euler) {
6313
7186
  const deg2rad = Math.PI / 180;
6314
7187
  const x = euler.x * deg2rad;
@@ -6331,6 +7204,9 @@ function validate_value_type(current_value, new_value) {
6331
7204
  const current = current_value.trim();
6332
7205
  const incoming = new_value.trim();
6333
7206
  if (/^\{fileID:/.test(current)) {
7207
+ if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
7208
+ return null;
7209
+ }
6334
7210
  if (!/^\{fileID:/.test(incoming)) {
6335
7211
  return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
6336
7212
  }
@@ -6515,10 +7391,10 @@ function safeUnityYAMLEdit(filePath, objectName, propertyName, newValue, project
6515
7391
  const propertyPattern = new RegExp(`(^\\s*m_${normalizedProperty}:\\s*)([^\\n]*)`, "m");
6516
7392
  let updatedRaw = targetBlock.raw;
6517
7393
  if (propertyPattern.test(updatedRaw)) {
6518
- updatedRaw = updatedRaw.replace(propertyPattern, `$1${newValue}`);
7394
+ updatedRaw = updatedRaw.replace(propertyPattern, (_m, prefix) => prefix + newValue);
6519
7395
  } else {
6520
- updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, `
6521
- m_${normalizedProperty}: ${newValue}$1$2`);
7396
+ updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, (_m, g1, g2) => `
7397
+ m_${normalizedProperty}: ${newValue}${g1}${g2}`);
6522
7398
  }
6523
7399
  targetBlock.replace_raw(updatedRaw);
6524
7400
  const saveResult = doc.save();
@@ -6604,7 +7480,9 @@ function editComponentByFileId(options) {
6604
7480
  };
6605
7481
  }
6606
7482
  }
6607
- const currentValue = targetBlock.get_property(exactProperty) ?? (exactProperty !== prefixedProperty ? targetBlock.get_property(prefixedProperty) : null);
7483
+ const exactValue = targetBlock.get_property(exactProperty);
7484
+ const resolvedProperty = exactValue !== null ? exactProperty : exactProperty !== prefixedProperty && targetBlock.get_property(prefixedProperty) !== null ? prefixedProperty : null;
7485
+ const currentValue = exactValue ?? (resolvedProperty === prefixedProperty ? targetBlock.get_property(prefixedProperty) : null);
6608
7486
  if (currentValue !== null) {
6609
7487
  const typeError = validate_value_type(currentValue, new_value);
6610
7488
  if (typeError) {
@@ -6615,9 +7493,14 @@ function editComponentByFileId(options) {
6615
7493
  };
6616
7494
  }
6617
7495
  }
6618
- let modified = targetBlock.set_property(exactProperty, new_value, "{fileID: 0}");
6619
- if (!modified && exactProperty !== prefixedProperty) {
6620
- modified = targetBlock.set_property(prefixedProperty, new_value, "{fileID: 0}");
7496
+ let modified;
7497
+ if (resolvedProperty) {
7498
+ modified = targetBlock.set_property(resolvedProperty, new_value, "{fileID: 0}");
7499
+ } else {
7500
+ modified = targetBlock.set_property(exactProperty, new_value, "{fileID: 0}");
7501
+ if (!modified && exactProperty !== prefixedProperty) {
7502
+ modified = targetBlock.set_property(prefixedProperty, new_value, "{fileID: 0}");
7503
+ }
6621
7504
  }
6622
7505
  if (!modified) {
6623
7506
  if (currentValue !== null) {
@@ -6654,8 +7537,10 @@ function editComponentByFileId(options) {
6654
7537
  };
6655
7538
  }
6656
7539
  function editPrefabOverride(options) {
6657
- const { file_path, prefab_instance, property_path, new_value, object_reference, target } = options;
6658
- const objRef = object_reference ?? "{fileID: 0}";
7540
+ const { file_path, prefab_instance, new_value, object_reference, target, managed_reference } = options;
7541
+ const property_path = /[\[\]]/.test(options.property_path) && !options.property_path.startsWith("'") ? `'${options.property_path}'` : options.property_path;
7542
+ const effectiveValue = managed_reference ?? new_value;
7543
+ const objRef = managed_reference ? "{fileID: 0}" : object_reference ?? "{fileID: 0}";
6659
7544
  if (!import_fs10.existsSync(file_path)) {
6660
7545
  return { success: false, file_path, error: `File not found: ${file_path}` };
6661
7546
  }
@@ -6669,11 +7554,16 @@ function editPrefabOverride(options) {
6669
7554
  if (!targetBlock) {
6670
7555
  return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
6671
7556
  }
6672
- const escapedPath = property_path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6673
- const entryPattern = new RegExp(`(- target:\\s*\\{[^}]+\\}\\s*\\n\\s*propertyPath:\\s*)${escapedPath}(\\s*\\n\\s*value:\\s*)(.*)(\\s*\\n\\s*objectReference:\\s*)(.*)`, "m");
7557
+ const unquotedPath = property_path.replace(/^'|'$/g, "");
7558
+ const quotedPath = `'${unquotedPath}'`;
7559
+ const escapedUnquoted = unquotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7560
+ const escapedQuoted = quotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7561
+ const pathAlternation = escapedUnquoted === escapedQuoted ? escapedUnquoted : `(?:${escapedQuoted}|${escapedUnquoted})`;
7562
+ const entryPattern = new RegExp(`(- target:\\s*\\{[^}]+\\}\\s*\\n\\s*propertyPath:\\s*)${pathAlternation}(\\s*\\n\\s*value:\\s*)(.*)(\\s*\\n\\s*objectReference:\\s*)(.*)`, "m");
6674
7563
  const entryMatch = targetBlock.raw.match(entryPattern);
6675
7564
  if (entryMatch) {
6676
- const updatedText2 = targetBlock.raw.replace(entryPattern, `$1${property_path}$2${new_value}$4${objRef}`);
7565
+ const quotedValue = yaml_quote_if_needed(effectiveValue);
7566
+ const updatedText2 = targetBlock.raw.replace(entryPattern, (_m, g1, g2, _g3, g4) => g1 + property_path + g2 + quotedValue + g4 + objRef);
6677
7567
  targetBlock.replace_raw(updatedText2);
6678
7568
  const saveResult2 = doc.save();
6679
7569
  if (!saveResult2.success) {
@@ -6710,9 +7600,13 @@ function editPrefabOverride(options) {
6710
7600
  error: `Invalid target reference "${targetRef}". Expected format: {fileID: N, guid: ..., type: T}`
6711
7601
  };
6712
7602
  }
7603
+ const enriched = enrich_target_ref(targetRef, targetBlock.raw);
7604
+ if (enriched)
7605
+ targetRef = enriched;
7606
+ const quotedNewValue = yaml_quote_if_needed(effectiveValue);
6713
7607
  const newEntry = ` - target: ${targetRef}
6714
7608
  propertyPath: ${property_path}
6715
- value: ${new_value}
7609
+ value: ${quotedNewValue}
6716
7610
  objectReference: ${objRef}`;
6717
7611
  const removedPattern = /(\n\s*m_RemovedComponents:)/m;
6718
7612
  const removedMatch = targetBlock.raw.match(removedPattern);
@@ -6759,6 +7653,118 @@ ${newEntry}$1`);
6759
7653
  action: "added"
6760
7654
  };
6761
7655
  }
7656
+ function batchEditPrefabOverrides(file_path, prefab_instance, edits) {
7657
+ if (!import_fs10.existsSync(file_path)) {
7658
+ return { success: false, file_path, error: `File not found: ${file_path}` };
7659
+ }
7660
+ let doc;
7661
+ try {
7662
+ doc = UnityDocument.from_file(file_path);
7663
+ } catch (err) {
7664
+ return { success: false, file_path, error: `Failed to read file: ${err instanceof Error ? err.message : String(err)}` };
7665
+ }
7666
+ const targetBlock = findPrefabInstanceBlock(doc, prefab_instance);
7667
+ if (!targetBlock) {
7668
+ return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
7669
+ }
7670
+ const actions = [];
7671
+ for (const edit of edits) {
7672
+ const { value, object_reference, target, managed_reference } = edit;
7673
+ const property_path = /[\[\]]/.test(edit.property_path) && !edit.property_path.startsWith("'") ? `'${edit.property_path}'` : edit.property_path;
7674
+ const effectiveValue = managed_reference ?? value;
7675
+ const objRef = managed_reference ? "{fileID: 0}" : object_reference ?? "{fileID: 0}";
7676
+ const unquotedPath = property_path.replace(/^'|'$/g, "");
7677
+ const quotedPathStr = `'${unquotedPath}'`;
7678
+ const batchEscapedUnquoted = unquotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7679
+ const batchEscapedQuoted = quotedPathStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7680
+ const batchPathAlt = batchEscapedUnquoted === batchEscapedQuoted ? batchEscapedUnquoted : `(?:${batchEscapedQuoted}|${batchEscapedUnquoted})`;
7681
+ const entryPattern = new RegExp(`(- target:\\s*\\{[^}]+\\}\\s*\\n\\s*propertyPath:\\s*)${batchPathAlt}(\\s*\\n\\s*value:\\s*)(.*)(\\s*\\n\\s*objectReference:\\s*)(.*)`, "m");
7682
+ const entryMatch = targetBlock.raw.match(entryPattern);
7683
+ if (entryMatch) {
7684
+ const quotedValue = yaml_quote_if_needed(effectiveValue);
7685
+ const updatedText = targetBlock.raw.replace(entryPattern, (_m, g1, g2, _g3, g4) => g1 + property_path + g2 + quotedValue + g4 + objRef);
7686
+ targetBlock.replace_raw(updatedText);
7687
+ actions.push({ property_path, action: "updated" });
7688
+ } else {
7689
+ let targetRef = target;
7690
+ if (!targetRef) {
7691
+ const rootProp = property_path.split(".")[0];
7692
+ const siblingPattern = new RegExp(`- target:\\s*(\\{[^}]+\\})\\s*\\n\\s*propertyPath:\\s*${rootProp.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "m");
7693
+ const siblingMatch = targetBlock.raw.match(siblingPattern);
7694
+ if (siblingMatch) {
7695
+ targetRef = siblingMatch[1];
7696
+ }
7697
+ }
7698
+ if (!targetRef) {
7699
+ return {
7700
+ success: false,
7701
+ file_path,
7702
+ error: `Cannot infer target for new override "${property_path}". Provide "target" in the edit entry (e.g., "{fileID: 400000, guid: ..., type: 3}").`
7703
+ };
7704
+ }
7705
+ if (!/^\{fileID:\s*-?\d+/.test(targetRef)) {
7706
+ return {
7707
+ success: false,
7708
+ file_path,
7709
+ error: `Invalid target reference "${targetRef}" for "${property_path}". Expected format: {fileID: N, guid: ..., type: T}`
7710
+ };
7711
+ }
7712
+ const enriched = enrich_target_ref(targetRef, targetBlock.raw);
7713
+ if (enriched)
7714
+ targetRef = enriched;
7715
+ const quotedNewValue = yaml_quote_if_needed(effectiveValue);
7716
+ const newEntry = ` - target: ${targetRef}
7717
+ propertyPath: ${property_path}
7718
+ value: ${quotedNewValue}
7719
+ objectReference: ${objRef}`;
7720
+ const removedPattern = /(\n\s*m_RemovedComponents:)/m;
7721
+ const removedMatch = targetBlock.raw.match(removedPattern);
7722
+ let updatedText;
7723
+ if (removedMatch) {
7724
+ updatedText = targetBlock.raw.replace(removedPattern, `
7725
+ ${newEntry}$1`);
7726
+ } else {
7727
+ const lines = targetBlock.raw.split(`
7728
+ `);
7729
+ let lastObjRefIdx = -1;
7730
+ let inModifications = false;
7731
+ for (let i = 0;i < lines.length; i++) {
7732
+ if (/m_Modifications:/.test(lines[i])) {
7733
+ inModifications = true;
7734
+ continue;
7735
+ }
7736
+ if (inModifications && /^\s*objectReference:/.test(lines[i])) {
7737
+ lastObjRefIdx = i;
7738
+ }
7739
+ if (inModifications && /^\s*m_\w+:/.test(lines[i]) && !/objectReference/.test(lines[i]) && !/propertyPath/.test(lines[i])) {
7740
+ if (lastObjRefIdx !== -1)
7741
+ break;
7742
+ }
7743
+ }
7744
+ if (lastObjRefIdx !== -1) {
7745
+ lines.splice(lastObjRefIdx + 1, 0, newEntry);
7746
+ updatedText = lines.join(`
7747
+ `);
7748
+ } else {
7749
+ return { success: false, file_path, error: `Could not find insertion point in m_Modifications for "${property_path}"` };
7750
+ }
7751
+ }
7752
+ targetBlock.replace_raw(updatedText);
7753
+ actions.push({ property_path, action: "added" });
7754
+ }
7755
+ }
7756
+ const saveResult = doc.save();
7757
+ if (!saveResult.success) {
7758
+ return { success: false, file_path, error: saveResult.error };
7759
+ }
7760
+ return {
7761
+ success: true,
7762
+ file_path,
7763
+ prefab_instance_id: prefab_instance,
7764
+ applied: actions.length,
7765
+ actions
7766
+ };
7767
+ }
6762
7768
  function editTransform(options) {
6763
7769
  const { file_path, transform_id, position, rotation, scale } = options;
6764
7770
  const pathError = validate_file_path(file_path, "write");
@@ -6855,10 +7861,10 @@ function batchEditProperties(filePath, edits) {
6855
7861
  const propertyPattern = new RegExp(`(^\\s*m_${normalizedProperty}:\\s*)([^\\n]*)`, "m");
6856
7862
  let updatedRaw = targetBlock.raw;
6857
7863
  if (propertyPattern.test(updatedRaw)) {
6858
- updatedRaw = updatedRaw.replace(propertyPattern, `$1${edit.new_value}`);
7864
+ updatedRaw = updatedRaw.replace(propertyPattern, (_m, prefix) => prefix + edit.new_value);
6859
7865
  } else {
6860
- updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, `
6861
- m_${normalizedProperty}: ${edit.new_value}$1$2`);
7866
+ updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, (_m, g1, g2) => `
7867
+ m_${normalizedProperty}: ${edit.new_value}${g1}${g2}`);
6862
7868
  }
6863
7869
  targetBlock.replace_raw(updatedRaw);
6864
7870
  }
@@ -6963,9 +7969,9 @@ function reparentGameObject(options) {
6963
7969
  return {
6964
7970
  success: true,
6965
7971
  file_path,
6966
- child_transform_id: parseInt(childTransformId, 10),
6967
- old_parent_transform_id: parseInt(oldParentTransformId, 10),
6968
- new_parent_transform_id: parseInt(newParentTransformId, 10)
7972
+ child_transform_id: childTransformId,
7973
+ old_parent_transform_id: oldParentTransformId,
7974
+ new_parent_transform_id: newParentTransformId
6969
7975
  };
6970
7976
  }
6971
7977
  function findPrefabInstanceBlock(doc, identifier) {
@@ -7329,30 +8335,277 @@ function removeRemovedGameObject(options) {
7329
8335
  if (!refPattern.test(targetBlock.raw)) {
7330
8336
  return { success: false, file_path, error: `GameObject reference "${component_ref}" not found in m_RemovedGameObjects` };
7331
8337
  }
7332
- const updatedRaw = targetBlock.raw.replace(refPattern, "");
7333
- targetBlock.replace_raw(updatedRaw);
7334
- if (!doc.validate()) {
7335
- return { success: false, file_path, error: "Validation failed after removing GameObject" };
8338
+ const updatedRaw = targetBlock.raw.replace(refPattern, "");
8339
+ targetBlock.replace_raw(updatedRaw);
8340
+ if (!doc.validate()) {
8341
+ return { success: false, file_path, error: "Validation failed after removing GameObject" };
8342
+ }
8343
+ const restoreGoSaveResult = doc.save();
8344
+ if (!restoreGoSaveResult.success) {
8345
+ return { success: false, file_path, error: restoreGoSaveResult.error };
8346
+ }
8347
+ return {
8348
+ success: true,
8349
+ file_path,
8350
+ prefab_instance_id: targetBlock.file_id
8351
+ };
8352
+ }
8353
+ function generate_managed_reference_id(existing_raw, count = 1) {
8354
+ const { randomBytes } = require("crypto");
8355
+ const existingIds = new Set;
8356
+ const idPattern = /\b(\d{1,19})\b/g;
8357
+ for (const line of existing_raw.split(`
8358
+ `)) {
8359
+ if (line.includes("managedReferences[") || line.includes("rid:") || line.includes("objectReference:") && /\d{10,}/.test(line) || line.includes("value:") && /\d{16,}/.test(line)) {
8360
+ let m;
8361
+ while ((m = idPattern.exec(line)) !== null) {
8362
+ const val = BigInt(m[1]);
8363
+ if (val > 0n)
8364
+ existingIds.add(val);
8365
+ }
8366
+ }
8367
+ }
8368
+ const buf = randomBytes(8);
8369
+ const raw64 = buf.readBigUInt64BE();
8370
+ const mask46 = (1n << 46n) - 1n;
8371
+ let base = (raw64 & mask46 | 1n) << 18n;
8372
+ const INT64_MAX = 9223372036854775807n;
8373
+ if (base > INT64_MAX)
8374
+ base = base & INT64_MAX >> 18n << 18n;
8375
+ if (base <= 0n)
8376
+ base = 1n << 18n;
8377
+ const results = [];
8378
+ for (let i = 0;i < count; i++) {
8379
+ let id = base + BigInt(i);
8380
+ while (id <= 0n || id > INT64_MAX || existingIds.has(id)) {
8381
+ id += 1n;
8382
+ }
8383
+ existingIds.add(id);
8384
+ results.push(id.toString());
8385
+ }
8386
+ return results;
8387
+ }
8388
+ function addPrefabManagedReference(options) {
8389
+ const { file_path, prefab_instance, field_path, type_name, target, project_path } = options;
8390
+ const index = options.index ?? 0;
8391
+ if (!import_fs10.existsSync(file_path)) {
8392
+ return { success: false, file_path, error: `File not found: ${file_path}` };
8393
+ }
8394
+ const typeInfo = resolve_managed_type(type_name, project_path);
8395
+ let finalTypeInfo;
8396
+ if (typeInfo) {
8397
+ finalTypeInfo = typeInfo;
8398
+ } else {
8399
+ const spaceIdx = type_name.indexOf(" ");
8400
+ if (spaceIdx > 0) {
8401
+ const asm = type_name.substring(0, spaceIdx);
8402
+ const fullType = type_name.substring(spaceIdx + 1);
8403
+ const lastDot = fullType.lastIndexOf(".");
8404
+ finalTypeInfo = {
8405
+ assembly: asm,
8406
+ namespace: lastDot >= 0 ? fullType.substring(0, lastDot) : "",
8407
+ class_name: lastDot >= 0 ? fullType.substring(lastDot + 1) : fullType
8408
+ };
8409
+ } else {
8410
+ return {
8411
+ success: false,
8412
+ file_path,
8413
+ error: `Type "${type_name}" not found in type registry. Use "Assembly Namespace.ClassName" format or run "setup" to rebuild.`
8414
+ };
8415
+ }
8416
+ }
8417
+ let doc;
8418
+ try {
8419
+ doc = UnityDocument.from_file(file_path);
8420
+ } catch (err) {
8421
+ return { success: false, file_path, error: `Failed to read file: ${err instanceof Error ? err.message : String(err)}` };
8422
+ }
8423
+ const piBlock = findPrefabInstanceBlock(doc, prefab_instance);
8424
+ if (!piBlock) {
8425
+ return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
8426
+ }
8427
+ const [rid] = generate_managed_reference_id(piBlock.raw);
8428
+ const typeValue = finalTypeInfo.namespace ? `${finalTypeInfo.assembly} ${finalTypeInfo.namespace}.${finalTypeInfo.class_name}` : `${finalTypeInfo.assembly} ${finalTypeInfo.class_name}`;
8429
+ const basePath = field_path.replace(/\.Array$/, "");
8430
+ const overrides = [
8431
+ {
8432
+ property_path: `${basePath}.Array.data[${index}].managedReferenceType`,
8433
+ value: typeValue,
8434
+ target
8435
+ },
8436
+ {
8437
+ property_path: `${basePath}.Array.data[${index}].managedReferenceValue`,
8438
+ value: rid,
8439
+ target
8440
+ },
8441
+ {
8442
+ property_path: `managedReferences[-2]`,
8443
+ value: "2",
8444
+ target
8445
+ }
8446
+ ];
8447
+ const result = batchEditPrefabOverrides(file_path, prefab_instance, overrides);
8448
+ if (!result.success) {
8449
+ return { success: false, file_path, error: result.error };
8450
+ }
8451
+ return {
8452
+ success: true,
8453
+ file_path,
8454
+ rid,
8455
+ type_info: finalTypeInfo,
8456
+ overrides_created: overrides.map((o) => o.property_path)
8457
+ };
8458
+ }
8459
+ function resolve_managed_type(type_name, project_path) {
8460
+ if (!project_path)
8461
+ return null;
8462
+ const { join: join10 } = require("path");
8463
+ const registryPath = join10(project_path, ".unity-agentic", "type-registry.json");
8464
+ if (!import_fs10.existsSync(registryPath))
8465
+ return null;
8466
+ try {
8467
+ const registry = JSON.parse(import_fs11.readFileSync(registryPath, "utf-8"));
8468
+ let targetName = type_name;
8469
+ let targetNs = null;
8470
+ const dotIndex = type_name.lastIndexOf(".");
8471
+ if (dotIndex > 0) {
8472
+ targetNs = type_name.substring(0, dotIndex);
8473
+ targetName = type_name.substring(dotIndex + 1);
8474
+ }
8475
+ const matches = registry.filter((t) => {
8476
+ if (t.name.toLowerCase() !== targetName.toLowerCase())
8477
+ return false;
8478
+ if (targetNs && t.namespace?.toLowerCase() !== targetNs.toLowerCase())
8479
+ return false;
8480
+ return true;
8481
+ });
8482
+ if (matches.length === 0)
8483
+ return null;
8484
+ const match = matches[0];
8485
+ let assembly = "Assembly-CSharp";
8486
+ if (match.filePath && (match.filePath.includes("Packages/") || match.filePath.includes("PackageCache/"))) {
8487
+ const pkgMatch = match.filePath.match(/(?:Packages|PackageCache)\/([^/]+)/);
8488
+ if (pkgMatch)
8489
+ assembly = pkgMatch[1].replace(/@.*$/, "");
8490
+ }
8491
+ return { class_name: match.name, namespace: match.namespace || "", assembly };
8492
+ } catch {
8493
+ return null;
8494
+ }
8495
+ }
8496
+ function editManagedReference(options) {
8497
+ const { file_path, file_id, field_path, type_name, project_path, append, initial_values } = options;
8498
+ if (!import_fs10.existsSync(file_path)) {
8499
+ return { success: false, file_path, error: `File not found: ${file_path}` };
8500
+ }
8501
+ let doc;
8502
+ try {
8503
+ doc = UnityDocument.from_file(file_path);
8504
+ } catch (err) {
8505
+ return { success: false, file_path, error: `Failed to read file: ${err instanceof Error ? err.message : String(err)}` };
8506
+ }
8507
+ const block = doc.find_by_file_id(file_id);
8508
+ if (!block) {
8509
+ return { success: false, file_path, error: `Component with fileID ${file_id} not found` };
8510
+ }
8511
+ if (block.class_id !== 114) {
8512
+ return { success: false, file_path, error: `fileID ${file_id} is not a MonoBehaviour (class ${block.class_id}). Managed references only apply to MonoBehaviours.` };
8513
+ }
8514
+ const typeInfo = resolve_managed_type(type_name, project_path);
8515
+ if (!typeInfo) {
8516
+ const spaceIdx = type_name.indexOf(" ");
8517
+ if (spaceIdx > 0) {
8518
+ const asm = type_name.substring(0, spaceIdx);
8519
+ const fullType = type_name.substring(spaceIdx + 1);
8520
+ const lastDot = fullType.lastIndexOf(".");
8521
+ const manual = {
8522
+ assembly: asm,
8523
+ namespace: lastDot >= 0 ? fullType.substring(0, lastDot) : "",
8524
+ class_name: lastDot >= 0 ? fullType.substring(lastDot + 1) : fullType
8525
+ };
8526
+ return applyManagedReference(doc, block, file_path, field_path, manual, append, initial_values);
8527
+ }
8528
+ return {
8529
+ success: false,
8530
+ file_path,
8531
+ error: `Type "${type_name}" not found in type registry. Use "Assembly Namespace.ClassName" format or run "setup" to rebuild.`
8532
+ };
8533
+ }
8534
+ return applyManagedReference(doc, block, file_path, field_path, typeInfo, append, initial_values);
8535
+ }
8536
+ function applyManagedReference(doc, block, file_path, field_path, typeInfo, append, initial_values) {
8537
+ let raw = block.raw;
8538
+ const ridMatches = [...raw.matchAll(/rid:[ \t]*(\d+)/g)];
8539
+ const existingRids = ridMatches.map((m) => parseInt(m[1], 10)).filter((n) => !isNaN(n));
8540
+ const nextRid = existingRids.length > 0 ? Math.max(...existingRids) + 1 : 1;
8541
+ let dataBlock = "{}";
8542
+ if (initial_values && Object.keys(initial_values).length > 0) {
8543
+ const lines = Object.entries(initial_values).map(([k, v]) => ` ${k}: ${v}`);
8544
+ dataBlock = `
8545
+ ` + lines.join(`
8546
+ `);
8547
+ }
8548
+ const refEntry = ` - rid: ${nextRid}
8549
+ type: {class: ${typeInfo.class_name}, ns: ${typeInfo.namespace}, asm: ${typeInfo.assembly}}
8550
+ data: ${dataBlock}`;
8551
+ const refIdsBlockPattern = /(\s*RefIds:\s*\n)/;
8552
+ const refIdsEmptyPattern = /(\s*RefIds:)[ \t]*\[\]/;
8553
+ const referencesPattern = /(\s*references:\s*\n\s*version:\s*\d+\s*\n)/;
8554
+ if (refIdsBlockPattern.test(raw)) {
8555
+ raw = raw.replace(refIdsBlockPattern, `$1${refEntry}
8556
+ `);
8557
+ } else if (refIdsEmptyPattern.test(raw)) {
8558
+ raw = raw.replace(refIdsEmptyPattern, `$1
8559
+ ${refEntry}`);
8560
+ } else if (referencesPattern.test(raw)) {
8561
+ raw = raw.replace(referencesPattern, `$1 RefIds:
8562
+ ${refEntry}
8563
+ `);
8564
+ } else {
8565
+ raw = raw.trimEnd() + `
8566
+ references:
8567
+ version: 2
8568
+ RefIds:
8569
+ ${refEntry}
8570
+ `;
8571
+ }
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");
8589
+ if (fieldRidPattern.test(raw)) {
8590
+ raw = raw.replace(fieldRidPattern, `$1${nextRid}`);
8591
+ }
7336
8592
  }
7337
- const saveResult = doc.save();
7338
- if (!saveResult.success) {
7339
- return { success: false, file_path, error: saveResult.error };
8593
+ block.replace_raw(raw);
8594
+ const mrSave = doc.save();
8595
+ if (!mrSave.success) {
8596
+ return { success: false, file_path, error: mrSave.error };
7340
8597
  }
7341
- return {
7342
- success: true,
7343
- file_path,
7344
- prefab_instance_id: targetBlock.file_id
7345
- };
8598
+ return { success: true, file_path, rid: nextRid, type_info: typeInfo };
7346
8599
  }
7347
8600
  // src/editor/delete.ts
7348
- var import_fs11 = require("fs");
8601
+ var import_fs12 = require("fs");
7349
8602
  function removeComponent(options) {
7350
8603
  const { file_path, file_id } = options;
7351
8604
  const pathError = validate_file_path(file_path, "write");
7352
8605
  if (pathError) {
7353
8606
  return { success: false, file_path, error: pathError };
7354
8607
  }
7355
- if (!import_fs11.existsSync(file_path)) {
8608
+ if (!import_fs12.existsSync(file_path)) {
7356
8609
  return { success: false, file_path, error: `File not found: ${file_path}` };
7357
8610
  }
7358
8611
  let doc;
@@ -7376,7 +8629,8 @@ function removeComponent(options) {
7376
8629
  const parentGoId = goMatch[1];
7377
8630
  const goBlock = doc.find_by_file_id(parentGoId);
7378
8631
  if (goBlock) {
7379
- const compLinePattern = new RegExp(`\\s*- component: \\{fileID: ${file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\}\\n?`);
8632
+ const escaped = file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8633
+ const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
7380
8634
  const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
7381
8635
  goBlock.replace_raw(modifiedRaw);
7382
8636
  }
@@ -7402,7 +8656,7 @@ function deleteGameObject(options) {
7402
8656
  if (pathError) {
7403
8657
  return { success: false, file_path, error: pathError };
7404
8658
  }
7405
- if (!import_fs11.existsSync(file_path)) {
8659
+ if (!import_fs12.existsSync(file_path)) {
7406
8660
  return { success: false, file_path, error: `File not found: ${file_path}` };
7407
8661
  }
7408
8662
  let doc;
@@ -7481,7 +8735,7 @@ function deletePrefabInstance(options) {
7481
8735
  if (pathError) {
7482
8736
  return { success: false, file_path, error: pathError };
7483
8737
  }
7484
- if (!import_fs11.existsSync(file_path)) {
8738
+ if (!import_fs12.existsSync(file_path)) {
7485
8739
  return { success: false, file_path, error: `File not found: ${file_path}` };
7486
8740
  }
7487
8741
  let doc;
@@ -7561,7 +8815,7 @@ function deletePrefabInstance(options) {
7561
8815
  };
7562
8816
  }
7563
8817
  // src/editor/duplicate.ts
7564
- var import_fs12 = require("fs");
8818
+ var import_fs13 = require("fs");
7565
8819
  var path5 = __toESM(require("path"));
7566
8820
  init_scanner();
7567
8821
  function findPrefabInstanceByName(doc, name) {
@@ -7577,7 +8831,7 @@ function findPrefabInstanceByName(doc, name) {
7577
8831
  }
7578
8832
  function duplicateGameObject(options) {
7579
8833
  const { file_path, object_name, new_name } = options;
7580
- if (!import_fs12.existsSync(file_path)) {
8834
+ if (!import_fs13.existsSync(file_path)) {
7581
8835
  return { success: false, file_path, error: `File not found: ${file_path}` };
7582
8836
  }
7583
8837
  let doc;
@@ -7710,7 +8964,7 @@ function duplicateGameObject(options) {
7710
8964
  }
7711
8965
  function unpackPrefab(options) {
7712
8966
  const { file_path, prefab_instance, project_path } = options;
7713
- if (!import_fs12.existsSync(file_path)) {
8967
+ if (!import_fs13.existsSync(file_path)) {
7714
8968
  return { success: false, file_path, error: `File not found: ${file_path}` };
7715
8969
  }
7716
8970
  let sceneDoc;
@@ -7756,7 +9010,7 @@ function unpackPrefab(options) {
7756
9010
  sourcePrefabPath = resolvedPath;
7757
9011
  }
7758
9012
  }
7759
- if (!sourcePrefabPath || !import_fs12.existsSync(sourcePrefabPath)) {
9013
+ if (!sourcePrefabPath || !import_fs13.existsSync(sourcePrefabPath)) {
7760
9014
  const inferredProject = project_path || find_unity_project_root(path5.dirname(file_path));
7761
9015
  if (inferredProject) {
7762
9016
  const nativeBuild = getNativeBuildGuidCache();
@@ -7770,7 +9024,7 @@ function unpackPrefab(options) {
7770
9024
  }
7771
9025
  }
7772
9026
  }
7773
- if (!sourcePrefabPath || !import_fs12.existsSync(sourcePrefabPath)) {
9027
+ if (!sourcePrefabPath || !import_fs13.existsSync(sourcePrefabPath)) {
7774
9028
  const searchedPaths = [];
7775
9029
  if (project_path)
7776
9030
  searchedPaths.push(`GUID cache: ${path5.join(project_path, ".unity-agentic", "guid-cache.json")}`);
@@ -8203,15 +9457,15 @@ function move_scene(projectPath, scenePath, newPosition) {
8203
9457
  }
8204
9458
 
8205
9459
  // src/packages.ts
8206
- var import_fs13 = require("fs");
9460
+ var import_fs14 = require("fs");
8207
9461
  var import_path6 = require("path");
8208
9462
  function load_manifest(project_path) {
8209
9463
  const manifest_path = import_path6.join(project_path, "Packages", "manifest.json");
8210
- if (!import_fs13.existsSync(manifest_path)) {
9464
+ if (!import_fs14.existsSync(manifest_path)) {
8211
9465
  return { error: `manifest.json not found at ${manifest_path}` };
8212
9466
  }
8213
9467
  try {
8214
- const raw = import_fs13.readFileSync(manifest_path, "utf-8");
9468
+ const raw = import_fs14.readFileSync(manifest_path, "utf-8");
8215
9469
  const parsed = JSON.parse(raw);
8216
9470
  if (!parsed.dependencies || typeof parsed.dependencies !== "object") {
8217
9471
  return { error: `Invalid manifest.json: missing "dependencies" object` };
@@ -8227,7 +9481,7 @@ function save_manifest(manifest_path, manifest) {
8227
9481
  sorted_deps[key] = manifest.dependencies[key];
8228
9482
  }
8229
9483
  const output = { ...manifest, dependencies: sorted_deps };
8230
- import_fs13.writeFileSync(manifest_path, JSON.stringify(output, null, 2) + `
9484
+ import_fs14.writeFileSync(manifest_path, JSON.stringify(output, null, 2) + `
8231
9485
  `, "utf-8");
8232
9486
  }
8233
9487
  function list_packages(project_path, search) {
@@ -8281,7 +9535,7 @@ function remove_package(project_path, name) {
8281
9535
  }
8282
9536
 
8283
9537
  // src/input-actions.ts
8284
- var import_fs14 = require("fs");
9538
+ var import_fs15 = require("fs");
8285
9539
  var import_crypto = require("crypto");
8286
9540
  function generate_action_id() {
8287
9541
  const bytes = import_crypto.randomBytes(16);
@@ -8295,18 +9549,18 @@ function generate_action_id() {
8295
9549
  ].join("-");
8296
9550
  }
8297
9551
  function load_input_actions(file) {
8298
- if (!import_fs14.existsSync(file)) {
9552
+ if (!import_fs15.existsSync(file)) {
8299
9553
  return { error: `File not found: ${file}` };
8300
9554
  }
8301
9555
  try {
8302
- const raw = import_fs14.readFileSync(file, "utf-8");
9556
+ const raw = import_fs15.readFileSync(file, "utf-8");
8303
9557
  return JSON.parse(raw);
8304
9558
  } catch (err) {
8305
9559
  return { error: `Failed to parse input actions: ${err instanceof Error ? err.message : String(err)}` };
8306
9560
  }
8307
9561
  }
8308
9562
  function save_input_actions(file, data) {
8309
- import_fs14.writeFileSync(file, JSON.stringify(data, null, 4) + `
9563
+ import_fs15.writeFileSync(file, JSON.stringify(data, null, 4) + `
8310
9564
  `, "utf-8");
8311
9565
  }
8312
9566
  function add_map(data, name) {
@@ -8460,11 +9714,52 @@ function build_create_command() {
8460
9714
  if (!result.success)
8461
9715
  process.exitCode = 1;
8462
9716
  });
8463
- 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("-j, --json", "Output as JSON").action((output_path, script, options) => {
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
+ });
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) => {
9748
+ let initial_values;
9749
+ if (options.set) {
9750
+ try {
9751
+ initial_values = JSON.parse(options.set);
9752
+ } catch {
9753
+ console.log(JSON.stringify({ success: false, error: `Invalid JSON for --set: ${options.set}` }, null, 2));
9754
+ process.exitCode = 1;
9755
+ return;
9756
+ }
9757
+ }
8464
9758
  const result = createScriptableObject({
8465
9759
  output_path,
8466
9760
  script,
8467
- project_path: options.project
9761
+ project_path: options.project,
9762
+ initial_values
8468
9763
  });
8469
9764
  console.log(JSON.stringify(result, null, 2));
8470
9765
  if (!result.success)
@@ -8521,7 +9816,7 @@ function build_create_command() {
8521
9816
  console.log(JSON.stringify({ success: false, error: "--shader <guid> is required" }, null, 2));
8522
9817
  process.exit(1);
8523
9818
  }
8524
- if (import_fs15.existsSync(output_path)) {
9819
+ if (import_fs16.existsSync(output_path)) {
8525
9820
  console.log(JSON.stringify({ success: false, error: `File already exists: ${output_path}` }, null, 2));
8526
9821
  process.exit(1);
8527
9822
  }
@@ -8593,7 +9888,7 @@ Material:
8593
9888
  m_Colors: ${color_section}
8594
9889
  m_BuildTextureStacks: []
8595
9890
  `;
8596
- import_fs15.writeFileSync(output_path, mat_content, "utf-8");
9891
+ import_fs16.writeFileSync(output_path, mat_content, "utf-8");
8597
9892
  const guid = import_crypto2.randomBytes(16).toString("hex");
8598
9893
  const meta_content = `fileFormatVersion: 2
8599
9894
  guid: ${guid}
@@ -8604,7 +9899,7 @@ NativeFormatImporter:
8604
9899
  assetBundleName:
8605
9900
  assetBundleVariant:
8606
9901
  `;
8607
- import_fs15.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
9902
+ import_fs16.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
8608
9903
  console.log(JSON.stringify({
8609
9904
  success: true,
8610
9905
  file: output_path,
@@ -8634,7 +9929,7 @@ NativeFormatImporter:
8634
9929
  process.exitCode = 1;
8635
9930
  return;
8636
9931
  }
8637
- if (import_fs15.existsSync(output_path)) {
9932
+ if (import_fs16.existsSync(output_path)) {
8638
9933
  console.log(JSON.stringify({ success: false, error: `File already exists: ${output_path}` }, null, 2));
8639
9934
  process.exitCode = 1;
8640
9935
  return;
@@ -8661,7 +9956,7 @@ ScriptedImporter:
8661
9956
  wrapperClassName:
8662
9957
  wrapperCodeNamespace:
8663
9958
  `;
8664
- import_fs15.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
9959
+ import_fs16.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
8665
9960
  console.log(JSON.stringify({
8666
9961
  success: true,
8667
9962
  file: output_path,
@@ -8677,7 +9972,7 @@ ScriptedImporter:
8677
9972
  return;
8678
9973
  }
8679
9974
  const name = name_arg || import_path7.basename(output_path).replace(/\.anim$/i, "");
8680
- if (import_fs15.existsSync(output_path)) {
9975
+ if (import_fs16.existsSync(output_path)) {
8681
9976
  console.log(JSON.stringify({ success: false, error: `File already exists: ${output_path}` }, null, 2));
8682
9977
  process.exitCode = 1;
8683
9978
  return;
@@ -8744,7 +10039,7 @@ AnimationClip:
8744
10039
  m_HasMotionFloatCurves: 0
8745
10040
  m_Events: []
8746
10041
  `;
8747
- import_fs15.writeFileSync(output_path, anim_content, "utf-8");
10042
+ import_fs16.writeFileSync(output_path, anim_content, "utf-8");
8748
10043
  const guid = import_crypto2.randomBytes(16).toString("hex");
8749
10044
  const meta_content = `fileFormatVersion: 2
8750
10045
  guid: ${guid}
@@ -8755,7 +10050,7 @@ NativeFormatImporter:
8755
10050
  assetBundleName:
8756
10051
  assetBundleVariant:
8757
10052
  `;
8758
- import_fs15.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
10053
+ import_fs16.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
8759
10054
  console.log(JSON.stringify({
8760
10055
  success: true,
8761
10056
  file: output_path,
@@ -8773,7 +10068,7 @@ NativeFormatImporter:
8773
10068
  process.exitCode = 1;
8774
10069
  return;
8775
10070
  }
8776
- if (import_fs15.existsSync(output_path)) {
10071
+ if (import_fs16.existsSync(output_path)) {
8777
10072
  console.log(JSON.stringify({ success: false, error: `File already exists: ${output_path}` }, null, 2));
8778
10073
  process.exitCode = 1;
8779
10074
  return;
@@ -8823,7 +10118,7 @@ AnimatorStateMachine:
8823
10118
  m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
8824
10119
  m_DefaultState: {fileID: 0}
8825
10120
  `;
8826
- import_fs15.writeFileSync(output_path, ctrl_content, "utf-8");
10121
+ import_fs16.writeFileSync(output_path, ctrl_content, "utf-8");
8827
10122
  const guid = import_crypto2.randomBytes(16).toString("hex");
8828
10123
  const meta_content = `fileFormatVersion: 2
8829
10124
  guid: ${guid}
@@ -8834,7 +10129,7 @@ NativeFormatImporter:
8834
10129
  assetBundleName:
8835
10130
  assetBundleVariant:
8836
10131
  `;
8837
- import_fs15.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
10132
+ import_fs16.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
8838
10133
  console.log(JSON.stringify({
8839
10134
  success: true,
8840
10135
  file: output_path,
@@ -8851,7 +10146,7 @@ NativeFormatImporter:
8851
10146
  process.exitCode = 1;
8852
10147
  return;
8853
10148
  }
8854
- if (import_fs15.existsSync(output_path)) {
10149
+ if (import_fs16.existsSync(output_path)) {
8855
10150
  console.log(JSON.stringify({ success: false, error: `File already exists: ${output_path}` }, null, 2));
8856
10151
  process.exitCode = 1;
8857
10152
  return;
@@ -8890,7 +10185,7 @@ Transform:
8890
10185
  m_Father: {fileID: 0}
8891
10186
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
8892
10187
  `;
8893
- import_fs15.writeFileSync(output_path, prefab_content, "utf-8");
10188
+ import_fs16.writeFileSync(output_path, prefab_content, "utf-8");
8894
10189
  const guid = import_crypto2.randomBytes(16).toString("hex");
8895
10190
  const meta_content = `fileFormatVersion: 2
8896
10191
  guid: ${guid}
@@ -8900,7 +10195,7 @@ PrefabImporter:
8900
10195
  assetBundleName:
8901
10196
  assetBundleVariant:
8902
10197
  `;
8903
- import_fs15.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
10198
+ import_fs16.writeFileSync(`${output_path}.meta`, meta_content, "utf-8");
8904
10199
  console.log(JSON.stringify({
8905
10200
  success: true,
8906
10201
  file: output_path,
@@ -8914,7 +10209,7 @@ PrefabImporter:
8914
10209
 
8915
10210
  // src/cmd-read.ts
8916
10211
  init_scanner();
8917
- var import_fs16 = require("fs");
10212
+ var import_fs17 = require("fs");
8918
10213
  var import_path8 = require("path");
8919
10214
  var import_os = require("os");
8920
10215
  function parse_material_yaml(content) {
@@ -9161,7 +10456,7 @@ function categorize_asset(filePath) {
9161
10456
  function find_project_root_from_file(filePath) {
9162
10457
  let dir = import_path8.dirname(import_path8.resolve(filePath));
9163
10458
  for (let i = 0;i < 20; i++) {
9164
- if (import_fs16.existsSync(import_path8.join(dir, "Assets")) && import_fs16.existsSync(import_path8.join(dir, "ProjectSettings"))) {
10459
+ if (import_fs17.existsSync(import_path8.join(dir, "Assets")) && import_fs17.existsSync(import_path8.join(dir, "ProjectSettings"))) {
9165
10460
  return dir;
9166
10461
  }
9167
10462
  const parent = import_path8.dirname(dir);
@@ -9507,7 +10802,7 @@ function walk_files(dir, extensions) {
9507
10802
  const current = stack.pop();
9508
10803
  let entries;
9509
10804
  try {
9510
- entries = import_fs16.readdirSync(current);
10805
+ entries = import_fs17.readdirSync(current);
9511
10806
  } catch {
9512
10807
  continue;
9513
10808
  }
@@ -9516,7 +10811,7 @@ function walk_files(dir, extensions) {
9516
10811
  continue;
9517
10812
  const full = import_path8.join(current, entry);
9518
10813
  try {
9519
- const stat = import_fs16.statSync(full);
10814
+ const stat = import_fs17.statSync(full);
9520
10815
  if (stat.isDirectory()) {
9521
10816
  stack.push(full);
9522
10817
  } else if (extensions.has(import_path8.extname(full).toLowerCase())) {
@@ -9530,7 +10825,7 @@ function walk_files(dir, extensions) {
9530
10825
  return results;
9531
10826
  }
9532
10827
  function validate_unity_yaml(file) {
9533
- if (!import_fs16.existsSync(file)) {
10828
+ if (!import_fs17.existsSync(file)) {
9534
10829
  return `File not found: ${file}`;
9535
10830
  }
9536
10831
  try {
@@ -9615,13 +10910,59 @@ function build_read_command(getScanner) {
9615
10910
  result.warning = result.warning ? `${result.warning}; ${maxDepthWarning}` : maxDepthWarning;
9616
10911
  }
9617
10912
  if (result.total === 0 && !result.error) {
9618
- try {
9619
- const fileSize = import_fs16.statSync(file).size;
9620
- if (fileSize > 100) {
9621
- const corruptWarning = "File has valid Unity YAML header but contains no parseable GameObjects -- file may be corrupt or malformed";
9622
- result.warning = result.warning ? `${result.warning}; ${corruptWarning}` : corruptWarning;
9623
- }
9624
- } catch {}
10913
+ const prefabInstances = result.prefabInstances;
10914
+ if (prefabInstances && prefabInstances.length > 0) {
10915
+ try {
10916
+ const doc = UnityDocument.from_file(file);
10917
+ const projectPath = find_unity_project_root(import_path8.dirname(file));
10918
+ const resolved = resolve_source_prefab(doc, file, projectPath ?? undefined);
10919
+ if (resolved) {
10920
+ const sourceResult = getScanner().inspect_all_paginated({
10921
+ file: resolved.source_path,
10922
+ include_properties: options.properties === true,
10923
+ verbose: options.verbose === true,
10924
+ page_size: pageSize,
10925
+ cursor: 0,
10926
+ max_depth: maxDepth,
10927
+ filter_component: options.filterComponent
10928
+ });
10929
+ if (sourceResult.total > 0 && sourceResult.gameobjects) {
10930
+ const piBlock = resolved.prefab_instance_block;
10931
+ const nameOverrides = new Map;
10932
+ const namePattern = /- target:[ \t]*\{fileID:[ \t]*(-?\d+)[^}]*\}\s*\n\s*propertyPath:[ \t]*m_Name\s*\n\s*value:[ \t]*(.*)/g;
10933
+ let nameMatch;
10934
+ while ((nameMatch = namePattern.exec(piBlock.raw)) !== null) {
10935
+ nameOverrides.set(nameMatch[1], nameMatch[2].trim());
10936
+ }
10937
+ for (const go of sourceResult.gameobjects) {
10938
+ const goRecord = go;
10939
+ const override = nameOverrides.get(goRecord.fileId);
10940
+ if (override) {
10941
+ goRecord.name = override;
10942
+ }
10943
+ }
10944
+ const resultRecord = result;
10945
+ resultRecord.gameobjects = sourceResult.gameobjects;
10946
+ resultRecord.total = sourceResult.total;
10947
+ resultRecord.totalInScene = sourceResult.totalInScene;
10948
+ resultRecord.resolvedFromSource = true;
10949
+ resultRecord.sourcePrefab = resolved.source_path;
10950
+ resultRecord.sourceGuid = resolved.source_guid;
10951
+ const variantNote = `PrefabVariant resolved from source prefab: ${resolved.source_path}`;
10952
+ result.warning = result.warning ? `${result.warning}; ${variantNote}` : variantNote;
10953
+ }
10954
+ }
10955
+ } catch {}
10956
+ }
10957
+ if (result.total === 0) {
10958
+ try {
10959
+ const fileSize = import_fs17.statSync(file).size;
10960
+ if (fileSize > 100) {
10961
+ const corruptWarning = "File has valid Unity YAML header but contains no parseable GameObjects -- file may be corrupt or malformed";
10962
+ result.warning = result.warning ? `${result.warning}; ${corruptWarning}` : corruptWarning;
10963
+ }
10964
+ } catch {}
10965
+ }
9625
10966
  }
9626
10967
  if (options.summary) {
9627
10968
  const component_counts = {};
@@ -9718,12 +11059,6 @@ function build_read_command(getScanner) {
9718
11059
  };
9719
11060
  console.log(JSON.stringify(output, null, 2));
9720
11061
  });
9721
- cmd.command("scriptable-object").description('(Deprecated: renamed to "read asset")').argument("[file]").allowUnknownOption().action(() => {
9722
- console.log(JSON.stringify({
9723
- error: 'Command "read scriptable-object" has been renamed to "read asset". Use: unity-agentic-tools read asset <file>'
9724
- }, null, 2));
9725
- process.exit(1);
9726
- });
9727
11062
  cmd.command("material <file>").description("Read a Unity Material file (.mat) with structured property output").option("--project <path>", "Unity project root (for GUID resolution)").option("--summary", "Show shader name, property count, texture count only").option("-j, --json", "Output as JSON").action((file, options) => {
9728
11063
  if (!file.toLowerCase().endsWith(".mat")) {
9729
11064
  console.log(JSON.stringify({ error: `File must be a .mat file: ${file}` }, null, 2));
@@ -9734,7 +11069,7 @@ function build_read_command(getScanner) {
9734
11069
  console.log(JSON.stringify({ error: matValidationError }, null, 2));
9735
11070
  process.exit(1);
9736
11071
  }
9737
- const content = import_fs16.readFileSync(file, "utf-8");
11072
+ const content = import_fs17.readFileSync(file, "utf-8");
9738
11073
  if (!content.includes("Material:")) {
9739
11074
  console.log(JSON.stringify({ error: `File "${file}" does not contain a Material block. Use 'read asset' for generic Unity YAML files.` }, null, 2));
9740
11075
  process.exit(1);
@@ -9776,13 +11111,13 @@ function build_read_command(getScanner) {
9776
11111
  }, null, 2));
9777
11112
  });
9778
11113
  cmd.command("dependencies <file>").description("List asset dependencies (GUIDs referenced by this file)").option("--project <path>", "Unity project root (for GUID resolution)").option("--unresolved", "Show only GUIDs that could not be resolved").option("--recursive [depth]", "Follow dependency chain N levels deep (default: 3)").option("-j, --json", "Output as JSON").action((file, options) => {
9779
- if (!import_fs16.existsSync(file)) {
11114
+ if (!import_fs17.existsSync(file)) {
9780
11115
  console.log(JSON.stringify({ error: `File not found: ${file}` }, null, 2));
9781
11116
  process.exit(1);
9782
11117
  }
9783
11118
  let content;
9784
11119
  try {
9785
- content = import_fs16.readFileSync(file, "utf-8");
11120
+ content = import_fs17.readFileSync(file, "utf-8");
9786
11121
  } catch {
9787
11122
  console.log(JSON.stringify({ error: `Cannot read file: ${file}` }, null, 2));
9788
11123
  process.exit(1);
@@ -9823,7 +11158,7 @@ function build_read_command(getScanner) {
9823
11158
  const result = [];
9824
11159
  let fileContent;
9825
11160
  try {
9826
- fileContent = import_fs16.readFileSync(filePath, "utf-8");
11161
+ fileContent = import_fs17.readFileSync(filePath, "utf-8");
9827
11162
  } catch {
9828
11163
  return [];
9829
11164
  }
@@ -9840,7 +11175,7 @@ function build_read_command(getScanner) {
9840
11175
  const rAbsPath = cache.resolve_absolute(g);
9841
11176
  const rType = rPath ? categorize_asset(rPath) : "unknown";
9842
11177
  const dep = { guid: g, path: rPath, type: rType, depth };
9843
- if (rAbsPath && depth < max_depth && import_fs16.existsSync(rAbsPath)) {
11178
+ if (rAbsPath && depth < max_depth && import_fs17.existsSync(rAbsPath)) {
9844
11179
  const subs = traverse_file(rAbsPath, depth + 1);
9845
11180
  if (subs.length > 0)
9846
11181
  dep.sub_dependencies = subs;
@@ -9869,7 +11204,7 @@ function build_read_command(getScanner) {
9869
11204
  const rDep = { guid: dep.guid, path: dep.path, type: dep.type, depth: 0 };
9870
11205
  if (dep.path) {
9871
11206
  const absPath = cache.resolve_absolute(dep.guid);
9872
- if (absPath && import_fs16.existsSync(absPath)) {
11207
+ if (absPath && import_fs17.existsSync(absPath)) {
9873
11208
  const subs = traverse_file(absPath, 1);
9874
11209
  if (subs.length > 0)
9875
11210
  rDep.sub_dependencies = subs;
@@ -9960,14 +11295,31 @@ function build_read_command(getScanner) {
9960
11295
  const target_match = lines[i].match(/\{fileID:\s*(-?\d+)/);
9961
11296
  const property_match = i + 1 < lines.length ? lines[i + 1].match(/propertyPath:\s*(.+)/) : null;
9962
11297
  const value_match = i + 2 < lines.length ? lines[i + 2].match(/value:\s*(.*)/) : null;
9963
- const obj_ref_match = i + 3 < lines.length ? lines[i + 3].match(/objectReference:\s*\{fileID:\s*(-?\d+)/) : null;
11298
+ const obj_ref_match = i + 3 < lines.length ? lines[i + 3].match(/objectReference:[ \t]*(.+)/) : null;
9964
11299
  if (target_match && property_match) {
9965
- modifications.push({
11300
+ const mod = {
9966
11301
  target_file_id: target_match[1],
9967
11302
  property_path: property_match[1].trim(),
9968
11303
  value: value_match ? value_match[1].trim() : "",
9969
- object_reference: obj_ref_match ? obj_ref_match[1] : null
9970
- });
11304
+ object_reference: obj_ref_match ? obj_ref_match[1].trim() : null
11305
+ };
11306
+ const propPath = mod.property_path;
11307
+ const modValue = mod.value;
11308
+ if (propPath.endsWith(".managedReferenceType") && modValue) {
11309
+ const spaceIdx = modValue.indexOf(" ");
11310
+ if (spaceIdx > 0) {
11311
+ const assembly = modValue.substring(0, spaceIdx);
11312
+ const full_type = modValue.substring(spaceIdx + 1);
11313
+ const lastDot = full_type.lastIndexOf(".");
11314
+ mod.managed_reference_type = {
11315
+ assembly,
11316
+ full_type,
11317
+ namespace: lastDot >= 0 ? full_type.substring(0, lastDot) : "",
11318
+ class_name: lastDot >= 0 ? full_type.substring(lastDot + 1) : full_type
11319
+ };
11320
+ }
11321
+ }
11322
+ modifications.push(mod);
9971
11323
  }
9972
11324
  i += 4;
9973
11325
  } else {
@@ -10035,8 +11387,129 @@ function build_read_command(getScanner) {
10035
11387
  process.exit(1);
10036
11388
  }
10037
11389
  });
11390
+ cmd.command("target <file> <gameobject_name> [component_type]").description("Build a --target reference string for prefab override commands").option("-p, --project <path>", "Unity project path").option("-j, --json", "Output as JSON").action((file, gameobject_name, component_type, options) => {
11391
+ try {
11392
+ if (!import_fs17.existsSync(file)) {
11393
+ console.log(JSON.stringify({ error: `File not found: ${file}` }, null, 2));
11394
+ process.exit(1);
11395
+ }
11396
+ const doc = UnityDocument.from_file(file);
11397
+ let guid;
11398
+ const piBlocks = doc.find_by_class_id(1001);
11399
+ if (piBlocks.length > 0) {
11400
+ const sourceMatch = piBlocks[0].raw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
11401
+ guid = sourceMatch ? sourceMatch[1] : null;
11402
+ if (!guid) {
11403
+ console.log(JSON.stringify({ error: "Cannot extract source GUID from PrefabInstance m_SourcePrefab." }, null, 2));
11404
+ process.exit(1);
11405
+ }
11406
+ } else {
11407
+ const metaPath = file + ".meta";
11408
+ guid = extractGuidFromMeta(metaPath);
11409
+ if (!guid) {
11410
+ console.log(JSON.stringify({ error: `Cannot read GUID from ${metaPath}. Ensure the .meta file exists.` }, null, 2));
11411
+ process.exit(1);
11412
+ }
11413
+ }
11414
+ const goResult = doc.require_unique_game_object(gameobject_name);
11415
+ let targetFileId;
11416
+ if ("error" in goResult) {
11417
+ let found = false;
11418
+ for (const block of doc.blocks) {
11419
+ if (block.class_id !== 1001)
11420
+ continue;
11421
+ const nameMatch = block.raw.match(new RegExp(`- target:[ \\t]*\\{fileID:[ \\t]*(-?\\d+)[^}]*\\}\\s*\\n\\s*propertyPath:[ \\t]*m_Name\\s*\\n\\s*value:[ \\t]*${gameobject_name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m"));
11422
+ if (nameMatch) {
11423
+ targetFileId = nameMatch[1];
11424
+ found = true;
11425
+ break;
11426
+ }
11427
+ }
11428
+ if (!found) {
11429
+ console.log(JSON.stringify({ error: goResult.error }, null, 2));
11430
+ process.exit(1);
11431
+ }
11432
+ } else {
11433
+ targetFileId = goResult.file_id;
11434
+ }
11435
+ if (component_type) {
11436
+ const classId = get_class_id(component_type);
11437
+ const goBlock = doc.find_by_file_id(targetFileId);
11438
+ if (goBlock && !goBlock.is_stripped) {
11439
+ const compRefs = [...goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g)].map((m) => m[1]);
11440
+ let compFound = false;
11441
+ for (const refId of compRefs) {
11442
+ const compBlock = doc.find_by_file_id(refId);
11443
+ if (!compBlock)
11444
+ continue;
11445
+ if (classId !== null && compBlock.class_id === classId) {
11446
+ targetFileId = refId;
11447
+ compFound = true;
11448
+ break;
11449
+ }
11450
+ if (classId === null && compBlock.class_id === 114) {
11451
+ const scriptMatch = compBlock.raw.match(/m_Script:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
11452
+ if (scriptMatch) {
11453
+ const project = options.project || find_unity_project_root(import_path8.dirname(file));
11454
+ if (project) {
11455
+ const cache = load_guid_cache(project);
11456
+ if (cache) {
11457
+ const scriptPath = cache.resolve(scriptMatch[1]);
11458
+ if (scriptPath && import_path8.basename(scriptPath, ".cs").toLowerCase() === component_type.toLowerCase().replace(/\.cs$/, "")) {
11459
+ targetFileId = refId;
11460
+ compFound = true;
11461
+ break;
11462
+ }
11463
+ }
11464
+ }
11465
+ }
11466
+ }
11467
+ }
11468
+ if (!compFound) {
11469
+ console.log(JSON.stringify({ error: `Component "${component_type}" not found on GameObject "${gameobject_name}"` }, null, 2));
11470
+ process.exit(1);
11471
+ }
11472
+ } else {
11473
+ const project = options.project || find_unity_project_root(import_path8.dirname(file));
11474
+ const resolved = resolve_source_prefab(doc, file, project ?? undefined);
11475
+ if (resolved) {
11476
+ const sourceDoc = UnityDocument.from_file(resolved.source_path);
11477
+ const sourceGo = sourceDoc.require_unique_game_object(gameobject_name);
11478
+ if (!("error" in sourceGo)) {
11479
+ const compRefs = [...sourceGo.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g)].map((m) => m[1]);
11480
+ let compFound = false;
11481
+ for (const refId of compRefs) {
11482
+ const compBlock = sourceDoc.find_by_file_id(refId);
11483
+ if (!compBlock)
11484
+ continue;
11485
+ if (classId !== null && compBlock.class_id === classId) {
11486
+ targetFileId = refId;
11487
+ compFound = true;
11488
+ break;
11489
+ }
11490
+ }
11491
+ if (!compFound) {
11492
+ console.log(JSON.stringify({ error: `Component "${component_type}" not found on "${gameobject_name}" in source prefab` }, null, 2));
11493
+ process.exit(1);
11494
+ }
11495
+ }
11496
+ }
11497
+ }
11498
+ }
11499
+ const target = `{fileID: ${targetFileId}, guid: ${guid}, type: 3}`;
11500
+ console.log(JSON.stringify({
11501
+ target,
11502
+ file_id: targetFileId,
11503
+ guid,
11504
+ type: 3
11505
+ }, null, 2));
11506
+ } catch (err) {
11507
+ console.log(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }, null, 2));
11508
+ process.exit(1);
11509
+ }
11510
+ });
10038
11511
  cmd.command("script <file>").description("Extract C# type declarations from a .cs file or .NET DLL").option("-j, --json", "Output as JSON").action((file, _options) => {
10039
- if (!import_fs16.existsSync(file)) {
11512
+ if (!import_fs17.existsSync(file)) {
10040
11513
  console.log(JSON.stringify({ error: `File not found: ${file}` }, null, 2));
10041
11514
  process.exit(1);
10042
11515
  }
@@ -10121,18 +11594,20 @@ function build_read_command(getScanner) {
10121
11594
  types: displayed
10122
11595
  }, null, 2));
10123
11596
  });
10124
- cmd.command("log").description("Read and filter the Unity Editor.log").option("--path <file>", "Path to Editor.log (auto-detected if omitted)").option("--project <path>", "Filter to log entries from a specific Unity project session").option("--tail <n>", "Show last N lines (default 50)", "50").option("--errors", "Show only error entries").option("--warnings", "Show only warning entries").option("--compile-errors", "Show only C# compilation errors").option("--import-errors", "Show only asset import errors").option("--since <timestamp>", "Filter entries after this timestamp (YYYY-MM-DD or HH:MM:SS)").option("--search <pattern>", "Regex filter on log content").option("-j, --json", "Output as JSON").action((options) => {
11597
+ cmd.command("log [project-path]").description("Read and filter the Unity Editor.log").option("--path <file>", "Path to Editor.log (auto-detected if omitted)").option("--project <path>", "Filter to log entries from a specific Unity project session").option("--tail <n>", "Show last N lines (default 50)", "50").option("--errors", "Show only error entries").option("--warnings", "Show only warning entries").option("--compile-errors", "Show only C# compilation errors").option("--import-errors", "Show only asset import errors").option("--since <timestamp>", "Filter entries after this timestamp (YYYY-MM-DD or HH:MM:SS)").option("--search <pattern>", "Regex filter on log content").option("-j, --json", "Output as JSON").action((projectPath, options) => {
11598
+ if (projectPath && !options.project)
11599
+ options.project = projectPath;
10125
11600
  const logPath = options.path || get_editor_log_path();
10126
- if (!logPath || !import_fs16.existsSync(logPath)) {
11601
+ if (!logPath || !import_fs17.existsSync(logPath)) {
10127
11602
  console.log(JSON.stringify({ error: `Editor.log not found${logPath ? `: ${logPath}` : ". Could not detect platform log path."}` }, null, 2));
10128
11603
  process.exit(1);
10129
11604
  }
10130
- const content = import_fs16.readFileSync(logPath, "utf-8");
11605
+ const content = import_fs17.readFileSync(logPath, "utf-8");
10131
11606
  let lines = content.split(/\r?\n/);
10132
11607
  if (options.project) {
10133
- const projectPath = import_path8.resolve(options.project);
10134
- const projectName = import_path8.basename(projectPath);
10135
- const escapedPath = projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11608
+ const projectPath2 = import_path8.resolve(options.project);
11609
+ const projectName = import_path8.basename(projectPath2);
11610
+ const escapedPath = projectPath2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10136
11611
  const escapedName = projectName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10137
11612
  const session_markers = [
10138
11613
  new RegExp(`Loading project at '${escapedPath}'`),
@@ -10248,11 +11723,11 @@ function build_read_command(getScanner) {
10248
11723
  });
10249
11724
  cmd.command("meta <file>").description("Read a Unity .meta file and show importer settings").option("--summary", "Show importer type and key settings only").option("-j, --json", "Output as JSON").action((file, options) => {
10250
11725
  const metaPath = file.endsWith(".meta") ? file : `${file}.meta`;
10251
- if (!import_fs16.existsSync(metaPath)) {
11726
+ if (!import_fs17.existsSync(metaPath)) {
10252
11727
  console.log(JSON.stringify({ error: `Meta file not found: ${metaPath}` }, null, 2));
10253
11728
  process.exit(1);
10254
11729
  }
10255
- const content = import_fs16.readFileSync(metaPath, "utf-8");
11730
+ const content = import_fs17.readFileSync(metaPath, "utf-8");
10256
11731
  const lines = content.split(/\r?\n/);
10257
11732
  let guid = "";
10258
11733
  let importer_type = "Unknown";
@@ -10340,7 +11815,7 @@ function build_read_command(getScanner) {
10340
11815
  console.log(JSON.stringify({ error: animValidationError }, null, 2));
10341
11816
  process.exit(1);
10342
11817
  }
10343
- const content = import_fs16.readFileSync(file, "utf-8");
11818
+ const content = import_fs17.readFileSync(file, "utf-8");
10344
11819
  const clip = parse_animation_yaml(content, options.curves === true);
10345
11820
  if (!clip) {
10346
11821
  console.log(JSON.stringify({ error: `No AnimationClip found in "${file}". Is this an .anim file?` }, null, 2));
@@ -10401,7 +11876,7 @@ function build_read_command(getScanner) {
10401
11876
  console.log(JSON.stringify({ error: ctrlValidationError }, null, 2));
10402
11877
  process.exit(1);
10403
11878
  }
10404
- const content = import_fs16.readFileSync(file, "utf-8");
11879
+ const content = import_fs17.readFileSync(file, "utf-8");
10405
11880
  const blocks = split_yaml_blocks(content);
10406
11881
  const controller_block = blocks.find((b) => b.class_id === 91);
10407
11882
  if (!controller_block) {
@@ -10631,7 +12106,7 @@ function build_read_command(getScanner) {
10631
12106
  process.exit(1);
10632
12107
  }
10633
12108
  const assetsDir = import_path8.join(import_path8.resolve(project_path), "Assets");
10634
- if (!import_fs16.existsSync(assetsDir)) {
12109
+ if (!import_fs17.existsSync(assetsDir)) {
10635
12110
  console.log(JSON.stringify({ error: `Assets directory not found in "${project_path}"` }, null, 2));
10636
12111
  process.exit(1);
10637
12112
  }
@@ -10660,7 +12135,7 @@ function build_read_command(getScanner) {
10660
12135
  const guid_pattern = `guid: ${guid}`;
10661
12136
  for (const f of files) {
10662
12137
  try {
10663
- const fc = import_fs16.readFileSync(f, "utf-8");
12138
+ const fc = import_fs17.readFileSync(f, "utf-8");
10664
12139
  if (fc.includes(guid_pattern)) {
10665
12140
  const rel = import_path8.relative(import_path8.resolve(project_path), f);
10666
12141
  const ftype = categorize_asset(f);
@@ -10690,7 +12165,7 @@ function build_read_command(getScanner) {
10690
12165
  cmd.command("unused <project_path>").description("Find potentially unused assets (zero inbound GUID references)").option("--type <type>", "Filter to specific asset types").option("--ignore <glob>", "Exclude paths matching this pattern").option("--max <n>", "Maximum results to return (default 200)", "200").option("-j, --json", "Output as JSON").action((project_path, options) => {
10691
12166
  const resolvedProject = import_path8.resolve(project_path);
10692
12167
  const assetsDir = import_path8.join(resolvedProject, "Assets");
10693
- if (!import_fs16.existsSync(assetsDir)) {
12168
+ if (!import_fs17.existsSync(assetsDir)) {
10694
12169
  console.log(JSON.stringify({ error: `Assets directory not found in "${project_path}"` }, null, 2));
10695
12170
  process.exit(1);
10696
12171
  }
@@ -10718,7 +12193,7 @@ function build_read_command(getScanner) {
10718
12193
  const guid_re = /guid:\s*([a-f0-9]{32})/g;
10719
12194
  for (const f of files) {
10720
12195
  try {
10721
- const fc = import_fs16.readFileSync(f, "utf-8");
12196
+ const fc = import_fs17.readFileSync(f, "utf-8");
10722
12197
  let gm;
10723
12198
  while ((gm = guid_re.exec(fc)) !== null) {
10724
12199
  referenced_guids.add(gm[1]);
@@ -10729,8 +12204,8 @@ function build_read_command(getScanner) {
10729
12204
  }
10730
12205
  const buildSettingsPath = import_path8.join(resolvedProject, "ProjectSettings", "EditorBuildSettings.asset");
10731
12206
  const build_scene_guids = new Set;
10732
- if (import_fs16.existsSync(buildSettingsPath)) {
10733
- const bsc = import_fs16.readFileSync(buildSettingsPath, "utf-8");
12207
+ if (import_fs17.existsSync(buildSettingsPath)) {
12208
+ const bsc = import_fs17.readFileSync(buildSettingsPath, "utf-8");
10734
12209
  let bm;
10735
12210
  while ((bm = guid_re.exec(bsc)) !== null) {
10736
12211
  build_scene_guids.add(bm[1]);
@@ -10848,19 +12323,11 @@ function build_read_command(getScanner) {
10848
12323
  }
10849
12324
  console.log(JSON.stringify({ file, ...ia }, null, 2));
10850
12325
  });
10851
- cmd.command("prefab").argument("[file]").allowUnknownOption().action(() => {
10852
- console.log(JSON.stringify({
10853
- success: false,
10854
- error: '"read prefab" does not exist. Use "read scene" -- it handles both .unity and .prefab files.',
10855
- correct_usage: "unity-agentic-tools read scene <file.prefab>"
10856
- }, null, 2));
10857
- process.exitCode = 1;
10858
- });
10859
12326
  return cmd;
10860
12327
  }
10861
12328
 
10862
12329
  // src/cmd-update.ts
10863
- var import_fs17 = require("fs");
12330
+ var import_fs18 = require("fs");
10864
12331
  var import_path9 = require("path");
10865
12332
 
10866
12333
  // src/animator-utils.ts
@@ -11006,10 +12473,10 @@ function resolve_transform_id(scanner, file, identifier) {
11006
12473
  if (result && !result.is_error) {
11007
12474
  const transform = result.components?.find((c) => c.class_id === 4 || c.class_id === 224);
11008
12475
  if (transform)
11009
- return { transform_id: parseInt(transform.file_id, 10) };
12476
+ return { transform_id: transform.file_id };
11010
12477
  }
11011
12478
  if (/^-?\d+$/.test(identifier)) {
11012
- return { transform_id: parseInt(identifier, 10) };
12479
+ return { transform_id: identifier };
11013
12480
  }
11014
12481
  return { error: `Could not resolve "${identifier}" to a Transform component. Use a GameObject name or transform fileID.` };
11015
12482
  }
@@ -11027,7 +12494,7 @@ function build_update_command(getScanner) {
11027
12494
  if (!result.success)
11028
12495
  process.exitCode = 1;
11029
12496
  });
11030
- cmd.command("component <file> <file_id> <property> <value>").description("Edit any component property by file ID. Supports dotted paths (m_LocalPosition.x) and array paths (m_Materials.Array.data[0])").option("-j, --json", "Output as JSON").action((file, file_id, property, value, _options) => {
12497
+ cmd.command("component <file> <file_id> <property> <value>").description("Edit any component property by file ID. Supports dotted paths (m_LocalPosition.x) and array paths (m_Materials.Array.data[0]). Quote paths with brackets or use dot notation (data.0) to avoid shell glob expansion").option("-j, --json", "Output as JSON").action((file, file_id, property, value, _options) => {
11031
12498
  if (property === "m_RootOrder") {
11032
12499
  const num = Number(value);
11033
12500
  if (!Number.isInteger(num) || num < 0) {
@@ -11183,14 +12650,71 @@ function build_update_command(getScanner) {
11183
12650
  if (!result.success)
11184
12651
  process.exitCode = 1;
11185
12652
  });
11186
- prefab_cmd.command("override <file> <prefab_instance> <property_path> <value>").description("Edit or add a property override in a PrefabInstance m_Modifications list").option("--object-reference <ref>", "Object reference value (default: {fileID: 0})").option("--target <target>", 'Target reference for new entries (e.g., "{fileID: 400000, guid: abc, type: 3}")').option("-j, --json", "Output as JSON").action((file, prefab_instance, property_path, value, options) => {
12653
+ prefab_cmd.command("override <file> <prefab_instance> <property_path> <value>").description("Edit or add a property override in a PrefabInstance m_Modifications list. Quote paths with brackets or use dot notation (data.0) to avoid shell glob expansion").option("--object-reference <ref>", "Object reference value (default: {fileID: 0})").option("--managed-reference <id>", "Managed reference ID -- placed in value: field, forces objectReference: {fileID: 0}").option("--target <target>", 'Target reference for new entries (e.g., "{fileID: 400000, guid: abc, type: 3}")').option("-j, --json", "Output as JSON").action((file, prefab_instance, property_path, value, options) => {
12654
+ if (options.objectReference && !/^\{fileID:/.test(options.objectReference)) {
12655
+ console.error(`Warning: --object-reference "${options.objectReference}" does not look like a Unity object reference ({fileID: ...}). For managed reference IDs, use --managed-reference instead or pass the ID as the <value> argument.`);
12656
+ }
11187
12657
  const result = editPrefabOverride({
11188
12658
  file_path: file,
11189
12659
  prefab_instance,
11190
12660
  property_path,
11191
12661
  new_value: value,
11192
12662
  object_reference: options.objectReference,
11193
- target: options.target
12663
+ target: options.target,
12664
+ managed_reference: options.managedReference
12665
+ });
12666
+ console.log(JSON.stringify(result, null, 2));
12667
+ if (!result.success)
12668
+ process.exitCode = 1;
12669
+ });
12670
+ prefab_cmd.command("batch-overrides <file> <prefab_instance> <edits_json>").description('Batch edit multiple property overrides in a PrefabInstance. JSON format: [{"property_path":"...","value":"...","target":"...","object_reference":"..."}]').option("-j, --json", "Output as JSON").action((file, prefab_instance, edits_json, _options) => {
12671
+ let raw_edits;
12672
+ try {
12673
+ const parsed = JSON.parse(edits_json);
12674
+ if (!Array.isArray(parsed)) {
12675
+ console.log(JSON.stringify({ success: false, error: 'Edits must be a JSON array. Format: [{"property_path":"...","value":"..."}]' }, null, 2));
12676
+ process.exit(1);
12677
+ }
12678
+ raw_edits = parsed;
12679
+ } catch {
12680
+ console.log(JSON.stringify({ success: false, error: "Invalid JSON for edits" }, null, 2));
12681
+ process.exit(1);
12682
+ }
12683
+ const edits = raw_edits.map((e) => ({
12684
+ property_path: String(e.property_path ?? ""),
12685
+ value: String(e.new_value ?? e.value ?? ""),
12686
+ object_reference: e.object_reference != null ? String(e.object_reference) : undefined,
12687
+ target: e.target != null ? String(e.target) : undefined,
12688
+ managed_reference: e.managed_reference != null ? String(e.managed_reference) : undefined
12689
+ }));
12690
+ if (edits.length === 0) {
12691
+ console.log(JSON.stringify({ success: false, error: 'Empty edits array. Provide at least one edit: [{"property_path":"...","value":"..."}]' }, null, 2));
12692
+ process.exit(1);
12693
+ }
12694
+ for (const edit of edits) {
12695
+ if (!edit.property_path) {
12696
+ console.log(JSON.stringify({ success: false, error: 'Missing "property_path" in edit entry. JSON format: [{"property_path":"...","value":"..."}]' }, null, 2));
12697
+ process.exit(1);
12698
+ }
12699
+ if (!edit.value && !edit.object_reference && !edit.managed_reference) {
12700
+ console.log(JSON.stringify({ success: false, error: `Missing "value" for override "${edit.property_path}". Provide "value", "object_reference", or "managed_reference".` }, null, 2));
12701
+ process.exit(1);
12702
+ }
12703
+ }
12704
+ const result = batchEditPrefabOverrides(file, prefab_instance, edits);
12705
+ console.log(JSON.stringify(result, null, 2));
12706
+ if (!result.success)
12707
+ process.exitCode = 1;
12708
+ });
12709
+ prefab_cmd.command("managed-reference <file> <prefab_instance> <field_path> <type_name>").description("Add a [SerializeReference] managed reference to a prefab override. Auto-generates rid and creates type/data/version override entries.").requiredOption("--target <ref>", 'Target reference (e.g., "{fileID: 400000, guid: ..., type: 3}")').option("--index <n>", "Array index (default: 0)", "0").option("-p, --project <path>", "Unity project path (for type registry)").option("-j, --json", "Output as JSON").action((file, prefab_instance, field_path, type_name, options) => {
12710
+ const result = addPrefabManagedReference({
12711
+ file_path: file,
12712
+ prefab_instance,
12713
+ field_path,
12714
+ type_name,
12715
+ target: options.target,
12716
+ index: parseInt(options.index, 10),
12717
+ project_path: options.project
11194
12718
  });
11195
12719
  console.log(JSON.stringify(result, null, 2));
11196
12720
  if (!result.success)
@@ -11222,7 +12746,7 @@ function build_update_command(getScanner) {
11222
12746
  process.exitCode = 1;
11223
12747
  }
11224
12748
  });
11225
- cmd.command("array <file> <file_id> <array_property> <action> [args...]").description("Insert, append, or remove array elements in a component. Insert: <index> <value> or <value> --index <n>. Append: <value>. Remove: <index> or --index <n>.").option("--index <n>", "Index for insert/remove").option("-j, --json", "Output as JSON").action((file, file_id, array_property, action, args, options) => {
12749
+ cmd.command("array <file> <file_id> <array_property> <action> [args...]").description("Insert, append, or remove array elements in a component. Insert: <index> <value> or <value> --index <n>. Append: <value>. Remove: <index> or --index <n>. Quote paths with brackets or use dot notation (data.0) to avoid shell glob expansion").option("--index <n>", "Index for insert/remove").option("-j, --json", "Output as JSON").action((file, file_id, array_property, action, args, options) => {
11226
12750
  if (action !== "insert" && action !== "append" && action !== "remove") {
11227
12751
  console.log(JSON.stringify({ success: false, error: 'Action must be "insert", "append", or "remove"' }, null, 2));
11228
12752
  process.exit(1);
@@ -11357,7 +12881,7 @@ function build_update_command(getScanner) {
11357
12881
  if (!result.success)
11358
12882
  process.exitCode = 1;
11359
12883
  });
11360
- prefab_cmd.command("remove-override <file> <prefab_instance> <property_path>").description("Remove a property override from a PrefabInstance").option("--target <ref>", "Target reference to match (for disambiguation)").option("-j, --json", "Output as JSON").action((file, prefab_instance, property_path, options) => {
12884
+ prefab_cmd.command("remove-override <file> <prefab_instance> <property_path>").description("Remove a property override from a PrefabInstance. Quote paths with brackets or use dot notation (data.0) to avoid shell glob expansion").option("--target <ref>", "Target reference to match (for disambiguation)").option("-j, --json", "Output as JSON").action((file, prefab_instance, property_path, options) => {
11361
12885
  const result = removePrefabOverride({
11362
12886
  file_path: file,
11363
12887
  prefab_instance,
@@ -11418,11 +12942,11 @@ function build_update_command(getScanner) {
11418
12942
  }
11419
12943
  }
11420
12944
  }).action((file, options) => {
11421
- if (!import_fs17.existsSync(file)) {
12945
+ if (!import_fs18.existsSync(file)) {
11422
12946
  console.log(JSON.stringify({ success: false, error: `File not found: ${file}` }, null, 2));
11423
12947
  process.exit(1);
11424
12948
  }
11425
- let content = import_fs17.readFileSync(file, "utf-8");
12949
+ let content = import_fs18.readFileSync(file, "utf-8");
11426
12950
  const changes = [];
11427
12951
  if (options.shader) {
11428
12952
  const guid = options.shader;
@@ -11571,7 +13095,7 @@ ${lm[1]} - ${kw}
11571
13095
  process.exitCode = 1;
11572
13096
  return;
11573
13097
  }
11574
- import_fs17.writeFileSync(file, content, "utf-8");
13098
+ import_fs18.writeFileSync(file, content, "utf-8");
11575
13099
  console.log(JSON.stringify({ success: true, file, changes }, null, 2));
11576
13100
  });
11577
13101
  cmd.command("meta [file]").description("Edit Unity .meta file importer settings").option("--set <key=value>", "Set an importer setting (e.g., isReadable=1)", (v, p) => [...p, v], []).option("--max-size <n>", "Set TextureImporter maxTextureSize").option("--compression <type>", "Set textureCompression (0=None, 1=LowQuality, 2=Normal, 3=HighQuality)").option("--filter-mode <mode>", "Set filterMode (0=Point, 1=Bilinear, 2=Trilinear)").option("--read-write", "Enable isReadable").option("--no-read-write", "Disable isReadable").option("--batch <glob>", "Apply to all matching files").option("--dry-run", "Preview changes without writing").option("-j, --json", "Output as JSON").action((file, options) => {
@@ -11632,9 +13156,9 @@ ${lm[1]} - ${kw}
11632
13156
  const suffix = import_path9.basename(pattern).replace(/^\*+/, "");
11633
13157
  const meta_files = [];
11634
13158
  const scan = (d) => {
11635
- for (const entry of import_fs17.readdirSync(d)) {
13159
+ for (const entry of import_fs18.readdirSync(d)) {
11636
13160
  const full = import_path9.join(d, entry);
11637
- if (import_fs17.statSync(full).isDirectory()) {
13161
+ if (import_fs18.statSync(full).isDirectory()) {
11638
13162
  if (pattern.includes("**"))
11639
13163
  scan(full);
11640
13164
  } else if (entry.endsWith(suffix)) {
@@ -11642,7 +13166,7 @@ ${lm[1]} - ${kw}
11642
13166
  }
11643
13167
  }
11644
13168
  };
11645
- if (import_fs17.existsSync(dir))
13169
+ if (import_fs18.existsSync(dir))
11646
13170
  scan(dir);
11647
13171
  if (meta_files.length === 0) {
11648
13172
  console.log(JSON.stringify({ success: false, error: `No files matched pattern: ${glob_pattern}` }, null, 2));
@@ -11650,7 +13174,7 @@ ${lm[1]} - ${kw}
11650
13174
  }
11651
13175
  const results = [];
11652
13176
  for (const mf of meta_files) {
11653
- let mc = import_fs17.readFileSync(mf, "utf-8");
13177
+ let mc = import_fs18.readFileSync(mf, "utf-8");
11654
13178
  const file_changes = [];
11655
13179
  for (const edit of edits) {
11656
13180
  const re = new RegExp(`^(\\s*${edit.key}:)[ \\t]*.*$`, "m");
@@ -11661,7 +13185,7 @@ ${lm[1]} - ${kw}
11661
13185
  }
11662
13186
  if (file_changes.length > 0) {
11663
13187
  if (!options.dryRun)
11664
- import_fs17.writeFileSync(mf, mc, "utf-8");
13188
+ import_fs18.writeFileSync(mf, mc, "utf-8");
11665
13189
  results.push({ file: mf, changes: file_changes });
11666
13190
  }
11667
13191
  }
@@ -11674,11 +13198,11 @@ ${lm[1]} - ${kw}
11674
13198
  }, null, 2));
11675
13199
  return;
11676
13200
  }
11677
- if (!import_fs17.existsSync(metaPath)) {
13201
+ if (!import_fs18.existsSync(metaPath)) {
11678
13202
  console.log(JSON.stringify({ success: false, error: `Meta file not found: ${metaPath}` }, null, 2));
11679
13203
  process.exit(1);
11680
13204
  }
11681
- let content = import_fs17.readFileSync(metaPath, "utf-8");
13205
+ let content = import_fs18.readFileSync(metaPath, "utf-8");
11682
13206
  const changes = [];
11683
13207
  for (const edit of edits) {
11684
13208
  const re = new RegExp(`^(\\s*${edit.key}:)[ \\t]*.*$`, "m");
@@ -11693,11 +13217,11 @@ ${lm[1]} - ${kw}
11693
13217
  console.log(JSON.stringify({ success: true, dry_run: true, file: metaPath, changes }, null, 2));
11694
13218
  return;
11695
13219
  }
11696
- import_fs17.writeFileSync(metaPath, content, "utf-8");
13220
+ import_fs18.writeFileSync(metaPath, content, "utf-8");
11697
13221
  console.log(JSON.stringify({ success: true, file: metaPath, changes }, null, 2));
11698
13222
  });
11699
13223
  cmd.command("animation <file>").description("Edit AnimationClip settings and events").option("--set <property=value>", "Set a clip property (e.g., wrap-mode=2 for Loop)", (v, p) => [...p, v], []).option("--add-event <time,function[,data]>", "Add an animation event (e.g., 0.5,OnFootstep,left)", (v, p) => [...p, v], []).option("--remove-event <index>", "Remove an animation event by index (0-based)").option("-j, --json", "Output as JSON").action((file, options) => {
11700
- if (!import_fs17.existsSync(file)) {
13224
+ if (!import_fs18.existsSync(file)) {
11701
13225
  console.log(JSON.stringify({ success: false, error: `File not found: ${file}` }, null, 2));
11702
13226
  process.exit(1);
11703
13227
  }
@@ -11705,7 +13229,7 @@ ${lm[1]} - ${kw}
11705
13229
  console.log(JSON.stringify({ success: false, error: `File is not an AnimationClip (.anim): ${file}` }, null, 2));
11706
13230
  process.exit(1);
11707
13231
  }
11708
- let content = import_fs17.readFileSync(file, "utf-8");
13232
+ let content = import_fs18.readFileSync(file, "utf-8");
11709
13233
  const changes = [];
11710
13234
  const property_map = {
11711
13235
  "wrap-mode": "m_WrapMode",
@@ -11866,16 +13390,16 @@ ${event_yaml}
11866
13390
  process.exitCode = 1;
11867
13391
  return;
11868
13392
  }
11869
- import_fs17.writeFileSync(file, content, "utf-8");
13393
+ import_fs18.writeFileSync(file, content, "utf-8");
11870
13394
  console.log(JSON.stringify({ success: true, file, changes }, null, 2));
11871
13395
  });
11872
13396
  const PARAM_TYPE_MAP = { float: 1, int: 3, bool: 4, trigger: 9 };
11873
13397
  cmd.command("animator <file>").description("Edit AnimatorController parameters").option("--add-parameter <name>", "Add a new parameter").option("--type <float|int|bool|trigger>", "Parameter type (required with --add-parameter)").option("--remove-parameter <name>", "Remove a parameter by name").option("--set-default <param=value>", "Set parameter default value (e.g., Speed=1.5)", (v, p) => [...p, v], []).option("-j, --json", "Output as JSON").action((file, options) => {
11874
- if (!import_fs17.existsSync(file)) {
13398
+ if (!import_fs18.existsSync(file)) {
11875
13399
  console.log(JSON.stringify({ success: false, error: `File not found: ${file}` }, null, 2));
11876
13400
  process.exit(1);
11877
13401
  }
11878
- let content = import_fs17.readFileSync(file, "utf-8");
13402
+ let content = import_fs18.readFileSync(file, "utf-8");
11879
13403
  const had_crlf = content.includes(`\r
11880
13404
  `);
11881
13405
  if (had_crlf)
@@ -11977,7 +13501,7 @@ $1`);
11977
13501
  if (had_crlf)
11978
13502
  content = content.replace(/\n/g, `\r
11979
13503
  `);
11980
- import_fs17.writeFileSync(file, content, "utf-8");
13504
+ import_fs18.writeFileSync(file, content, "utf-8");
11981
13505
  console.log(JSON.stringify({ success: true, file, changes }, null, 2));
11982
13506
  });
11983
13507
  cmd.command("sibling-index <file> <object_name> <index>").description("Set the sibling index of a GameObject, renumbering all siblings").option("-j, --json", "Output as JSON").action((file, object_name, index_str, _options) => {
@@ -11987,7 +13511,7 @@ $1`);
11987
13511
  process.exitCode = 1;
11988
13512
  return;
11989
13513
  }
11990
- if (!import_fs17.existsSync(file)) {
13514
+ if (!import_fs18.existsSync(file)) {
11991
13515
  console.log(JSON.stringify({ success: false, error: `File not found: ${file}` }, null, 2));
11992
13516
  process.exitCode = 1;
11993
13517
  return;
@@ -12182,7 +13706,7 @@ $1`);
12182
13706
  console.log(JSON.stringify({ success: true, file, changes }, null, 2));
12183
13707
  });
12184
13708
  cmd.command("animation-curves <file>").description("Add, remove, or modify animation curves in an .anim file").option("--add-curve <json>", 'Add a curve (JSON: {"type":"float","path":"Body","attribute":"m_Alpha","classID":23,"keyframes":[{"time":0,"value":1}]})').option("--remove-curve <spec>", "Remove a curve by path:attribute (e.g., Body/Mesh:m_Alpha)").option("--set-keyframes <json>", 'Replace keyframes (JSON: {"curve":"path:attribute","keyframes":[{"time":0,"value":10}]})').option("-j, --json", "Output as JSON").action((file, options) => {
12185
- if (!import_fs17.existsSync(file)) {
13709
+ if (!import_fs18.existsSync(file)) {
12186
13710
  console.log(JSON.stringify({ success: false, error: `File not found: ${file}` }, null, 2));
12187
13711
  process.exitCode = 1;
12188
13712
  return;
@@ -12192,7 +13716,7 @@ $1`);
12192
13716
  process.exitCode = 1;
12193
13717
  return;
12194
13718
  }
12195
- let content = import_fs17.readFileSync(file, "utf-8");
13719
+ let content = import_fs18.readFileSync(file, "utf-8");
12196
13720
  const had_crlf = content.includes(`\r
12197
13721
  `);
12198
13722
  if (had_crlf)
@@ -12490,16 +14014,16 @@ ${curve_yaml}`);
12490
14014
  if (had_crlf)
12491
14015
  content = content.replace(/\n/g, `\r
12492
14016
  `);
12493
- import_fs17.writeFileSync(file, content, "utf-8");
14017
+ import_fs18.writeFileSync(file, content, "utf-8");
12494
14018
  console.log(JSON.stringify({ success: true, file, changes }, null, 2));
12495
14019
  });
12496
14020
  cmd.command("animator-state <file>").description("Add/remove states and transitions in an AnimatorController").option("--add-state <name>", "Add a new AnimatorState").option("--motion <guid-or-path>", "Motion clip GUID or file path (companion to --add-state)").option("--layer <name>", "Target layer name (default: first layer)").option("--speed <n>", "State speed (companion to --add-state, default: 1)").option("--remove-state <name>", "Remove a state and all its transitions").option("--add-transition <src:dst>", 'Add a transition (use "any" for AnyState source)').option("--condition <param,mode,threshold>", "Transition condition (repeatable)", (v, p) => [...p, v], []).option("--has-exit-time", "Enable exit time on transition").option("--exit-time <n>", "Exit time value (default: 0.75)").option("--duration <n>", "Transition duration (default: 0.25)").option("--remove-transition <src:dst>", "Remove a transition by source:destination state names").option("--set-default-state <name>", "Set the default state in the state machine").option("-j, --json", "Output as JSON").action((file, options) => {
12497
- if (!import_fs17.existsSync(file)) {
14021
+ if (!import_fs18.existsSync(file)) {
12498
14022
  console.log(JSON.stringify({ success: false, error: `File not found: ${file}` }, null, 2));
12499
14023
  process.exitCode = 1;
12500
14024
  return;
12501
14025
  }
12502
- let content = import_fs17.readFileSync(file, "utf-8");
14026
+ let content = import_fs18.readFileSync(file, "utf-8");
12503
14027
  const had_crlf = content.includes(`\r
12504
14028
  `);
12505
14029
  if (had_crlf)
@@ -12838,10 +14362,34 @@ $1`);
12838
14362
  if (had_crlf)
12839
14363
  content = content.replace(/\n/g, `\r
12840
14364
  `);
12841
- import_fs17.writeFileSync(file, content, "utf-8");
14365
+ import_fs18.writeFileSync(file, content, "utf-8");
12842
14366
  console.log(JSON.stringify({ success: true, file, changes }, null, 2));
12843
14367
  });
12844
14368
  cmd.addCommand(prefab_cmd);
14369
+ cmd.command("managed-reference <file> <component_id> <field_path> <type_name>").description('Add a managed reference (SerializeReference) to a component field. Type can be "Namespace.ClassName" (registry lookup) or "Assembly Namespace.ClassName" (manual).').option("-p, --project <path>", "Unity project path (for type registry)").option("--append", "Append to array (do not update field rid)").option("--properties <json>", `JSON object of initial field values for the data block (e.g. '{"damage": "10"}')`).option("-j, --json", "Output as JSON").action((file, component_id, field_path, type_name, options) => {
14370
+ let initial_values;
14371
+ if (options.properties) {
14372
+ try {
14373
+ initial_values = JSON.parse(options.properties);
14374
+ } catch {
14375
+ console.log(JSON.stringify({ success: false, error: `Invalid JSON for --properties: ${options.properties}` }, null, 2));
14376
+ process.exitCode = 1;
14377
+ return;
14378
+ }
14379
+ }
14380
+ const result = editManagedReference({
14381
+ file_path: file,
14382
+ file_id: component_id,
14383
+ field_path,
14384
+ type_name,
14385
+ project_path: options.project,
14386
+ append: options.append === true,
14387
+ initial_values
14388
+ });
14389
+ console.log(JSON.stringify(result, null, 2));
14390
+ if (!result.success)
14391
+ process.exitCode = 1;
14392
+ });
12845
14393
  return cmd;
12846
14394
  }
12847
14395
 
@@ -12907,16 +14455,16 @@ function build_delete_command() {
12907
14455
  var import_path11 = require("path");
12908
14456
 
12909
14457
  // src/editor-client.ts
12910
- var import_fs18 = require("fs");
14458
+ var import_fs19 = require("fs");
12911
14459
  var import_path10 = require("path");
12912
14460
  function read_editor_config(project_path) {
12913
14461
  const config_path = import_path10.join(project_path, ".unity-agentic", "editor.json");
12914
- if (!import_fs18.existsSync(config_path)) {
14462
+ if (!import_fs19.existsSync(config_path)) {
12915
14463
  return { error: `Editor bridge not found at ${config_path}. Is the Unity Editor running with the bridge package installed?` };
12916
14464
  }
12917
14465
  let config;
12918
14466
  try {
12919
- const raw = import_fs18.readFileSync(config_path, "utf-8");
14467
+ const raw = import_fs19.readFileSync(config_path, "utf-8");
12920
14468
  config = JSON.parse(raw);
12921
14469
  } catch (err) {
12922
14470
  return { error: `Failed to parse editor.json: ${err instanceof Error ? err.message : String(err)}` };
@@ -12929,23 +14477,45 @@ function read_editor_config(project_path) {
12929
14477
  }
12930
14478
  return config;
12931
14479
  }
14480
+ var RETRYABLE_CODES = new Set([-32000, -32002, -32003]);
14481
+ var RETRY_DELAYS = [500, 1000, 2000];
12932
14482
  async function call_editor(options) {
12933
- const { method, params, timeout = 1e4 } = options;
14483
+ const maxRetries = options.retries ?? 2;
14484
+ let lastResponse;
14485
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
14486
+ lastResponse = await call_editor_once(options);
14487
+ if (!lastResponse.error || !RETRYABLE_CODES.has(lastResponse.error.code)) {
14488
+ return lastResponse;
14489
+ }
14490
+ if (attempt < maxRetries) {
14491
+ const delay = RETRY_DELAYS[Math.min(attempt, RETRY_DELAYS.length - 1)];
14492
+ await new Promise((r) => setTimeout(r, delay));
14493
+ }
14494
+ }
14495
+ return lastResponse;
14496
+ }
14497
+ function call_editor_once(options) {
14498
+ const { method, params, timeout = 1e4, no_wait } = options;
12934
14499
  const config = resolve_config(options);
12935
14500
  if ("error" in config) {
12936
- return {
14501
+ return Promise.resolve({
12937
14502
  jsonrpc: "2.0",
12938
14503
  id: "0",
12939
14504
  error: { code: -32000, message: config.error }
12940
- };
14505
+ });
12941
14506
  }
12942
14507
  const url = `ws://127.0.0.1:${config.port}/unity-agentic`;
12943
14508
  const request_id = generate_id();
14509
+ const wire_params = { ...params };
14510
+ if (timeout !== 1e4)
14511
+ wire_params._timeout = timeout;
14512
+ if (no_wait)
14513
+ wire_params.no_wait = true;
12944
14514
  const request = {
12945
14515
  jsonrpc: "2.0",
12946
14516
  id: request_id,
12947
14517
  method,
12948
- ...params ? { params } : {}
14518
+ ...Object.keys(wire_params).length > 0 ? { params: wire_params } : {}
12949
14519
  };
12950
14520
  return new Promise((resolve7) => {
12951
14521
  let resolved = false;
@@ -12967,6 +14537,20 @@ async function call_editor(options) {
12967
14537
  ws = new WebSocket(url);
12968
14538
  ws.onopen = () => {
12969
14539
  ws.send(JSON.stringify(request));
14540
+ if (no_wait && !resolved) {
14541
+ resolved = true;
14542
+ clearTimeout(timer);
14543
+ setTimeout(() => {
14544
+ try {
14545
+ ws.close();
14546
+ } catch {}
14547
+ }, 200);
14548
+ resolve7({
14549
+ jsonrpc: "2.0",
14550
+ id: request_id,
14551
+ result: { queued: true }
14552
+ });
14553
+ }
12970
14554
  };
12971
14555
  ws.onmessage = (event) => {
12972
14556
  try {
@@ -13022,42 +14606,75 @@ async function stream_editor(options) {
13022
14606
  if ("error" in config) {
13023
14607
  throw new Error(config.error);
13024
14608
  }
13025
- const url = `ws://127.0.0.1:${config.port}/unity-agentic`;
13026
- const request_id = generate_id();
13027
- const request = {
13028
- jsonrpc: "2.0",
13029
- id: request_id,
13030
- method,
13031
- ...params ? { params } : {}
13032
- };
14609
+ const MAX_RECONNECTS = 5;
14610
+ let reconnect_count = 0;
14611
+ let stopped = false;
13033
14612
  return new Promise((resolve7, reject) => {
13034
- let connected = false;
13035
- const timer = setTimeout(() => {
13036
- if (!connected) {
13037
- reject(new Error(`Timeout connecting to ${url}`));
13038
- }
13039
- }, timeout);
13040
- const ws = new WebSocket(url);
13041
- ws.onopen = () => {
13042
- connected = true;
13043
- clearTimeout(timer);
13044
- ws.send(JSON.stringify(request));
13045
- resolve7({ close: () => ws.close() });
13046
- };
13047
- ws.onmessage = (event) => {
13048
- try {
13049
- const data = JSON.parse(String(event.data));
13050
- if ("method" in data && !("id" in data)) {
13051
- on_event(data);
14613
+ let resolved = false;
14614
+ function connect(url) {
14615
+ const ws = new WebSocket(url);
14616
+ const request_id = generate_id();
14617
+ const request = {
14618
+ jsonrpc: "2.0",
14619
+ id: request_id,
14620
+ method,
14621
+ ...params ? { params } : {}
14622
+ };
14623
+ let connected = false;
14624
+ const timer = !resolved ? setTimeout(() => {
14625
+ if (!connected && !resolved) {
14626
+ reject(new Error(`Timeout connecting to ${url}`));
13052
14627
  }
13053
- } catch {}
13054
- };
13055
- ws.onerror = () => {
13056
- if (!connected) {
13057
- clearTimeout(timer);
13058
- reject(new Error(`WebSocket connection failed to ${url}`));
13059
- }
13060
- };
14628
+ }, timeout) : null;
14629
+ ws.onopen = () => {
14630
+ connected = true;
14631
+ if (timer)
14632
+ clearTimeout(timer);
14633
+ reconnect_count = 0;
14634
+ ws.send(JSON.stringify(request));
14635
+ if (!resolved) {
14636
+ resolved = true;
14637
+ resolve7({ close: () => {
14638
+ stopped = true;
14639
+ ws.close();
14640
+ } });
14641
+ }
14642
+ };
14643
+ ws.onmessage = (event) => {
14644
+ try {
14645
+ const data = JSON.parse(String(event.data));
14646
+ if ("method" in data && !("id" in data)) {
14647
+ on_event(data);
14648
+ }
14649
+ } catch {}
14650
+ };
14651
+ ws.onerror = () => {
14652
+ if (!connected && !resolved) {
14653
+ if (timer)
14654
+ clearTimeout(timer);
14655
+ reject(new Error(`WebSocket connection failed to ${url}`));
14656
+ }
14657
+ };
14658
+ ws.onclose = () => {
14659
+ if (stopped)
14660
+ return;
14661
+ if (!connected && !resolved)
14662
+ return;
14663
+ if (reconnect_count >= MAX_RECONNECTS)
14664
+ return;
14665
+ reconnect_count++;
14666
+ const delay = Math.min(500 * reconnect_count, 3000);
14667
+ setTimeout(() => {
14668
+ if (stopped)
14669
+ return;
14670
+ const fresh_config = resolve_config(options);
14671
+ if ("error" in fresh_config)
14672
+ return;
14673
+ connect(`ws://127.0.0.1:${fresh_config.port}/unity-agentic`);
14674
+ }, delay);
14675
+ };
14676
+ }
14677
+ connect(`ws://127.0.0.1:${config.port}/unity-agentic`);
13061
14678
  });
13062
14679
  }
13063
14680
  function resolve_config(options) {
@@ -13101,12 +14718,12 @@ function get_common_options(cmd) {
13101
14718
  const port = port_str ? parseInt(port_str, 10) : undefined;
13102
14719
  return { project_path, timeout, port };
13103
14720
  }
13104
- function build_call_options(cmd, method, params) {
14721
+ function build_call_options(cmd, method, params, extra) {
13105
14722
  const { project_path, timeout, port } = get_common_options(cmd);
13106
- return { project_path, method, params, timeout, port };
14723
+ return { project_path, method, params, timeout, port, ...extra?.no_wait ? { no_wait: true } : {} };
13107
14724
  }
13108
- async function handle_rpc(cmd, method, params) {
13109
- const options = build_call_options(cmd, method, params);
14725
+ async function handle_rpc(cmd, method, params, extra) {
14726
+ const options = build_call_options(cmd, method, params, extra);
13110
14727
  const response = await call_editor(options);
13111
14728
  output_response(response);
13112
14729
  }
@@ -13183,24 +14800,27 @@ function build_editor_command() {
13183
14800
  console.log(JSON.stringify(response.result, null, 2));
13184
14801
  }
13185
14802
  });
13186
- cmd.command("invoke <type> <member> [args...]").description("Call a static Unity Editor API method or read/set a static property").option("--set <value>", "Set a static property value").option("--args <json>", "JSON array of method arguments (overrides positional args)").action(async function(type, member, args, options) {
14803
+ cmd.command("invoke <type> <member> [args...]").description("Call a static Unity Editor API method or read/set a static property").option("--set <value>", "Set a static property value").option("--args <json>", "JSON array of method arguments (overrides positional args)").option("--no-wait", "Fire and forget -- return immediately without waiting for result").action(async function(type, member, args, options) {
13187
14804
  const params = { type, member };
13188
14805
  if (options.set !== undefined) {
13189
14806
  params.set = options.set;
13190
14807
  } else if (options.args) {
13191
14808
  params.args = options.args;
14809
+ } else if (args.length === 1 && args[0].startsWith("[")) {
14810
+ params.args = args[0];
13192
14811
  } else if (args.length > 0) {
13193
14812
  params.args = JSON.stringify(args);
13194
14813
  }
13195
- await handle_rpc(this, "editor.invoke", params);
14814
+ await handle_rpc(this, "editor.invoke", params, { no_wait: options.noWait });
13196
14815
  });
13197
- cmd.command("console-logs").description("Get recent console log entries").option("-c, --count <n>", "Number of log entries to retrieve", "50").option("--limit <n>", "Alias for --count").option("-t, --type <type>", "Filter by log type (Log, Warning, Error, Assert, Exception)").action(async function(options) {
14816
+ cmd.command("console-logs").description("Get recent console log entries").option("-c, --count <n>", "Number of log entries to retrieve", "50").option("--limit <n>", "Alias for --count").option("-t, --type <type>", "Filter by log type (Log, Warning, Error, Assert, Exception)").option("-s, --severity <type>", "Alias for --type").action(async function(options) {
13198
14817
  const params = {};
13199
14818
  const count = options.limit || options.count;
13200
14819
  if (count)
13201
14820
  params.count = parseInt(count, 10);
13202
- if (options.type)
13203
- params.type = options.type;
14821
+ const typeFilter = options.severity || options.type;
14822
+ if (typeFilter)
14823
+ params.type = typeFilter;
13204
14824
  await handle_rpc(this, "editor.console.getLogs", params);
13205
14825
  });
13206
14826
  cmd.command("console-clear").description("Clear the console log buffer").action(async function() {
@@ -13217,7 +14837,7 @@ function build_editor_command() {
13217
14837
  port,
13218
14838
  method: "editor.console.subscribe",
13219
14839
  on_event: (event) => {
13220
- if (event.method === "editor.console.logReceived" || event.method === "editor.event.logMessage") {
14840
+ if (event.method === "editor.console.logReceived") {
13221
14841
  const params = event.params ?? {};
13222
14842
  if (type_filter && typeof params.type === "string" && params.type.toLowerCase() !== type_filter) {
13223
14843
  return;
@@ -13475,23 +15095,12 @@ function build_editor_command() {
13475
15095
  process.exitCode = 1;
13476
15096
  }
13477
15097
  });
13478
- cmd.command("log").allowUnknownOption().argument("[args...]").action(() => {
13479
- console.log(JSON.stringify({
13480
- success: false,
13481
- error: '"editor log" does not exist. Use one of:',
13482
- alternatives: [
13483
- { command: "editor console-logs", description: "Get recent console entries from the running Editor (live bridge)" },
13484
- { command: "read log", description: "Read and filter the Unity Editor.log file on disk (no bridge needed)" }
13485
- ]
13486
- }, null, 2));
13487
- process.exitCode = 1;
13488
- });
13489
15098
  return cmd;
13490
15099
  }
13491
15100
 
13492
15101
  // src/project-search.ts
13493
15102
  init_scanner();
13494
- var import_fs19 = require("fs");
15103
+ var import_fs20 = require("fs");
13495
15104
  var path8 = __toESM(require("path"));
13496
15105
  var BINARY_EXTENSIONS = new Set([
13497
15106
  ".png",
@@ -13554,7 +15163,7 @@ function walk_project_files_js(project_path, extensions, exclude_dirs) {
13554
15163
  function walk(dir) {
13555
15164
  let entries;
13556
15165
  try {
13557
- entries = import_fs19.readdirSync(dir);
15166
+ entries = import_fs20.readdirSync(dir);
13558
15167
  } catch {
13559
15168
  return;
13560
15169
  }
@@ -13562,7 +15171,7 @@ function walk_project_files_js(project_path, extensions, exclude_dirs) {
13562
15171
  const full = path8.join(dir, entry);
13563
15172
  let stat;
13564
15173
  try {
13565
- stat = import_fs19.statSync(full);
15174
+ stat = import_fs20.statSync(full);
13566
15175
  } catch {
13567
15176
  continue;
13568
15177
  }
@@ -13579,14 +15188,14 @@ function walk_project_files_js(project_path, extensions, exclude_dirs) {
13579
15188
  }
13580
15189
  }
13581
15190
  const assetsDir = path8.join(project_path, "Assets");
13582
- if (import_fs19.existsSync(assetsDir)) {
15191
+ if (import_fs20.existsSync(assetsDir)) {
13583
15192
  walk(assetsDir);
13584
15193
  } else {
13585
15194
  walk(project_path);
13586
15195
  }
13587
15196
  if (extSet.has(".asset")) {
13588
15197
  const settingsDir = path8.join(project_path, "ProjectSettings");
13589
- if (import_fs19.existsSync(settingsDir)) {
15198
+ if (import_fs20.existsSync(settingsDir)) {
13590
15199
  walk(settingsDir);
13591
15200
  }
13592
15201
  }
@@ -13627,7 +15236,7 @@ function search_project(options) {
13627
15236
  error: "Name pattern must not be empty"
13628
15237
  };
13629
15238
  }
13630
- if (!import_fs19.existsSync(project_path)) {
15239
+ if (!import_fs20.existsSync(project_path)) {
13631
15240
  return {
13632
15241
  success: false,
13633
15242
  project_path,
@@ -13669,7 +15278,7 @@ function search_project(options) {
13669
15278
  }
13670
15279
  let display_name = fileName;
13671
15280
  try {
13672
- const content = import_fs19.readFileSync(file, "utf-8");
15281
+ const content = import_fs20.readFileSync(file, "utf-8");
13673
15282
  const nameMatch = /^\s*m_Name:\s*(.+)$/m.exec(content.slice(0, 2000));
13674
15283
  if (nameMatch)
13675
15284
  display_name = nameMatch[1].trim();
@@ -13850,7 +15459,7 @@ function grep_project_js(options) {
13850
15459
  max_results = 100,
13851
15460
  context_lines = 0
13852
15461
  } = options;
13853
- if (!import_fs19.existsSync(project_path)) {
15462
+ if (!import_fs20.existsSync(project_path)) {
13854
15463
  return {
13855
15464
  success: false,
13856
15465
  project_path,
@@ -13940,7 +15549,7 @@ function grep_project_js(options) {
13940
15549
  totalFilesScanned++;
13941
15550
  let content;
13942
15551
  try {
13943
- content = import_fs19.readFileSync(file, "utf-8");
15552
+ content = import_fs20.readFileSync(file, "utf-8");
13944
15553
  } catch {
13945
15554
  continue;
13946
15555
  }
@@ -14219,9 +15828,9 @@ program.command("status").description("Show current configuration and status").o
14219
15828
  let config = null;
14220
15829
  let guidCacheCount = 0;
14221
15830
  try {
14222
- const { existsSync: existsSync22, readFileSync: readFileSync15 } = require("fs");
15831
+ const { existsSync: existsSync22, readFileSync: readFileSync17 } = require("fs");
14223
15832
  if (existsSync22(configFile)) {
14224
- config = JSON.parse(readFileSync15(configFile, "utf-8"));
15833
+ config = JSON.parse(readFileSync17(configFile, "utf-8"));
14225
15834
  }
14226
15835
  const guidCacheObj = load_guid_cache(projectPath);
14227
15836
  guidCacheCount = guidCacheObj?.count ?? 0;