unity-agentic-tools 0.3.3 → 0.4.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/cli.js +1233 -870
- package/dist/index.js +352 -37
- package/native/unity-file-tools.darwin-arm64.node +0 -0
- package/native/unity-file-tools.darwin-x64.node +0 -0
- package/native/unity-file-tools.linux-x64-gnu.node +0 -0
- package/native/unity-file-tools.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
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.
|
|
208
|
+
version: "0.4.1",
|
|
209
209
|
description: "Fast, token-efficient Unity YAML parser for AI agents",
|
|
210
210
|
exports: {
|
|
211
211
|
".": {
|
|
@@ -284,6 +284,7 @@ __export(exports_src, {
|
|
|
284
284
|
removeRemovedGameObject: () => removeRemovedGameObject,
|
|
285
285
|
removeRemovedComponent: () => removeRemovedComponent,
|
|
286
286
|
removePrefabOverride: () => removePrefabOverride,
|
|
287
|
+
removeComponentBatch: () => removeComponentBatch,
|
|
287
288
|
removeComponent: () => removeComponent,
|
|
288
289
|
read_settings: () => read_settings,
|
|
289
290
|
isNativeModuleAvailable: () => isNativeModuleAvailable,
|
|
@@ -560,6 +561,12 @@ function find_unity_project_root(startDir) {
|
|
|
560
561
|
}
|
|
561
562
|
return null;
|
|
562
563
|
}
|
|
564
|
+
function ensure_parent_dir(file_path) {
|
|
565
|
+
const dir = import_path4.dirname(file_path);
|
|
566
|
+
if (dir && dir !== "." && !import_fs4.existsSync(dir)) {
|
|
567
|
+
import_fs4.mkdirSync(dir, { recursive: true });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
563
570
|
function atomicWrite(filePath, content) {
|
|
564
571
|
if (import_fs4.existsSync(filePath)) {
|
|
565
572
|
try {
|
|
@@ -1510,6 +1517,7 @@ function search_project(options) {
|
|
|
1510
1517
|
const files = walk_project_files(project_path, extensions);
|
|
1511
1518
|
const scanner = new UnityScanner;
|
|
1512
1519
|
const matches = [];
|
|
1520
|
+
let files_with_errors = 0;
|
|
1513
1521
|
for (const file of files) {
|
|
1514
1522
|
try {
|
|
1515
1523
|
let gameObjects;
|
|
@@ -1596,6 +1604,7 @@ function search_project(options) {
|
|
|
1596
1604
|
if (max_matches !== undefined && matches.length >= max_matches)
|
|
1597
1605
|
break;
|
|
1598
1606
|
} catch {
|
|
1607
|
+
files_with_errors++;
|
|
1599
1608
|
continue;
|
|
1600
1609
|
}
|
|
1601
1610
|
}
|
|
@@ -1604,6 +1613,7 @@ function search_project(options) {
|
|
|
1604
1613
|
project_path,
|
|
1605
1614
|
total_files_scanned: files.length,
|
|
1606
1615
|
total_matches: matches.length,
|
|
1616
|
+
files_with_errors: files_with_errors > 0 ? files_with_errors : undefined,
|
|
1607
1617
|
cursor: 0,
|
|
1608
1618
|
truncated: max_matches !== undefined && matches.length >= max_matches,
|
|
1609
1619
|
matches
|
|
@@ -2208,6 +2218,7 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2208
2218
|
}
|
|
2209
2219
|
}
|
|
2210
2220
|
if (matches[0].filePath?.endsWith(".dll")) {
|
|
2221
|
+
const classNameLower = targetName.toLowerCase();
|
|
2211
2222
|
const dllBasename = path3.basename(matches[0].filePath).toLowerCase();
|
|
2212
2223
|
for (const cacheName of ["package-cache.json", "local-package-cache.json"]) {
|
|
2213
2224
|
const cachePath = path3.join(projectPath, ".unity-agentic", cacheName);
|
|
@@ -2215,6 +2226,11 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2215
2226
|
continue;
|
|
2216
2227
|
try {
|
|
2217
2228
|
const cache = JSON.parse(import_fs8.readFileSync(cachePath, "utf-8"));
|
|
2229
|
+
for (const [guid, assetPath] of Object.entries(cache)) {
|
|
2230
|
+
if (assetPath.endsWith(".cs") && path3.basename(assetPath, ".cs").toLowerCase() === classNameLower) {
|
|
2231
|
+
return { guid, path: assetPath };
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2218
2234
|
for (const [guid, assetPath] of Object.entries(cache)) {
|
|
2219
2235
|
if (assetPath.toLowerCase().endsWith(".dll") && path3.basename(assetPath).toLowerCase() === dllBasename) {
|
|
2220
2236
|
return { guid, path: assetPath };
|
|
@@ -2240,6 +2256,7 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2240
2256
|
}
|
|
2241
2257
|
}
|
|
2242
2258
|
if (!guid && m.filePath?.endsWith(".dll")) {
|
|
2259
|
+
const clsLower = (m.name ?? targetName).toLowerCase();
|
|
2243
2260
|
const dllBase = path3.basename(m.filePath).toLowerCase();
|
|
2244
2261
|
for (const cn of ["package-cache.json", "local-package-cache.json"]) {
|
|
2245
2262
|
const cp = path3.join(projectPath, ".unity-agentic", cn);
|
|
@@ -2248,11 +2265,19 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2248
2265
|
try {
|
|
2249
2266
|
const c = JSON.parse(import_fs8.readFileSync(cp, "utf-8"));
|
|
2250
2267
|
for (const [g, p] of Object.entries(c)) {
|
|
2251
|
-
if (p.
|
|
2268
|
+
if (p.endsWith(".cs") && path3.basename(p, ".cs").toLowerCase() === clsLower) {
|
|
2252
2269
|
guid = g;
|
|
2253
2270
|
break;
|
|
2254
2271
|
}
|
|
2255
2272
|
}
|
|
2273
|
+
if (!guid) {
|
|
2274
|
+
for (const [g, p] of Object.entries(c)) {
|
|
2275
|
+
if (p.toLowerCase().endsWith(".dll") && path3.basename(p).toLowerCase() === dllBase) {
|
|
2276
|
+
guid = g;
|
|
2277
|
+
break;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2256
2281
|
} catch {}
|
|
2257
2282
|
if (guid)
|
|
2258
2283
|
break;
|
|
@@ -2283,7 +2308,11 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2283
2308
|
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
2309
|
}
|
|
2285
2310
|
}
|
|
2286
|
-
} catch {
|
|
2311
|
+
} catch (err) {
|
|
2312
|
+
if (err instanceof Error && err.message.startsWith("Ambiguous type")) {
|
|
2313
|
+
throw err;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2287
2316
|
}
|
|
2288
2317
|
const scriptNameLower = script.toLowerCase().replace(/\.cs$/, "");
|
|
2289
2318
|
const dotIdx = scriptNameLower.lastIndexOf(".");
|
|
@@ -2310,6 +2339,54 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2310
2339
|
}
|
|
2311
2340
|
return null;
|
|
2312
2341
|
}
|
|
2342
|
+
var ASSET_PPTR_MAP = {
|
|
2343
|
+
".inputactions": { fileID: 11400000, type: 2 },
|
|
2344
|
+
".asset": { fileID: 11400000, type: 2 },
|
|
2345
|
+
".mat": { fileID: 2100000, type: 2 },
|
|
2346
|
+
".prefab": { fileID: 100100000, type: 2 },
|
|
2347
|
+
".controller": { fileID: 9100000, type: 2 },
|
|
2348
|
+
".anim": { fileID: 7400000, type: 2 }
|
|
2349
|
+
};
|
|
2350
|
+
var ASSET_EXTENSIONS = new Set(Object.keys(ASSET_PPTR_MAP));
|
|
2351
|
+
function resolveAssetPathToPPtr(value, file_path, project_path) {
|
|
2352
|
+
if (value.startsWith("{fileID:") || value.startsWith("{"))
|
|
2353
|
+
return;
|
|
2354
|
+
const ext = path3.extname(value).toLowerCase();
|
|
2355
|
+
if (!ASSET_EXTENSIONS.has(ext))
|
|
2356
|
+
return;
|
|
2357
|
+
const resolved_project = project_path || find_unity_project_root(path3.dirname(file_path));
|
|
2358
|
+
if (!resolved_project)
|
|
2359
|
+
return null;
|
|
2360
|
+
const mapping = ASSET_PPTR_MAP[ext];
|
|
2361
|
+
let guid = null;
|
|
2362
|
+
const basename_val = path3.basename(value, ext);
|
|
2363
|
+
if (value.includes("/") || value.includes("\\")) {
|
|
2364
|
+
const abs = path3.isAbsolute(value) ? value : path3.join(resolved_project, value);
|
|
2365
|
+
guid = extractGuidFromMeta(abs + ".meta");
|
|
2366
|
+
}
|
|
2367
|
+
if (!guid) {
|
|
2368
|
+
const cache = load_guid_cache_for_file(file_path, resolved_project);
|
|
2369
|
+
if (cache) {
|
|
2370
|
+
const found = cache.find_by_name(basename_val, ext);
|
|
2371
|
+
if (found)
|
|
2372
|
+
guid = found.guid;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
if (!guid) {
|
|
2376
|
+
const candidates = [
|
|
2377
|
+
path3.join(resolved_project, "Assets", value),
|
|
2378
|
+
path3.join(resolved_project, value)
|
|
2379
|
+
];
|
|
2380
|
+
for (const candidate of candidates) {
|
|
2381
|
+
guid = extractGuidFromMeta(candidate + ".meta");
|
|
2382
|
+
if (guid)
|
|
2383
|
+
break;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
if (!guid)
|
|
2387
|
+
return null;
|
|
2388
|
+
return `{fileID: ${mapping.fileID}, guid: ${guid}, type: ${mapping.type}}`;
|
|
2389
|
+
}
|
|
2313
2390
|
function resolve_script_with_fields(script, project_path) {
|
|
2314
2391
|
const resolved = resolveScriptGuid(script, project_path);
|
|
2315
2392
|
if (!resolved)
|
|
@@ -2866,7 +2943,9 @@ ${element_indent2}- ${value}`);
|
|
|
2866
2943
|
if (!header) {
|
|
2867
2944
|
throw new Error(`Invalid Unity YAML block header in replacement text: "${new_text.slice(0, 80)}"`);
|
|
2868
2945
|
}
|
|
2869
|
-
this._raw = new_text
|
|
2946
|
+
this._raw = new_text.endsWith(`
|
|
2947
|
+
`) ? new_text : new_text + `
|
|
2948
|
+
`;
|
|
2870
2949
|
this._header = header;
|
|
2871
2950
|
this._dirty = true;
|
|
2872
2951
|
this._format_cache.clear();
|
|
@@ -2876,6 +2955,11 @@ ${element_indent2}- ${value}`);
|
|
|
2876
2955
|
}
|
|
2877
2956
|
_check_dirty(old_raw) {
|
|
2878
2957
|
if (this._raw !== old_raw) {
|
|
2958
|
+
if (!this._raw.endsWith(`
|
|
2959
|
+
`)) {
|
|
2960
|
+
this._raw += `
|
|
2961
|
+
`;
|
|
2962
|
+
}
|
|
2879
2963
|
this._dirty = true;
|
|
2880
2964
|
return true;
|
|
2881
2965
|
}
|
|
@@ -3937,14 +4021,82 @@ function read_project_version(projectPath) {
|
|
|
3937
4021
|
}
|
|
3938
4022
|
|
|
3939
4023
|
// src/editor/create.ts
|
|
4024
|
+
function md4(data) {
|
|
4025
|
+
function F(x, y, z) {
|
|
4026
|
+
return x & y | ~x & z;
|
|
4027
|
+
}
|
|
4028
|
+
function G(x, y, z) {
|
|
4029
|
+
return x & y | x & z | y & z;
|
|
4030
|
+
}
|
|
4031
|
+
function H(x, y, z) {
|
|
4032
|
+
return x ^ y ^ z;
|
|
4033
|
+
}
|
|
4034
|
+
function rotl(x, n) {
|
|
4035
|
+
return (x << n | x >>> 32 - n) >>> 0;
|
|
4036
|
+
}
|
|
4037
|
+
const len = data.length;
|
|
4038
|
+
const bitLen = len * 8;
|
|
4039
|
+
const padded_len = len + 9 + 63 & ~63;
|
|
4040
|
+
const buf = Buffer.alloc(padded_len);
|
|
4041
|
+
data.copy(buf);
|
|
4042
|
+
buf[len] = 128;
|
|
4043
|
+
buf.writeUInt32LE(bitLen >>> 0, padded_len - 8);
|
|
4044
|
+
buf.writeUInt32LE(Math.floor(bitLen / 4294967296) >>> 0, padded_len - 4);
|
|
4045
|
+
let a0 = 1732584193;
|
|
4046
|
+
let b0 = 4023233417;
|
|
4047
|
+
let c0 = 2562383102;
|
|
4048
|
+
let d0 = 271733878;
|
|
4049
|
+
const R1_K = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
4050
|
+
const R1_S = [3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19];
|
|
4051
|
+
const R2_K = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
|
|
4052
|
+
const R2_S = [3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13];
|
|
4053
|
+
const R3_K = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
|
|
4054
|
+
const R3_S = [3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15];
|
|
4055
|
+
for (let i = 0;i < padded_len; i += 64) {
|
|
4056
|
+
const X = [];
|
|
4057
|
+
for (let j = 0;j < 16; j++)
|
|
4058
|
+
X[j] = buf.readUInt32LE(i + j * 4);
|
|
4059
|
+
let a = a0, b = b0, c = c0, d = d0;
|
|
4060
|
+
for (let j = 0;j < 16; j++) {
|
|
4061
|
+
const t = a + F(b, c, d) + X[R1_K[j]] >>> 0;
|
|
4062
|
+
a = d;
|
|
4063
|
+
d = c;
|
|
4064
|
+
c = b;
|
|
4065
|
+
b = rotl(t, R1_S[j]);
|
|
4066
|
+
}
|
|
4067
|
+
for (let j = 0;j < 16; j++) {
|
|
4068
|
+
const t = a + G(b, c, d) + X[R2_K[j]] + 1518500249 >>> 0;
|
|
4069
|
+
a = d;
|
|
4070
|
+
d = c;
|
|
4071
|
+
c = b;
|
|
4072
|
+
b = rotl(t, R2_S[j]);
|
|
4073
|
+
}
|
|
4074
|
+
for (let j = 0;j < 16; j++) {
|
|
4075
|
+
const t = a + H(b, c, d) + X[R3_K[j]] + 1859775393 >>> 0;
|
|
4076
|
+
a = d;
|
|
4077
|
+
d = c;
|
|
4078
|
+
c = b;
|
|
4079
|
+
b = rotl(t, R3_S[j]);
|
|
4080
|
+
}
|
|
4081
|
+
a0 = a0 + a >>> 0;
|
|
4082
|
+
b0 = b0 + b >>> 0;
|
|
4083
|
+
c0 = c0 + c >>> 0;
|
|
4084
|
+
d0 = d0 + d >>> 0;
|
|
4085
|
+
}
|
|
4086
|
+
const result = Buffer.alloc(16);
|
|
4087
|
+
result.writeUInt32LE(a0, 0);
|
|
4088
|
+
result.writeUInt32LE(b0, 4);
|
|
4089
|
+
result.writeUInt32LE(c0, 8);
|
|
4090
|
+
result.writeUInt32LE(d0, 12);
|
|
4091
|
+
return result;
|
|
4092
|
+
}
|
|
3940
4093
|
function compute_dll_script_file_id(namespace, class_name) {
|
|
3941
|
-
const { createHash } = require("crypto");
|
|
3942
4094
|
const full_name = namespace ? `${namespace}.${class_name}` : class_name;
|
|
3943
4095
|
const name_bytes = Buffer.from(full_name, "utf-8");
|
|
3944
4096
|
const input = Buffer.alloc(4 + name_bytes.length);
|
|
3945
4097
|
input.writeInt32LE(114, 0);
|
|
3946
4098
|
name_bytes.copy(input, 4);
|
|
3947
|
-
const hash =
|
|
4099
|
+
const hash = md4(input);
|
|
3948
4100
|
const a = hash.readInt32LE(0);
|
|
3949
4101
|
const b = hash.readInt32LE(4);
|
|
3950
4102
|
const c = hash.readInt32LE(8);
|
|
@@ -3957,13 +4109,13 @@ function get_layer_from_parent(doc, parentTransformId) {
|
|
|
3957
4109
|
const parentTransform = doc.find_by_file_id(parentTransformId);
|
|
3958
4110
|
if (!parentTransform)
|
|
3959
4111
|
return 0;
|
|
3960
|
-
const goMatch = parentTransform.raw.match(/m_GameObject
|
|
4112
|
+
const goMatch = parentTransform.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
3961
4113
|
if (!goMatch)
|
|
3962
4114
|
return 0;
|
|
3963
4115
|
const parentGo = doc.find_by_file_id(goMatch[1]);
|
|
3964
4116
|
if (!parentGo)
|
|
3965
4117
|
return 0;
|
|
3966
|
-
const layerMatch = parentGo.raw.match(/m_Layer
|
|
4118
|
+
const layerMatch = parentGo.raw.match(/m_Layer:[ \t]*(\d+)/);
|
|
3967
4119
|
return layerMatch ? parseInt(layerMatch[1], 10) : 0;
|
|
3968
4120
|
}
|
|
3969
4121
|
function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
|
|
@@ -4284,11 +4436,14 @@ ${defaults}`;
|
|
|
4284
4436
|
function addComponentToGameObject(doc, gameObjectId, componentId) {
|
|
4285
4437
|
const goBlock = doc.find_by_file_id(gameObjectId);
|
|
4286
4438
|
if (!goBlock)
|
|
4287
|
-
return;
|
|
4288
|
-
|
|
4289
|
-
|
|
4439
|
+
return false;
|
|
4440
|
+
const raw = goBlock.raw;
|
|
4441
|
+
const updated = raw.replace(/(m_Component:[ \t]*\n(?:[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*\d+\}[ \t]*\n)*)/, `$1 - component: {fileID: ${componentId}}
|
|
4290
4442
|
`);
|
|
4291
|
-
|
|
4443
|
+
if (updated === raw)
|
|
4444
|
+
return false;
|
|
4445
|
+
goBlock.replace_raw(updated);
|
|
4446
|
+
return true;
|
|
4292
4447
|
}
|
|
4293
4448
|
function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000, type_lookup) {
|
|
4294
4449
|
const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version, " ", type_lookup) : `
|
|
@@ -4360,7 +4515,7 @@ function createGameObject(options) {
|
|
|
4360
4515
|
if (parentBlock.class_id === 4) {
|
|
4361
4516
|
parentTransformIdStr = parentIdStr;
|
|
4362
4517
|
} else if (parentBlock.class_id === 1) {
|
|
4363
|
-
const compMatch = parentBlock.raw.match(/m_Component
|
|
4518
|
+
const compMatch = parentBlock.raw.match(/m_Component:[ \t]*\n[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
4364
4519
|
if (compMatch) {
|
|
4365
4520
|
parentTransformIdStr = compMatch[1];
|
|
4366
4521
|
} else {
|
|
@@ -4774,6 +4929,7 @@ Light:
|
|
|
4774
4929
|
`;
|
|
4775
4930
|
}
|
|
4776
4931
|
try {
|
|
4932
|
+
ensure_parent_dir(output_path);
|
|
4777
4933
|
import_fs10.writeFileSync(output_path, yaml, "utf-8");
|
|
4778
4934
|
} catch (err) {
|
|
4779
4935
|
return {
|
|
@@ -4912,6 +5068,7 @@ PrefabInstance:
|
|
|
4912
5068
|
m_SourcePrefab: {fileID: 100100000, guid: ${sourceGuid}, type: 3}
|
|
4913
5069
|
`;
|
|
4914
5070
|
try {
|
|
5071
|
+
ensure_parent_dir(output_path);
|
|
4915
5072
|
import_fs10.writeFileSync(output_path, variantYaml, "utf-8");
|
|
4916
5073
|
} catch (err) {
|
|
4917
5074
|
return {
|
|
@@ -5072,6 +5229,7 @@ ${yaml_lines.join(`
|
|
|
5072
5229
|
}
|
|
5073
5230
|
}
|
|
5074
5231
|
try {
|
|
5232
|
+
ensure_parent_dir(output_path);
|
|
5075
5233
|
import_fs10.writeFileSync(output_path, assetYaml, "utf-8");
|
|
5076
5234
|
} catch (err) {
|
|
5077
5235
|
return { success: false, output_path, error: `Failed to write asset file: ${err instanceof Error ? err.message : String(err)}` };
|
|
@@ -5267,7 +5425,7 @@ function addComponent(options) {
|
|
|
5267
5425
|
let duplicateWarning;
|
|
5268
5426
|
const goBlock = doc.find_by_file_id(gameObjectIdStr);
|
|
5269
5427
|
if (goBlock && classId !== null) {
|
|
5270
|
-
const compRefs = [...goBlock.raw.matchAll(/component
|
|
5428
|
+
const compRefs = [...goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/g)].map((m) => m[1]);
|
|
5271
5429
|
for (const refId of compRefs) {
|
|
5272
5430
|
const compBlock = doc.find_by_file_id(refId);
|
|
5273
5431
|
if (compBlock && compBlock.class_id === classId) {
|
|
@@ -5299,12 +5457,16 @@ function addComponent(options) {
|
|
|
5299
5457
|
if (!resolved) {
|
|
5300
5458
|
const hints = [];
|
|
5301
5459
|
if (project_path) {
|
|
5460
|
+
const agenticDir = path5.join(project_path, ".unity-agentic");
|
|
5302
5461
|
const cacheExists = load_guid_cache(project_path) !== null;
|
|
5303
|
-
const registryExists = import_fs10.existsSync(path5.join(
|
|
5462
|
+
const registryExists = import_fs10.existsSync(path5.join(agenticDir, "type-registry.json"));
|
|
5463
|
+
const pkgCacheExists = import_fs10.existsSync(path5.join(agenticDir, "package-cache.json"));
|
|
5304
5464
|
if (!cacheExists && !registryExists) {
|
|
5305
|
-
hints.push(`No GUID cache or type registry found at ${
|
|
5465
|
+
hints.push(`No GUID cache or type registry found at ${agenticDir}/. Run "unity-agentic-tools setup" first.`);
|
|
5306
5466
|
} else if (!registryExists) {
|
|
5307
5467
|
hints.push('Type registry not found. Re-run "unity-agentic-tools setup" to rebuild.');
|
|
5468
|
+
} else if (!pkgCacheExists) {
|
|
5469
|
+
hints.push('Package cache not found. Re-run "unity-agentic-tools setup" to index package scripts.');
|
|
5308
5470
|
}
|
|
5309
5471
|
} else {
|
|
5310
5472
|
hints.push("No Unity project detected. Provide --project or run from inside a Unity project directory.");
|
|
@@ -5348,9 +5510,33 @@ function addComponent(options) {
|
|
|
5348
5510
|
if (variantInfo) {
|
|
5349
5511
|
appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
|
|
5350
5512
|
} else {
|
|
5351
|
-
addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
5513
|
+
const added = addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
5514
|
+
if (!added) {
|
|
5515
|
+
return {
|
|
5516
|
+
success: false,
|
|
5517
|
+
file_path,
|
|
5518
|
+
error: `Failed to add component reference to GameObject ${gameObjectIdStr}: m_Component array not found or malformed`
|
|
5519
|
+
};
|
|
5520
|
+
}
|
|
5352
5521
|
}
|
|
5353
5522
|
doc.append_raw(componentYAML);
|
|
5523
|
+
if (!doc.validate()) {
|
|
5524
|
+
return {
|
|
5525
|
+
success: false,
|
|
5526
|
+
file_path,
|
|
5527
|
+
error: "Validation failed after adding component. The generated YAML may be malformed."
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5530
|
+
if (!variantInfo) {
|
|
5531
|
+
const goBlockAfter = doc.find_by_file_id(gameObjectIdStr);
|
|
5532
|
+
if (!goBlockAfter || !goBlockAfter.raw.includes(`component: {fileID: ${componentIdStr}}`)) {
|
|
5533
|
+
return {
|
|
5534
|
+
success: false,
|
|
5535
|
+
file_path,
|
|
5536
|
+
error: `Component block appended but reference not found in GameObject ${gameObjectIdStr}'s m_Component list`
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5354
5540
|
const saveResult = doc.save();
|
|
5355
5541
|
if (!saveResult.success) {
|
|
5356
5542
|
return {
|
|
@@ -5406,8 +5592,11 @@ function copyComponent(options) {
|
|
|
5406
5592
|
const newIdStr = doc.generate_file_id();
|
|
5407
5593
|
const newId = parseInt(newIdStr, 10);
|
|
5408
5594
|
let clonedBlock = sourceBlock.raw.replace(new RegExp(`^(--- !u!${sourceBlock.class_id} &)${source_file_id}`), `$1${newId}`);
|
|
5409
|
-
clonedBlock = clonedBlock.replace(/m_GameObject
|
|
5410
|
-
addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
5595
|
+
clonedBlock = clonedBlock.replace(/m_GameObject:[ \t]*\{fileID:[ \t]*\d+\}/, `m_GameObject: {fileID: ${targetGoId}}`);
|
|
5596
|
+
const added = addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
5597
|
+
if (!added) {
|
|
5598
|
+
return { success: false, file_path, error: `Failed to wire component to target GameObject ${targetGoIdStr}: m_Component array not found or malformed` };
|
|
5599
|
+
}
|
|
5411
5600
|
doc.append_raw(clonedBlock);
|
|
5412
5601
|
if (!doc.validate()) {
|
|
5413
5602
|
return { success: false, file_path, error: "Validation failed after copying component" };
|
|
@@ -5457,7 +5646,17 @@ function validate_value_type(current_value, new_value) {
|
|
|
5457
5646
|
const incoming = new_value.trim();
|
|
5458
5647
|
if (/^\{fileID:/.test(current)) {
|
|
5459
5648
|
if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
|
|
5460
|
-
|
|
5649
|
+
if (/^\{fileID:/.test(incoming))
|
|
5650
|
+
return null;
|
|
5651
|
+
if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(incoming))
|
|
5652
|
+
return null;
|
|
5653
|
+
if (/^(true|false|yes|no|on|off)$/i.test(incoming))
|
|
5654
|
+
return null;
|
|
5655
|
+
if (/^'[^']*'$/.test(incoming) || /^"[^"]*"$/.test(incoming))
|
|
5656
|
+
return null;
|
|
5657
|
+
if (/^\{.*:.*\}$/.test(incoming))
|
|
5658
|
+
return null;
|
|
5659
|
+
return `Current value is a null reference ({fileID: 0}). New value "${incoming}" is not a valid reference, number, boolean, quoted string, or compound value.`;
|
|
5461
5660
|
}
|
|
5462
5661
|
if (!/^\{fileID:/.test(incoming)) {
|
|
5463
5662
|
return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
|
|
@@ -5667,7 +5866,19 @@ function editProperty(options) {
|
|
|
5667
5866
|
return result;
|
|
5668
5867
|
}
|
|
5669
5868
|
function editComponentByFileId(options) {
|
|
5670
|
-
const { file_path, file_id, property,
|
|
5869
|
+
const { file_path, file_id, property, project_path } = options;
|
|
5870
|
+
let { new_value } = options;
|
|
5871
|
+
const resolved = resolveAssetPathToPPtr(new_value, file_path, project_path);
|
|
5872
|
+
if (resolved === null) {
|
|
5873
|
+
return {
|
|
5874
|
+
success: false,
|
|
5875
|
+
file_path,
|
|
5876
|
+
error: `Could not resolve "${new_value}" to asset reference. Ensure GUID cache exists (run "setup <project>" first).`
|
|
5877
|
+
};
|
|
5878
|
+
}
|
|
5879
|
+
if (resolved !== undefined) {
|
|
5880
|
+
new_value = resolved;
|
|
5881
|
+
}
|
|
5671
5882
|
const pathError = validate_file_path(file_path, "write");
|
|
5672
5883
|
if (pathError) {
|
|
5673
5884
|
return { success: false, file_path, error: pathError };
|
|
@@ -6492,8 +6703,56 @@ function removeRemovedGameObject(options) {
|
|
|
6492
6703
|
}
|
|
6493
6704
|
// src/editor/delete.ts
|
|
6494
6705
|
var import_fs12 = require("fs");
|
|
6706
|
+
function find_component_by_type(doc, type_name, game_object, project_path) {
|
|
6707
|
+
let scope_blocks;
|
|
6708
|
+
if (game_object) {
|
|
6709
|
+
const goResult = doc.require_unique_game_object(game_object);
|
|
6710
|
+
if ("error" in goResult)
|
|
6711
|
+
return goResult;
|
|
6712
|
+
const comp_ids = [];
|
|
6713
|
+
const matches = goResult.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
|
|
6714
|
+
for (const m of matches)
|
|
6715
|
+
comp_ids.push(m[1]);
|
|
6716
|
+
scope_blocks = comp_ids.map((id) => doc.find_by_file_id(id)).filter((b) => b !== null);
|
|
6717
|
+
} else {
|
|
6718
|
+
scope_blocks = doc.blocks.filter((b) => b.class_id !== 1 && b.class_id !== 4 && b.class_id !== 224);
|
|
6719
|
+
}
|
|
6720
|
+
const candidates = [];
|
|
6721
|
+
const class_id = get_class_id(type_name);
|
|
6722
|
+
if (class_id !== null) {
|
|
6723
|
+
for (const block of scope_blocks) {
|
|
6724
|
+
if (block.class_id === class_id)
|
|
6725
|
+
candidates.push(block);
|
|
6726
|
+
}
|
|
6727
|
+
} else {
|
|
6728
|
+
let resolved = null;
|
|
6729
|
+
try {
|
|
6730
|
+
resolved = resolveScriptGuid(type_name, project_path);
|
|
6731
|
+
} catch {}
|
|
6732
|
+
if (resolved) {
|
|
6733
|
+
for (const block of scope_blocks) {
|
|
6734
|
+
if (block.class_id !== 114)
|
|
6735
|
+
continue;
|
|
6736
|
+
const scriptMatch = block.raw.match(/m_Script:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]+)/);
|
|
6737
|
+
if (scriptMatch && scriptMatch[1] === resolved.guid) {
|
|
6738
|
+
candidates.push(block);
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
} else {
|
|
6742
|
+
return { error: `Component type "${type_name}" not found. For MonoBehaviour scripts, ensure "unity-agentic-tools setup" has been run.` };
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6745
|
+
if (candidates.length === 0) {
|
|
6746
|
+
return { error: `No "${type_name}" component found${game_object ? ` on "${game_object}"` : ""} in ${doc.file_path}` };
|
|
6747
|
+
}
|
|
6748
|
+
if (candidates.length > 1) {
|
|
6749
|
+
const ids = candidates.map((b) => b.file_id).join(", ");
|
|
6750
|
+
return { error: `Multiple "${type_name}" components found (fileIDs: ${ids}). Use a numeric fileID to specify which one${game_object ? "" : ", or use --on <gameobject> to scope the search"}.` };
|
|
6751
|
+
}
|
|
6752
|
+
return candidates[0];
|
|
6753
|
+
}
|
|
6495
6754
|
function removeComponent(options) {
|
|
6496
|
-
const { file_path, file_id } = options;
|
|
6755
|
+
const { file_path, file_id, game_object, project_path } = options;
|
|
6497
6756
|
const pathError = validate_file_path(file_path, "write");
|
|
6498
6757
|
if (pathError) {
|
|
6499
6758
|
return { success: false, file_path, error: pathError };
|
|
@@ -6507,9 +6766,18 @@ function removeComponent(options) {
|
|
|
6507
6766
|
} catch (err) {
|
|
6508
6767
|
return { success: false, file_path, error: `Failed to read file: ${err instanceof Error ? err.message : String(err)}` };
|
|
6509
6768
|
}
|
|
6510
|
-
|
|
6511
|
-
if (
|
|
6512
|
-
|
|
6769
|
+
let found;
|
|
6770
|
+
if (/^-?\d+$/.test(file_id)) {
|
|
6771
|
+
found = doc.find_by_file_id(file_id);
|
|
6772
|
+
if (!found) {
|
|
6773
|
+
return { success: false, file_path, error: `Component with file ID ${file_id} not found` };
|
|
6774
|
+
}
|
|
6775
|
+
} else {
|
|
6776
|
+
const result = find_component_by_type(doc, file_id, game_object, project_path);
|
|
6777
|
+
if ("error" in result) {
|
|
6778
|
+
return { success: false, file_path, error: result.error };
|
|
6779
|
+
}
|
|
6780
|
+
found = result;
|
|
6513
6781
|
}
|
|
6514
6782
|
if (found.class_id === 1) {
|
|
6515
6783
|
return { success: false, file_path, error: "Cannot remove a GameObject with remove-component. Use delete instead." };
|
|
@@ -6517,18 +6785,28 @@ function removeComponent(options) {
|
|
|
6517
6785
|
if (found.class_id === 4) {
|
|
6518
6786
|
return { success: false, file_path, error: "Cannot remove a Transform with remove-component. Use delete to remove the entire GameObject." };
|
|
6519
6787
|
}
|
|
6520
|
-
const
|
|
6788
|
+
const resolved_file_id = found.file_id;
|
|
6789
|
+
const goMatch = found.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
6521
6790
|
if (goMatch) {
|
|
6522
6791
|
const parentGoId = goMatch[1];
|
|
6523
6792
|
const goBlock = doc.find_by_file_id(parentGoId);
|
|
6524
6793
|
if (goBlock) {
|
|
6525
|
-
const escaped =
|
|
6794
|
+
const escaped = resolved_file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6526
6795
|
const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
|
|
6527
6796
|
const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
|
|
6797
|
+
if (modifiedRaw === goBlock.raw) {}
|
|
6528
6798
|
goBlock.replace_raw(modifiedRaw);
|
|
6529
6799
|
}
|
|
6530
6800
|
}
|
|
6531
|
-
|
|
6801
|
+
const dangling = [];
|
|
6802
|
+
for (const block of doc.blocks) {
|
|
6803
|
+
if (block.file_id === resolved_file_id)
|
|
6804
|
+
continue;
|
|
6805
|
+
if (block.raw.includes(`{fileID: ${resolved_file_id}}`)) {
|
|
6806
|
+
dangling.push(block.file_id);
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
doc.remove_block(resolved_file_id);
|
|
6532
6810
|
if (!doc.validate()) {
|
|
6533
6811
|
return { success: false, file_path, error: "Validation failed after removing component" };
|
|
6534
6812
|
}
|
|
@@ -6539,8 +6817,45 @@ function removeComponent(options) {
|
|
|
6539
6817
|
return {
|
|
6540
6818
|
success: true,
|
|
6541
6819
|
file_path,
|
|
6542
|
-
removed_file_id:
|
|
6543
|
-
removed_class_id: found.class_id
|
|
6820
|
+
removed_file_id: resolved_file_id,
|
|
6821
|
+
removed_class_id: found.class_id,
|
|
6822
|
+
warning: dangling.length > 0 ? `Dangling references to deleted component found in blocks: ${dangling.join(", ")}` : undefined
|
|
6823
|
+
};
|
|
6824
|
+
}
|
|
6825
|
+
function removeComponentBatch(options) {
|
|
6826
|
+
const { project_path, component_type, game_object } = options;
|
|
6827
|
+
const files = walk_project_files(project_path, [".unity", ".prefab"]);
|
|
6828
|
+
const removals = [];
|
|
6829
|
+
const errors = [];
|
|
6830
|
+
let skipped = 0;
|
|
6831
|
+
for (const file of files) {
|
|
6832
|
+
const result = removeComponent({
|
|
6833
|
+
file_path: file,
|
|
6834
|
+
file_id: component_type,
|
|
6835
|
+
game_object,
|
|
6836
|
+
project_path
|
|
6837
|
+
});
|
|
6838
|
+
if (result.success && result.removed_file_id) {
|
|
6839
|
+
removals.push({
|
|
6840
|
+
file,
|
|
6841
|
+
file_id: result.removed_file_id,
|
|
6842
|
+
class_id: result.removed_class_id ?? 0
|
|
6843
|
+
});
|
|
6844
|
+
} else if (result.error?.includes("not found") || result.error?.startsWith('No "')) {
|
|
6845
|
+
skipped++;
|
|
6846
|
+
} else if (result.error) {
|
|
6847
|
+
errors.push({ file, error: result.error });
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
return {
|
|
6851
|
+
success: errors.length === 0,
|
|
6852
|
+
project_path,
|
|
6853
|
+
component: component_type,
|
|
6854
|
+
files_scanned: files.length,
|
|
6855
|
+
files_modified: removals.length,
|
|
6856
|
+
removals,
|
|
6857
|
+
skipped,
|
|
6858
|
+
errors
|
|
6544
6859
|
};
|
|
6545
6860
|
}
|
|
6546
6861
|
function deleteGameObject(options) {
|
|
@@ -6565,7 +6880,7 @@ function deleteGameObject(options) {
|
|
|
6565
6880
|
const goBlock = goResult;
|
|
6566
6881
|
const goId = goBlock.file_id;
|
|
6567
6882
|
const componentIds = new Set;
|
|
6568
|
-
const compMatches = goBlock.raw.matchAll(/component
|
|
6883
|
+
const compMatches = goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
|
|
6569
6884
|
for (const cm of compMatches) {
|
|
6570
6885
|
componentIds.add(cm[1]);
|
|
6571
6886
|
}
|
|
@@ -6575,7 +6890,7 @@ function deleteGameObject(options) {
|
|
|
6575
6890
|
const block = doc.find_by_file_id(compId);
|
|
6576
6891
|
if (block && block.class_id === 4) {
|
|
6577
6892
|
transformId = compId;
|
|
6578
|
-
const fatherMatch = block.raw.match(/m_Father
|
|
6893
|
+
const fatherMatch = block.raw.match(/m_Father:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
6579
6894
|
if (fatherMatch) {
|
|
6580
6895
|
fatherId = fatherMatch[1];
|
|
6581
6896
|
}
|
|
@@ -6643,7 +6958,7 @@ function deletePrefabInstance(options) {
|
|
|
6643
6958
|
}
|
|
6644
6959
|
const piId = piBlock.file_id;
|
|
6645
6960
|
const allToRemove = new Set([piId]);
|
|
6646
|
-
const piRefPattern = new RegExp(`m_PrefabInstance
|
|
6961
|
+
const piRefPattern = new RegExp(`m_PrefabInstance:[ \\t]*\\{fileID:[ \\t]*${piId}\\}`);
|
|
6647
6962
|
for (const block of doc.blocks) {
|
|
6648
6963
|
if (block.is_stripped && piRefPattern.test(block.raw)) {
|
|
6649
6964
|
allToRemove.add(block.file_id);
|
|
@@ -6653,13 +6968,13 @@ function deletePrefabInstance(options) {
|
|
|
6653
6968
|
const addedGOMatch = piBlock.raw.match(addedGOPattern);
|
|
6654
6969
|
if (addedGOMatch) {
|
|
6655
6970
|
const addedGOSection = addedGOMatch[1];
|
|
6656
|
-
const goMatches = addedGOSection.matchAll(/fileID
|
|
6971
|
+
const goMatches = addedGOSection.matchAll(/fileID:[ \t]*(\d+)/g);
|
|
6657
6972
|
for (const match of goMatches) {
|
|
6658
6973
|
const goId = match[1];
|
|
6659
6974
|
allToRemove.add(goId);
|
|
6660
6975
|
const goBlock = doc.find_by_file_id(goId);
|
|
6661
6976
|
if (goBlock) {
|
|
6662
|
-
const compMatch = goBlock.raw.match(/component
|
|
6977
|
+
const compMatch = goBlock.raw.match(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
6663
6978
|
if (compMatch) {
|
|
6664
6979
|
const transformId = compMatch[1];
|
|
6665
6980
|
allToRemove.add(transformId);
|
|
@@ -6675,12 +6990,12 @@ function deletePrefabInstance(options) {
|
|
|
6675
6990
|
const addedCompMatch = piBlock.raw.match(addedCompPattern);
|
|
6676
6991
|
if (addedCompMatch) {
|
|
6677
6992
|
const addedCompSection = addedCompMatch[1];
|
|
6678
|
-
const compMatches = addedCompSection.matchAll(/fileID
|
|
6993
|
+
const compMatches = addedCompSection.matchAll(/fileID:[ \t]*(\d+)/g);
|
|
6679
6994
|
for (const match of compMatches) {
|
|
6680
6995
|
allToRemove.add(match[1]);
|
|
6681
6996
|
}
|
|
6682
6997
|
}
|
|
6683
|
-
const parentMatch = piBlock.raw.match(/m_TransformParent
|
|
6998
|
+
const parentMatch = piBlock.raw.match(/m_TransformParent:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
6684
6999
|
const parentTransformId = parentMatch ? parentMatch[1] : "0";
|
|
6685
7000
|
let strippedRootTransformId = null;
|
|
6686
7001
|
for (const id of allToRemove) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|