unity-agentic-tools 0.3.0 → 0.3.2

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/index.js CHANGED
@@ -76,6 +76,7 @@ __export(exports_scanner, {
76
76
  getNativeExtractCsharpTypes: () => getNativeExtractCsharpTypes,
77
77
  getNativeBuildTypeRegistry: () => getNativeBuildTypeRegistry,
78
78
  getNativeBuildPackageGuidCache: () => getNativeBuildPackageGuidCache,
79
+ getNativeBuildLocalPackageGuidCache: () => getNativeBuildLocalPackageGuidCache,
79
80
  getNativeBuildGuidCache: () => getNativeBuildGuidCache,
80
81
  UnityScanner: () => UnityScanner
81
82
  });
@@ -156,13 +157,16 @@ function getNativeExtractDllTypes() {
156
157
  function getNativeBuildPackageGuidCache() {
157
158
  return nativeBuildPackageGuidCache;
158
159
  }
160
+ function getNativeBuildLocalPackageGuidCache() {
161
+ return nativeBuildLocalPackageGuidCache;
162
+ }
159
163
  function getNativeExtractSerializedFields() {
160
164
  return nativeExtractSerializedFields;
161
165
  }
162
166
  function getNativeExtractDllFields() {
163
167
  return nativeExtractDllFields;
164
168
  }
165
- 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;
169
+ 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;
166
170
  var init_scanner = __esm(() => {
167
171
  import_module = require("module");
168
172
  import_fs = require("fs");
@@ -186,6 +190,7 @@ var init_scanner = __esm(() => {
186
190
  nativeBuildTypeRegistry = rustModule.buildTypeRegistry || null;
187
191
  nativeExtractDllTypes = rustModule.extractDllTypes || null;
188
192
  nativeBuildPackageGuidCache = rustModule.buildPackageGuidCache || null;
193
+ nativeBuildLocalPackageGuidCache = rustModule.buildLocalPackageGuidCache || null;
189
194
  nativeExtractSerializedFields = rustModule.extractSerializedFields || null;
190
195
  nativeExtractDllFields = rustModule.extractDllFields || null;
191
196
  } catch (err) {
@@ -200,7 +205,7 @@ var require_package = __commonJS((exports2, module2) => {
200
205
  module2.exports = {
201
206
  name: "unity-agentic-tools",
202
207
  packageManager: "bun@latest",
203
- version: "0.3.0",
208
+ version: "0.3.2",
204
209
  description: "Fast, token-efficient Unity YAML parser for AI agents",
205
210
  exports: {
206
211
  ".": {
@@ -330,6 +335,7 @@ var CONFIG_DIR = ".unity-agentic";
330
335
  var CONFIG_FILE = "config.json";
331
336
  var GUID_CACHE_FILE = "guid-cache.json";
332
337
  var PACKAGE_CACHE_FILE = "package-cache.json";
338
+ var LOCAL_PACKAGE_CACHE_FILE = "local-package-cache.json";
333
339
  var TYPE_REGISTRY_FILE = "type-registry.json";
334
340
  var DOC_INDEX_FILE = "doc-index.json";
335
341
  function setup(options = {}) {
@@ -373,6 +379,17 @@ function setup(options = {}) {
373
379
  packageGuidCount = Object.keys(packageCache).length;
374
380
  } catch {}
375
381
  }
382
+ const nativeLocalPkgBuild = getNativeBuildLocalPackageGuidCache();
383
+ if (nativeLocalPkgBuild) {
384
+ try {
385
+ const localPkgCache = nativeLocalPkgBuild(projectPath);
386
+ const localPkgCount = Object.keys(localPkgCache).length;
387
+ if (localPkgCount > 0) {
388
+ const localPkgCachePath = import_path2.join(configPath, LOCAL_PACKAGE_CACHE_FILE);
389
+ import_fs2.writeFileSync(localPkgCachePath, JSON.stringify(localPkgCache, null, 2));
390
+ }
391
+ } catch {}
392
+ }
376
393
  let typeRegistryCreated = false;
377
394
  let typeCount;
378
395
  const nativeRegistryBuild = getNativeBuildTypeRegistry();
@@ -555,31 +572,44 @@ function atomicWrite(filePath, content) {
555
572
  };
556
573
  }
557
574
  }
558
- const tmpPath = `${filePath}.tmp`;
575
+ const suffix = `${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
576
+ const tmpPath = `${filePath}.${suffix}.tmp`;
577
+ const bakPath = `${filePath}.${suffix}.bak`;
578
+ let bakCreated = false;
559
579
  try {
560
580
  import_fs4.writeFileSync(tmpPath, content, "utf-8");
561
- if (import_fs4.existsSync(filePath)) {
562
- import_fs4.renameSync(filePath, `${filePath}.bak`);
563
- }
564
- import_fs4.renameSync(tmpPath, filePath);
565
581
  try {
566
- if (import_fs4.existsSync(`${filePath}.bak`)) {
567
- import_fs4.unlinkSync(`${filePath}.bak`);
582
+ import_fs4.renameSync(filePath, bakPath);
583
+ bakCreated = true;
584
+ } catch (err) {
585
+ if (is_enoent(err)) {
586
+ bakCreated = false;
587
+ } else {
588
+ throw err;
568
589
  }
569
- } catch {}
590
+ }
591
+ import_fs4.renameSync(tmpPath, filePath);
592
+ if (bakCreated) {
593
+ try {
594
+ import_fs4.unlinkSync(bakPath);
595
+ } catch {}
596
+ }
570
597
  return {
571
598
  success: true,
572
599
  file_path: filePath,
573
600
  bytes_written: Buffer.byteLength(content, "utf-8")
574
601
  };
575
602
  } catch (error) {
576
- if (import_fs4.existsSync(`${filePath}.bak`)) {
603
+ if (bakCreated) {
577
604
  try {
578
- import_fs4.renameSync(`${filePath}.bak`, filePath);
605
+ import_fs4.renameSync(bakPath, filePath);
579
606
  } catch (restoreError) {
580
607
  console.error("Failed to restore backup:", restoreError);
581
608
  }
582
609
  }
610
+ try {
611
+ import_fs4.unlinkSync(tmpPath);
612
+ } catch {}
583
613
  return {
584
614
  success: false,
585
615
  file_path: filePath,
@@ -587,6 +617,12 @@ function atomicWrite(filePath, content) {
587
617
  };
588
618
  }
589
619
  }
620
+ function is_enoent(err) {
621
+ return err instanceof Error && "code" in err && err.code === "ENOENT";
622
+ }
623
+ function normalize_property_path(path) {
624
+ return path.replace(/\.Array\.data\.(\d+)/g, ".Array.data[$1]");
625
+ }
590
626
  function is_match_all(pattern) {
591
627
  return pattern === "*" || pattern === "." || pattern === "**" || pattern === ".*";
592
628
  }
@@ -2076,6 +2112,31 @@ function extractGuidFromMeta(metaPath) {
2076
2112
  return null;
2077
2113
  }
2078
2114
  }
2115
+ function resolve_source_prefab(doc, file_path, project_path) {
2116
+ const pi_blocks = doc.find_by_class_id(1001);
2117
+ if (pi_blocks.length === 0)
2118
+ return null;
2119
+ const pi_block = pi_blocks[0];
2120
+ const source_match = pi_block.raw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
2121
+ if (!source_match)
2122
+ return null;
2123
+ const source_guid = source_match[1];
2124
+ const resolved_project = project_path || find_unity_project_root(path3.dirname(file_path));
2125
+ if (!resolved_project)
2126
+ return null;
2127
+ const cache = load_guid_cache_for_file(file_path, resolved_project);
2128
+ if (!cache)
2129
+ return null;
2130
+ const source_path = cache.resolve_absolute(source_guid);
2131
+ if (!source_path || !import_fs8.existsSync(source_path))
2132
+ return null;
2133
+ return {
2134
+ source_guid,
2135
+ source_path,
2136
+ prefab_instance_id: pi_block.file_id,
2137
+ prefab_instance_block: pi_block
2138
+ };
2139
+ }
2079
2140
  function resolveScriptGuid(script, projectPath) {
2080
2141
  if (/^[a-f0-9]{32}$/i.test(script)) {
2081
2142
  return { guid: script.toLowerCase(), path: null };
@@ -2125,27 +2186,122 @@ function resolveScriptGuid(script, projectPath) {
2125
2186
  return false;
2126
2187
  return true;
2127
2188
  });
2128
- if (matches.length === 1 && matches[0].guid) {
2129
- return { guid: matches[0].guid, path: matches[0].filePath };
2189
+ if (matches.length === 1) {
2190
+ if (matches[0].guid) {
2191
+ return { guid: matches[0].guid, path: matches[0].filePath };
2192
+ }
2193
+ if (matches[0].filePath && projectPath) {
2194
+ const fullFilePath = path3.isAbsolute(matches[0].filePath) ? matches[0].filePath : path3.join(projectPath, matches[0].filePath);
2195
+ const metaPath = fullFilePath + ".meta";
2196
+ if (import_fs8.existsSync(metaPath)) {
2197
+ const guid = extractGuidFromMeta(metaPath);
2198
+ if (guid)
2199
+ return { guid, path: matches[0].filePath };
2200
+ }
2201
+ if (matches[0].filePath.startsWith("Packages/")) {
2202
+ const pkgPath = path3.join(projectPath, matches[0].filePath);
2203
+ const pkgMetaPath = pkgPath + ".meta";
2204
+ if (import_fs8.existsSync(pkgMetaPath)) {
2205
+ const guid = extractGuidFromMeta(pkgMetaPath);
2206
+ if (guid)
2207
+ return { guid, path: matches[0].filePath };
2208
+ }
2209
+ }
2210
+ if (matches[0].filePath?.endsWith(".dll")) {
2211
+ const dllBasename = path3.basename(matches[0].filePath).toLowerCase();
2212
+ for (const cacheName of ["package-cache.json", "local-package-cache.json"]) {
2213
+ const cachePath = path3.join(projectPath, ".unity-agentic", cacheName);
2214
+ if (!import_fs8.existsSync(cachePath))
2215
+ continue;
2216
+ try {
2217
+ const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
2218
+ for (const [guid, assetPath] of Object.entries(cache)) {
2219
+ if (assetPath.toLowerCase().endsWith(".dll") && path3.basename(assetPath).toLowerCase() === dllBasename) {
2220
+ return { guid, path: assetPath };
2221
+ }
2222
+ }
2223
+ } catch {}
2224
+ }
2225
+ }
2226
+ }
2130
2227
  }
2131
2228
  if (matches.length > 1) {
2132
2229
  const withGuid = matches.filter((m) => m.guid);
2133
2230
  if (withGuid.length === 1) {
2134
2231
  return { guid: withGuid[0].guid, path: withGuid[0].filePath };
2135
2232
  }
2233
+ const resolvedFromMeta = matches.filter((m) => !m.guid && m.filePath).map((m) => {
2234
+ const fullPath = path3.isAbsolute(m.filePath) ? m.filePath : path3.join(projectPath, m.filePath);
2235
+ let guid = import_fs8.existsSync(fullPath + ".meta") ? extractGuidFromMeta(fullPath + ".meta") : null;
2236
+ if (!guid && m.filePath.startsWith("Packages/")) {
2237
+ const pkgPath = path3.join(projectPath, m.filePath);
2238
+ if (import_fs8.existsSync(pkgPath + ".meta")) {
2239
+ guid = extractGuidFromMeta(pkgPath + ".meta");
2240
+ }
2241
+ }
2242
+ if (!guid && m.filePath?.endsWith(".dll")) {
2243
+ const dllBase = path3.basename(m.filePath).toLowerCase();
2244
+ for (const cn of ["package-cache.json", "local-package-cache.json"]) {
2245
+ const cp = path3.join(projectPath, ".unity-agentic", cn);
2246
+ if (!import_fs8.existsSync(cp))
2247
+ continue;
2248
+ try {
2249
+ const c = JSON.parse(import_fs8.readFileSync(cp, "utf-8"));
2250
+ for (const [g, p] of Object.entries(c)) {
2251
+ if (p.toLowerCase().endsWith(".dll") && path3.basename(p).toLowerCase() === dllBase) {
2252
+ guid = g;
2253
+ break;
2254
+ }
2255
+ }
2256
+ } catch {}
2257
+ if (guid)
2258
+ break;
2259
+ }
2260
+ }
2261
+ return guid ? { guid, path: m.filePath } : null;
2262
+ }).filter((r) => r !== null);
2263
+ const allResolved = [...withGuid.map((m) => ({ guid: m.guid, path: m.filePath })), ...resolvedFromMeta];
2264
+ if (allResolved.length === 1) {
2265
+ return allResolved[0];
2266
+ }
2267
+ if (allResolved.length > 1) {
2268
+ const pkgCachePaths = [
2269
+ path3.join(projectPath, ".unity-agentic", "package-cache.json"),
2270
+ path3.join(projectPath, ".unity-agentic", "local-package-cache.json")
2271
+ ];
2272
+ for (const cachePath of pkgCachePaths) {
2273
+ if (!import_fs8.existsSync(cachePath))
2274
+ continue;
2275
+ try {
2276
+ const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
2277
+ const inCache = allResolved.filter((r) => (r.guid in cache));
2278
+ if (inCache.length === 1)
2279
+ return inCache[0];
2280
+ } catch {}
2281
+ }
2282
+ const paths = allResolved.map((m) => m.path).join(", ");
2283
+ throw new Error(`Ambiguous type "${script}": found ${allResolved.length} matches (${paths}). ` + `Use a qualified name (e.g., "Namespace.${targetName}") or provide the full path.`);
2284
+ }
2136
2285
  }
2137
2286
  } catch {}
2138
2287
  }
2139
- const packageCachePath = path3.join(projectPath, ".unity-agentic", "package-cache.json");
2140
- if (import_fs8.existsSync(packageCachePath)) {
2288
+ const scriptNameLower = script.toLowerCase().replace(/\.cs$/, "");
2289
+ const dotIdx = scriptNameLower.lastIndexOf(".");
2290
+ const classNameOnly = dotIdx > 0 ? scriptNameLower.substring(dotIdx + 1) : null;
2291
+ const cachePaths = [
2292
+ path3.join(projectPath, ".unity-agentic", "package-cache.json"),
2293
+ path3.join(projectPath, ".unity-agentic", "local-package-cache.json")
2294
+ ];
2295
+ for (const cachePath of cachePaths) {
2296
+ if (!import_fs8.existsSync(cachePath))
2297
+ continue;
2141
2298
  try {
2142
- const packageCache = JSON.parse(import_fs8.readFileSync(packageCachePath, "utf-8"));
2143
- const scriptNameLower = script.toLowerCase().replace(/\.cs$/, "");
2144
- for (const [guid, assetPath] of Object.entries(packageCache)) {
2299
+ const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
2300
+ for (const [guid, assetPath] of Object.entries(cache)) {
2145
2301
  if (!assetPath.endsWith(".cs"))
2146
2302
  continue;
2147
2303
  const fileName = path3.basename(assetPath, ".cs").toLowerCase();
2148
- if (fileName === scriptNameLower) {
2304
+ if (fileName === scriptNameLower || classNameOnly && fileName === classNameOnly) {
2149
2305
  return { guid, path: assetPath };
2150
2306
  }
2151
2307
  }
@@ -2196,6 +2352,8 @@ function resolve_script_with_fields(script, project_path) {
2196
2352
  result.fields = chosen.fields;
2197
2353
  result.base_class = chosen.baseClass ?? undefined;
2198
2354
  result.kind = chosen.kind;
2355
+ result.namespace = chosen.namespace ?? undefined;
2356
+ result.class_name = chosen.name;
2199
2357
  }
2200
2358
  } else {
2201
2359
  result.extraction_error = "Native extractDllFields function not available";
@@ -2391,6 +2549,16 @@ function yaml_default_for_type(csharp_type, version) {
2391
2549
  function generate_field_yaml(fields, version, indent = " ") {
2392
2550
  const lines = [];
2393
2551
  for (const field of fields) {
2552
+ if (field.hasSerializeReference) {
2553
+ const t = field.typeName;
2554
+ if (t.endsWith("[]") || t.startsWith("List<") && t.endsWith(">")) {
2555
+ lines.push(`${indent}${field.name}: []`);
2556
+ } else {
2557
+ lines.push(`${indent}${field.name}:`);
2558
+ lines.push(`${indent} rid: 0`);
2559
+ }
2560
+ continue;
2561
+ }
2394
2562
  const default_value = yaml_default_for_type(field.typeName, version);
2395
2563
  if (default_value === null) {
2396
2564
  continue;
@@ -2434,6 +2602,17 @@ function parse_header(raw) {
2434
2602
  function escape_regex(str) {
2435
2603
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2436
2604
  }
2605
+ function yaml_quote_if_needed(value) {
2606
+ if (value === "")
2607
+ return value;
2608
+ if (value.startsWith("{") && value.endsWith("}") || value.startsWith("[") && value.endsWith("]")) {
2609
+ return value;
2610
+ }
2611
+ if (value.includes(": ") || value.includes(" #") || /^[#\{\[*&!|>'"]/.test(value) || value !== value.trim()) {
2612
+ return `'${value.replace(/'/g, "''")}'`;
2613
+ }
2614
+ return value;
2615
+ }
2437
2616
 
2438
2617
  class UnityBlock {
2439
2618
  _raw;
@@ -2469,6 +2648,7 @@ class UnityBlock {
2469
2648
  return this._dirty;
2470
2649
  }
2471
2650
  get_property(path4) {
2651
+ path4 = normalize_property_path(path4);
2472
2652
  if (path4.includes("Array.data[")) {
2473
2653
  return this._get_array_element(path4);
2474
2654
  }
@@ -2495,13 +2675,17 @@ class UnityBlock {
2495
2675
  return match ? match[1].trim() : null;
2496
2676
  }
2497
2677
  set_property(path4, value, object_reference) {
2678
+ path4 = normalize_property_path(path4);
2498
2679
  const old_raw = this._raw;
2499
2680
  const effective_ref = object_reference && object_reference !== "{fileID: 0}" ? object_reference : undefined;
2500
2681
  if (!path4.includes(".") && !path4.includes("Array")) {
2501
2682
  const prop_pattern = new RegExp(`(^\\s*${escape_regex(path4)}:\\s*)(.*)$`, "m");
2502
2683
  if (prop_pattern.test(this._raw)) {
2503
- const replacement_value = effective_ref ?? value;
2504
- this._raw = this._raw.replace(prop_pattern, `$1${replacement_value}`);
2684
+ const replacement_value = effective_ref ?? yaml_quote_if_needed(value);
2685
+ this._raw = this._raw.replace(prop_pattern, (_match, prefix) => prefix + replacement_value);
2686
+ } else {
2687
+ const insert_value = effective_ref ?? yaml_quote_if_needed(value);
2688
+ this._insert_property(path4, insert_value);
2505
2689
  }
2506
2690
  return this._check_dirty(old_raw);
2507
2691
  }
@@ -2516,8 +2700,8 @@ class UnityBlock {
2516
2700
  const fields = inline_match[2];
2517
2701
  const field_pattern = new RegExp(`(${escape_regex(sub_field)}:\\s*)([^,}]+)`);
2518
2702
  if (field_pattern.test(fields)) {
2519
- const updated_fields = fields.replace(field_pattern, `$1${value}`);
2520
- this._raw = this._raw.replace(inline_pattern, `$1${updated_fields}$3`);
2703
+ const updated_fields = fields.replace(field_pattern, (_m, prefix) => prefix + value);
2704
+ this._raw = this._raw.replace(inline_pattern, (_m, g1, _g2, g3) => g1 + updated_fields + g3);
2521
2705
  inline_applied = true;
2522
2706
  }
2523
2707
  }
@@ -2526,6 +2710,8 @@ class UnityBlock {
2526
2710
  const block_result = this._resolve_block_style_path(parts, effective_value);
2527
2711
  if (block_result !== null) {
2528
2712
  this._raw = block_result;
2713
+ } else if (parts.length === 2) {
2714
+ this._insert_struct_property(parent_prop, sub_field, effective_value);
2529
2715
  }
2530
2716
  }
2531
2717
  return this._check_dirty(old_raw);
@@ -2543,7 +2729,7 @@ class UnityBlock {
2543
2729
  `).filter((l) => l.trim().startsWith("-"));
2544
2730
  if (index < lines.length) {
2545
2731
  const old_line = lines[index];
2546
- const new_line = old_line.replace(/-\s*.*/, `- ${ref_value}`);
2732
+ const new_line = old_line.replace(/-\s*.*/, () => `- ${ref_value}`);
2547
2733
  this._raw = this._raw.replace(old_line, new_line);
2548
2734
  }
2549
2735
  }
@@ -2552,6 +2738,44 @@ class UnityBlock {
2552
2738
  }
2553
2739
  return false;
2554
2740
  }
2741
+ _insert_property(path4, value) {
2742
+ const alt = path4.startsWith("m_") ? path4.slice(2) : "m_" + path4;
2743
+ const alt_pattern = new RegExp(`^\\s*${escape_regex(alt)}:`, "m");
2744
+ if (alt_pattern.test(this._raw)) {
2745
+ return;
2746
+ }
2747
+ const indented_line = ` ${path4}: ${value}`;
2748
+ const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
2749
+ const eci_match = this._raw.match(eci_pattern);
2750
+ if (eci_match && eci_match.index !== undefined) {
2751
+ const insert_after = eci_match.index + eci_match[0].length;
2752
+ this._raw = this._raw.slice(0, insert_after) + `
2753
+ ` + indented_line + this._raw.slice(insert_after);
2754
+ return;
2755
+ }
2756
+ this._raw = this._raw.trimEnd() + `
2757
+ ` + indented_line + `
2758
+ `;
2759
+ }
2760
+ _insert_struct_property(parent, child, value) {
2761
+ const parent_pattern = new RegExp(`^[ ]*${escape_regex(parent)}:`, "m");
2762
+ if (parent_pattern.test(this._raw)) {
2763
+ return;
2764
+ }
2765
+ const struct_block = ` ${parent}:
2766
+ ${child}: ${value}`;
2767
+ const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
2768
+ const eci_match = this._raw.match(eci_pattern);
2769
+ if (eci_match && eci_match.index !== undefined) {
2770
+ const insert_after = eci_match.index + eci_match[0].length;
2771
+ this._raw = this._raw.slice(0, insert_after) + `
2772
+ ` + struct_block + this._raw.slice(insert_after);
2773
+ return;
2774
+ }
2775
+ this._raw = this._raw.trimEnd() + `
2776
+ ` + struct_block + `
2777
+ `;
2778
+ }
2555
2779
  has_property(path4) {
2556
2780
  return this.get_property(path4) !== null;
2557
2781
  }
@@ -2607,7 +2831,7 @@ class UnityBlock {
2607
2831
  if (empty_match) {
2608
2832
  const base_indent = empty_match[2];
2609
2833
  const element_indent2 = base_indent + " ";
2610
- this._raw = this._raw.replace(empty_pattern, `$1
2834
+ this._raw = this._raw.replace(empty_pattern, (_m, g1) => `${g1}
2611
2835
  ${element_indent2}- ${value}`);
2612
2836
  return this._check_dirty(old_raw);
2613
2837
  }
@@ -2707,7 +2931,7 @@ ${element_indent2}- ${value}`);
2707
2931
  }
2708
2932
  extract_file_id_refs() {
2709
2933
  const refs = [];
2710
- const pattern = /\{fileID:\s*(\d+)/g;
2934
+ const pattern = /\{fileID:\s*(-?\d+)/g;
2711
2935
  const first_newline = this._raw.indexOf(`
2712
2936
  `);
2713
2937
  const body = first_newline === -1 ? "" : this._raw.slice(first_newline);
@@ -3013,7 +3237,7 @@ class UnityDocument {
3013
3237
  const game_objects = this.find_game_objects_by_name(name);
3014
3238
  const transform_ids = [];
3015
3239
  for (const go of game_objects) {
3016
- const comp_matches = go.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/g);
3240
+ const comp_matches = go.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
3017
3241
  for (const cm of comp_matches) {
3018
3242
  const comp_block = this.find_by_file_id(cm[1]);
3019
3243
  if (comp_block && (comp_block.class_id === 4 || comp_block.class_id === 224)) {
@@ -3025,7 +3249,7 @@ class UnityDocument {
3025
3249
  return transform_ids;
3026
3250
  }
3027
3251
  require_unique_game_object(name_or_id) {
3028
- if (/^\d+$/.test(name_or_id)) {
3252
+ if (/^-?\d+$/.test(name_or_id)) {
3029
3253
  const block = this.find_by_file_id(name_or_id);
3030
3254
  if (!block) {
3031
3255
  return { error: `GameObject with fileID ${name_or_id} not found` };
@@ -3046,7 +3270,7 @@ class UnityDocument {
3046
3270
  return matches[0];
3047
3271
  }
3048
3272
  require_unique_transform(name_or_id) {
3049
- if (/^\d+$/.test(name_or_id)) {
3273
+ if (/^-?\d+$/.test(name_or_id)) {
3050
3274
  const block = this.find_by_file_id(name_or_id);
3051
3275
  if (!block) {
3052
3276
  return { error: `Block with fileID ${name_or_id} not found` };
@@ -3058,7 +3282,7 @@ class UnityDocument {
3058
3282
  return block;
3059
3283
  }
3060
3284
  if (block.class_id === 1) {
3061
- const comp_matches = block.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/g);
3285
+ const comp_matches = block.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
3062
3286
  for (const cm of comp_matches) {
3063
3287
  const comp_block = this.find_by_file_id(cm[1]);
3064
3288
  if (comp_block && (comp_block.class_id === 4 || comp_block.class_id === 224)) {
@@ -3086,7 +3310,7 @@ class UnityDocument {
3086
3310
  find_prefab_root() {
3087
3311
  for (const block of this._blocks) {
3088
3312
  if (block.class_id === 4 && !block.is_stripped && /m_Father:\s*\{fileID:\s*0\}/.test(block.raw)) {
3089
- const go_match = block.raw.match(/m_GameObject:\s*\{fileID:\s*(\d+)\}/);
3313
+ const go_match = block.raw.match(/m_GameObject:\s*\{fileID:\s*(-?\d+)\}/);
3090
3314
  if (go_match) {
3091
3315
  const go_block = this.find_by_file_id(go_match[1]);
3092
3316
  if (go_block) {
@@ -3283,7 +3507,7 @@ class UnityDocument {
3283
3507
  return true;
3284
3508
  }
3285
3509
  if (raw.includes("m_Children:") && !raw.includes(`fileID: ${child_id}`)) {
3286
- raw = raw.replace(/(m_Children:\s*\n(?:\s*-\s*\{fileID:\s*\d+\}\s*\n)*)/, `$1 - {fileID: ${child_id}}
3510
+ raw = raw.replace(/(m_Children:\s*\n(?:\s*-\s*\{fileID:\s*-?\d+\}\s*\n)*)/, `$1 - {fileID: ${child_id}}
3287
3511
  `);
3288
3512
  parent.replace_raw(raw);
3289
3513
  return true;
@@ -3319,7 +3543,7 @@ class UnityDocument {
3319
3543
  if (!children_section)
3320
3544
  return;
3321
3545
  const child_ids = [];
3322
- const child_matches = children_section[0].matchAll(/\{fileID:\s*(\d+)\}/g);
3546
+ const child_matches = children_section[0].matchAll(/\{fileID:\s*(-?\d+)\}/g);
3323
3547
  for (const m of child_matches) {
3324
3548
  if (m[1] !== "0")
3325
3549
  child_ids.push(m[1]);
@@ -3328,13 +3552,13 @@ class UnityDocument {
3328
3552
  result.add(child_transform_id);
3329
3553
  const child_transform = this.find_by_file_id(child_transform_id);
3330
3554
  if (child_transform) {
3331
- const go_match = child_transform.raw.match(/m_GameObject:\s*\{fileID:\s*(\d+)\}/);
3555
+ const go_match = child_transform.raw.match(/m_GameObject:\s*\{fileID:\s*(-?\d+)\}/);
3332
3556
  if (go_match) {
3333
3557
  const go_id = go_match[1];
3334
3558
  result.add(go_id);
3335
3559
  const go_block = this.find_by_file_id(go_id);
3336
3560
  if (go_block) {
3337
- const comp_matches = go_block.raw.matchAll(/component:\s*\{fileID:\s*(\d+)\}/g);
3561
+ const comp_matches = go_block.raw.matchAll(/component:\s*\{fileID:\s*(-?\d+)\}/g);
3338
3562
  for (const cm of comp_matches) {
3339
3563
  result.add(cm[1]);
3340
3564
  }
@@ -3359,7 +3583,7 @@ class UnityDocument {
3359
3583
  return 0;
3360
3584
  const children_match = parent.raw.match(/m_Children:[\s\S]*?(?=\s*m_Father:)/);
3361
3585
  if (children_match) {
3362
- const entries = children_match[0].match(/\{fileID:\s*\d+\}/g);
3586
+ const entries = children_match[0].match(/\{fileID:\s*-?\d+\}/g);
3363
3587
  return entries ? entries.length : 0;
3364
3588
  }
3365
3589
  return 0;
@@ -3394,7 +3618,7 @@ ${new_children}`);
3394
3618
  const transform = this.find_by_file_id(transform_id);
3395
3619
  if (!transform)
3396
3620
  continue;
3397
- const go_match = transform.raw.match(/m_GameObject:\s*\{fileID:\s*(\d+)\}/);
3621
+ const go_match = transform.raw.match(/m_GameObject:\s*\{fileID:\s*(-?\d+)\}/);
3398
3622
  if (!go_match)
3399
3623
  continue;
3400
3624
  const go_id = go_match[1];
@@ -3402,7 +3626,7 @@ ${new_children}`);
3402
3626
  if (!go)
3403
3627
  continue;
3404
3628
  const comp_ids = [];
3405
- const comp_matches = go.raw.matchAll(/component:\s*\{fileID:\s*(\d+)\}/g);
3629
+ const comp_matches = go.raw.matchAll(/component:\s*\{fileID:\s*(-?\d+)\}/g);
3406
3630
  for (const m of comp_matches) {
3407
3631
  comp_ids.push(m[1]);
3408
3632
  }
@@ -3531,6 +3755,20 @@ function read_project_version(projectPath) {
3531
3755
  }
3532
3756
 
3533
3757
  // src/editor/create.ts
3758
+ function compute_dll_script_file_id(namespace, class_name) {
3759
+ const { createHash } = require("crypto");
3760
+ const full_name = namespace ? `${namespace}.${class_name}` : class_name;
3761
+ const name_bytes = Buffer.from(full_name, "utf-8");
3762
+ const input = Buffer.alloc(4 + name_bytes.length);
3763
+ input.writeInt32LE(114, 0);
3764
+ name_bytes.copy(input, 4);
3765
+ const hash = createHash("md4").update(input).digest();
3766
+ const a = hash.readInt32LE(0);
3767
+ const b = hash.readInt32LE(4);
3768
+ const c = hash.readInt32LE(8);
3769
+ const d = hash.readInt32LE(12);
3770
+ return a ^ b ^ c ^ d;
3771
+ }
3534
3772
  function get_layer_from_parent(doc, parentTransformId) {
3535
3773
  if (parentTransformId === "0")
3536
3774
  return 0;
@@ -3546,7 +3784,7 @@ function get_layer_from_parent(doc, parentTransformId) {
3546
3784
  const layerMatch = parentGo.raw.match(/m_Layer:\s*(\d+)/);
3547
3785
  return layerMatch ? parseInt(layerMatch[1], 10) : 0;
3548
3786
  }
3549
- function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = 0, rootOrder = 0, layer = 0) {
3787
+ function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
3550
3788
  return `--- !u!1 &${gameObjectId}
3551
3789
  GameObject:
3552
3790
  m_ObjectHideFlags: 0
@@ -3607,9 +3845,9 @@ function findStrippedRootTransform(doc, prefabInstanceId) {
3607
3845
  for (const block of doc.blocks) {
3608
3846
  if (block.class_id !== 4 || !block.is_stripped)
3609
3847
  continue;
3610
- const piMatch = block.raw.match(/m_PrefabInstance:\s*\{fileID:\s*(\d+)\}/);
3848
+ const piMatch = block.raw.match(/m_PrefabInstance:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
3611
3849
  if (piMatch && piMatch[1] === prefabInstanceId) {
3612
- const sourceMatch = block.raw.match(/m_CorrespondingSourceObject:\s*(\{[^}]+\})/);
3850
+ const sourceMatch = block.raw.match(/m_CorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
3613
3851
  if (sourceMatch) {
3614
3852
  return {
3615
3853
  transformId: block.file_id,
@@ -3620,6 +3858,66 @@ function findStrippedRootTransform(doc, prefabInstanceId) {
3620
3858
  }
3621
3859
  return null;
3622
3860
  }
3861
+ function find_game_object_in_variant(doc, name, file_path, project_path) {
3862
+ const pi_blocks = doc.find_by_class_id(1001);
3863
+ for (const pi_block of pi_blocks) {
3864
+ const mod_pattern = /- target:[ \t]*(\{[^}]+\})\s*propertyPath:[ \t]*m_Name\s*value:[ \t]*([^\n]*)/g;
3865
+ let mod_match;
3866
+ while ((mod_match = mod_pattern.exec(pi_block.raw)) !== null) {
3867
+ const target_ref = mod_match[1];
3868
+ const mod_value = mod_match[2].trim();
3869
+ if (mod_value !== name)
3870
+ continue;
3871
+ for (const block of doc.blocks) {
3872
+ if (block.class_id !== 1 || !block.is_stripped)
3873
+ continue;
3874
+ const source_match = block.raw.match(/m_CorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
3875
+ if (!source_match)
3876
+ continue;
3877
+ const normalized_source = source_match[1].replace(/\s+/g, " ");
3878
+ const normalized_target = target_ref.replace(/\s+/g, " ");
3879
+ if (normalized_source === normalized_target) {
3880
+ return {
3881
+ stripped_go_id: block.file_id,
3882
+ source_ref: target_ref,
3883
+ prefab_instance_id: pi_block.file_id
3884
+ };
3885
+ }
3886
+ }
3887
+ }
3888
+ }
3889
+ const source_info = resolve_source_prefab(doc, file_path, project_path);
3890
+ if (!source_info)
3891
+ return null;
3892
+ let source_doc;
3893
+ try {
3894
+ source_doc = UnityDocument.from_file(source_info.source_path);
3895
+ } catch {
3896
+ return null;
3897
+ }
3898
+ const source_gos = source_doc.find_game_objects_by_name(name);
3899
+ if (source_gos.length !== 1)
3900
+ return null;
3901
+ const source_go = source_gos[0];
3902
+ const source_go_file_id = source_go.file_id;
3903
+ const source_guid = source_info.source_guid;
3904
+ for (const block of doc.blocks) {
3905
+ if (block.class_id !== 1 || !block.is_stripped)
3906
+ continue;
3907
+ const source_match = block.raw.match(/m_CorrespondingSourceObject:[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)[^}]*guid:[ \t]*([a-f0-9]{32})/);
3908
+ if (!source_match)
3909
+ continue;
3910
+ if (source_match[1] === source_go_file_id && source_match[2] === source_guid) {
3911
+ const source_ref = `{fileID: ${source_go_file_id}, guid: ${source_guid}, type: 3}`;
3912
+ return {
3913
+ stripped_go_id: block.file_id,
3914
+ source_ref,
3915
+ prefab_instance_id: source_info.prefab_instance_id
3916
+ };
3917
+ }
3918
+ }
3919
+ return null;
3920
+ }
3623
3921
  function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
3624
3922
  const piBlock = doc.find_by_file_id(piId);
3625
3923
  if (!piBlock)
@@ -3629,13 +3927,34 @@ function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
3629
3927
  insertIndex: -1
3630
3928
  addedObject: {fileID: ${newGoId}}`;
3631
3929
  let raw = piBlock.raw;
3632
- const emptyPattern = /m_AddedGameObjects:\s*\[\]/;
3930
+ const emptyPattern = /m_AddedGameObjects:[ \t]*\[\]/;
3633
3931
  if (emptyPattern.test(raw)) {
3634
3932
  raw = raw.replace(emptyPattern, `m_AddedGameObjects:${entry}`);
3635
3933
  piBlock.replace_raw(raw);
3636
3934
  return;
3637
3935
  }
3638
- const existingPattern = /(m_AddedGameObjects:\s*\n(?:\s+-[\s\S]*?(?=\n\s+m_|$))*)/;
3936
+ const existingPattern = /(m_AddedGameObjects:[ \t]*\n(?:[ \t]+-[\s\S]*?(?=\n[ \t]+m_|$))*)/;
3937
+ if (existingPattern.test(raw)) {
3938
+ raw = raw.replace(existingPattern, `$1${entry}`);
3939
+ piBlock.replace_raw(raw);
3940
+ }
3941
+ }
3942
+ function appendToAddedComponents(doc, piId, targetSourceRef, newComponentId) {
3943
+ const piBlock = doc.find_by_file_id(piId);
3944
+ if (!piBlock)
3945
+ return;
3946
+ const entry = `
3947
+ - targetCorrespondingSourceObject: ${targetSourceRef}
3948
+ insertIndex: -1
3949
+ addedObject: {fileID: ${newComponentId}}`;
3950
+ let raw = piBlock.raw;
3951
+ const emptyPattern = /m_AddedComponents:[ \t]*\[\]/;
3952
+ if (emptyPattern.test(raw)) {
3953
+ raw = raw.replace(emptyPattern, `m_AddedComponents:${entry}`);
3954
+ piBlock.replace_raw(raw);
3955
+ return;
3956
+ }
3957
+ const existingPattern = /(m_AddedComponents:[ \t]*\n(?:[ \t]+-[\s\S]*?(?=\n[ \t]+m_|$))*)/;
3639
3958
  if (existingPattern.test(raw)) {
3640
3959
  raw = raw.replace(existingPattern, `$1${entry}`);
3641
3960
  piBlock.replace_raw(raw);
@@ -3658,11 +3977,31 @@ var COMPONENT_DEFAULTS = {
3658
3977
  m_Materials:
3659
3978
  - {fileID: 0}`,
3660
3979
  33: ` m_Mesh: {fileID: 0}`,
3980
+ 50: ` m_BodyType: 0
3981
+ m_Mass: 1
3982
+ m_LinearDrag: 0
3983
+ m_AngularDrag: 0.05
3984
+ m_GravityScale: 1
3985
+ m_Interpolate: 0
3986
+ m_SleepingMode: 1
3987
+ m_CollisionDetection: 0`,
3661
3988
  54: ` m_Mass: 1
3662
3989
  m_Drag: 0
3663
3990
  m_AngularDrag: 0.05
3664
3991
  m_UseGravity: 1
3665
3992
  m_IsKinematic: 0`,
3993
+ 58: ` m_IsTrigger: 0
3994
+ m_Material: {fileID: 0}
3995
+ m_Offset: {x: 0, y: 0}
3996
+ m_Radius: 0.5`,
3997
+ 60: ` m_IsTrigger: 0
3998
+ m_Material: {fileID: 0}
3999
+ m_Offset: {x: 0, y: 0}
4000
+ m_Points: []`,
4001
+ 61: ` m_IsTrigger: 0
4002
+ m_Material: {fileID: 0}
4003
+ m_Offset: {x: 0, y: 0}
4004
+ m_Size: {x: 1, y: 1}`,
3666
4005
  64: ` m_IsTrigger: 0
3667
4006
  m_Convex: 0
3668
4007
  m_CookingOptions: 30
@@ -3671,6 +4010,19 @@ var COMPONENT_DEFAULTS = {
3671
4010
  m_Material: {fileID: 0}
3672
4011
  m_Center: {x: 0, y: 0, z: 0}
3673
4012
  m_Size: {x: 1, y: 1, z: 1}`,
4013
+ 66: ` m_IsTrigger: 0
4014
+ m_Material: {fileID: 0}
4015
+ m_Offset: {x: 0, y: 0}
4016
+ m_GeometryType: 0`,
4017
+ 68: ` m_IsTrigger: 0
4018
+ m_Material: {fileID: 0}
4019
+ m_Offset: {x: 0, y: 0}
4020
+ m_Points: []`,
4021
+ 70: ` m_IsTrigger: 0
4022
+ m_Material: {fileID: 0}
4023
+ m_Offset: {x: 0, y: 0}
4024
+ m_Size: {x: 1, y: 1}
4025
+ m_Direction: 0`,
3674
4026
  82: ` m_PlayOnAwake: 1
3675
4027
  m_Volume: 1
3676
4028
  m_Pitch: 1
@@ -3756,9 +4108,14 @@ function addComponentToGameObject(doc, gameObjectId, componentId) {
3756
4108
  `);
3757
4109
  goBlock.replace_raw(raw);
3758
4110
  }
3759
- function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version) {
4111
+ function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000) {
3760
4112
  const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version) : `
3761
4113
  `;
4114
+ const has_serialize_ref = fields?.some((f) => f.hasSerializeReference) ?? false;
4115
+ const references_section = has_serialize_ref ? ` references:
4116
+ version: 2
4117
+ RefIds: []
4118
+ ` : "";
3762
4119
  return `--- !u!114 &${componentId}
3763
4120
  MonoBehaviour:
3764
4121
  m_ObjectHideFlags: 0
@@ -3768,9 +4125,9 @@ MonoBehaviour:
3768
4125
  m_GameObject: {fileID: ${gameObjectId}}
3769
4126
  m_Enabled: 1
3770
4127
  m_EditorHideFlags: 0
3771
- m_Script: {fileID: 11500000, guid: ${scriptGuid}, type: 3}
4128
+ m_Script: {fileID: ${scriptFileId}, guid: ${scriptGuid}, type: 3}
3772
4129
  m_Name:
3773
- m_EditorClassIdentifier:${field_yaml}`;
4130
+ m_EditorClassIdentifier:${field_yaml}${references_section}`;
3774
4131
  }
3775
4132
  function createGameObject(options) {
3776
4133
  const { file_path, name, parent } = options;
@@ -3869,9 +4226,9 @@ function createGameObject(options) {
3869
4226
  const layer = get_layer_from_parent(doc, parentTransformIdStr);
3870
4227
  const gameObjectIdStr = doc.generate_file_id();
3871
4228
  const transformIdStr = doc.generate_file_id();
3872
- const gameObjectId = parseInt(gameObjectIdStr, 10);
3873
- const transformId = parseInt(transformIdStr, 10);
3874
- const parentTransformId = parseInt(parentTransformIdStr, 10);
4229
+ const gameObjectId = gameObjectIdStr;
4230
+ const transformId = transformIdStr;
4231
+ const parentTransformId = parentTransformIdStr;
3875
4232
  const newBlocks = createGameObjectYAML(gameObjectId, transformId, name.trim(), parentTransformId, rootOrder, layer);
3876
4233
  doc.append_raw(newBlocks);
3877
4234
  if (parentTransformIdStr !== "0" && !variantPiId) {
@@ -3893,7 +4250,7 @@ function createGameObject(options) {
3893
4250
  file_path,
3894
4251
  game_object_id: gameObjectId,
3895
4252
  transform_id: transformId,
3896
- prefab_instance_id: variantPiId ? parseInt(variantPiId, 10) : undefined
4253
+ prefab_instance_id: variantPiId || undefined
3897
4254
  };
3898
4255
  }
3899
4256
  function createScene(options) {
@@ -4339,9 +4696,9 @@ function createPrefabVariant(options) {
4339
4696
  const tempDoc = UnityDocument.from_string(`%YAML 1.1
4340
4697
  %TAG !u! tag:unity3d.com,2011:
4341
4698
  `);
4342
- const prefabInstanceId = parseInt(tempDoc.generate_file_id(), 10);
4343
- const strippedGoId = parseInt(tempDoc.generate_file_id(), 10);
4344
- const strippedTransformId = parseInt(tempDoc.generate_file_id(), 10);
4699
+ const prefabInstanceId = tempDoc.generate_file_id();
4700
+ const strippedGoId = tempDoc.generate_file_id();
4701
+ const strippedTransformId = tempDoc.generate_file_id();
4345
4702
  const finalName = variant_name || `${rootInfo.name} Variant`;
4346
4703
  const variantYaml = `%YAML 1.1
4347
4704
  %TAG !u! tag:unity3d.com,2011:
@@ -4457,7 +4814,7 @@ function createScriptableObject(options) {
4457
4814
  const baseName = path5.basename(output_path, ".asset");
4458
4815
  const field_yaml = resolved.fields && resolved.fields.length > 0 ? generate_field_yaml(resolved.fields, version) : `
4459
4816
  `;
4460
- const assetYaml = `%YAML 1.1
4817
+ let assetYaml = `%YAML 1.1
4461
4818
  %TAG !u! tag:unity3d.com,2011:
4462
4819
  --- !u!114 &11400000
4463
4820
  MonoBehaviour:
@@ -4471,6 +4828,16 @@ MonoBehaviour:
4471
4828
  m_Script: {fileID: 11500000, guid: ${resolved.guid}, type: 3}
4472
4829
  m_Name: ${baseName}
4473
4830
  m_EditorClassIdentifier:${field_yaml}`;
4831
+ if (options.initial_values && Object.keys(options.initial_values).length > 0) {
4832
+ const doc = UnityDocument.from_string(assetYaml);
4833
+ const block = doc.blocks[0];
4834
+ if (block) {
4835
+ for (const [key, val] of Object.entries(options.initial_values)) {
4836
+ block.set_property(key, val);
4837
+ }
4838
+ assetYaml = doc.serialize();
4839
+ }
4840
+ }
4474
4841
  try {
4475
4842
  import_fs10.writeFileSync(output_path, assetYaml, "utf-8");
4476
4843
  } catch (err) {
@@ -4551,6 +4918,80 @@ MonoImporter:
4551
4918
  guid
4552
4919
  };
4553
4920
  }
4921
+ var COMPONENT_BASE_CLASSES = new Set([
4922
+ "MonoBehaviour",
4923
+ "NetworkBehaviour",
4924
+ "StateMachineBehaviour"
4925
+ ]);
4926
+ function is_valid_component_base(base_class, project_path, depth = 0) {
4927
+ if (depth > 5)
4928
+ return false;
4929
+ if (COMPONENT_BASE_CLASSES.has(base_class))
4930
+ return true;
4931
+ if (!project_path)
4932
+ return false;
4933
+ const registryPath = path5.join(project_path, ".unity-agentic", "type-registry.json");
4934
+ if (import_fs10.existsSync(registryPath)) {
4935
+ try {
4936
+ const registry = JSON.parse(import_fs10.readFileSync(registryPath, "utf-8"));
4937
+ const match = registry.find((t) => t.name === base_class && t.kind === "class");
4938
+ if (match?.filePath) {
4939
+ const fullPath = path5.isAbsolute(match.filePath) ? match.filePath : path5.join(project_path, match.filePath);
4940
+ if (fullPath.endsWith(".cs") && import_fs10.existsSync(fullPath)) {
4941
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
4942
+ const extract = getNativeExtractSerializedFields2();
4943
+ if (extract) {
4944
+ const typeInfos = extract(fullPath);
4945
+ const info = typeInfos?.find((t) => t.name === base_class);
4946
+ if (info?.baseClass) {
4947
+ return is_valid_component_base(info.baseClass, project_path, depth + 1);
4948
+ }
4949
+ }
4950
+ }
4951
+ if (!fullPath.endsWith(".cs") || !import_fs10.existsSync(fullPath)) {
4952
+ if (match.kind === "class") {
4953
+ return is_likely_component_base_name(base_class);
4954
+ }
4955
+ }
4956
+ }
4957
+ } catch {}
4958
+ }
4959
+ const cachePaths = [
4960
+ path5.join(project_path, ".unity-agentic", "package-cache.json"),
4961
+ path5.join(project_path, ".unity-agentic", "local-package-cache.json")
4962
+ ];
4963
+ for (const cachePath of cachePaths) {
4964
+ if (!import_fs10.existsSync(cachePath))
4965
+ continue;
4966
+ try {
4967
+ const cache = JSON.parse(import_fs10.readFileSync(cachePath, "utf-8"));
4968
+ const baseClassLower = base_class.toLowerCase();
4969
+ for (const [, assetPath] of Object.entries(cache)) {
4970
+ if (!assetPath.endsWith(".cs"))
4971
+ continue;
4972
+ if (path5.basename(assetPath, ".cs").toLowerCase() !== baseClassLower)
4973
+ continue;
4974
+ const fullPath = path5.join(project_path, assetPath);
4975
+ if (!import_fs10.existsSync(fullPath))
4976
+ continue;
4977
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
4978
+ const extract = getNativeExtractSerializedFields2();
4979
+ if (!extract)
4980
+ continue;
4981
+ const typeInfos = extract(fullPath);
4982
+ const info = typeInfos?.find((t) => t.name === base_class);
4983
+ if (info?.baseClass) {
4984
+ return is_valid_component_base(info.baseClass, project_path, depth + 1);
4985
+ }
4986
+ }
4987
+ } catch {}
4988
+ }
4989
+ return false;
4990
+ }
4991
+ function is_likely_component_base_name(name) {
4992
+ const lower = name.toLowerCase();
4993
+ return lower.endsWith("behaviour") || lower.endsWith("behavior") || lower.includes("monobehaviour") || lower.includes("networkbehaviour");
4994
+ }
4554
4995
  function addComponent(options) {
4555
4996
  const { file_path, game_object_name, component_type } = options;
4556
4997
  const project_path = options.project_path || find_unity_project_root(path5.dirname(file_path)) || undefined;
@@ -4576,11 +5017,19 @@ function addComponent(options) {
4576
5017
  };
4577
5018
  }
4578
5019
  const goResult = doc.require_unique_game_object(game_object_name);
5020
+ let gameObjectIdStr;
5021
+ let variantInfo = null;
4579
5022
  if ("error" in goResult) {
4580
- return { success: false, file_path, error: goResult.error };
5023
+ const vResult = find_game_object_in_variant(doc, game_object_name, file_path, project_path);
5024
+ if (!vResult) {
5025
+ return { success: false, file_path, error: goResult.error };
5026
+ }
5027
+ gameObjectIdStr = vResult.stripped_go_id;
5028
+ variantInfo = { source_ref: vResult.source_ref, prefab_instance_id: vResult.prefab_instance_id };
5029
+ } else {
5030
+ gameObjectIdStr = goResult.file_id;
4581
5031
  }
4582
- const gameObjectIdStr = goResult.file_id;
4583
- const gameObjectId = parseInt(gameObjectIdStr, 10);
5032
+ const gameObjectId = gameObjectIdStr;
4584
5033
  const classId = get_class_id(component_type);
4585
5034
  let duplicateWarning;
4586
5035
  const goBlock = doc.find_by_file_id(gameObjectIdStr);
@@ -4595,7 +5044,7 @@ function addComponent(options) {
4595
5044
  }
4596
5045
  }
4597
5046
  const componentIdStr = doc.generate_file_id();
4598
- const componentId = parseInt(componentIdStr, 10);
5047
+ const componentId = componentIdStr;
4599
5048
  let componentYAML;
4600
5049
  let scriptGuid;
4601
5050
  let scriptPath;
@@ -4604,7 +5053,16 @@ function addComponent(options) {
4604
5053
  const componentName = UNITY_CLASS_IDS[classId] || component_type;
4605
5054
  componentYAML = createGenericComponentYAML(componentName, classId, componentId, gameObjectId);
4606
5055
  } else {
4607
- const resolved = resolve_script_with_fields(component_type, project_path);
5056
+ let resolved;
5057
+ try {
5058
+ resolved = resolve_script_with_fields(component_type, project_path);
5059
+ } catch (e) {
5060
+ return {
5061
+ success: false,
5062
+ file_path,
5063
+ error: e instanceof Error ? e.message : String(e)
5064
+ };
5065
+ }
4608
5066
  if (!resolved) {
4609
5067
  const hints = [];
4610
5068
  if (project_path) {
@@ -4631,11 +5089,11 @@ function addComponent(options) {
4631
5089
  error: `"${component_type}" is ${resolved.kind === "enum" ? "an enum" : "an interface"}, not a MonoBehaviour. Cannot add as a component.`
4632
5090
  };
4633
5091
  }
4634
- if (resolved.base_class && !["MonoBehaviour", "NetworkBehaviour", "StateMachineBehaviour"].includes(resolved.base_class)) {
5092
+ if (resolved.base_class && !COMPONENT_BASE_CLASSES.has(resolved.base_class) && !is_valid_component_base(resolved.base_class, project_path)) {
4635
5093
  return {
4636
5094
  success: false,
4637
5095
  file_path,
4638
- error: `"${component_type}" extends ${resolved.base_class}, not MonoBehaviour. Cannot add as a component.`
5096
+ 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).`
4639
5097
  };
4640
5098
  }
4641
5099
  let version;
@@ -4644,12 +5102,20 @@ function addComponent(options) {
4644
5102
  version = read_project_version(project_path);
4645
5103
  } catch {}
4646
5104
  }
4647
- componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version);
5105
+ let scriptFileId = 11500000;
5106
+ if (resolved.path?.endsWith(".dll") && resolved.class_name) {
5107
+ scriptFileId = compute_dll_script_file_id(resolved.namespace ?? "", resolved.class_name);
5108
+ }
5109
+ componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version, scriptFileId);
4648
5110
  scriptGuid = resolved.guid;
4649
5111
  scriptPath = resolved.path || undefined;
4650
5112
  extractionError = resolved.extraction_error;
4651
5113
  }
4652
- addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
5114
+ if (variantInfo) {
5115
+ appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
5116
+ } else {
5117
+ addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
5118
+ }
4653
5119
  doc.append_raw(componentYAML);
4654
5120
  const saveResult = doc.save();
4655
5121
  if (!saveResult.success) {
@@ -4726,6 +5192,14 @@ function copyComponent(options) {
4726
5192
  }
4727
5193
  // src/editor/update.ts
4728
5194
  var import_fs11 = require("fs");
5195
+ function enrich_target_ref(targetRef, blockRaw) {
5196
+ if (/guid:/.test(targetRef))
5197
+ return targetRef;
5198
+ const sourceMatch = blockRaw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
5199
+ if (!sourceMatch)
5200
+ return null;
5201
+ return targetRef.replace(/\}$/, `, guid: ${sourceMatch[1]}, type: 3}`);
5202
+ }
4729
5203
  function eulerToQuaternion(euler) {
4730
5204
  const deg2rad = Math.PI / 180;
4731
5205
  const x = euler.x * deg2rad;
@@ -4748,6 +5222,9 @@ function validate_value_type(current_value, new_value) {
4748
5222
  const current = current_value.trim();
4749
5223
  const incoming = new_value.trim();
4750
5224
  if (/^\{fileID:/.test(current)) {
5225
+ if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
5226
+ return null;
5227
+ }
4751
5228
  if (!/^\{fileID:/.test(incoming)) {
4752
5229
  return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
4753
5230
  }
@@ -4799,7 +5276,7 @@ function isAncestor(doc, childTransformId, candidateAncestorTransformId) {
4799
5276
  const block = doc.find_by_file_id(currentId);
4800
5277
  if (!block || block.class_id !== 4)
4801
5278
  break;
4802
- const fatherMatch = block.raw.match(/m_Father:\s*\{fileID:\s*(\d+)\}/);
5279
+ const fatherMatch = block.raw.match(/m_Father:\s*\{fileID:\s*(-?\d+)\}/);
4803
5280
  currentId = fatherMatch ? fatherMatch[1] : "0";
4804
5281
  }
4805
5282
  return false;
@@ -4809,7 +5286,7 @@ function resolveTransformByGameObjectId(doc, gameObjectFileId) {
4809
5286
  if (!found || found.class_id !== 1) {
4810
5287
  return { error: `GameObject with fileID ${gameObjectFileId} not found` };
4811
5288
  }
4812
- const componentMatch = found.raw.match(/m_Component:\s*\n\s*-\s*component:\s*\{fileID:\s*(\d+)\}/);
5289
+ const componentMatch = found.raw.match(/m_Component:\s*\n\s*-\s*component:\s*\{fileID:\s*(-?\d+)\}/);
4813
5290
  if (!componentMatch) {
4814
5291
  return { error: `GameObject fileID ${gameObjectFileId} has no Transform component` };
4815
5292
  }
@@ -4894,7 +5371,7 @@ function safeUnityYAMLEdit(filePath, objectName, propertyName, newValue, project
4894
5371
  newValue = "0";
4895
5372
  }
4896
5373
  let targetBlock = null;
4897
- if (/^\d+$/.test(objectName)) {
5374
+ if (/^-?\d+$/.test(objectName)) {
4898
5375
  targetBlock = doc.find_by_file_id(objectName);
4899
5376
  if (!targetBlock) {
4900
5377
  return {
@@ -4932,10 +5409,10 @@ function safeUnityYAMLEdit(filePath, objectName, propertyName, newValue, project
4932
5409
  const propertyPattern = new RegExp(`(^\\s*m_${normalizedProperty}:\\s*)([^\\n]*)`, "m");
4933
5410
  let updatedRaw = targetBlock.raw;
4934
5411
  if (propertyPattern.test(updatedRaw)) {
4935
- updatedRaw = updatedRaw.replace(propertyPattern, `$1${newValue}`);
5412
+ updatedRaw = updatedRaw.replace(propertyPattern, (_m, prefix) => prefix + newValue);
4936
5413
  } else {
4937
- updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, `
4938
- m_${normalizedProperty}: ${newValue}$1$2`);
5414
+ updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, (_m, g1, g2) => `
5415
+ m_${normalizedProperty}: ${newValue}${g1}${g2}`);
4939
5416
  }
4940
5417
  targetBlock.replace_raw(updatedRaw);
4941
5418
  const saveResult = doc.save();
@@ -5010,7 +5487,7 @@ function editComponentByFileId(options) {
5010
5487
  };
5011
5488
  }
5012
5489
  const classId = targetBlock.class_id;
5013
- const sameFileRefMatch = new_value.match(/^\{fileID:\s*(\d+)\}$/);
5490
+ const sameFileRefMatch = new_value.match(/^\{fileID:\s*(-?\d+)\}$/);
5014
5491
  if (sameFileRefMatch) {
5015
5492
  const refId = sameFileRefMatch[1];
5016
5493
  if (refId !== "0" && !doc.find_by_file_id(refId)) {
@@ -5021,7 +5498,9 @@ function editComponentByFileId(options) {
5021
5498
  };
5022
5499
  }
5023
5500
  }
5024
- const currentValue = targetBlock.get_property(exactProperty) ?? (exactProperty !== prefixedProperty ? targetBlock.get_property(prefixedProperty) : null);
5501
+ const exactValue = targetBlock.get_property(exactProperty);
5502
+ const resolvedProperty = exactValue !== null ? exactProperty : exactProperty !== prefixedProperty && targetBlock.get_property(prefixedProperty) !== null ? prefixedProperty : null;
5503
+ const currentValue = exactValue ?? (resolvedProperty === prefixedProperty ? targetBlock.get_property(prefixedProperty) : null);
5025
5504
  if (currentValue !== null) {
5026
5505
  const typeError = validate_value_type(currentValue, new_value);
5027
5506
  if (typeError) {
@@ -5032,9 +5511,14 @@ function editComponentByFileId(options) {
5032
5511
  };
5033
5512
  }
5034
5513
  }
5035
- let modified = targetBlock.set_property(exactProperty, new_value, "{fileID: 0}");
5036
- if (!modified && exactProperty !== prefixedProperty) {
5037
- modified = targetBlock.set_property(prefixedProperty, new_value, "{fileID: 0}");
5514
+ let modified;
5515
+ if (resolvedProperty) {
5516
+ modified = targetBlock.set_property(resolvedProperty, new_value, "{fileID: 0}");
5517
+ } else {
5518
+ modified = targetBlock.set_property(exactProperty, new_value, "{fileID: 0}");
5519
+ if (!modified && exactProperty !== prefixedProperty) {
5520
+ modified = targetBlock.set_property(prefixedProperty, new_value, "{fileID: 0}");
5521
+ }
5038
5522
  }
5039
5523
  if (!modified) {
5040
5524
  if (currentValue !== null) {
@@ -5071,8 +5555,10 @@ function editComponentByFileId(options) {
5071
5555
  };
5072
5556
  }
5073
5557
  function editPrefabOverride(options) {
5074
- const { file_path, prefab_instance, property_path, new_value, object_reference, target } = options;
5075
- const objRef = object_reference ?? "{fileID: 0}";
5558
+ const { file_path, prefab_instance, new_value, object_reference, target, managed_reference } = options;
5559
+ const property_path = /[\[\]]/.test(options.property_path) && !options.property_path.startsWith("'") ? `'${options.property_path}'` : options.property_path;
5560
+ const effectiveValue = managed_reference ?? new_value;
5561
+ const objRef = managed_reference ? "{fileID: 0}" : object_reference ?? "{fileID: 0}";
5076
5562
  if (!import_fs11.existsSync(file_path)) {
5077
5563
  return { success: false, file_path, error: `File not found: ${file_path}` };
5078
5564
  }
@@ -5086,11 +5572,16 @@ function editPrefabOverride(options) {
5086
5572
  if (!targetBlock) {
5087
5573
  return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
5088
5574
  }
5089
- const escapedPath = property_path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5090
- const entryPattern = new RegExp(`(- target:\\s*\\{[^}]+\\}\\s*\\n\\s*propertyPath:\\s*)${escapedPath}(\\s*\\n\\s*value:\\s*)(.*)(\\s*\\n\\s*objectReference:\\s*)(.*)`, "m");
5575
+ const unquotedPath = property_path.replace(/^'|'$/g, "");
5576
+ const quotedPath = `'${unquotedPath}'`;
5577
+ const escapedUnquoted = unquotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5578
+ const escapedQuoted = quotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5579
+ const pathAlternation = escapedUnquoted === escapedQuoted ? escapedUnquoted : `(?:${escapedQuoted}|${escapedUnquoted})`;
5580
+ const entryPattern = new RegExp(`(- target:\\s*\\{[^}]+\\}\\s*\\n\\s*propertyPath:\\s*)${pathAlternation}(\\s*\\n\\s*value:\\s*)(.*)(\\s*\\n\\s*objectReference:\\s*)(.*)`, "m");
5091
5581
  const entryMatch = targetBlock.raw.match(entryPattern);
5092
5582
  if (entryMatch) {
5093
- const updatedText2 = targetBlock.raw.replace(entryPattern, `$1${property_path}$2${new_value}$4${objRef}`);
5583
+ const quotedValue = yaml_quote_if_needed(effectiveValue);
5584
+ const updatedText2 = targetBlock.raw.replace(entryPattern, (_m, g1, g2, _g3, g4) => g1 + property_path + g2 + quotedValue + g4 + objRef);
5094
5585
  targetBlock.replace_raw(updatedText2);
5095
5586
  const saveResult2 = doc.save();
5096
5587
  if (!saveResult2.success) {
@@ -5127,9 +5618,13 @@ function editPrefabOverride(options) {
5127
5618
  error: `Invalid target reference "${targetRef}". Expected format: {fileID: N, guid: ..., type: T}`
5128
5619
  };
5129
5620
  }
5621
+ const enriched = enrich_target_ref(targetRef, targetBlock.raw);
5622
+ if (enriched)
5623
+ targetRef = enriched;
5624
+ const quotedNewValue = yaml_quote_if_needed(effectiveValue);
5130
5625
  const newEntry = ` - target: ${targetRef}
5131
5626
  propertyPath: ${property_path}
5132
- value: ${new_value}
5627
+ value: ${quotedNewValue}
5133
5628
  objectReference: ${objRef}`;
5134
5629
  const removedPattern = /(\n\s*m_RemovedComponents:)/m;
5135
5630
  const removedMatch = targetBlock.raw.match(removedPattern);
@@ -5272,10 +5767,10 @@ function batchEditProperties(filePath, edits) {
5272
5767
  const propertyPattern = new RegExp(`(^\\s*m_${normalizedProperty}:\\s*)([^\\n]*)`, "m");
5273
5768
  let updatedRaw = targetBlock.raw;
5274
5769
  if (propertyPattern.test(updatedRaw)) {
5275
- updatedRaw = updatedRaw.replace(propertyPattern, `$1${edit.new_value}`);
5770
+ updatedRaw = updatedRaw.replace(propertyPattern, (_m, prefix) => prefix + edit.new_value);
5276
5771
  } else {
5277
- updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, `
5278
- m_${normalizedProperty}: ${edit.new_value}$1$2`);
5772
+ updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, (_m, g1, g2) => `
5773
+ m_${normalizedProperty}: ${edit.new_value}${g1}${g2}`);
5279
5774
  }
5280
5775
  targetBlock.replace_raw(updatedRaw);
5281
5776
  }
@@ -5305,7 +5800,7 @@ function reparentGameObject(options) {
5305
5800
  return { success: false, file_path, error: `Failed to read file: ${err instanceof Error ? err.message : String(err)}` };
5306
5801
  }
5307
5802
  let childTransformId;
5308
- if (by_id || /^\d+$/.test(object_name)) {
5803
+ if (by_id || /^-?\d+$/.test(object_name)) {
5309
5804
  if (isNaN(parseInt(object_name, 10))) {
5310
5805
  return { success: false, file_path, error: `Invalid fileID: "${object_name}" \u2014 expected a numeric value` };
5311
5806
  }
@@ -5325,11 +5820,11 @@ function reparentGameObject(options) {
5325
5820
  if (!childBlock) {
5326
5821
  return { success: false, file_path, error: `Transform ${childTransformId} not found` };
5327
5822
  }
5328
- const fatherMatch = childBlock.raw.match(/m_Father:\s*\{fileID:\s*(\d+)\}/);
5823
+ const fatherMatch = childBlock.raw.match(/m_Father:\s*\{fileID:\s*(-?\d+)\}/);
5329
5824
  const oldParentTransformId = fatherMatch ? fatherMatch[1] : "0";
5330
5825
  let newParentTransformId = "0";
5331
5826
  if (new_parent.toLowerCase() !== "root") {
5332
- if (by_id || /^\d+$/.test(new_parent)) {
5827
+ if (by_id || /^-?\d+$/.test(new_parent)) {
5333
5828
  if (isNaN(parseInt(new_parent, 10))) {
5334
5829
  return { success: false, file_path, error: `Invalid parent fileID: "${new_parent}" \u2014 expected a numeric value, or "root"` };
5335
5830
  }
@@ -5380,9 +5875,9 @@ function reparentGameObject(options) {
5380
5875
  return {
5381
5876
  success: true,
5382
5877
  file_path,
5383
- child_transform_id: parseInt(childTransformId, 10),
5384
- old_parent_transform_id: parseInt(oldParentTransformId, 10),
5385
- new_parent_transform_id: parseInt(newParentTransformId, 10)
5878
+ child_transform_id: childTransformId,
5879
+ old_parent_transform_id: oldParentTransformId,
5880
+ new_parent_transform_id: newParentTransformId
5386
5881
  };
5387
5882
  }
5388
5883
  function findPrefabInstanceBlock(doc, identifier) {
@@ -5751,9 +6246,9 @@ function removeRemovedGameObject(options) {
5751
6246
  if (!doc.validate()) {
5752
6247
  return { success: false, file_path, error: "Validation failed after removing GameObject" };
5753
6248
  }
5754
- const saveResult = doc.save();
5755
- if (!saveResult.success) {
5756
- return { success: false, file_path, error: saveResult.error };
6249
+ const restoreGoSaveResult = doc.save();
6250
+ if (!restoreGoSaveResult.success) {
6251
+ return { success: false, file_path, error: restoreGoSaveResult.error };
5757
6252
  }
5758
6253
  return {
5759
6254
  success: true,
@@ -5788,12 +6283,13 @@ function removeComponent(options) {
5788
6283
  if (found.class_id === 4) {
5789
6284
  return { success: false, file_path, error: "Cannot remove a Transform with remove-component. Use delete to remove the entire GameObject." };
5790
6285
  }
5791
- const goMatch = found.raw.match(/m_GameObject:\s*\{fileID:\s*(\d+)\}/);
6286
+ const goMatch = found.raw.match(/m_GameObject:\s*\{fileID:\s*(-?\d+)\}/);
5792
6287
  if (goMatch) {
5793
6288
  const parentGoId = goMatch[1];
5794
6289
  const goBlock = doc.find_by_file_id(parentGoId);
5795
6290
  if (goBlock) {
5796
- const compLinePattern = new RegExp(`\\s*- component: \\{fileID: ${file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\}\\n?`);
6291
+ const escaped = file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6292
+ const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
5797
6293
  const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
5798
6294
  goBlock.replace_raw(modifiedRaw);
5799
6295
  }
@@ -5835,7 +6331,7 @@ function deleteGameObject(options) {
5835
6331
  const goBlock = goResult;
5836
6332
  const goId = goBlock.file_id;
5837
6333
  const componentIds = new Set;
5838
- const compMatches = goBlock.raw.matchAll(/component:\s*\{fileID:\s*(\d+)\}/g);
6334
+ const compMatches = goBlock.raw.matchAll(/component:\s*\{fileID:\s*(-?\d+)\}/g);
5839
6335
  for (const cm of compMatches) {
5840
6336
  componentIds.add(cm[1]);
5841
6337
  }
@@ -5845,7 +6341,7 @@ function deleteGameObject(options) {
5845
6341
  const block = doc.find_by_file_id(compId);
5846
6342
  if (block && block.class_id === 4) {
5847
6343
  transformId = compId;
5848
- const fatherMatch = block.raw.match(/m_Father:\s*\{fileID:\s*(\d+)\}/);
6344
+ const fatherMatch = block.raw.match(/m_Father:\s*\{fileID:\s*(-?\d+)\}/);
5849
6345
  if (fatherMatch) {
5850
6346
  fatherId = fatherMatch[1];
5851
6347
  }