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/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.1",
208
+ version: "0.3.3",
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";
@@ -2244,178 +2402,61 @@ function resolve_enum_fields(fields, project_path) {
2244
2402
  }
2245
2403
  } catch {}
2246
2404
  }
2247
-
2248
- // src/editor/yaml-fields.ts
2249
- var PRIMITIVE_DEFAULTS = {
2250
- int: "0",
2251
- float: "0",
2252
- double: "0",
2253
- bool: "0",
2254
- string: "",
2255
- byte: "0",
2256
- sbyte: "0",
2257
- short: "0",
2258
- ushort: "0",
2259
- uint: "0",
2260
- long: "0",
2261
- ulong: "0",
2262
- char: "0",
2263
- Int32: "0",
2264
- Single: "0",
2265
- Double: "0",
2266
- Boolean: "0",
2267
- String: "",
2268
- Byte: "0",
2269
- SByte: "0",
2270
- Int16: "0",
2271
- UInt16: "0",
2272
- UInt32: "0",
2273
- Int64: "0",
2274
- UInt64: "0",
2275
- Char: "0"
2276
- };
2277
- var STRUCT_DEFAULTS = {
2278
- Vector2: "{x: 0, y: 0}",
2279
- Vector3: "{x: 0, y: 0, z: 0}",
2280
- Vector4: "{x: 0, y: 0, z: 0, w: 0}",
2281
- Vector2Int: "{x: 0, y: 0}",
2282
- Vector3Int: "{x: 0, y: 0, z: 0}",
2283
- Quaternion: "{x: 0, y: 0, z: 0, w: 1}",
2284
- Color: "{r: 0, g: 0, b: 0, a: 0}",
2285
- Color32: "{r: 0, g: 0, b: 0, a: 0}",
2286
- Rect: `serializedVersion: 2
2287
- x: 0
2288
- y: 0
2289
- width: 0
2290
- height: 0`,
2291
- RectInt: "{x: 0, y: 0, width: 0, height: 0}",
2292
- RectOffset: "{m_Left: 0, m_Right: 0, m_Top: 0, m_Bottom: 0}",
2293
- 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}",
2294
- LayerMask: `serializedVersion: 2
2295
- m_Bits: 0`
2296
- };
2297
- var VERSION_GATED_STRUCT_DEFAULTS = {
2298
- Hash128: { value: `serializedVersion: 2
2299
- Hash: 00000000000000000000000000000000`, min_major: 2021, min_minor: 1 },
2300
- RenderingLayerMask: { value: `serializedVersion: 2
2301
- m_Bits: 0`, min_major: 6000, min_minor: 0 }
2302
- };
2303
- var BLOCK_STRUCT_DEFAULTS = {
2304
- Bounds: `m_Center: {x: 0, y: 0, z: 0}
2305
- m_Extent: {x: 0, y: 0, z: 0}`,
2306
- BoundsInt: `m_Position: {x: 0, y: 0, z: 0}
2307
- m_Size: {x: 0, y: 0, z: 0}`
2308
- };
2309
- var OBJECT_REF_TYPES = new Set([
2310
- "GameObject",
2311
- "Transform",
2312
- "RectTransform",
2313
- "Material",
2314
- "Texture",
2315
- "Texture2D",
2316
- "Texture3D",
2317
- "RenderTexture",
2318
- "Sprite",
2319
- "AudioClip",
2320
- "VideoClip",
2321
- "Mesh",
2322
- "Shader",
2323
- "ComputeShader",
2324
- "AnimationClip",
2325
- "AnimatorController",
2326
- "RuntimeAnimatorController",
2327
- "PhysicMaterial",
2328
- "PhysicsMaterial",
2329
- "PhysicsMaterial2D",
2330
- "Font",
2331
- "TMP_FontAsset",
2332
- "Object",
2333
- "Component",
2334
- "Behaviour",
2335
- "MonoBehaviour",
2336
- "ScriptableObject",
2337
- "Rigidbody",
2338
- "Rigidbody2D",
2339
- "Collider",
2340
- "Collider2D",
2341
- "Camera",
2342
- "Light",
2343
- "Canvas",
2344
- "CanvasGroup",
2345
- "EventSystem",
2346
- "AudioSource",
2347
- "ParticleSystem",
2348
- "TextAsset",
2349
- "TerrainData"
2350
- ]);
2351
- function version_at_least(version, min_major, min_minor) {
2352
- if (!version)
2353
- return false;
2354
- if (version.major > min_major)
2355
- return true;
2356
- if (version.major === min_major)
2357
- return version.minor >= min_minor;
2358
- return false;
2359
- }
2360
- function yaml_default_for_type(csharp_type, version) {
2361
- if (csharp_type.endsWith("?")) {
2362
- return null;
2363
- }
2364
- if (csharp_type in PRIMITIVE_DEFAULTS) {
2365
- return PRIMITIVE_DEFAULTS[csharp_type];
2366
- }
2367
- if (csharp_type in STRUCT_DEFAULTS) {
2368
- return STRUCT_DEFAULTS[csharp_type];
2369
- }
2370
- if (csharp_type in VERSION_GATED_STRUCT_DEFAULTS) {
2371
- const gated = VERSION_GATED_STRUCT_DEFAULTS[csharp_type];
2372
- if (version_at_least(version, gated.min_major, gated.min_minor)) {
2373
- return gated.value;
2405
+ function build_type_lookup(project_path) {
2406
+ let extractFn = null;
2407
+ try {
2408
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
2409
+ extractFn = getNativeExtractSerializedFields2();
2410
+ } catch {}
2411
+ if (!extractFn)
2412
+ return () => null;
2413
+ const assetsDir = path3.join(project_path, "Assets");
2414
+ const csIndex = new Map;
2415
+ try {
2416
+ const { readdirSync: readdirSync4 } = require("fs");
2417
+ const entries = readdirSync4(assetsDir, { recursive: true, withFileTypes: false });
2418
+ for (const entry of entries) {
2419
+ if (typeof entry === "string" && entry.endsWith(".cs")) {
2420
+ const basename4 = entry.substring(entry.lastIndexOf("/") + 1).replace(/\.cs$/, "");
2421
+ if (!csIndex.has(basename4)) {
2422
+ csIndex.set(basename4, path3.join(assetsDir, entry));
2423
+ }
2424
+ }
2374
2425
  }
2375
- return null;
2376
- }
2377
- if (csharp_type in BLOCK_STRUCT_DEFAULTS) {
2378
- return BLOCK_STRUCT_DEFAULTS[csharp_type];
2379
- }
2380
- if (csharp_type.endsWith("[]")) {
2381
- return "[]";
2382
- }
2383
- if (csharp_type.startsWith("List<") && csharp_type.endsWith(">")) {
2384
- return "[]";
2385
- }
2386
- if (OBJECT_REF_TYPES.has(csharp_type)) {
2387
- return "{fileID: 0}";
2388
- }
2389
- return "{fileID: 0}";
2390
- }
2391
- function generate_field_yaml(fields, version, indent = " ") {
2392
- const lines = [];
2393
- for (const field of fields) {
2394
- const default_value = yaml_default_for_type(field.typeName, version);
2395
- if (default_value === null) {
2396
- continue;
2426
+ } catch {}
2427
+ if (csIndex.size === 0)
2428
+ return () => null;
2429
+ const cache = new Map;
2430
+ return (typeName) => {
2431
+ if (cache.has(typeName))
2432
+ return cache.get(typeName) ?? null;
2433
+ const shortName = typeName.includes(".") ? typeName.substring(typeName.lastIndexOf(".") + 1) : typeName;
2434
+ const csPath = csIndex.get(shortName);
2435
+ if (!csPath || !import_fs8.existsSync(csPath)) {
2436
+ cache.set(typeName, null);
2437
+ return null;
2397
2438
  }
2398
- if (default_value.includes(`
2399
- `)) {
2400
- lines.push(`${indent}${field.name}:`);
2401
- for (const sub_line of default_value.split(`
2402
- `)) {
2403
- lines.push(`${indent}${sub_line}`);
2439
+ try {
2440
+ const typeInfos = extractFn(csPath);
2441
+ if (!typeInfos || typeInfos.length === 0) {
2442
+ cache.set(typeName, null);
2443
+ return null;
2404
2444
  }
2405
- } else {
2406
- lines.push(`${indent}${field.name}: ${default_value}`);
2445
+ const match = typeInfos.find((t) => t.name === shortName);
2446
+ if (match && match.fields && match.fields.length > 0) {
2447
+ resolve_enum_fields(match.fields, project_path);
2448
+ cache.set(typeName, match.fields);
2449
+ return match.fields;
2450
+ }
2451
+ cache.set(typeName, null);
2452
+ return null;
2453
+ } catch {
2454
+ cache.set(typeName, null);
2455
+ return null;
2407
2456
  }
2408
- }
2409
- return lines.length > 0 ? `
2410
- ` + lines.join(`
2411
- `) + `
2412
- ` : `
2413
- `;
2457
+ };
2414
2458
  }
2415
2459
 
2416
- // src/editor/unity-document.ts
2417
- var import_fs9 = require("fs");
2418
-
2419
2460
  // src/editor/unity-block.ts
2420
2461
  var HEADER_PATTERN = /^--- !u!(\d+) &(-?\d+)(\s+stripped)?/;
2421
2462
  function parse_header(raw) {
@@ -2434,6 +2475,17 @@ function parse_header(raw) {
2434
2475
  function escape_regex(str) {
2435
2476
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2436
2477
  }
2478
+ function yaml_quote_if_needed(value) {
2479
+ if (value === "")
2480
+ return value;
2481
+ if (value.startsWith("{") && value.endsWith("}") || value.startsWith("[") && value.endsWith("]")) {
2482
+ return value;
2483
+ }
2484
+ if (value.includes(": ") || value.includes(" #") || /^[#\{\[*&!|>'"]/.test(value) || value !== value.trim()) {
2485
+ return `'${value.replace(/'/g, "''")}'`;
2486
+ }
2487
+ return value;
2488
+ }
2437
2489
 
2438
2490
  class UnityBlock {
2439
2491
  _raw;
@@ -2469,6 +2521,7 @@ class UnityBlock {
2469
2521
  return this._dirty;
2470
2522
  }
2471
2523
  get_property(path4) {
2524
+ path4 = normalize_property_path(path4);
2472
2525
  if (path4.includes("Array.data[")) {
2473
2526
  return this._get_array_element(path4);
2474
2527
  }
@@ -2495,13 +2548,43 @@ class UnityBlock {
2495
2548
  return match ? match[1].trim() : null;
2496
2549
  }
2497
2550
  set_property(path4, value, object_reference) {
2551
+ path4 = normalize_property_path(path4);
2498
2552
  const old_raw = this._raw;
2499
2553
  const effective_ref = object_reference && object_reference !== "{fileID: 0}" ? object_reference : undefined;
2500
2554
  if (!path4.includes(".") && !path4.includes("Array")) {
2501
- const prop_pattern = new RegExp(`(^\\s*${escape_regex(path4)}:\\s*)(.*)$`, "m");
2502
- if (prop_pattern.test(this._raw)) {
2503
- const replacement_value = effective_ref ?? value;
2504
- this._raw = this._raw.replace(prop_pattern, `$1${replacement_value}`);
2555
+ const prop_pattern = new RegExp(`(^\\s*${escape_regex(path4)}:[ \\t]*)(.*)$`, "m");
2556
+ const match = this._raw.match(prop_pattern);
2557
+ if (match && match.index !== undefined) {
2558
+ const replacement_value = effective_ref ?? yaml_quote_if_needed(value);
2559
+ const current_inline = match[2];
2560
+ if (current_inline.trim() === "") {
2561
+ const key_indent = (match[0].match(/^[ \t]*/)?.[0] || "").length;
2562
+ const after = match.index + match[0].length;
2563
+ let end = after;
2564
+ const rest = this._raw.slice(after);
2565
+ const rest_lines = rest.split(`
2566
+ `);
2567
+ for (let i = 1;i < rest_lines.length; i++) {
2568
+ const line = rest_lines[i];
2569
+ if (line.trim() === "") {
2570
+ end += 1 + line.length;
2571
+ continue;
2572
+ }
2573
+ const line_indent = (line.match(/^[ \t]*/)?.[0] || "").length;
2574
+ if (line_indent > key_indent) {
2575
+ end += 1 + line.length;
2576
+ } else {
2577
+ break;
2578
+ }
2579
+ }
2580
+ const prefix = match[1].trimEnd() + " ";
2581
+ this._raw = this._raw.slice(0, match.index) + prefix + replacement_value + this._raw.slice(end);
2582
+ } else {
2583
+ this._raw = this._raw.replace(prop_pattern, (_match, prefix) => prefix + replacement_value);
2584
+ }
2585
+ } else {
2586
+ const insert_value = effective_ref ?? yaml_quote_if_needed(value);
2587
+ this._insert_property(path4, insert_value);
2505
2588
  }
2506
2589
  return this._check_dirty(old_raw);
2507
2590
  }
@@ -2516,8 +2599,8 @@ class UnityBlock {
2516
2599
  const fields = inline_match[2];
2517
2600
  const field_pattern = new RegExp(`(${escape_regex(sub_field)}:\\s*)([^,}]+)`);
2518
2601
  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`);
2602
+ const updated_fields = fields.replace(field_pattern, (_m, prefix) => prefix + value);
2603
+ this._raw = this._raw.replace(inline_pattern, (_m, g1, _g2, g3) => g1 + updated_fields + g3);
2521
2604
  inline_applied = true;
2522
2605
  }
2523
2606
  }
@@ -2526,6 +2609,8 @@ class UnityBlock {
2526
2609
  const block_result = this._resolve_block_style_path(parts, effective_value);
2527
2610
  if (block_result !== null) {
2528
2611
  this._raw = block_result;
2612
+ } else if (parts.length === 2) {
2613
+ this._insert_struct_property(parent_prop, sub_field, effective_value);
2529
2614
  }
2530
2615
  }
2531
2616
  return this._check_dirty(old_raw);
@@ -2543,7 +2628,7 @@ class UnityBlock {
2543
2628
  `).filter((l) => l.trim().startsWith("-"));
2544
2629
  if (index < lines.length) {
2545
2630
  const old_line = lines[index];
2546
- const new_line = old_line.replace(/-\s*.*/, `- ${ref_value}`);
2631
+ const new_line = old_line.replace(/-\s*.*/, () => `- ${ref_value}`);
2547
2632
  this._raw = this._raw.replace(old_line, new_line);
2548
2633
  }
2549
2634
  }
@@ -2552,6 +2637,49 @@ class UnityBlock {
2552
2637
  }
2553
2638
  return false;
2554
2639
  }
2640
+ _insert_property(path4, value) {
2641
+ const alt = path4.startsWith("m_") ? path4.slice(2) : "m_" + path4;
2642
+ const alt_pattern = new RegExp(`^\\s*${escape_regex(alt)}:`, "m");
2643
+ if (alt_pattern.test(this._raw)) {
2644
+ return;
2645
+ }
2646
+ const indented_line = ` ${path4}: ${value}`;
2647
+ const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
2648
+ const eci_match = this._raw.match(eci_pattern);
2649
+ if (eci_match && eci_match.index !== undefined) {
2650
+ const insert_after = eci_match.index + eci_match[0].length;
2651
+ this._raw = this._raw.slice(0, insert_after) + `
2652
+ ` + indented_line + this._raw.slice(insert_after);
2653
+ return;
2654
+ }
2655
+ this._raw = this._raw.trimEnd() + `
2656
+ ` + indented_line + `
2657
+ `;
2658
+ }
2659
+ _insert_struct_property(parent, child, value) {
2660
+ const parent_pattern = new RegExp(`^[ ]*${escape_regex(parent)}:`, "m");
2661
+ if (parent_pattern.test(this._raw)) {
2662
+ return;
2663
+ }
2664
+ const alt = parent.startsWith("m_") ? parent.slice(2) : "m_" + parent;
2665
+ const alt_pattern = new RegExp(`^[ \\t]*${escape_regex(alt)}:`, "m");
2666
+ if (alt_pattern.test(this._raw)) {
2667
+ return;
2668
+ }
2669
+ const struct_block = ` ${parent}:
2670
+ ${child}: ${value}`;
2671
+ const eci_pattern = /^([ \t]*m_EditorClassIdentifier:[ \t]*[^\n]*)/m;
2672
+ const eci_match = this._raw.match(eci_pattern);
2673
+ if (eci_match && eci_match.index !== undefined) {
2674
+ const insert_after = eci_match.index + eci_match[0].length;
2675
+ this._raw = this._raw.slice(0, insert_after) + `
2676
+ ` + struct_block + this._raw.slice(insert_after);
2677
+ return;
2678
+ }
2679
+ this._raw = this._raw.trimEnd() + `
2680
+ ` + struct_block + `
2681
+ `;
2682
+ }
2555
2683
  has_property(path4) {
2556
2684
  return this.get_property(path4) !== null;
2557
2685
  }
@@ -2607,7 +2735,7 @@ class UnityBlock {
2607
2735
  if (empty_match) {
2608
2736
  const base_indent = empty_match[2];
2609
2737
  const element_indent2 = base_indent + " ";
2610
- this._raw = this._raw.replace(empty_pattern, `$1
2738
+ this._raw = this._raw.replace(empty_pattern, (_m, g1) => `${g1}
2611
2739
  ${element_indent2}- ${value}`);
2612
2740
  return this._check_dirty(old_raw);
2613
2741
  }
@@ -2840,81 +2968,359 @@ ${element_indent2}- ${value}`);
2840
2968
  }
2841
2969
  return null;
2842
2970
  }
2843
- _resolve_block_style_path(segments, value) {
2844
- const lines = this._raw.split(`
2971
+ _resolve_block_style_path(segments, value) {
2972
+ const lines = this._raw.split(`
2973
+ `);
2974
+ let search_start = 0;
2975
+ let search_end = lines.length;
2976
+ for (let seg = 0;seg < segments.length - 1; seg++) {
2977
+ const segment = segments[seg];
2978
+ let parent_idx = -1;
2979
+ let parent_indent = -1;
2980
+ for (let i = search_start;i < search_end; i++) {
2981
+ const match = lines[i].match(new RegExp(`^(\\s*)${escape_regex(segment)}:\\s*(.*)$`));
2982
+ if (match) {
2983
+ const trailing = match[2].trim();
2984
+ if (trailing === "") {
2985
+ parent_idx = i;
2986
+ parent_indent = match[1].length;
2987
+ break;
2988
+ }
2989
+ for (let j = i + 1;j < search_end; j++) {
2990
+ if (lines[j].trim() === "")
2991
+ continue;
2992
+ const nextLeading = lines[j].match(/^(\s*)/);
2993
+ if (nextLeading && nextLeading[1].length > match[1].length) {
2994
+ parent_idx = i;
2995
+ parent_indent = match[1].length;
2996
+ }
2997
+ break;
2998
+ }
2999
+ if (parent_idx !== -1)
3000
+ break;
3001
+ }
3002
+ }
3003
+ if (parent_idx === -1)
3004
+ return null;
3005
+ let child_indent = -1;
3006
+ for (let i = parent_idx + 1;i < search_end; i++) {
3007
+ if (lines[i].trim() === "")
3008
+ continue;
3009
+ const leading_spaces = lines[i].match(/^(\s*)/);
3010
+ if (leading_spaces) {
3011
+ const indent = leading_spaces[1].length;
3012
+ if (indent > parent_indent) {
3013
+ child_indent = indent;
3014
+ break;
3015
+ }
3016
+ }
3017
+ break;
3018
+ }
3019
+ if (child_indent === -1)
3020
+ return null;
3021
+ search_start = parent_idx + 1;
3022
+ for (let i = parent_idx + 1;i < search_end; i++) {
3023
+ if (lines[i].trim() === "")
3024
+ continue;
3025
+ const leading_spaces = lines[i].match(/^(\s*)/);
3026
+ if (leading_spaces && leading_spaces[1].length < child_indent) {
3027
+ search_end = i;
3028
+ break;
3029
+ }
3030
+ }
3031
+ }
3032
+ const final_prop = segments[segments.length - 1];
3033
+ for (let i = search_start;i < search_end; i++) {
3034
+ const match = lines[i].match(new RegExp(`^(\\s*${escape_regex(final_prop)}:\\s*).+$`));
3035
+ if (match) {
3036
+ lines[i] = match[1] + value;
3037
+ return lines.join(`
3038
+ `);
3039
+ }
3040
+ }
3041
+ return null;
3042
+ }
3043
+ }
3044
+
3045
+ // src/editor/yaml-fields.ts
3046
+ var PRIMITIVE_DEFAULTS = {
3047
+ int: "0",
3048
+ float: "0",
3049
+ double: "0",
3050
+ bool: "0",
3051
+ string: "",
3052
+ byte: "0",
3053
+ sbyte: "0",
3054
+ short: "0",
3055
+ ushort: "0",
3056
+ uint: "0",
3057
+ long: "0",
3058
+ ulong: "0",
3059
+ char: "0",
3060
+ Int32: "0",
3061
+ Single: "0",
3062
+ Double: "0",
3063
+ Boolean: "0",
3064
+ String: "",
3065
+ Byte: "0",
3066
+ SByte: "0",
3067
+ Int16: "0",
3068
+ UInt16: "0",
3069
+ UInt32: "0",
3070
+ Int64: "0",
3071
+ UInt64: "0",
3072
+ Char: "0"
3073
+ };
3074
+ var STRUCT_DEFAULTS = {
3075
+ Vector2: "{x: 0, y: 0}",
3076
+ Vector3: "{x: 0, y: 0, z: 0}",
3077
+ Vector4: "{x: 0, y: 0, z: 0, w: 0}",
3078
+ Vector2Int: "{x: 0, y: 0}",
3079
+ Vector3Int: "{x: 0, y: 0, z: 0}",
3080
+ Quaternion: "{x: 0, y: 0, z: 0, w: 1}",
3081
+ Color: "{r: 0, g: 0, b: 0, a: 0}",
3082
+ Color32: "{r: 0, g: 0, b: 0, a: 0}",
3083
+ Rect: `serializedVersion: 2
3084
+ x: 0
3085
+ y: 0
3086
+ width: 0
3087
+ height: 0`,
3088
+ RectInt: "{x: 0, y: 0, width: 0, height: 0}",
3089
+ RectOffset: "{m_Left: 0, m_Right: 0, m_Top: 0, m_Bottom: 0}",
3090
+ 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}",
3091
+ LayerMask: `serializedVersion: 2
3092
+ m_Bits: 0`
3093
+ };
3094
+ var VERSION_GATED_STRUCT_DEFAULTS = {
3095
+ Hash128: { value: `serializedVersion: 2
3096
+ Hash: 00000000000000000000000000000000`, min_major: 2021, min_minor: 1 },
3097
+ RenderingLayerMask: { value: `serializedVersion: 2
3098
+ m_Bits: 0`, min_major: 6000, min_minor: 0 }
3099
+ };
3100
+ var BLOCK_STRUCT_DEFAULTS = {
3101
+ Bounds: `m_Center: {x: 0, y: 0, z: 0}
3102
+ m_Extent: {x: 0, y: 0, z: 0}`,
3103
+ BoundsInt: `m_Position: {x: 0, y: 0, z: 0}
3104
+ m_Size: {x: 0, y: 0, z: 0}`
3105
+ };
3106
+ var OBJECT_REF_TYPES = new Set([
3107
+ "GameObject",
3108
+ "Transform",
3109
+ "RectTransform",
3110
+ "Material",
3111
+ "Texture",
3112
+ "Texture2D",
3113
+ "Texture3D",
3114
+ "RenderTexture",
3115
+ "Sprite",
3116
+ "AudioClip",
3117
+ "VideoClip",
3118
+ "Mesh",
3119
+ "Shader",
3120
+ "ComputeShader",
3121
+ "AnimationClip",
3122
+ "AnimatorController",
3123
+ "RuntimeAnimatorController",
3124
+ "PhysicMaterial",
3125
+ "PhysicsMaterial",
3126
+ "PhysicsMaterial2D",
3127
+ "Font",
3128
+ "TMP_FontAsset",
3129
+ "Object",
3130
+ "Component",
3131
+ "Behaviour",
3132
+ "MonoBehaviour",
3133
+ "ScriptableObject",
3134
+ "Rigidbody",
3135
+ "Rigidbody2D",
3136
+ "Collider",
3137
+ "Collider2D",
3138
+ "Camera",
3139
+ "Light",
3140
+ "Canvas",
3141
+ "CanvasGroup",
3142
+ "EventSystem",
3143
+ "AudioSource",
3144
+ "ParticleSystem",
3145
+ "TextAsset",
3146
+ "TerrainData"
3147
+ ]);
3148
+ function version_at_least(version, min_major, min_minor) {
3149
+ if (!version)
3150
+ return false;
3151
+ if (version.major > min_major)
3152
+ return true;
3153
+ if (version.major === min_major)
3154
+ return version.minor >= min_minor;
3155
+ return false;
3156
+ }
3157
+ function yaml_default_for_type(csharp_type, version) {
3158
+ if (csharp_type.endsWith("?")) {
3159
+ return null;
3160
+ }
3161
+ if (csharp_type in PRIMITIVE_DEFAULTS) {
3162
+ return PRIMITIVE_DEFAULTS[csharp_type];
3163
+ }
3164
+ if (csharp_type in STRUCT_DEFAULTS) {
3165
+ return STRUCT_DEFAULTS[csharp_type];
3166
+ }
3167
+ if (csharp_type in VERSION_GATED_STRUCT_DEFAULTS) {
3168
+ const gated = VERSION_GATED_STRUCT_DEFAULTS[csharp_type];
3169
+ if (version_at_least(version, gated.min_major, gated.min_minor)) {
3170
+ return gated.value;
3171
+ }
3172
+ return null;
3173
+ }
3174
+ if (csharp_type in BLOCK_STRUCT_DEFAULTS) {
3175
+ return BLOCK_STRUCT_DEFAULTS[csharp_type];
3176
+ }
3177
+ if (csharp_type.endsWith("[]")) {
3178
+ return "[]";
3179
+ }
3180
+ if (csharp_type.startsWith("List<") && csharp_type.endsWith(">")) {
3181
+ return "[]";
3182
+ }
3183
+ if (OBJECT_REF_TYPES.has(csharp_type)) {
3184
+ return "{fileID: 0}";
3185
+ }
3186
+ return "{fileID: 0}";
3187
+ }
3188
+ function generate_field_yaml(fields, version, indent = " ", type_lookup) {
3189
+ const lines = [];
3190
+ for (const field of fields) {
3191
+ if (field.hasSerializeReference) {
3192
+ const t = field.typeName;
3193
+ if (t.endsWith("[]") || t.startsWith("List<") && t.endsWith(">")) {
3194
+ lines.push(`${indent}${field.name}: []`);
3195
+ } else {
3196
+ lines.push(`${indent}${field.name}:`);
3197
+ lines.push(`${indent} rid: 0`);
3198
+ }
3199
+ continue;
3200
+ }
3201
+ const default_value = yaml_default_for_type(field.typeName, version);
3202
+ if (default_value === null) {
3203
+ continue;
3204
+ }
3205
+ if (default_value === "{fileID: 0}" && type_lookup) {
3206
+ const struct_fields = type_lookup(field.typeName);
3207
+ if (struct_fields && struct_fields.length > 0) {
3208
+ lines.push(`${indent}${field.name}:`);
3209
+ const nested = generate_field_yaml(struct_fields, version, indent + " ", type_lookup);
3210
+ const nested_lines = nested.slice(1).replace(/\n$/, "").split(`
2845
3211
  `);
2846
- let search_start = 0;
2847
- let search_end = lines.length;
2848
- for (let seg = 0;seg < segments.length - 1; seg++) {
2849
- const segment = segments[seg];
2850
- let parent_idx = -1;
2851
- let parent_indent = -1;
2852
- for (let i = search_start;i < search_end; i++) {
2853
- const match = lines[i].match(new RegExp(`^(\\s*)${escape_regex(segment)}:\\s*(.*)$`));
2854
- if (match) {
2855
- const trailing = match[2].trim();
2856
- if (trailing === "") {
2857
- parent_idx = i;
2858
- parent_indent = match[1].length;
2859
- break;
2860
- }
2861
- for (let j = i + 1;j < search_end; j++) {
2862
- if (lines[j].trim() === "")
2863
- continue;
2864
- const nextLeading = lines[j].match(/^(\s*)/);
2865
- if (nextLeading && nextLeading[1].length > match[1].length) {
2866
- parent_idx = i;
2867
- parent_indent = match[1].length;
2868
- }
2869
- break;
2870
- }
2871
- if (parent_idx !== -1)
2872
- break;
3212
+ for (const nl of nested_lines) {
3213
+ lines.push(nl);
2873
3214
  }
3215
+ continue;
2874
3216
  }
2875
- if (parent_idx === -1)
2876
- return null;
2877
- let child_indent = -1;
2878
- for (let i = parent_idx + 1;i < search_end; i++) {
2879
- if (lines[i].trim() === "")
2880
- continue;
2881
- const leading_spaces = lines[i].match(/^(\s*)/);
2882
- if (leading_spaces) {
2883
- const indent = leading_spaces[1].length;
2884
- if (indent > parent_indent) {
2885
- child_indent = indent;
2886
- break;
2887
- }
2888
- }
2889
- break;
3217
+ }
3218
+ if (default_value.includes(`
3219
+ `)) {
3220
+ lines.push(`${indent}${field.name}:`);
3221
+ for (const sub_line of default_value.split(`
3222
+ `)) {
3223
+ lines.push(`${indent}${sub_line}`);
2890
3224
  }
2891
- if (child_indent === -1)
2892
- return null;
2893
- search_start = parent_idx + 1;
2894
- for (let i = parent_idx + 1;i < search_end; i++) {
2895
- if (lines[i].trim() === "")
2896
- continue;
2897
- const leading_spaces = lines[i].match(/^(\s*)/);
2898
- if (leading_spaces && leading_spaces[1].length < child_indent) {
2899
- search_end = i;
2900
- break;
3225
+ } else {
3226
+ lines.push(`${indent}${field.name}: ${default_value}`);
3227
+ }
3228
+ }
3229
+ return lines.length > 0 ? `
3230
+ ` + lines.join(`
3231
+ `) + `
3232
+ ` : `
3233
+ `;
3234
+ }
3235
+ function json_value_to_yaml_lines(value, indent = " ") {
3236
+ if (value === null || value === undefined) {
3237
+ return [""];
3238
+ }
3239
+ if (typeof value === "string") {
3240
+ return [yaml_quote_if_needed(value)];
3241
+ }
3242
+ if (typeof value === "number" || typeof value === "boolean") {
3243
+ return [String(value)];
3244
+ }
3245
+ if (Array.isArray(value)) {
3246
+ if (value.length === 0)
3247
+ return ["[]"];
3248
+ const lines = [];
3249
+ for (const item of value) {
3250
+ if (is_scalar(item) || is_empty_collection(item)) {
3251
+ const scalar = json_value_to_yaml_lines(item);
3252
+ lines.push(`${indent}- ${scalar[0]}`);
3253
+ } else if (is_flow_mappable(item)) {
3254
+ lines.push(`${indent}- ${to_flow_mapping(item)}`);
3255
+ } else {
3256
+ const child_lines = json_value_to_yaml_lines(item, indent + " ");
3257
+ lines.push(`${indent}- ${child_lines[0].trimStart()}`);
3258
+ for (let i = 1;i < child_lines.length; i++) {
3259
+ lines.push(child_lines[i]);
2901
3260
  }
2902
3261
  }
2903
3262
  }
2904
- const final_prop = segments[segments.length - 1];
2905
- for (let i = search_start;i < search_end; i++) {
2906
- const match = lines[i].match(new RegExp(`^(\\s*${escape_regex(final_prop)}:\\s*).+$`));
2907
- if (match) {
2908
- lines[i] = match[1] + value;
2909
- return lines.join(`
2910
- `);
3263
+ return lines;
3264
+ }
3265
+ if (typeof value === "object") {
3266
+ const entries = Object.entries(value);
3267
+ if (entries.length === 0)
3268
+ return ["{}"];
3269
+ const lines = [];
3270
+ for (const [key, val] of entries) {
3271
+ if (is_scalar(val) || is_empty_collection(val)) {
3272
+ const scalar = json_value_to_yaml_lines(val);
3273
+ lines.push(`${indent}${key}: ${scalar[0]}`);
3274
+ } else if (is_flow_mappable(val)) {
3275
+ lines.push(`${indent}${key}: ${to_flow_mapping(val)}`);
3276
+ } else {
3277
+ const child_indent = Array.isArray(val) ? indent : indent + " ";
3278
+ lines.push(`${indent}${key}:`);
3279
+ const child_lines = json_value_to_yaml_lines(val, child_indent);
3280
+ for (const cl of child_lines) {
3281
+ lines.push(cl);
3282
+ }
2911
3283
  }
2912
3284
  }
2913
- return null;
3285
+ return lines;
2914
3286
  }
3287
+ return [String(value)];
3288
+ }
3289
+ function is_scalar(value) {
3290
+ return value === null || value === undefined || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
3291
+ }
3292
+ function is_empty_collection(value) {
3293
+ if (Array.isArray(value))
3294
+ return value.length === 0;
3295
+ if (typeof value === "object" && value !== null)
3296
+ return Object.keys(value).length === 0;
3297
+ return false;
3298
+ }
3299
+ function is_flow_mappable(value) {
3300
+ if (typeof value !== "object" || value === null || Array.isArray(value))
3301
+ return false;
3302
+ const entries = Object.entries(value);
3303
+ if (entries.length === 0)
3304
+ return false;
3305
+ return entries.every(([, v]) => is_scalar(v) || is_empty_collection(v));
3306
+ }
3307
+ function to_flow_mapping(obj) {
3308
+ const parts = Object.entries(obj).map(([k, v]) => {
3309
+ if (v === null || v === undefined)
3310
+ return `${k}: `;
3311
+ if (typeof v === "string")
3312
+ return `${k}: ${yaml_quote_if_needed(v)}`;
3313
+ if (Array.isArray(v) && v.length === 0)
3314
+ return `${k}: []`;
3315
+ if (typeof v === "object" && v !== null && Object.keys(v).length === 0)
3316
+ return `${k}: {}`;
3317
+ return `${k}: ${String(v)}`;
3318
+ });
3319
+ return `{${parts.join(", ")}}`;
2915
3320
  }
2916
3321
 
2917
3322
  // src/editor/unity-document.ts
3323
+ var import_fs9 = require("fs");
2918
3324
  class UnityDocument {
2919
3325
  file_path;
2920
3326
  _header;
@@ -3531,6 +3937,20 @@ function read_project_version(projectPath) {
3531
3937
  }
3532
3938
 
3533
3939
  // src/editor/create.ts
3940
+ function compute_dll_script_file_id(namespace, class_name) {
3941
+ const { createHash } = require("crypto");
3942
+ const full_name = namespace ? `${namespace}.${class_name}` : class_name;
3943
+ const name_bytes = Buffer.from(full_name, "utf-8");
3944
+ const input = Buffer.alloc(4 + name_bytes.length);
3945
+ input.writeInt32LE(114, 0);
3946
+ name_bytes.copy(input, 4);
3947
+ const hash = createHash("md4").update(input).digest();
3948
+ const a = hash.readInt32LE(0);
3949
+ const b = hash.readInt32LE(4);
3950
+ const c = hash.readInt32LE(8);
3951
+ const d = hash.readInt32LE(12);
3952
+ return a ^ b ^ c ^ d;
3953
+ }
3534
3954
  function get_layer_from_parent(doc, parentTransformId) {
3535
3955
  if (parentTransformId === "0")
3536
3956
  return 0;
@@ -3546,7 +3966,7 @@ function get_layer_from_parent(doc, parentTransformId) {
3546
3966
  const layerMatch = parentGo.raw.match(/m_Layer:\s*(\d+)/);
3547
3967
  return layerMatch ? parseInt(layerMatch[1], 10) : 0;
3548
3968
  }
3549
- function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = 0, rootOrder = 0, layer = 0) {
3969
+ function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
3550
3970
  return `--- !u!1 &${gameObjectId}
3551
3971
  GameObject:
3552
3972
  m_ObjectHideFlags: 0
@@ -3607,9 +4027,9 @@ function findStrippedRootTransform(doc, prefabInstanceId) {
3607
4027
  for (const block of doc.blocks) {
3608
4028
  if (block.class_id !== 4 || !block.is_stripped)
3609
4029
  continue;
3610
- const piMatch = block.raw.match(/m_PrefabInstance:\s*\{fileID:\s*(\d+)\}/);
4030
+ const piMatch = block.raw.match(/m_PrefabInstance:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
3611
4031
  if (piMatch && piMatch[1] === prefabInstanceId) {
3612
- const sourceMatch = block.raw.match(/m_CorrespondingSourceObject:\s*(\{[^}]+\})/);
4032
+ const sourceMatch = block.raw.match(/m_CorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
3613
4033
  if (sourceMatch) {
3614
4034
  return {
3615
4035
  transformId: block.file_id,
@@ -3620,6 +4040,66 @@ function findStrippedRootTransform(doc, prefabInstanceId) {
3620
4040
  }
3621
4041
  return null;
3622
4042
  }
4043
+ function find_game_object_in_variant(doc, name, file_path, project_path) {
4044
+ const pi_blocks = doc.find_by_class_id(1001);
4045
+ for (const pi_block of pi_blocks) {
4046
+ const mod_pattern = /- target:[ \t]*(\{[^}]+\})\s*propertyPath:[ \t]*m_Name\s*value:[ \t]*([^\n]*)/g;
4047
+ let mod_match;
4048
+ while ((mod_match = mod_pattern.exec(pi_block.raw)) !== null) {
4049
+ const target_ref = mod_match[1];
4050
+ const mod_value = mod_match[2].trim();
4051
+ if (mod_value !== name)
4052
+ continue;
4053
+ for (const block of doc.blocks) {
4054
+ if (block.class_id !== 1 || !block.is_stripped)
4055
+ continue;
4056
+ const source_match = block.raw.match(/m_CorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
4057
+ if (!source_match)
4058
+ continue;
4059
+ const normalized_source = source_match[1].replace(/\s+/g, " ");
4060
+ const normalized_target = target_ref.replace(/\s+/g, " ");
4061
+ if (normalized_source === normalized_target) {
4062
+ return {
4063
+ stripped_go_id: block.file_id,
4064
+ source_ref: target_ref,
4065
+ prefab_instance_id: pi_block.file_id
4066
+ };
4067
+ }
4068
+ }
4069
+ }
4070
+ }
4071
+ const source_info = resolve_source_prefab(doc, file_path, project_path);
4072
+ if (!source_info)
4073
+ return null;
4074
+ let source_doc;
4075
+ try {
4076
+ source_doc = UnityDocument.from_file(source_info.source_path);
4077
+ } catch {
4078
+ return null;
4079
+ }
4080
+ const source_gos = source_doc.find_game_objects_by_name(name);
4081
+ if (source_gos.length !== 1)
4082
+ return null;
4083
+ const source_go = source_gos[0];
4084
+ const source_go_file_id = source_go.file_id;
4085
+ const source_guid = source_info.source_guid;
4086
+ for (const block of doc.blocks) {
4087
+ if (block.class_id !== 1 || !block.is_stripped)
4088
+ continue;
4089
+ const source_match = block.raw.match(/m_CorrespondingSourceObject:[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)[^}]*guid:[ \t]*([a-f0-9]{32})/);
4090
+ if (!source_match)
4091
+ continue;
4092
+ if (source_match[1] === source_go_file_id && source_match[2] === source_guid) {
4093
+ const source_ref = `{fileID: ${source_go_file_id}, guid: ${source_guid}, type: 3}`;
4094
+ return {
4095
+ stripped_go_id: block.file_id,
4096
+ source_ref,
4097
+ prefab_instance_id: source_info.prefab_instance_id
4098
+ };
4099
+ }
4100
+ }
4101
+ return null;
4102
+ }
3623
4103
  function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
3624
4104
  const piBlock = doc.find_by_file_id(piId);
3625
4105
  if (!piBlock)
@@ -3629,13 +4109,34 @@ function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
3629
4109
  insertIndex: -1
3630
4110
  addedObject: {fileID: ${newGoId}}`;
3631
4111
  let raw = piBlock.raw;
3632
- const emptyPattern = /m_AddedGameObjects:\s*\[\]/;
4112
+ const emptyPattern = /m_AddedGameObjects:[ \t]*\[\]/;
3633
4113
  if (emptyPattern.test(raw)) {
3634
4114
  raw = raw.replace(emptyPattern, `m_AddedGameObjects:${entry}`);
3635
4115
  piBlock.replace_raw(raw);
3636
4116
  return;
3637
4117
  }
3638
- const existingPattern = /(m_AddedGameObjects:\s*\n(?:\s+-[\s\S]*?(?=\n\s+m_|$))*)/;
4118
+ const existingPattern = /(m_AddedGameObjects:[ \t]*\n(?:[ \t]+-[\s\S]*?(?=\n[ \t]+m_|$))*)/;
4119
+ if (existingPattern.test(raw)) {
4120
+ raw = raw.replace(existingPattern, `$1${entry}`);
4121
+ piBlock.replace_raw(raw);
4122
+ }
4123
+ }
4124
+ function appendToAddedComponents(doc, piId, targetSourceRef, newComponentId) {
4125
+ const piBlock = doc.find_by_file_id(piId);
4126
+ if (!piBlock)
4127
+ return;
4128
+ const entry = `
4129
+ - targetCorrespondingSourceObject: ${targetSourceRef}
4130
+ insertIndex: -1
4131
+ addedObject: {fileID: ${newComponentId}}`;
4132
+ let raw = piBlock.raw;
4133
+ const emptyPattern = /m_AddedComponents:[ \t]*\[\]/;
4134
+ if (emptyPattern.test(raw)) {
4135
+ raw = raw.replace(emptyPattern, `m_AddedComponents:${entry}`);
4136
+ piBlock.replace_raw(raw);
4137
+ return;
4138
+ }
4139
+ const existingPattern = /(m_AddedComponents:[ \t]*\n(?:[ \t]+-[\s\S]*?(?=\n[ \t]+m_|$))*)/;
3639
4140
  if (existingPattern.test(raw)) {
3640
4141
  raw = raw.replace(existingPattern, `$1${entry}`);
3641
4142
  piBlock.replace_raw(raw);
@@ -3658,11 +4159,31 @@ var COMPONENT_DEFAULTS = {
3658
4159
  m_Materials:
3659
4160
  - {fileID: 0}`,
3660
4161
  33: ` m_Mesh: {fileID: 0}`,
4162
+ 50: ` m_BodyType: 0
4163
+ m_Mass: 1
4164
+ m_LinearDrag: 0
4165
+ m_AngularDrag: 0.05
4166
+ m_GravityScale: 1
4167
+ m_Interpolate: 0
4168
+ m_SleepingMode: 1
4169
+ m_CollisionDetection: 0`,
3661
4170
  54: ` m_Mass: 1
3662
4171
  m_Drag: 0
3663
4172
  m_AngularDrag: 0.05
3664
4173
  m_UseGravity: 1
3665
4174
  m_IsKinematic: 0`,
4175
+ 58: ` m_IsTrigger: 0
4176
+ m_Material: {fileID: 0}
4177
+ m_Offset: {x: 0, y: 0}
4178
+ m_Radius: 0.5`,
4179
+ 60: ` m_IsTrigger: 0
4180
+ m_Material: {fileID: 0}
4181
+ m_Offset: {x: 0, y: 0}
4182
+ m_Points: []`,
4183
+ 61: ` m_IsTrigger: 0
4184
+ m_Material: {fileID: 0}
4185
+ m_Offset: {x: 0, y: 0}
4186
+ m_Size: {x: 1, y: 1}`,
3666
4187
  64: ` m_IsTrigger: 0
3667
4188
  m_Convex: 0
3668
4189
  m_CookingOptions: 30
@@ -3671,6 +4192,19 @@ var COMPONENT_DEFAULTS = {
3671
4192
  m_Material: {fileID: 0}
3672
4193
  m_Center: {x: 0, y: 0, z: 0}
3673
4194
  m_Size: {x: 1, y: 1, z: 1}`,
4195
+ 66: ` m_IsTrigger: 0
4196
+ m_Material: {fileID: 0}
4197
+ m_Offset: {x: 0, y: 0}
4198
+ m_GeometryType: 0`,
4199
+ 68: ` m_IsTrigger: 0
4200
+ m_Material: {fileID: 0}
4201
+ m_Offset: {x: 0, y: 0}
4202
+ m_Points: []`,
4203
+ 70: ` m_IsTrigger: 0
4204
+ m_Material: {fileID: 0}
4205
+ m_Offset: {x: 0, y: 0}
4206
+ m_Size: {x: 1, y: 1}
4207
+ m_Direction: 0`,
3674
4208
  82: ` m_PlayOnAwake: 1
3675
4209
  m_Volume: 1
3676
4210
  m_Pitch: 1
@@ -3756,9 +4290,14 @@ function addComponentToGameObject(doc, gameObjectId, componentId) {
3756
4290
  `);
3757
4291
  goBlock.replace_raw(raw);
3758
4292
  }
3759
- function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version) {
3760
- const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version) : `
4293
+ function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000, type_lookup) {
4294
+ const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version, " ", type_lookup) : `
3761
4295
  `;
4296
+ const has_serialize_ref = fields?.some((f) => f.hasSerializeReference) ?? false;
4297
+ const references_section = has_serialize_ref ? ` references:
4298
+ version: 2
4299
+ RefIds: []
4300
+ ` : "";
3762
4301
  return `--- !u!114 &${componentId}
3763
4302
  MonoBehaviour:
3764
4303
  m_ObjectHideFlags: 0
@@ -3768,9 +4307,9 @@ MonoBehaviour:
3768
4307
  m_GameObject: {fileID: ${gameObjectId}}
3769
4308
  m_Enabled: 1
3770
4309
  m_EditorHideFlags: 0
3771
- m_Script: {fileID: 11500000, guid: ${scriptGuid}, type: 3}
4310
+ m_Script: {fileID: ${scriptFileId}, guid: ${scriptGuid}, type: 3}
3772
4311
  m_Name:
3773
- m_EditorClassIdentifier:${field_yaml}`;
4312
+ m_EditorClassIdentifier:${field_yaml}${references_section}`;
3774
4313
  }
3775
4314
  function createGameObject(options) {
3776
4315
  const { file_path, name, parent } = options;
@@ -3869,9 +4408,9 @@ function createGameObject(options) {
3869
4408
  const layer = get_layer_from_parent(doc, parentTransformIdStr);
3870
4409
  const gameObjectIdStr = doc.generate_file_id();
3871
4410
  const transformIdStr = doc.generate_file_id();
3872
- const gameObjectId = parseInt(gameObjectIdStr, 10);
3873
- const transformId = parseInt(transformIdStr, 10);
3874
- const parentTransformId = parseInt(parentTransformIdStr, 10);
4411
+ const gameObjectId = gameObjectIdStr;
4412
+ const transformId = transformIdStr;
4413
+ const parentTransformId = parentTransformIdStr;
3875
4414
  const newBlocks = createGameObjectYAML(gameObjectId, transformId, name.trim(), parentTransformId, rootOrder, layer);
3876
4415
  doc.append_raw(newBlocks);
3877
4416
  if (parentTransformIdStr !== "0" && !variantPiId) {
@@ -3893,7 +4432,7 @@ function createGameObject(options) {
3893
4432
  file_path,
3894
4433
  game_object_id: gameObjectId,
3895
4434
  transform_id: transformId,
3896
- prefab_instance_id: variantPiId ? parseInt(variantPiId, 10) : undefined
4435
+ prefab_instance_id: variantPiId || undefined
3897
4436
  };
3898
4437
  }
3899
4438
  function createScene(options) {
@@ -4339,9 +4878,9 @@ function createPrefabVariant(options) {
4339
4878
  const tempDoc = UnityDocument.from_string(`%YAML 1.1
4340
4879
  %TAG !u! tag:unity3d.com,2011:
4341
4880
  `);
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);
4881
+ const prefabInstanceId = tempDoc.generate_file_id();
4882
+ const strippedGoId = tempDoc.generate_file_id();
4883
+ const strippedTransformId = tempDoc.generate_file_id();
4345
4884
  const finalName = variant_name || `${rootInfo.name} Variant`;
4346
4885
  const variantYaml = `%YAML 1.1
4347
4886
  %TAG !u! tag:unity3d.com,2011:
@@ -4455,9 +4994,10 @@ function createScriptableObject(options) {
4455
4994
  } catch {}
4456
4995
  }
4457
4996
  const baseName = path5.basename(output_path, ".asset");
4458
- const field_yaml = resolved.fields && resolved.fields.length > 0 ? generate_field_yaml(resolved.fields, version) : `
4997
+ const type_lookup = project_path ? build_type_lookup(project_path) : undefined;
4998
+ const field_yaml = resolved.fields && resolved.fields.length > 0 ? generate_field_yaml(resolved.fields, version, " ", type_lookup) : `
4459
4999
  `;
4460
- const assetYaml = `%YAML 1.1
5000
+ let assetYaml = `%YAML 1.1
4461
5001
  %TAG !u! tag:unity3d.com,2011:
4462
5002
  --- !u!114 &11400000
4463
5003
  MonoBehaviour:
@@ -4471,6 +5011,66 @@ MonoBehaviour:
4471
5011
  m_Script: {fileID: 11500000, guid: ${resolved.guid}, type: 3}
4472
5012
  m_Name: ${baseName}
4473
5013
  m_EditorClassIdentifier:${field_yaml}`;
5014
+ if (options.initial_values && Object.keys(options.initial_values).length > 0) {
5015
+ const doc = UnityDocument.from_string(assetYaml);
5016
+ const block = doc.blocks[0];
5017
+ if (block) {
5018
+ const complex_entries = [];
5019
+ for (const [key, val] of Object.entries(options.initial_values)) {
5020
+ if (val === null || val === undefined || typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
5021
+ block.set_property(key, String(val ?? ""));
5022
+ } else if (Array.isArray(val) && val.length === 0) {
5023
+ block.set_property(key, "[]");
5024
+ } else if (typeof val === "object" && val !== null && !Array.isArray(val) && Object.keys(val).length === 0) {
5025
+ block.set_property(key, "{}");
5026
+ } else {
5027
+ complex_entries.push([key, val]);
5028
+ }
5029
+ }
5030
+ if (complex_entries.length > 0) {
5031
+ let raw = block.raw;
5032
+ for (const [key, val] of complex_entries) {
5033
+ const yaml_lines = json_value_to_yaml_lines(val, " ");
5034
+ const key_pattern = new RegExp(`^([ \\t]*${key}:)[^\\n]*$`, "m");
5035
+ const key_match = raw.match(key_pattern);
5036
+ if (key_match && key_match.index !== undefined) {
5037
+ const key_indent = key_match[0].match(/^[ \t]*/)?.[0].length ?? 0;
5038
+ const after_key = key_match.index + key_match[0].length;
5039
+ let end = after_key;
5040
+ const remaining = raw.slice(after_key);
5041
+ const remaining_lines = remaining.split(`
5042
+ `);
5043
+ for (let i = 1;i < remaining_lines.length; i++) {
5044
+ const line = remaining_lines[i];
5045
+ if (line.trim() === "") {
5046
+ end += 1 + line.length;
5047
+ continue;
5048
+ }
5049
+ const line_indent = line.match(/^[ \t]*/)?.[0].length ?? 0;
5050
+ if (line_indent > key_indent) {
5051
+ end += 1 + line.length;
5052
+ } else {
5053
+ break;
5054
+ }
5055
+ }
5056
+ const block_yaml = ` ${key}:
5057
+ ${yaml_lines.join(`
5058
+ `)}`;
5059
+ raw = raw.slice(0, key_match.index) + block_yaml + raw.slice(end);
5060
+ } else {
5061
+ const block_yaml = ` ${key}:
5062
+ ${yaml_lines.join(`
5063
+ `)}`;
5064
+ raw = raw.trimEnd() + `
5065
+ ` + block_yaml + `
5066
+ `;
5067
+ }
5068
+ }
5069
+ block.replace_raw(raw);
5070
+ }
5071
+ assetYaml = doc.serialize();
5072
+ }
5073
+ }
4474
5074
  try {
4475
5075
  import_fs10.writeFileSync(output_path, assetYaml, "utf-8");
4476
5076
  } catch (err) {
@@ -4551,6 +5151,80 @@ MonoImporter:
4551
5151
  guid
4552
5152
  };
4553
5153
  }
5154
+ var COMPONENT_BASE_CLASSES = new Set([
5155
+ "MonoBehaviour",
5156
+ "NetworkBehaviour",
5157
+ "StateMachineBehaviour"
5158
+ ]);
5159
+ function is_valid_component_base(base_class, project_path, depth = 0) {
5160
+ if (depth > 5)
5161
+ return false;
5162
+ if (COMPONENT_BASE_CLASSES.has(base_class))
5163
+ return true;
5164
+ if (!project_path)
5165
+ return false;
5166
+ const registryPath = path5.join(project_path, ".unity-agentic", "type-registry.json");
5167
+ if (import_fs10.existsSync(registryPath)) {
5168
+ try {
5169
+ const registry = JSON.parse(import_fs10.readFileSync(registryPath, "utf-8"));
5170
+ const match = registry.find((t) => t.name === base_class && t.kind === "class");
5171
+ if (match?.filePath) {
5172
+ const fullPath = path5.isAbsolute(match.filePath) ? match.filePath : path5.join(project_path, match.filePath);
5173
+ if (fullPath.endsWith(".cs") && import_fs10.existsSync(fullPath)) {
5174
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
5175
+ const extract = getNativeExtractSerializedFields2();
5176
+ if (extract) {
5177
+ const typeInfos = extract(fullPath);
5178
+ const info = typeInfos?.find((t) => t.name === base_class);
5179
+ if (info?.baseClass) {
5180
+ return is_valid_component_base(info.baseClass, project_path, depth + 1);
5181
+ }
5182
+ }
5183
+ }
5184
+ if (!fullPath.endsWith(".cs") || !import_fs10.existsSync(fullPath)) {
5185
+ if (match.kind === "class") {
5186
+ return is_likely_component_base_name(base_class);
5187
+ }
5188
+ }
5189
+ }
5190
+ } catch {}
5191
+ }
5192
+ const cachePaths = [
5193
+ path5.join(project_path, ".unity-agentic", "package-cache.json"),
5194
+ path5.join(project_path, ".unity-agentic", "local-package-cache.json")
5195
+ ];
5196
+ for (const cachePath of cachePaths) {
5197
+ if (!import_fs10.existsSync(cachePath))
5198
+ continue;
5199
+ try {
5200
+ const cache = JSON.parse(import_fs10.readFileSync(cachePath, "utf-8"));
5201
+ const baseClassLower = base_class.toLowerCase();
5202
+ for (const [, assetPath] of Object.entries(cache)) {
5203
+ if (!assetPath.endsWith(".cs"))
5204
+ continue;
5205
+ if (path5.basename(assetPath, ".cs").toLowerCase() !== baseClassLower)
5206
+ continue;
5207
+ const fullPath = path5.join(project_path, assetPath);
5208
+ if (!import_fs10.existsSync(fullPath))
5209
+ continue;
5210
+ const { getNativeExtractSerializedFields: getNativeExtractSerializedFields2 } = (init_scanner(), __toCommonJS(exports_scanner));
5211
+ const extract = getNativeExtractSerializedFields2();
5212
+ if (!extract)
5213
+ continue;
5214
+ const typeInfos = extract(fullPath);
5215
+ const info = typeInfos?.find((t) => t.name === base_class);
5216
+ if (info?.baseClass) {
5217
+ return is_valid_component_base(info.baseClass, project_path, depth + 1);
5218
+ }
5219
+ }
5220
+ } catch {}
5221
+ }
5222
+ return false;
5223
+ }
5224
+ function is_likely_component_base_name(name) {
5225
+ const lower = name.toLowerCase();
5226
+ return lower.endsWith("behaviour") || lower.endsWith("behavior") || lower.includes("monobehaviour") || lower.includes("networkbehaviour");
5227
+ }
4554
5228
  function addComponent(options) {
4555
5229
  const { file_path, game_object_name, component_type } = options;
4556
5230
  const project_path = options.project_path || find_unity_project_root(path5.dirname(file_path)) || undefined;
@@ -4576,11 +5250,19 @@ function addComponent(options) {
4576
5250
  };
4577
5251
  }
4578
5252
  const goResult = doc.require_unique_game_object(game_object_name);
5253
+ let gameObjectIdStr;
5254
+ let variantInfo = null;
4579
5255
  if ("error" in goResult) {
4580
- return { success: false, file_path, error: goResult.error };
5256
+ const vResult = find_game_object_in_variant(doc, game_object_name, file_path, project_path);
5257
+ if (!vResult) {
5258
+ return { success: false, file_path, error: goResult.error };
5259
+ }
5260
+ gameObjectIdStr = vResult.stripped_go_id;
5261
+ variantInfo = { source_ref: vResult.source_ref, prefab_instance_id: vResult.prefab_instance_id };
5262
+ } else {
5263
+ gameObjectIdStr = goResult.file_id;
4581
5264
  }
4582
- const gameObjectIdStr = goResult.file_id;
4583
- const gameObjectId = parseInt(gameObjectIdStr, 10);
5265
+ const gameObjectId = gameObjectIdStr;
4584
5266
  const classId = get_class_id(component_type);
4585
5267
  let duplicateWarning;
4586
5268
  const goBlock = doc.find_by_file_id(gameObjectIdStr);
@@ -4595,7 +5277,7 @@ function addComponent(options) {
4595
5277
  }
4596
5278
  }
4597
5279
  const componentIdStr = doc.generate_file_id();
4598
- const componentId = parseInt(componentIdStr, 10);
5280
+ const componentId = componentIdStr;
4599
5281
  let componentYAML;
4600
5282
  let scriptGuid;
4601
5283
  let scriptPath;
@@ -4604,7 +5286,16 @@ function addComponent(options) {
4604
5286
  const componentName = UNITY_CLASS_IDS[classId] || component_type;
4605
5287
  componentYAML = createGenericComponentYAML(componentName, classId, componentId, gameObjectId);
4606
5288
  } else {
4607
- const resolved = resolve_script_with_fields(component_type, project_path);
5289
+ let resolved;
5290
+ try {
5291
+ resolved = resolve_script_with_fields(component_type, project_path);
5292
+ } catch (e) {
5293
+ return {
5294
+ success: false,
5295
+ file_path,
5296
+ error: e instanceof Error ? e.message : String(e)
5297
+ };
5298
+ }
4608
5299
  if (!resolved) {
4609
5300
  const hints = [];
4610
5301
  if (project_path) {
@@ -4631,11 +5322,11 @@ function addComponent(options) {
4631
5322
  error: `"${component_type}" is ${resolved.kind === "enum" ? "an enum" : "an interface"}, not a MonoBehaviour. Cannot add as a component.`
4632
5323
  };
4633
5324
  }
4634
- if (resolved.base_class && !["MonoBehaviour", "NetworkBehaviour", "StateMachineBehaviour"].includes(resolved.base_class)) {
5325
+ if (resolved.base_class && !COMPONENT_BASE_CLASSES.has(resolved.base_class) && !is_valid_component_base(resolved.base_class, project_path)) {
4635
5326
  return {
4636
5327
  success: false,
4637
5328
  file_path,
4638
- error: `"${component_type}" extends ${resolved.base_class}, not MonoBehaviour. Cannot add as a component.`
5329
+ 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
5330
  };
4640
5331
  }
4641
5332
  let version;
@@ -4644,12 +5335,21 @@ function addComponent(options) {
4644
5335
  version = read_project_version(project_path);
4645
5336
  } catch {}
4646
5337
  }
4647
- componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version);
5338
+ let scriptFileId = 11500000;
5339
+ if (resolved.path?.endsWith(".dll") && resolved.class_name) {
5340
+ scriptFileId = compute_dll_script_file_id(resolved.namespace ?? "", resolved.class_name);
5341
+ }
5342
+ const type_lookup_comp = project_path ? build_type_lookup(project_path) : undefined;
5343
+ componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version, scriptFileId, type_lookup_comp);
4648
5344
  scriptGuid = resolved.guid;
4649
5345
  scriptPath = resolved.path || undefined;
4650
5346
  extractionError = resolved.extraction_error;
4651
5347
  }
4652
- addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
5348
+ if (variantInfo) {
5349
+ appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
5350
+ } else {
5351
+ addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
5352
+ }
4653
5353
  doc.append_raw(componentYAML);
4654
5354
  const saveResult = doc.save();
4655
5355
  if (!saveResult.success) {
@@ -4726,6 +5426,14 @@ function copyComponent(options) {
4726
5426
  }
4727
5427
  // src/editor/update.ts
4728
5428
  var import_fs11 = require("fs");
5429
+ function enrich_target_ref(targetRef, blockRaw) {
5430
+ if (/guid:/.test(targetRef))
5431
+ return targetRef;
5432
+ const sourceMatch = blockRaw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/);
5433
+ if (!sourceMatch)
5434
+ return null;
5435
+ return targetRef.replace(/\}$/, `, guid: ${sourceMatch[1]}, type: 3}`);
5436
+ }
4729
5437
  function eulerToQuaternion(euler) {
4730
5438
  const deg2rad = Math.PI / 180;
4731
5439
  const x = euler.x * deg2rad;
@@ -4748,6 +5456,9 @@ function validate_value_type(current_value, new_value) {
4748
5456
  const current = current_value.trim();
4749
5457
  const incoming = new_value.trim();
4750
5458
  if (/^\{fileID:/.test(current)) {
5459
+ if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
5460
+ return null;
5461
+ }
4751
5462
  if (!/^\{fileID:/.test(incoming)) {
4752
5463
  return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
4753
5464
  }
@@ -4932,10 +5643,10 @@ function safeUnityYAMLEdit(filePath, objectName, propertyName, newValue, project
4932
5643
  const propertyPattern = new RegExp(`(^\\s*m_${normalizedProperty}:\\s*)([^\\n]*)`, "m");
4933
5644
  let updatedRaw = targetBlock.raw;
4934
5645
  if (propertyPattern.test(updatedRaw)) {
4935
- updatedRaw = updatedRaw.replace(propertyPattern, `$1${newValue}`);
5646
+ updatedRaw = updatedRaw.replace(propertyPattern, (_m, prefix) => prefix + newValue);
4936
5647
  } else {
4937
- updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, `
4938
- m_${normalizedProperty}: ${newValue}$1$2`);
5648
+ updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, (_m, g1, g2) => `
5649
+ m_${normalizedProperty}: ${newValue}${g1}${g2}`);
4939
5650
  }
4940
5651
  targetBlock.replace_raw(updatedRaw);
4941
5652
  const saveResult = doc.save();
@@ -5021,7 +5732,9 @@ function editComponentByFileId(options) {
5021
5732
  };
5022
5733
  }
5023
5734
  }
5024
- const currentValue = targetBlock.get_property(exactProperty) ?? (exactProperty !== prefixedProperty ? targetBlock.get_property(prefixedProperty) : null);
5735
+ const exactValue = targetBlock.get_property(exactProperty);
5736
+ const resolvedProperty = exactValue !== null ? exactProperty : exactProperty !== prefixedProperty && targetBlock.get_property(prefixedProperty) !== null ? prefixedProperty : null;
5737
+ const currentValue = exactValue ?? (resolvedProperty === prefixedProperty ? targetBlock.get_property(prefixedProperty) : null);
5025
5738
  if (currentValue !== null) {
5026
5739
  const typeError = validate_value_type(currentValue, new_value);
5027
5740
  if (typeError) {
@@ -5032,9 +5745,14 @@ function editComponentByFileId(options) {
5032
5745
  };
5033
5746
  }
5034
5747
  }
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}");
5748
+ let modified;
5749
+ if (resolvedProperty) {
5750
+ modified = targetBlock.set_property(resolvedProperty, new_value, "{fileID: 0}");
5751
+ } else {
5752
+ modified = targetBlock.set_property(exactProperty, new_value, "{fileID: 0}");
5753
+ if (!modified && exactProperty !== prefixedProperty) {
5754
+ modified = targetBlock.set_property(prefixedProperty, new_value, "{fileID: 0}");
5755
+ }
5038
5756
  }
5039
5757
  if (!modified) {
5040
5758
  if (currentValue !== null) {
@@ -5071,8 +5789,10 @@ function editComponentByFileId(options) {
5071
5789
  };
5072
5790
  }
5073
5791
  function editPrefabOverride(options) {
5074
- const { file_path, prefab_instance, property_path, new_value, object_reference, target } = options;
5075
- const objRef = object_reference ?? "{fileID: 0}";
5792
+ const { file_path, prefab_instance, new_value, object_reference, target, managed_reference } = options;
5793
+ const property_path = /[\[\]]/.test(options.property_path) && !options.property_path.startsWith("'") ? `'${options.property_path}'` : options.property_path;
5794
+ const effectiveValue = managed_reference ?? new_value;
5795
+ const objRef = managed_reference ? "{fileID: 0}" : object_reference ?? "{fileID: 0}";
5076
5796
  if (!import_fs11.existsSync(file_path)) {
5077
5797
  return { success: false, file_path, error: `File not found: ${file_path}` };
5078
5798
  }
@@ -5086,11 +5806,16 @@ function editPrefabOverride(options) {
5086
5806
  if (!targetBlock) {
5087
5807
  return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
5088
5808
  }
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");
5809
+ const unquotedPath = property_path.replace(/^'|'$/g, "");
5810
+ const quotedPath = `'${unquotedPath}'`;
5811
+ const escapedUnquoted = unquotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5812
+ const escapedQuoted = quotedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5813
+ const pathAlternation = escapedUnquoted === escapedQuoted ? escapedUnquoted : `(?:${escapedQuoted}|${escapedUnquoted})`;
5814
+ const entryPattern = new RegExp(`(- target:\\s*\\{[^}]+\\}\\s*\\n\\s*propertyPath:\\s*)${pathAlternation}(\\s*\\n\\s*value:\\s*)(.*)(\\s*\\n\\s*objectReference:\\s*)(.*)`, "m");
5091
5815
  const entryMatch = targetBlock.raw.match(entryPattern);
5092
5816
  if (entryMatch) {
5093
- const updatedText2 = targetBlock.raw.replace(entryPattern, `$1${property_path}$2${new_value}$4${objRef}`);
5817
+ const quotedValue = yaml_quote_if_needed(effectiveValue);
5818
+ const updatedText2 = targetBlock.raw.replace(entryPattern, (_m, g1, g2, _g3, g4) => g1 + property_path + g2 + quotedValue + g4 + objRef);
5094
5819
  targetBlock.replace_raw(updatedText2);
5095
5820
  const saveResult2 = doc.save();
5096
5821
  if (!saveResult2.success) {
@@ -5127,9 +5852,13 @@ function editPrefabOverride(options) {
5127
5852
  error: `Invalid target reference "${targetRef}". Expected format: {fileID: N, guid: ..., type: T}`
5128
5853
  };
5129
5854
  }
5855
+ const enriched = enrich_target_ref(targetRef, targetBlock.raw);
5856
+ if (enriched)
5857
+ targetRef = enriched;
5858
+ const quotedNewValue = yaml_quote_if_needed(effectiveValue);
5130
5859
  const newEntry = ` - target: ${targetRef}
5131
5860
  propertyPath: ${property_path}
5132
- value: ${new_value}
5861
+ value: ${quotedNewValue}
5133
5862
  objectReference: ${objRef}`;
5134
5863
  const removedPattern = /(\n\s*m_RemovedComponents:)/m;
5135
5864
  const removedMatch = targetBlock.raw.match(removedPattern);
@@ -5272,10 +6001,10 @@ function batchEditProperties(filePath, edits) {
5272
6001
  const propertyPattern = new RegExp(`(^\\s*m_${normalizedProperty}:\\s*)([^\\n]*)`, "m");
5273
6002
  let updatedRaw = targetBlock.raw;
5274
6003
  if (propertyPattern.test(updatedRaw)) {
5275
- updatedRaw = updatedRaw.replace(propertyPattern, `$1${edit.new_value}`);
6004
+ updatedRaw = updatedRaw.replace(propertyPattern, (_m, prefix) => prefix + edit.new_value);
5276
6005
  } else {
5277
- updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, `
5278
- m_${normalizedProperty}: ${edit.new_value}$1$2`);
6006
+ updatedRaw = updatedRaw.replace(/(\n)(--- !u!|$)/, (_m, g1, g2) => `
6007
+ m_${normalizedProperty}: ${edit.new_value}${g1}${g2}`);
5279
6008
  }
5280
6009
  targetBlock.replace_raw(updatedRaw);
5281
6010
  }
@@ -5380,9 +6109,9 @@ function reparentGameObject(options) {
5380
6109
  return {
5381
6110
  success: true,
5382
6111
  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)
6112
+ child_transform_id: childTransformId,
6113
+ old_parent_transform_id: oldParentTransformId,
6114
+ new_parent_transform_id: newParentTransformId
5386
6115
  };
5387
6116
  }
5388
6117
  function findPrefabInstanceBlock(doc, identifier) {
@@ -5751,9 +6480,9 @@ function removeRemovedGameObject(options) {
5751
6480
  if (!doc.validate()) {
5752
6481
  return { success: false, file_path, error: "Validation failed after removing GameObject" };
5753
6482
  }
5754
- const saveResult = doc.save();
5755
- if (!saveResult.success) {
5756
- return { success: false, file_path, error: saveResult.error };
6483
+ const restoreGoSaveResult = doc.save();
6484
+ if (!restoreGoSaveResult.success) {
6485
+ return { success: false, file_path, error: restoreGoSaveResult.error };
5757
6486
  }
5758
6487
  return {
5759
6488
  success: true,
@@ -5793,7 +6522,8 @@ function removeComponent(options) {
5793
6522
  const parentGoId = goMatch[1];
5794
6523
  const goBlock = doc.find_by_file_id(parentGoId);
5795
6524
  if (goBlock) {
5796
- const compLinePattern = new RegExp(`\\s*- component: \\{fileID: ${file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\}\\n?`);
6525
+ const escaped = file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6526
+ const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
5797
6527
  const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
5798
6528
  goBlock.replace_raw(modifiedRaw);
5799
6529
  }