unity-agentic-tools 0.4.3 → 0.5.1

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
@@ -205,7 +205,7 @@ var require_package = __commonJS((exports2, module2) => {
205
205
  module2.exports = {
206
206
  name: "unity-agentic-tools",
207
207
  packageManager: "bun@latest",
208
- version: "0.4.3",
208
+ version: "0.5.1",
209
209
  description: "Fast, token-efficient Unity YAML parser for AI agents",
210
210
  exports: {
211
211
  ".": {
@@ -232,7 +232,8 @@ var require_package = __commonJS((exports2, module2) => {
232
232
  rebuild: "bun run clean && bun run build",
233
233
  test: "bun test",
234
234
  "test:watch": "bun test --watch",
235
- "test:coverage": "bun test --coverage"
235
+ "test:coverage": "bun test --coverage",
236
+ "test:unity-headless": "bun test/run-headless-validation.ts"
236
237
  },
237
238
  dependencies: {
238
239
  commander: "^14.0.2"
@@ -383,11 +384,11 @@ function setup(options = {}) {
383
384
  const nativeLocalPkgBuild = getNativeBuildLocalPackageGuidCache();
384
385
  if (nativeLocalPkgBuild) {
385
386
  try {
386
- const localPkgCache = nativeLocalPkgBuild(projectPath);
387
- const localPkgCount = Object.keys(localPkgCache).length;
387
+ const localPackageCache = nativeLocalPkgBuild(projectPath);
388
+ const localPkgCount = Object.keys(localPackageCache).length;
388
389
  if (localPkgCount > 0) {
389
390
  const localPkgCachePath = import_path2.join(configPath, LOCAL_PACKAGE_CACHE_FILE);
390
- import_fs2.writeFileSync(localPkgCachePath, JSON.stringify(localPkgCache, null, 2));
391
+ import_fs2.writeFileSync(localPkgCachePath, JSON.stringify(localPackageCache, null, 2));
391
392
  }
392
393
  } catch {}
393
394
  }
@@ -473,6 +474,8 @@ var CONFIG_DIR2 = ".unity-agentic";
473
474
  var CONFIG_FILE2 = "config.json";
474
475
  var GUID_CACHE_FILE2 = "guid-cache.json";
475
476
  var DOC_INDEX_FILE2 = "doc-index.json";
477
+ var EDITOR_LOCK_FILE = "editor.json";
478
+ var LAST_EDITOR_CONFIG_FILE = "editor.last.json";
476
479
  function cleanup(options = {}) {
477
480
  const projectPath = import_path3.resolve(options.project || process.cwd());
478
481
  const configPath = import_path3.join(projectPath, CONFIG_DIR2);
@@ -481,8 +484,7 @@ function cleanup(options = {}) {
481
484
  success: true,
482
485
  project_path: projectPath,
483
486
  files_removed: [],
484
- directory_removed: false,
485
- error: `No ${CONFIG_DIR2} directory found`
487
+ directory_removed: false
486
488
  };
487
489
  }
488
490
  const filesRemoved = [];
@@ -502,7 +504,12 @@ function cleanup(options = {}) {
502
504
  };
503
505
  }
504
506
  } else {
505
- const filesToRemove = [GUID_CACHE_FILE2, DOC_INDEX_FILE2];
507
+ const filesToRemove = [
508
+ GUID_CACHE_FILE2,
509
+ DOC_INDEX_FILE2,
510
+ EDITOR_LOCK_FILE,
511
+ LAST_EDITOR_CONFIG_FILE
512
+ ];
506
513
  for (const file of filesToRemove) {
507
514
  const filePath = import_path3.join(configPath, file);
508
515
  if (import_fs3.existsSync(filePath)) {
@@ -1806,88 +1813,6 @@ function grep_project_js(options) {
1806
1813
  var import_fs10 = require("fs");
1807
1814
  var path5 = __toESM(require("path"));
1808
1815
 
1809
- // src/guid-cache.ts
1810
- var import_fs7 = require("fs");
1811
- var import_path5 = require("path");
1812
- var _cache_store = new Map;
1813
- function build_guid_cache(project_path, raw) {
1814
- return {
1815
- project_path,
1816
- cache: raw,
1817
- count: Object.keys(raw).length,
1818
- resolve(guid) {
1819
- return raw[guid] ?? null;
1820
- },
1821
- resolve_absolute(guid) {
1822
- const rel = raw[guid];
1823
- if (!rel)
1824
- return null;
1825
- if (import_path5.isAbsolute(rel))
1826
- return rel;
1827
- return import_path5.join(project_path, rel);
1828
- },
1829
- resolve_many(guids) {
1830
- const result = {};
1831
- for (const guid of guids) {
1832
- result[guid] = raw[guid] ?? null;
1833
- }
1834
- return result;
1835
- },
1836
- find_by_name(name, extension) {
1837
- const nameLower = name.toLowerCase().replace(/\.[^.]+$/, "");
1838
- let substringMatch = null;
1839
- for (const [guid, assetPath] of Object.entries(raw)) {
1840
- if (extension && !assetPath.endsWith(extension))
1841
- continue;
1842
- const fileName = import_path5.basename(assetPath, import_path5.extname(assetPath)).toLowerCase();
1843
- if (fileName === nameLower) {
1844
- return { guid, path: assetPath };
1845
- }
1846
- if (!substringMatch && assetPath.toLowerCase().includes(nameLower)) {
1847
- substringMatch = { guid, path: assetPath };
1848
- }
1849
- }
1850
- return substringMatch;
1851
- },
1852
- find_all_by_name_exact(name, extension) {
1853
- const nameLower = name.toLowerCase().replace(/\.[^.]+$/, "");
1854
- const matches = [];
1855
- for (const [guid, assetPath] of Object.entries(raw)) {
1856
- if (extension && !assetPath.endsWith(extension))
1857
- continue;
1858
- const fileName = import_path5.basename(assetPath, import_path5.extname(assetPath)).toLowerCase();
1859
- if (fileName === nameLower) {
1860
- matches.push({ guid, path: assetPath });
1861
- }
1862
- }
1863
- return matches;
1864
- }
1865
- };
1866
- }
1867
- function load_guid_cache(project_path) {
1868
- const resolved = project_path;
1869
- const existing = _cache_store.get(resolved);
1870
- if (existing)
1871
- return existing;
1872
- const cachePath = import_path5.join(resolved, ".unity-agentic", "guid-cache.json");
1873
- if (!import_fs7.existsSync(cachePath))
1874
- return null;
1875
- try {
1876
- const raw = JSON.parse(import_fs7.readFileSync(cachePath, "utf-8"));
1877
- const cache = build_guid_cache(resolved, raw);
1878
- _cache_store.set(resolved, cache);
1879
- return cache;
1880
- } catch {
1881
- return null;
1882
- }
1883
- }
1884
- function load_guid_cache_for_file(file_path, explicit_project) {
1885
- const project_path = explicit_project || find_unity_project_root(import_path5.dirname(file_path));
1886
- if (!project_path)
1887
- return null;
1888
- return load_guid_cache(project_path);
1889
- }
1890
-
1891
1816
  // src/class-ids.ts
1892
1817
  var UNITY_CLASS_IDS = {
1893
1818
  1: "GameObject",
@@ -2101,25 +2026,328 @@ var UNITY_CLASS_IDS = {
2101
2026
  1839735487: "TilemapRendererLegacy"
2102
2027
  };
2103
2028
  var UNITY_CLASS_NAMES = Object.fromEntries(Object.entries(UNITY_CLASS_IDS).map(([id, name]) => [name, parseInt(id, 10)]));
2104
- function get_class_id_name(class_id) {
2105
- return UNITY_CLASS_IDS[class_id] || `Unknown_${class_id}`;
2029
+ var UNITY_COMPONENT_CLASS_IDS = new Set([
2030
+ 4,
2031
+ 8,
2032
+ 20,
2033
+ 23,
2034
+ 25,
2035
+ 33,
2036
+ 50,
2037
+ 53,
2038
+ 54,
2039
+ 56,
2040
+ 57,
2041
+ 58,
2042
+ 59,
2043
+ 60,
2044
+ 61,
2045
+ 64,
2046
+ 65,
2047
+ 66,
2048
+ 68,
2049
+ 70,
2050
+ 75,
2051
+ 81,
2052
+ 82,
2053
+ 95,
2054
+ 96,
2055
+ 102,
2056
+ 108,
2057
+ 111,
2058
+ 114,
2059
+ 119,
2060
+ 120,
2061
+ 124,
2062
+ 135,
2063
+ 136,
2064
+ 137,
2065
+ 138,
2066
+ 143,
2067
+ 144,
2068
+ 145,
2069
+ 146,
2070
+ 153,
2071
+ 154,
2072
+ 164,
2073
+ 165,
2074
+ 166,
2075
+ 167,
2076
+ 168,
2077
+ 169,
2078
+ 170,
2079
+ 180,
2080
+ 181,
2081
+ 182,
2082
+ 183,
2083
+ 191,
2084
+ 192,
2085
+ 193,
2086
+ 195,
2087
+ 198,
2088
+ 199,
2089
+ 205,
2090
+ 208,
2091
+ 210,
2092
+ 212,
2093
+ 215,
2094
+ 220,
2095
+ 222,
2096
+ 223,
2097
+ 224,
2098
+ 225,
2099
+ 229,
2100
+ 230,
2101
+ 231,
2102
+ 232,
2103
+ 233,
2104
+ 234,
2105
+ 235,
2106
+ 247,
2107
+ 248,
2108
+ 249,
2109
+ 250,
2110
+ 251,
2111
+ 252,
2112
+ 253,
2113
+ 254,
2114
+ 255,
2115
+ 256,
2116
+ 257,
2117
+ 259,
2118
+ 300,
2119
+ 320,
2120
+ 328,
2121
+ 330,
2122
+ 331,
2123
+ 156049354,
2124
+ 1742807556,
2125
+ 1839735485,
2126
+ 19719996,
2127
+ 483693784,
2128
+ 1839735486,
2129
+ 1839735487
2130
+ ]);
2131
+ var UNITY_ADDABLE_COMPONENT_CLASS_IDS = new Set([
2132
+ 20,
2133
+ 23,
2134
+ 33,
2135
+ 50,
2136
+ 54,
2137
+ 58,
2138
+ 59,
2139
+ 60,
2140
+ 61,
2141
+ 64,
2142
+ 65,
2143
+ 66,
2144
+ 68,
2145
+ 70,
2146
+ 75,
2147
+ 81,
2148
+ 82,
2149
+ 95,
2150
+ 96,
2151
+ 102,
2152
+ 108,
2153
+ 111,
2154
+ 119,
2155
+ 120,
2156
+ 124,
2157
+ 135,
2158
+ 136,
2159
+ 137,
2160
+ 138,
2161
+ 143,
2162
+ 144,
2163
+ 145,
2164
+ 146,
2165
+ 153,
2166
+ 154,
2167
+ 164,
2168
+ 165,
2169
+ 166,
2170
+ 167,
2171
+ 168,
2172
+ 169,
2173
+ 170,
2174
+ 182,
2175
+ 183,
2176
+ 191,
2177
+ 192,
2178
+ 193,
2179
+ 195,
2180
+ 198,
2181
+ 199,
2182
+ 205,
2183
+ 208,
2184
+ 210,
2185
+ 212,
2186
+ 215,
2187
+ 220,
2188
+ 222,
2189
+ 223,
2190
+ 225,
2191
+ 231,
2192
+ 232,
2193
+ 233,
2194
+ 234,
2195
+ 235,
2196
+ 247,
2197
+ 249,
2198
+ 250,
2199
+ 251,
2200
+ 252,
2201
+ 253,
2202
+ 254,
2203
+ 255,
2204
+ 256,
2205
+ 257,
2206
+ 259,
2207
+ 300,
2208
+ 320,
2209
+ 328,
2210
+ 330,
2211
+ 331,
2212
+ 156049354,
2213
+ 1742807556,
2214
+ 1839735485,
2215
+ 19719996,
2216
+ 483693784
2217
+ ]);
2218
+ var UNITY_BUILTIN_NAMESPACE_PREFIXES = ["UnityEngine", "UnityEditor"];
2219
+ function find_namespaced_class_id(component_name, allowed_class_ids) {
2220
+ const dot_index = component_name.lastIndexOf(".");
2221
+ if (dot_index <= 0)
2222
+ return null;
2223
+ const namespace_name = component_name.slice(0, dot_index);
2224
+ const short_name = component_name.slice(dot_index + 1);
2225
+ const is_unity_namespace = UNITY_BUILTIN_NAMESPACE_PREFIXES.some((prefix) => namespace_name === prefix || namespace_name.startsWith(`${prefix}.`));
2226
+ if (!is_unity_namespace || short_name.length === 0) {
2227
+ return null;
2228
+ }
2229
+ return find_class_id(short_name, allowed_class_ids);
2106
2230
  }
2107
- function get_class_id(component_name) {
2231
+ function find_class_id(component_name, allowed_class_ids) {
2232
+ const matches_allowed = (class_id) => {
2233
+ return allowed_class_ids ? allowed_class_ids.has(class_id) : true;
2234
+ };
2108
2235
  if (UNITY_CLASS_NAMES[component_name] !== undefined) {
2109
- return UNITY_CLASS_NAMES[component_name];
2236
+ const class_id = UNITY_CLASS_NAMES[component_name];
2237
+ return matches_allowed(class_id) ? class_id : null;
2110
2238
  }
2111
2239
  const lowerName = component_name.toLowerCase();
2112
2240
  for (const [name, id] of Object.entries(UNITY_CLASS_NAMES)) {
2113
2241
  if (name.toLowerCase() === lowerName) {
2114
- return id;
2242
+ return matches_allowed(id) ? id : null;
2115
2243
  }
2116
2244
  }
2245
+ const namespaced_match = find_namespaced_class_id(component_name, allowed_class_ids);
2246
+ if (namespaced_match !== null) {
2247
+ return namespaced_match;
2248
+ }
2117
2249
  return null;
2118
2250
  }
2251
+ function get_class_id_name(class_id) {
2252
+ return UNITY_CLASS_IDS[class_id] || `Unknown_${class_id}`;
2253
+ }
2254
+ function get_class_id(component_name) {
2255
+ return find_class_id(component_name);
2256
+ }
2257
+ function get_component_class_id(component_name) {
2258
+ return find_class_id(component_name, UNITY_COMPONENT_CLASS_IDS);
2259
+ }
2260
+ function get_addable_component_class_id(component_name) {
2261
+ return find_class_id(component_name, UNITY_ADDABLE_COMPONENT_CLASS_IDS);
2262
+ }
2119
2263
 
2120
2264
  // src/editor/shared.ts
2121
2265
  var import_fs8 = require("fs");
2122
2266
  var path3 = __toESM(require("path"));
2267
+
2268
+ // src/guid-cache.ts
2269
+ var import_fs7 = require("fs");
2270
+ var import_path5 = require("path");
2271
+ var _cache_store = new Map;
2272
+ function build_guid_cache(project_path, raw) {
2273
+ return {
2274
+ project_path,
2275
+ cache: raw,
2276
+ count: Object.keys(raw).length,
2277
+ resolve(guid) {
2278
+ return raw[guid] ?? null;
2279
+ },
2280
+ resolve_absolute(guid) {
2281
+ const rel = raw[guid];
2282
+ if (!rel)
2283
+ return null;
2284
+ if (import_path5.isAbsolute(rel))
2285
+ return rel;
2286
+ return import_path5.join(project_path, rel);
2287
+ },
2288
+ resolve_many(guids) {
2289
+ const result = {};
2290
+ for (const guid of guids) {
2291
+ result[guid] = raw[guid] ?? null;
2292
+ }
2293
+ return result;
2294
+ },
2295
+ find_by_name(name, extension) {
2296
+ const nameLower = name.toLowerCase().replace(/\.[^.]+$/, "");
2297
+ let substringMatch = null;
2298
+ for (const [guid, assetPath] of Object.entries(raw)) {
2299
+ if (extension && !assetPath.endsWith(extension))
2300
+ continue;
2301
+ const fileName = import_path5.basename(assetPath, import_path5.extname(assetPath)).toLowerCase();
2302
+ if (fileName === nameLower) {
2303
+ return { guid, path: assetPath };
2304
+ }
2305
+ if (!substringMatch && assetPath.toLowerCase().includes(nameLower)) {
2306
+ substringMatch = { guid, path: assetPath };
2307
+ }
2308
+ }
2309
+ return substringMatch;
2310
+ },
2311
+ find_all_by_name_exact(name, extension) {
2312
+ const nameLower = name.toLowerCase().replace(/\.[^.]+$/, "");
2313
+ const matches = [];
2314
+ for (const [guid, assetPath] of Object.entries(raw)) {
2315
+ if (extension && !assetPath.endsWith(extension))
2316
+ continue;
2317
+ const fileName = import_path5.basename(assetPath, import_path5.extname(assetPath)).toLowerCase();
2318
+ if (fileName === nameLower) {
2319
+ matches.push({ guid, path: assetPath });
2320
+ }
2321
+ }
2322
+ return matches;
2323
+ }
2324
+ };
2325
+ }
2326
+ function load_guid_cache(project_path) {
2327
+ const resolved = project_path;
2328
+ const existing = _cache_store.get(resolved);
2329
+ if (existing)
2330
+ return existing;
2331
+ const cachePath = import_path5.join(resolved, ".unity-agentic", "guid-cache.json");
2332
+ if (!import_fs7.existsSync(cachePath))
2333
+ return null;
2334
+ try {
2335
+ const raw = JSON.parse(import_fs7.readFileSync(cachePath, "utf-8"));
2336
+ const cache = build_guid_cache(resolved, raw);
2337
+ _cache_store.set(resolved, cache);
2338
+ return cache;
2339
+ } catch {
2340
+ return null;
2341
+ }
2342
+ }
2343
+ function load_guid_cache_for_file(file_path, explicit_project) {
2344
+ const project_path = explicit_project || find_unity_project_root(import_path5.dirname(file_path));
2345
+ if (!project_path)
2346
+ return null;
2347
+ return load_guid_cache(project_path);
2348
+ }
2349
+
2350
+ // src/editor/shared.ts
2123
2351
  function validateUnityYAML(content) {
2124
2352
  if (!content.startsWith("%YAML 1.1")) {
2125
2353
  return "Missing or invalid YAML header";
@@ -2172,8 +2400,100 @@ function resolve_source_prefab(doc, file_path, project_path) {
2172
2400
  prefab_instance_block: pi_block
2173
2401
  };
2174
2402
  }
2175
- function resolveScriptGuid(script, projectPath, options) {
2176
- const strictExactName = options?.strict_exact_name === true;
2403
+ function load_type_registry(projectPath) {
2404
+ const registryPath = path3.join(projectPath, ".unity-agentic", "type-registry.json");
2405
+ if (!import_fs8.existsSync(registryPath))
2406
+ return null;
2407
+ try {
2408
+ return JSON.parse(import_fs8.readFileSync(registryPath, "utf-8"));
2409
+ } catch {
2410
+ return null;
2411
+ }
2412
+ }
2413
+ function parse_type_query(script) {
2414
+ const dotIndex = script.lastIndexOf(".");
2415
+ if (dotIndex > 0) {
2416
+ return {
2417
+ targetNamespace: script.substring(0, dotIndex),
2418
+ targetName: script.substring(dotIndex + 1)
2419
+ };
2420
+ }
2421
+ return { targetName: script, targetNamespace: null };
2422
+ }
2423
+ function format_qualified_type(entry) {
2424
+ return entry.namespace ? `${entry.namespace}.${entry.name}` : entry.name;
2425
+ }
2426
+ function resolve_registry_entry_guid(match, projectPath) {
2427
+ if (match.guid) {
2428
+ return match.guid;
2429
+ }
2430
+ if (!match.filePath)
2431
+ return null;
2432
+ const fullFilePath = path3.isAbsolute(match.filePath) ? match.filePath : path3.join(projectPath, match.filePath);
2433
+ const metaPath = fullFilePath + ".meta";
2434
+ if (import_fs8.existsSync(metaPath)) {
2435
+ const guid = extractGuidFromMeta(metaPath);
2436
+ if (guid)
2437
+ return guid;
2438
+ }
2439
+ return null;
2440
+ }
2441
+ function materialize_registry_match(match, guid) {
2442
+ return { guid, path: match.filePath };
2443
+ }
2444
+ function dedupe_script_resolutions(resolutions) {
2445
+ const unique = new Map;
2446
+ for (const resolution of resolutions) {
2447
+ unique.set(`${resolution.guid}|${resolution.path ?? ""}`, resolution);
2448
+ }
2449
+ return [...unique.values()];
2450
+ }
2451
+ function resolve_script_guid_from_registry(script, projectPath, registry) {
2452
+ const { targetName, targetNamespace } = parse_type_query(script);
2453
+ const targetNameLower = targetName.toLowerCase();
2454
+ const matchingEntries = registry.filter((entry) => {
2455
+ if (entry.name.toLowerCase() !== targetNameLower)
2456
+ return false;
2457
+ if (targetNamespace && entry.namespace?.toLowerCase() !== targetNamespace.toLowerCase())
2458
+ return false;
2459
+ return true;
2460
+ });
2461
+ if (matchingEntries.length === 0) {
2462
+ return null;
2463
+ }
2464
+ const qualifiedMatches = [...new Set(matchingEntries.map(format_qualified_type))];
2465
+ if (!targetNamespace && qualifiedMatches.length > 1) {
2466
+ throw new Error(`Ambiguous type "${script}": found multiple qualified matches (${qualifiedMatches.join(", ")}). ` + `Specify which ${targetName} to use by qualified name (for example, "${qualifiedMatches[0]}") or provide the full script path.`);
2467
+ }
2468
+ const resolvedEntries = matchingEntries.map((entry) => ({
2469
+ entry,
2470
+ guid: resolve_registry_entry_guid(entry, projectPath)
2471
+ }));
2472
+ const sourceGuidCandidates = [...new Set(resolvedEntries.filter(({ entry, guid }) => entry.filePath && !entry.filePath.endsWith(".dll") && guid).map(({ guid }) => guid))];
2473
+ const dllMaterializedMatches = dedupe_script_resolutions(resolvedEntries.filter(({ entry }) => entry.filePath?.endsWith(".dll")).flatMap(({ entry, guid }) => {
2474
+ if (guid) {
2475
+ return [materialize_registry_match(entry, guid)];
2476
+ }
2477
+ return sourceGuidCandidates.map((sourceGuid) => materialize_registry_match(entry, sourceGuid));
2478
+ }));
2479
+ if (dllMaterializedMatches.length === 1) {
2480
+ return dllMaterializedMatches[0];
2481
+ }
2482
+ if (dllMaterializedMatches.length > 1) {
2483
+ const paths = dllMaterializedMatches.map((match) => match.path ?? match.guid).join(", ");
2484
+ throw new Error(`Ambiguous type "${script}": found ${dllMaterializedMatches.length} matches (${paths}). ` + `Use a qualified name (e.g., "Namespace.${targetName}") or provide the full path.`);
2485
+ }
2486
+ const materializedMatches = dedupe_script_resolutions(resolvedEntries.filter(({ entry, guid }) => !entry.filePath?.endsWith(".dll") && guid).map(({ entry, guid }) => materialize_registry_match(entry, guid)));
2487
+ if (materializedMatches.length === 1) {
2488
+ return materializedMatches[0];
2489
+ }
2490
+ if (materializedMatches.length > 1) {
2491
+ const paths = materializedMatches.map((match) => match.path ?? match.guid).join(", ");
2492
+ throw new Error(`Ambiguous type "${script}": found ${materializedMatches.length} matches (${paths}). ` + `Use a qualified name (e.g., "Namespace.${targetName}") or provide the full path.`);
2493
+ }
2494
+ return null;
2495
+ }
2496
+ function resolveScriptGuid(script, projectPath, _options) {
2177
2497
  if (/^[a-f0-9]{32}$/i.test(script)) {
2178
2498
  if (/^0{32}$/i.test(script)) {
2179
2499
  throw new Error(`Invalid script GUID "${script}": all-zero GUID is not allowed. Provide a real script GUID from a .meta file.`);
@@ -2200,215 +2520,14 @@ function resolveScriptGuid(script, projectPath, options) {
2200
2520
  }
2201
2521
  }
2202
2522
  if (projectPath) {
2203
- const guidCache = load_guid_cache(projectPath);
2204
- if (guidCache) {
2205
- if (strictExactName) {
2206
- const exactMatches = guidCache.find_all_by_name_exact(script, ".cs");
2207
- if (exactMatches.length === 1) {
2208
- return exactMatches[0];
2209
- }
2210
- if (exactMatches.length > 1) {
2211
- const paths = exactMatches.map((m) => m.path).join(", ");
2212
- throw new Error(`Ambiguous type "${script}": found ${exactMatches.length} exact script name matches (${paths}). ` + 'Use a qualified name (e.g., "Namespace.TypeName") or provide the full script path.');
2213
- }
2214
- } else {
2215
- const result = guidCache.find_by_name(script, ".cs");
2216
- if (result)
2217
- return result;
2523
+ const registry = load_type_registry(projectPath);
2524
+ if (registry) {
2525
+ const resolvedFromRegistry = resolve_script_guid_from_registry(script, projectPath, registry);
2526
+ if (resolvedFromRegistry) {
2527
+ return resolvedFromRegistry;
2218
2528
  }
2219
- }
2220
- const registryPath = path3.join(projectPath, ".unity-agentic", "type-registry.json");
2221
- if (import_fs8.existsSync(registryPath)) {
2222
- try {
2223
- const registry = JSON.parse(import_fs8.readFileSync(registryPath, "utf-8"));
2224
- let targetName = script;
2225
- let targetNamespace = null;
2226
- const dotIndex = script.lastIndexOf(".");
2227
- if (dotIndex > 0) {
2228
- targetNamespace = script.substring(0, dotIndex);
2229
- targetName = script.substring(dotIndex + 1);
2230
- }
2231
- const targetNameLower = targetName.toLowerCase();
2232
- const matches = registry.filter((t) => {
2233
- if (t.name.toLowerCase() !== targetNameLower)
2234
- return false;
2235
- if (targetNamespace && t.namespace?.toLowerCase() !== targetNamespace.toLowerCase())
2236
- return false;
2237
- return true;
2238
- });
2239
- if (matches.length === 1) {
2240
- if (matches[0].guid) {
2241
- return { guid: matches[0].guid, path: matches[0].filePath };
2242
- }
2243
- if (matches[0].filePath && projectPath) {
2244
- const fullFilePath = path3.isAbsolute(matches[0].filePath) ? matches[0].filePath : path3.join(projectPath, matches[0].filePath);
2245
- const metaPath = fullFilePath + ".meta";
2246
- if (import_fs8.existsSync(metaPath)) {
2247
- const guid = extractGuidFromMeta(metaPath);
2248
- if (guid)
2249
- return { guid, path: matches[0].filePath };
2250
- }
2251
- if (matches[0].filePath.startsWith("Packages/")) {
2252
- const pkgPath = path3.join(projectPath, matches[0].filePath);
2253
- const pkgMetaPath = pkgPath + ".meta";
2254
- if (import_fs8.existsSync(pkgMetaPath)) {
2255
- const guid = extractGuidFromMeta(pkgMetaPath);
2256
- if (guid)
2257
- return { guid, path: matches[0].filePath };
2258
- }
2259
- }
2260
- if (matches[0].filePath?.endsWith(".dll")) {
2261
- const classNameLower = targetName.toLowerCase();
2262
- const dllBasename = path3.basename(matches[0].filePath).toLowerCase();
2263
- for (const cacheName of ["package-cache.json", "local-package-cache.json"]) {
2264
- const cachePath = path3.join(projectPath, ".unity-agentic", cacheName);
2265
- if (!import_fs8.existsSync(cachePath))
2266
- continue;
2267
- try {
2268
- const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
2269
- for (const [guid, assetPath] of Object.entries(cache)) {
2270
- if (assetPath.endsWith(".cs") && path3.basename(assetPath, ".cs").toLowerCase() === classNameLower) {
2271
- return { guid, path: assetPath };
2272
- }
2273
- }
2274
- for (const [guid, assetPath] of Object.entries(cache)) {
2275
- if (assetPath.toLowerCase().endsWith(".dll") && path3.basename(assetPath).toLowerCase() === dllBasename) {
2276
- return { guid, path: assetPath };
2277
- }
2278
- }
2279
- } catch {}
2280
- }
2281
- }
2282
- }
2283
- }
2284
- if (matches.length > 1) {
2285
- const withGuid = matches.filter((m) => m.guid);
2286
- if (withGuid.length === 1) {
2287
- return { guid: withGuid[0].guid, path: withGuid[0].filePath };
2288
- }
2289
- const resolvedFromMeta = matches.filter((m) => !m.guid && m.filePath).map((m) => {
2290
- const fullPath = path3.isAbsolute(m.filePath) ? m.filePath : path3.join(projectPath, m.filePath);
2291
- let guid = import_fs8.existsSync(fullPath + ".meta") ? extractGuidFromMeta(fullPath + ".meta") : null;
2292
- if (!guid && m.filePath.startsWith("Packages/")) {
2293
- const pkgPath = path3.join(projectPath, m.filePath);
2294
- if (import_fs8.existsSync(pkgPath + ".meta")) {
2295
- guid = extractGuidFromMeta(pkgPath + ".meta");
2296
- }
2297
- }
2298
- if (!guid && m.filePath?.endsWith(".dll")) {
2299
- const clsLower = (m.name ?? targetName).toLowerCase();
2300
- const dllBase = path3.basename(m.filePath).toLowerCase();
2301
- for (const cn of ["package-cache.json", "local-package-cache.json"]) {
2302
- const cp = path3.join(projectPath, ".unity-agentic", cn);
2303
- if (!import_fs8.existsSync(cp))
2304
- continue;
2305
- try {
2306
- const c = JSON.parse(import_fs8.readFileSync(cp, "utf-8"));
2307
- for (const [g, p] of Object.entries(c)) {
2308
- if (p.endsWith(".cs") && path3.basename(p, ".cs").toLowerCase() === clsLower) {
2309
- guid = g;
2310
- break;
2311
- }
2312
- }
2313
- if (!guid) {
2314
- for (const [g, p] of Object.entries(c)) {
2315
- if (p.toLowerCase().endsWith(".dll") && path3.basename(p).toLowerCase() === dllBase) {
2316
- guid = g;
2317
- break;
2318
- }
2319
- }
2320
- }
2321
- } catch {}
2322
- if (guid)
2323
- break;
2324
- }
2325
- }
2326
- return guid ? { guid, path: m.filePath } : null;
2327
- }).filter((r) => r !== null);
2328
- const allResolved = [...withGuid.map((m) => ({ guid: m.guid, path: m.filePath })), ...resolvedFromMeta];
2329
- if (allResolved.length === 1) {
2330
- return allResolved[0];
2331
- }
2332
- if (allResolved.length > 1) {
2333
- const pkgCachePaths = [
2334
- path3.join(projectPath, ".unity-agentic", "package-cache.json"),
2335
- path3.join(projectPath, ".unity-agentic", "local-package-cache.json")
2336
- ];
2337
- for (const cachePath of pkgCachePaths) {
2338
- if (!import_fs8.existsSync(cachePath))
2339
- continue;
2340
- try {
2341
- const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
2342
- const inCache = allResolved.filter((r) => (r.guid in cache));
2343
- if (inCache.length === 1)
2344
- return inCache[0];
2345
- } catch {}
2346
- }
2347
- const paths = allResolved.map((m) => m.path).join(", ");
2348
- throw new Error(`Ambiguous type "${script}": found ${allResolved.length} matches (${paths}). ` + `Use a qualified name (e.g., "Namespace.${targetName}") or provide the full path.`);
2349
- }
2350
- }
2351
- } catch (err) {
2352
- if (err instanceof Error && err.message.startsWith("Ambiguous type")) {
2353
- throw err;
2354
- }
2355
- }
2356
- }
2357
- const scriptNameLower = script.toLowerCase().replace(/\.cs$/, "");
2358
- const dotIdx = scriptNameLower.lastIndexOf(".");
2359
- const classNameOnly = dotIdx > 0 ? scriptNameLower.substring(dotIdx + 1) : null;
2360
- const cachePaths = [
2361
- path3.join(projectPath, ".unity-agentic", "package-cache.json"),
2362
- path3.join(projectPath, ".unity-agentic", "local-package-cache.json")
2363
- ];
2364
- for (const cachePath of cachePaths) {
2365
- if (!import_fs8.existsSync(cachePath))
2366
- continue;
2367
- try {
2368
- const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
2369
- const exactMatches = [];
2370
- for (const [guid, assetPath] of Object.entries(cache)) {
2371
- if (!assetPath.endsWith(".cs"))
2372
- continue;
2373
- const fileName = path3.basename(assetPath, ".cs").toLowerCase();
2374
- if (fileName === scriptNameLower || classNameOnly && fileName === classNameOnly) {
2375
- exactMatches.push({ guid, path: assetPath });
2376
- }
2377
- }
2378
- if (exactMatches.length === 1) {
2379
- return exactMatches[0];
2380
- }
2381
- if (exactMatches.length > 1) {
2382
- const paths = exactMatches.map((m) => m.path).join(", ");
2383
- throw new Error(`Ambiguous type "${script}": found ${exactMatches.length} exact script name matches (${paths}). ` + 'Use a qualified name (e.g., "Namespace.TypeName") or provide the full script path.');
2384
- }
2385
- } catch (err) {
2386
- if (err instanceof Error && err.message.startsWith("Ambiguous type")) {
2387
- throw err;
2388
- }
2389
- }
2390
- }
2391
- if (strictExactName) {
2392
2529
  return null;
2393
2530
  }
2394
- try {
2395
- const assetsDir = path3.join(projectPath, "Assets");
2396
- if (import_fs8.existsSync(assetsDir)) {
2397
- const entries = import_fs8.readdirSync(assetsDir, { recursive: true, withFileTypes: false });
2398
- for (const entry of entries) {
2399
- if (!entry.endsWith(".cs"))
2400
- continue;
2401
- const fileName = path3.basename(entry, ".cs").toLowerCase();
2402
- if (fileName === scriptNameLower || classNameOnly && fileName === classNameOnly) {
2403
- const fullPath = path3.join(assetsDir, entry);
2404
- const guid = extractGuidFromMeta(fullPath + ".meta");
2405
- if (guid) {
2406
- return { guid, path: path3.join("Assets", entry) };
2407
- }
2408
- }
2409
- }
2410
- }
2411
- } catch {}
2412
2531
  }
2413
2532
  return null;
2414
2533
  }
@@ -2586,8 +2705,8 @@ function build_type_lookup(project_path) {
2586
2705
  const assetsDir = path3.join(project_path, "Assets");
2587
2706
  const csIndex = new Map;
2588
2707
  try {
2589
- const { readdirSync: readdirSync5 } = require("fs");
2590
- const entries = readdirSync5(assetsDir, { recursive: true, withFileTypes: false });
2708
+ const { readdirSync: readdirSync4 } = require("fs");
2709
+ const entries = readdirSync4(assetsDir, { recursive: true, withFileTypes: false });
2591
2710
  for (const entry of entries) {
2592
2711
  if (typeof entry === "string" && entry.endsWith(".cs")) {
2593
2712
  const basename4 = entry.substring(entry.lastIndexOf("/") + 1).replace(/\.cs$/, "");
@@ -5676,11 +5795,8 @@ function createScriptableObject(options) {
5676
5795
  if (!resolved) {
5677
5796
  const hints = [];
5678
5797
  if (project_path) {
5679
- const cacheExists = load_guid_cache(project_path) !== null;
5680
5798
  const registryExists = import_fs10.existsSync(path5.join(project_path, ".unity-agentic", "type-registry.json"));
5681
- if (!cacheExists && !registryExists) {
5682
- hints.push(`No GUID cache or type registry found at ${path5.join(project_path, ".unity-agentic/")}. Run "unity-agentic-tools setup" first.`);
5683
- } else if (!registryExists) {
5799
+ if (!registryExists) {
5684
5800
  hints.push('Type registry not found. Re-run "unity-agentic-tools setup" to rebuild.');
5685
5801
  }
5686
5802
  } else {
@@ -5976,14 +6092,39 @@ function addComponent(options) {
5976
6092
  gameObjectIdStr = goResult.file_id;
5977
6093
  }
5978
6094
  const gameObjectId = gameObjectIdStr;
5979
- const classId = get_class_id(component_type);
5980
- if (classId === 114 && component_type.toLowerCase() === "monobehaviour") {
6095
+ const shortComponentType = component_type.includes(".") ? component_type.slice(component_type.lastIndexOf(".") + 1) : component_type;
6096
+ const explicitBuiltInClassId = component_type.includes(".") ? get_addable_component_class_id(component_type) : null;
6097
+ const fallbackBuiltInClassId = component_type.includes(".") ? null : get_addable_component_class_id(component_type);
6098
+ const serializedTypeId = get_class_id(component_type) ?? get_class_id(shortComponentType);
6099
+ if (serializedTypeId === 114 && shortComponentType.toLowerCase() === "monobehaviour") {
5981
6100
  return {
5982
6101
  success: false,
5983
6102
  file_path,
5984
6103
  error: '"MonoBehaviour" is a base class, not a concrete component script. Provide a script type/path/GUID (for example, "PlayerController", "Assets/Scripts/PlayerController.cs", or a 32-char GUID).'
5985
6104
  };
5986
6105
  }
6106
+ const componentIdStr = doc.generate_file_id();
6107
+ const componentId = componentIdStr;
6108
+ let componentYAML;
6109
+ let scriptGuid;
6110
+ let scriptPath;
6111
+ let extractionError;
6112
+ let classId = explicitBuiltInClassId;
6113
+ let resolved = null;
6114
+ if (classId === null) {
6115
+ try {
6116
+ resolved = resolve_script_with_fields(component_type, project_path, { strict_exact_name: true });
6117
+ } catch (e) {
6118
+ return {
6119
+ success: false,
6120
+ file_path,
6121
+ error: e instanceof Error ? e.message : String(e)
6122
+ };
6123
+ }
6124
+ }
6125
+ if (!resolved && classId === null) {
6126
+ classId = fallbackBuiltInClassId;
6127
+ }
5987
6128
  let duplicateWarning;
5988
6129
  const goBlock = doc.find_by_file_id(gameObjectIdStr);
5989
6130
  if (goBlock && classId !== null) {
@@ -5996,39 +6137,20 @@ function addComponent(options) {
5996
6137
  }
5997
6138
  }
5998
6139
  }
5999
- const componentIdStr = doc.generate_file_id();
6000
- const componentId = componentIdStr;
6001
- let componentYAML;
6002
- let scriptGuid;
6003
- let scriptPath;
6004
- let extractionError;
6005
6140
  if (classId !== null) {
6006
6141
  const componentName = UNITY_CLASS_IDS[classId] || component_type;
6007
6142
  componentYAML = createGenericComponentYAML(componentName, classId, componentId, gameObjectId);
6008
6143
  } else {
6009
- let resolved;
6010
- try {
6011
- resolved = resolve_script_with_fields(component_type, project_path, { strict_exact_name: true });
6012
- } catch (e) {
6013
- return {
6014
- success: false,
6015
- file_path,
6016
- error: e instanceof Error ? e.message : String(e)
6017
- };
6018
- }
6019
6144
  if (!resolved) {
6020
6145
  const hints = [];
6146
+ if (serializedTypeId !== null) {
6147
+ hints.push(`"${component_type}" matches Unity serialized type class ${serializedTypeId}, but that type is not an addable GameObject component.`);
6148
+ }
6021
6149
  if (project_path) {
6022
6150
  const agenticDir = path5.join(project_path, ".unity-agentic");
6023
- const cacheExists = load_guid_cache(project_path) !== null;
6024
6151
  const registryExists = import_fs10.existsSync(path5.join(agenticDir, "type-registry.json"));
6025
- const pkgCacheExists = import_fs10.existsSync(path5.join(agenticDir, "package-cache.json"));
6026
- if (!cacheExists && !registryExists) {
6027
- hints.push(`No GUID cache or type registry found at ${agenticDir}/. Run "unity-agentic-tools setup" first.`);
6028
- } else if (!registryExists) {
6152
+ if (!registryExists) {
6029
6153
  hints.push('Type registry not found. Re-run "unity-agentic-tools setup" to rebuild.');
6030
- } else if (!pkgCacheExists) {
6031
- hints.push('Package cache not found. Re-run "unity-agentic-tools setup" to index package scripts.');
6032
6154
  }
6033
6155
  } else {
6034
6156
  hints.push("No Unity project detected. Provide --project or run from inside a Unity project directory.");
@@ -6563,7 +6685,7 @@ function editComponentByFileId(options) {
6563
6685
  return {
6564
6686
  success: false,
6565
6687
  file_path,
6566
- error: `Component with file ID ${file_id} not found. This file contains prefab instances \u2014 the fileID may be from the source prefab. Use \`update override\` to edit variant properties.`
6688
+ error: `Component with file ID ${file_id} not found. This file contains prefab instances \u2014 the fileID may be from the source prefab. Use \`unity-agentic-tools editor invoke UnityAgenticTools.Update.Prefabs PrefabOverride ...\` to edit variant properties.`
6567
6689
  };
6568
6690
  }
6569
6691
  return {
@@ -6576,7 +6698,7 @@ function editComponentByFileId(options) {
6576
6698
  return {
6577
6699
  success: false,
6578
6700
  file_path,
6579
- error: `Component ${file_id} is a stripped reference in a prefab variant. Use \`update override <file> <prefab_instance_id> <property_path> <value>\` to modify overrides.`
6701
+ error: `Component ${file_id} is a stripped reference in a prefab variant. Use \`unity-agentic-tools editor invoke UnityAgenticTools.Update.Prefabs PrefabOverride ...\` to modify overrides.`
6580
6702
  };
6581
6703
  }
6582
6704
  const classId = targetBlock.class_id;
@@ -6799,7 +6921,7 @@ function editTransform(options) {
6799
6921
  return {
6800
6922
  success: false,
6801
6923
  file_path,
6802
- error: `Transform ${transform_id} is a stripped reference in a prefab variant. Use \`update prefab override <file> <prefab_instance_id> <property_path> <value>\` to modify transform overrides (e.g., "m_LocalPosition.x").`
6924
+ error: `Transform ${transform_id} is a stripped reference in a prefab variant. Use \`unity-agentic-tools editor invoke UnityAgenticTools.Update.Prefabs PrefabOverride ...\` to modify transform overrides (for example, "m_LocalPosition.x").`
6803
6925
  };
6804
6926
  }
6805
6927
  let blockText = targetBlock.raw;
@@ -7622,29 +7744,32 @@ function find_component_by_type(doc, type_name, game_object, project_path) {
7622
7744
  scope_blocks = doc.blocks.filter((b) => b.class_id !== 1 && b.class_id !== 4 && b.class_id !== 224);
7623
7745
  }
7624
7746
  const candidates = [];
7625
- const class_id = get_class_id(type_name);
7747
+ const explicit_class_id = type_name.includes(".") ? get_component_class_id(type_name) : null;
7748
+ let resolved = null;
7749
+ if (explicit_class_id === null) {
7750
+ try {
7751
+ resolved = resolveScriptGuid(type_name, project_path, { strict_exact_name: true });
7752
+ } catch (error) {
7753
+ return { error: error instanceof Error ? error.message : String(error) };
7754
+ }
7755
+ }
7756
+ const class_id = resolved ? null : explicit_class_id ?? (!type_name.includes(".") ? get_component_class_id(type_name) : null);
7626
7757
  if (class_id !== null) {
7627
7758
  for (const block of scope_blocks) {
7628
7759
  if (block.class_id === class_id)
7629
7760
  candidates.push(block);
7630
7761
  }
7631
- } else {
7632
- let resolved = null;
7633
- try {
7634
- resolved = resolveScriptGuid(type_name, project_path);
7635
- } catch {}
7636
- if (resolved) {
7637
- for (const block of scope_blocks) {
7638
- if (block.class_id !== 114)
7639
- continue;
7640
- const scriptMatch = block.raw.match(/m_Script:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]+)/);
7641
- if (scriptMatch && scriptMatch[1] === resolved.guid) {
7642
- candidates.push(block);
7643
- }
7762
+ } else if (resolved) {
7763
+ for (const block of scope_blocks) {
7764
+ if (block.class_id !== 114)
7765
+ continue;
7766
+ const scriptMatch = block.raw.match(/m_Script:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]+)/);
7767
+ if (scriptMatch && scriptMatch[1] === resolved.guid) {
7768
+ candidates.push(block);
7644
7769
  }
7645
- } else {
7646
- return { error: `Component type "${type_name}" not found. For MonoBehaviour scripts, ensure "unity-agentic-tools setup" has been run.` };
7647
7770
  }
7771
+ } else {
7772
+ return { error: `Component type "${type_name}" not found. For MonoBehaviour scripts, ensure the type registry has been built with "unity-agentic-tools setup".` };
7648
7773
  }
7649
7774
  if (candidates.length === 0) {
7650
7775
  return { error: `No "${type_name}" component found${game_object ? ` on "${game_object}"` : ""} in ${doc.file_path}` };
@@ -8075,7 +8200,7 @@ function duplicateGameObject(options) {
8075
8200
  if ("error" in goResult) {
8076
8201
  const piMatch = findPrefabInstanceByName(doc, object_name);
8077
8202
  if (piMatch) {
8078
- return { success: false, file_path, error: `"${object_name}" is a PrefabInstance (fileID: ${piMatch.id}). Cloning PrefabInstances is not yet supported. Consider unpacking it first with \`update prefab\`.` };
8203
+ return { success: false, file_path, error: `"${object_name}" is a PrefabInstance (fileID: ${piMatch.id}). Cloning PrefabInstances is not yet supported. Consider unpacking it first with \`unity-agentic-tools editor invoke UnityAgenticTools.Update.Prefabs PrefabUnpack ...\`.` };
8079
8204
  }
8080
8205
  return { success: false, file_path, error: goResult.error };
8081
8206
  }