dominus-cli 0.2.1 → 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 +499 -75
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +18 -3
- package/dist/mcp.js.map +1 -1
- package/package.json +17 -16
- package/plugin/Dominus.rbxm +0 -0
- package/plugin/src/Executor.lua +1 -0
- package/plugin/src/Explorer.lua +123 -1
- package/plugin/src/Properties.lua +213 -28
- package/plugin/src/UIBuilder.lua +494 -32
- package/plugin/src/init.server.lua +111 -22
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { randomBytes } from 'crypto';
|
|
|
19
19
|
import Spinner from 'ink-spinner';
|
|
20
20
|
import OpenAI from 'openai';
|
|
21
21
|
import https from 'https';
|
|
22
|
+
import { fileURLToPath } from 'url';
|
|
22
23
|
|
|
23
24
|
var __defProp = Object.defineProperty;
|
|
24
25
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -221,6 +222,7 @@ var init_protocol = __esm({
|
|
|
221
222
|
SET_SCRIPT: "cli:set:script",
|
|
222
223
|
RUN_CODE: "cli:run",
|
|
223
224
|
GET_PROPERTIES: "cli:get:properties",
|
|
225
|
+
GET_DESCENDANTS_PROPERTIES: "cli:get:descendants:properties",
|
|
224
226
|
SET_PROPERTIES: "cli:set:properties",
|
|
225
227
|
INSERT_INSTANCE: "cli:insert",
|
|
226
228
|
DELETE_INSTANCE: "cli:delete",
|
|
@@ -234,7 +236,13 @@ var init_protocol = __esm({
|
|
|
234
236
|
END_TEST: "cli:test:end",
|
|
235
237
|
BUILD_UI: "cli:build:ui",
|
|
236
238
|
GET_REFLECTION: "cli:get:reflection",
|
|
237
|
-
UPLOAD_ASSET: "cli:upload:asset"
|
|
239
|
+
UPLOAD_ASSET: "cli:upload:asset",
|
|
240
|
+
SERIALIZE_UI: "cli:serialize:ui",
|
|
241
|
+
MOVE_INSTANCE: "cli:move:instance",
|
|
242
|
+
UNDO: "cli:undo",
|
|
243
|
+
REDO: "cli:redo",
|
|
244
|
+
BULK_SET_PROPERTIES: "cli:bulk:set:properties",
|
|
245
|
+
FIND_REPLACE_SCRIPTS: "cli:find:replace:scripts"
|
|
238
246
|
};
|
|
239
247
|
}
|
|
240
248
|
});
|
|
@@ -1119,16 +1127,65 @@ var init_get_properties = __esm({
|
|
|
1119
1127
|
}
|
|
1120
1128
|
});
|
|
1121
1129
|
|
|
1130
|
+
// src/tools/studio/get-descendants-properties.ts
|
|
1131
|
+
var get_descendants_properties_exports = {};
|
|
1132
|
+
__export(get_descendants_properties_exports, {
|
|
1133
|
+
tool: () => tool6
|
|
1134
|
+
});
|
|
1135
|
+
var tool6;
|
|
1136
|
+
var init_get_descendants_properties = __esm({
|
|
1137
|
+
"src/tools/studio/get-descendants-properties.ts"() {
|
|
1138
|
+
init_protocol();
|
|
1139
|
+
tool6 = {
|
|
1140
|
+
name: "get_descendants_properties",
|
|
1141
|
+
description: "Easily get all descendants of a UI instance (or any instance) and all of their properties.",
|
|
1142
|
+
parameters: {
|
|
1143
|
+
type: "object",
|
|
1144
|
+
properties: {
|
|
1145
|
+
path: {
|
|
1146
|
+
type: "string",
|
|
1147
|
+
description: 'Full instance path (e.g., "Workspace.Part", "StarterGui.ScreenGui")'
|
|
1148
|
+
},
|
|
1149
|
+
outputFile: {
|
|
1150
|
+
type: "string",
|
|
1151
|
+
description: "Optional file path to write the JSON result to, useful for very large trees where context limits apply."
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
required: ["path"]
|
|
1155
|
+
},
|
|
1156
|
+
async execute(params, ctx) {
|
|
1157
|
+
if (!ctx.isStudioConnected()) {
|
|
1158
|
+
return { success: false, error: "Studio is not connected" };
|
|
1159
|
+
}
|
|
1160
|
+
const response = await ctx.sendToStudio(
|
|
1161
|
+
CliMsg.GET_DESCENDANTS_PROPERTIES,
|
|
1162
|
+
{ path: params.path },
|
|
1163
|
+
1e3 * 60 * 5
|
|
1164
|
+
// 5 minute timeout since getting ALL descendants can take a very long time
|
|
1165
|
+
);
|
|
1166
|
+
if (params.outputFile && typeof params.outputFile === "string") {
|
|
1167
|
+
const fs6 = await import('fs/promises');
|
|
1168
|
+
const path5 = await import('path');
|
|
1169
|
+
const outputPath = path5.resolve(process.cwd(), params.outputFile);
|
|
1170
|
+
await fs6.writeFile(outputPath, JSON.stringify(response.payload, null, 2), "utf-8");
|
|
1171
|
+
return { success: true, data: `Successfully saved descendants to ${outputPath}` };
|
|
1172
|
+
}
|
|
1173
|
+
return { success: true, data: response.payload };
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1122
1179
|
// src/tools/studio/set-properties.ts
|
|
1123
1180
|
var set_properties_exports = {};
|
|
1124
1181
|
__export(set_properties_exports, {
|
|
1125
|
-
tool: () =>
|
|
1182
|
+
tool: () => tool7
|
|
1126
1183
|
});
|
|
1127
|
-
var
|
|
1184
|
+
var tool7;
|
|
1128
1185
|
var init_set_properties = __esm({
|
|
1129
1186
|
"src/tools/studio/set-properties.ts"() {
|
|
1130
1187
|
init_protocol();
|
|
1131
|
-
|
|
1188
|
+
tool7 = {
|
|
1132
1189
|
name: "set_properties",
|
|
1133
1190
|
description: "Set properties on an instance in Roblox Studio.",
|
|
1134
1191
|
parameters: {
|
|
@@ -1166,13 +1223,13 @@ var init_set_properties = __esm({
|
|
|
1166
1223
|
// src/tools/studio/insert-instance.ts
|
|
1167
1224
|
var insert_instance_exports = {};
|
|
1168
1225
|
__export(insert_instance_exports, {
|
|
1169
|
-
tool: () =>
|
|
1226
|
+
tool: () => tool8
|
|
1170
1227
|
});
|
|
1171
|
-
var
|
|
1228
|
+
var tool8;
|
|
1172
1229
|
var init_insert_instance = __esm({
|
|
1173
1230
|
"src/tools/studio/insert-instance.ts"() {
|
|
1174
1231
|
init_protocol();
|
|
1175
|
-
|
|
1232
|
+
tool8 = {
|
|
1176
1233
|
name: "insert_instance",
|
|
1177
1234
|
description: "Insert a new instance into the Roblox Studio DataModel.",
|
|
1178
1235
|
parameters: {
|
|
@@ -1220,13 +1277,13 @@ var init_insert_instance = __esm({
|
|
|
1220
1277
|
// src/tools/studio/delete-instance.ts
|
|
1221
1278
|
var delete_instance_exports = {};
|
|
1222
1279
|
__export(delete_instance_exports, {
|
|
1223
|
-
tool: () =>
|
|
1280
|
+
tool: () => tool9
|
|
1224
1281
|
});
|
|
1225
|
-
var
|
|
1282
|
+
var tool9;
|
|
1226
1283
|
var init_delete_instance = __esm({
|
|
1227
1284
|
"src/tools/studio/delete-instance.ts"() {
|
|
1228
1285
|
init_protocol();
|
|
1229
|
-
|
|
1286
|
+
tool9 = {
|
|
1230
1287
|
name: "delete_instance",
|
|
1231
1288
|
description: "Delete an instance from the Roblox Studio DataModel by path.",
|
|
1232
1289
|
parameters: {
|
|
@@ -1257,13 +1314,13 @@ var init_delete_instance = __esm({
|
|
|
1257
1314
|
// src/tools/studio/get-output.ts
|
|
1258
1315
|
var get_output_exports = {};
|
|
1259
1316
|
__export(get_output_exports, {
|
|
1260
|
-
tool: () =>
|
|
1317
|
+
tool: () => tool10
|
|
1261
1318
|
});
|
|
1262
|
-
var
|
|
1319
|
+
var tool10;
|
|
1263
1320
|
var init_get_output = __esm({
|
|
1264
1321
|
"src/tools/studio/get-output.ts"() {
|
|
1265
1322
|
init_protocol();
|
|
1266
|
-
|
|
1323
|
+
tool10 = {
|
|
1267
1324
|
name: "get_output",
|
|
1268
1325
|
description: "Get recent output log entries from Roblox Studio (prints, warnings, errors).",
|
|
1269
1326
|
parameters: {
|
|
@@ -1298,13 +1355,13 @@ var init_get_output = __esm({
|
|
|
1298
1355
|
// src/tools/studio/get-selection.ts
|
|
1299
1356
|
var get_selection_exports = {};
|
|
1300
1357
|
__export(get_selection_exports, {
|
|
1301
|
-
tool: () =>
|
|
1358
|
+
tool: () => tool11
|
|
1302
1359
|
});
|
|
1303
|
-
var
|
|
1360
|
+
var tool11;
|
|
1304
1361
|
var init_get_selection = __esm({
|
|
1305
1362
|
"src/tools/studio/get-selection.ts"() {
|
|
1306
1363
|
init_protocol();
|
|
1307
|
-
|
|
1364
|
+
tool11 = {
|
|
1308
1365
|
name: "get_selection",
|
|
1309
1366
|
description: "Get the currently selected instances in Roblox Studio.",
|
|
1310
1367
|
parameters: {
|
|
@@ -1325,13 +1382,13 @@ var init_get_selection = __esm({
|
|
|
1325
1382
|
// src/tools/studio/search-scripts.ts
|
|
1326
1383
|
var search_scripts_exports = {};
|
|
1327
1384
|
__export(search_scripts_exports, {
|
|
1328
|
-
tool: () =>
|
|
1385
|
+
tool: () => tool12
|
|
1329
1386
|
});
|
|
1330
|
-
var
|
|
1387
|
+
var tool12;
|
|
1331
1388
|
var init_search_scripts = __esm({
|
|
1332
1389
|
"src/tools/studio/search-scripts.ts"() {
|
|
1333
1390
|
init_protocol();
|
|
1334
|
-
|
|
1391
|
+
tool12 = {
|
|
1335
1392
|
name: "search_scripts",
|
|
1336
1393
|
description: "Search across all scripts in Roblox Studio for a text pattern. Returns matching lines with context.",
|
|
1337
1394
|
parameters: {
|
|
@@ -1366,13 +1423,13 @@ var init_search_scripts = __esm({
|
|
|
1366
1423
|
// src/tools/studio/run-tests.ts
|
|
1367
1424
|
var run_tests_exports = {};
|
|
1368
1425
|
__export(run_tests_exports, {
|
|
1369
|
-
tool: () =>
|
|
1426
|
+
tool: () => tool13
|
|
1370
1427
|
});
|
|
1371
|
-
var
|
|
1428
|
+
var tool13;
|
|
1372
1429
|
var init_run_tests = __esm({
|
|
1373
1430
|
"src/tools/studio/run-tests.ts"() {
|
|
1374
1431
|
init_protocol();
|
|
1375
|
-
|
|
1432
|
+
tool13 = {
|
|
1376
1433
|
name: "run_tests",
|
|
1377
1434
|
description: "Run tests in Roblox Studio using StudioTestService. Can run all tests or a specific test by name.",
|
|
1378
1435
|
parameters: {
|
|
@@ -1406,13 +1463,13 @@ var init_run_tests = __esm({
|
|
|
1406
1463
|
// src/tools/studio/get-class-info.ts
|
|
1407
1464
|
var get_class_info_exports = {};
|
|
1408
1465
|
__export(get_class_info_exports, {
|
|
1409
|
-
tool: () =>
|
|
1466
|
+
tool: () => tool14
|
|
1410
1467
|
});
|
|
1411
|
-
var
|
|
1468
|
+
var tool14;
|
|
1412
1469
|
var init_get_class_info = __esm({
|
|
1413
1470
|
"src/tools/studio/get-class-info.ts"() {
|
|
1414
1471
|
init_protocol();
|
|
1415
|
-
|
|
1472
|
+
tool14 = {
|
|
1416
1473
|
name: "get_class_info",
|
|
1417
1474
|
description: "Look up the full schema of a Roblox class using ReflectionService. Returns all properties (with their types), methods (with parameters), and events. ALWAYS call this before creating or modifying GUI/UI elements (Frame, TextLabel, ImageLabel, etc.) to know the correct property names and expected types (e.g., Size is UDim2 for GUI, Vector3 for Parts).",
|
|
1418
1475
|
parameters: {
|
|
@@ -1443,13 +1500,13 @@ var init_get_class_info = __esm({
|
|
|
1443
1500
|
// src/tools/studio/create-ui.ts
|
|
1444
1501
|
var create_ui_exports = {};
|
|
1445
1502
|
__export(create_ui_exports, {
|
|
1446
|
-
tool: () =>
|
|
1503
|
+
tool: () => tool15
|
|
1447
1504
|
});
|
|
1448
|
-
var
|
|
1505
|
+
var tool15;
|
|
1449
1506
|
var init_create_ui = __esm({
|
|
1450
1507
|
"src/tools/studio/create-ui.ts"() {
|
|
1451
1508
|
init_protocol();
|
|
1452
|
-
|
|
1509
|
+
tool15 = {
|
|
1453
1510
|
name: "create_ui",
|
|
1454
1511
|
description: 'Create an entire UI tree in Roblox Studio in a SINGLE call. Pass a declarative JSON tree with ClassName, Name, properties, and Children. All types are auto-coerced natively in Luau \u2014 UDim2, Color3, Font, enums all work. This is 10x faster than run_code for UI because it handles everything in one round-trip with no Luau syntax issues. Replaces any existing instance with the same name.\n\nExample: create a pastel hello world screen:\n{\n "parent": "StarterGui",\n "tree": {\n "ClassName": "ScreenGui",\n "Name": "HelloWorld",\n "ResetOnSpawn": false,\n "Children": [{\n "ClassName": "Frame",\n "Name": "Background",\n "Size": [1, 0, 1, 0],\n "BackgroundColor3": "#F0E6FF",\n "Children": [\n { "ClassName": "UICorner", "CornerRadius": [0, 12] },\n { "ClassName": "TextLabel", "Name": "Title", "Text": "Hello World!",\n "Size": [0.5, 0, 0.1, 0], "Position": [0.25, 0, 0.45, 0],\n "BackgroundTransparency": 1, "TextColor3": "#6C5CE7",\n "TextScaled": true, "Font": "GothamBold" }\n ]\n }]\n }\n}',
|
|
1455
1512
|
parameters: {
|
|
@@ -1461,7 +1518,11 @@ var init_create_ui = __esm({
|
|
|
1461
1518
|
},
|
|
1462
1519
|
tree: {
|
|
1463
1520
|
type: "object",
|
|
1464
|
-
description: 'The root UI node. Each node has: ClassName (string), Name (string), any properties as key-value pairs, and Children (array of child nodes). Property values are auto-coerced:\n UDim2: [xScale, xOffset, yScale, yOffset] or {XScale, XOffset, YScale, YOffset}\n Color3: "#hex", "rgb(r,g,b)", [r,g,b], or BrickColor name\n UDim: [scale, offset] or number\n Vector2: {x, y} or [
|
|
1521
|
+
description: 'The root UI node. Each node has: ClassName (string), Name (string), any properties as key-value pairs, and Children (array of child nodes). Property values are auto-coerced:\n UDim2: [xScale, xOffset, yScale, yOffset] or {XScale, XOffset, YScale, YOffset}\n Color3: "#hex", "rgb(r,g,b)", [r,g,b], or BrickColor name\n UDim: [scale, offset] or number\n Vector2: [x, y] or {x, y} (auto-disambiguated from UDim by property name)\n NumberRange: [min, max] or number\n Rect: [minX, minY, maxX, maxY] or {MinX, MinY, MaxX, MaxY}\n Font: "GothamBold", "SourceSans", etc. (Enum.Font names)\n Enums: string name like "Center", "Left", "Fit"\n FontFace: {Family, Weight, Style}\n 2-element arrays are auto-disambiguated: AnchorPoint\u2192Vector2, CornerRadius/Padding\u2192UDim, etc.'
|
|
1522
|
+
},
|
|
1523
|
+
animate: {
|
|
1524
|
+
type: "boolean",
|
|
1525
|
+
description: "If true, task.wait() will be injected between creation of nodes so that the UI writes out dynamically."
|
|
1465
1526
|
}
|
|
1466
1527
|
},
|
|
1467
1528
|
required: ["tree"]
|
|
@@ -1472,6 +1533,7 @@ var init_create_ui = __esm({
|
|
|
1472
1533
|
}
|
|
1473
1534
|
const response = await ctx.sendToStudio(CliMsg.BUILD_UI, {
|
|
1474
1535
|
parent: params.parent ?? "StarterGui",
|
|
1536
|
+
animate: params.animate,
|
|
1475
1537
|
tree: params.tree
|
|
1476
1538
|
});
|
|
1477
1539
|
const payload = response.payload;
|
|
@@ -1490,13 +1552,13 @@ var init_create_ui = __esm({
|
|
|
1490
1552
|
// src/tools/studio/execute-run-test.ts
|
|
1491
1553
|
var execute_run_test_exports = {};
|
|
1492
1554
|
__export(execute_run_test_exports, {
|
|
1493
|
-
tool: () =>
|
|
1555
|
+
tool: () => tool16
|
|
1494
1556
|
});
|
|
1495
|
-
var
|
|
1557
|
+
var tool16;
|
|
1496
1558
|
var init_execute_run_test = __esm({
|
|
1497
1559
|
"src/tools/studio/execute-run-test.ts"() {
|
|
1498
1560
|
init_protocol();
|
|
1499
|
-
|
|
1561
|
+
tool16 = {
|
|
1500
1562
|
name: "execute_run_test",
|
|
1501
1563
|
description: "Start a Run mode test session in Roblox Studio using StudioTestService:ExecuteRunModeAsync(). Passes args that server scripts can retrieve via GetTestArgs(). Yields until the session ends (via EndTest or manual stop). Returns the value passed to EndTest(). Use this for headless automated tests \u2014 no player character, just server scripts running.",
|
|
1502
1564
|
parameters: {
|
|
@@ -1535,13 +1597,13 @@ var init_execute_run_test = __esm({
|
|
|
1535
1597
|
// src/tools/studio/execute-play-test.ts
|
|
1536
1598
|
var execute_play_test_exports = {};
|
|
1537
1599
|
__export(execute_play_test_exports, {
|
|
1538
|
-
tool: () =>
|
|
1600
|
+
tool: () => tool17
|
|
1539
1601
|
});
|
|
1540
|
-
var
|
|
1602
|
+
var tool17;
|
|
1541
1603
|
var init_execute_play_test = __esm({
|
|
1542
1604
|
"src/tools/studio/execute-play-test.ts"() {
|
|
1543
1605
|
init_protocol();
|
|
1544
|
-
|
|
1606
|
+
tool17 = {
|
|
1545
1607
|
name: "execute_play_test",
|
|
1546
1608
|
description: "Start a solo Play mode test session in Roblox Studio using StudioTestService:ExecutePlayModeAsync(). Passes args that server scripts can retrieve via GetTestArgs(). Yields until the session ends (via EndTest or manual stop). Returns the value passed to EndTest(). Use this for tests that need a player character and full client-server simulation.",
|
|
1547
1609
|
parameters: {
|
|
@@ -1580,13 +1642,13 @@ var init_execute_play_test = __esm({
|
|
|
1580
1642
|
// src/tools/studio/create-part.ts
|
|
1581
1643
|
var create_part_exports = {};
|
|
1582
1644
|
__export(create_part_exports, {
|
|
1583
|
-
tool: () =>
|
|
1645
|
+
tool: () => tool18
|
|
1584
1646
|
});
|
|
1585
|
-
var
|
|
1647
|
+
var tool18;
|
|
1586
1648
|
var init_create_part = __esm({
|
|
1587
1649
|
"src/tools/studio/create-part.ts"() {
|
|
1588
1650
|
init_protocol();
|
|
1589
|
-
|
|
1651
|
+
tool18 = {
|
|
1590
1652
|
name: "create_part",
|
|
1591
1653
|
description: "Create a Part in Roblox Studio with position, size, color, material and more \u2014 all in one call. Much faster than insert_instance + set_properties separately.",
|
|
1592
1654
|
parameters: {
|
|
@@ -1679,7 +1741,7 @@ var init_create_part = __esm({
|
|
|
1679
1741
|
// src/tools/studio/clone-instance.ts
|
|
1680
1742
|
var clone_instance_exports = {};
|
|
1681
1743
|
__export(clone_instance_exports, {
|
|
1682
|
-
tool: () =>
|
|
1744
|
+
tool: () => tool19
|
|
1683
1745
|
});
|
|
1684
1746
|
function buildCloneCode(params) {
|
|
1685
1747
|
const path5 = params.path;
|
|
@@ -1714,11 +1776,11 @@ function buildCloneCode(params) {
|
|
|
1714
1776
|
lines.push(`print("Cloned: " .. clone:GetFullName())`);
|
|
1715
1777
|
return lines.join("\n");
|
|
1716
1778
|
}
|
|
1717
|
-
var
|
|
1779
|
+
var tool19;
|
|
1718
1780
|
var init_clone_instance = __esm({
|
|
1719
1781
|
"src/tools/studio/clone-instance.ts"() {
|
|
1720
1782
|
init_protocol();
|
|
1721
|
-
|
|
1783
|
+
tool19 = {
|
|
1722
1784
|
name: "clone_instance",
|
|
1723
1785
|
description: "Clone an existing instance in Roblox Studio and optionally reposition/rename it. Great for duplicating parts, models, or entire trees.",
|
|
1724
1786
|
parameters: {
|
|
@@ -1754,13 +1816,13 @@ var init_clone_instance = __esm({
|
|
|
1754
1816
|
// src/tools/studio/group-instances.ts
|
|
1755
1817
|
var group_instances_exports = {};
|
|
1756
1818
|
__export(group_instances_exports, {
|
|
1757
|
-
tool: () =>
|
|
1819
|
+
tool: () => tool20
|
|
1758
1820
|
});
|
|
1759
|
-
var
|
|
1821
|
+
var tool20;
|
|
1760
1822
|
var init_group_instances = __esm({
|
|
1761
1823
|
"src/tools/studio/group-instances.ts"() {
|
|
1762
1824
|
init_protocol();
|
|
1763
|
-
|
|
1825
|
+
tool20 = {
|
|
1764
1826
|
name: "group_instances",
|
|
1765
1827
|
description: 'Group multiple instances under a new Model or Folder. Useful for organizing parts into objects (e.g., grouping trunk + leaves into a "Tree" model).',
|
|
1766
1828
|
parameters: {
|
|
@@ -1820,7 +1882,7 @@ var init_group_instances = __esm({
|
|
|
1820
1882
|
// src/tools/studio/build-multiple.ts
|
|
1821
1883
|
var build_multiple_exports = {};
|
|
1822
1884
|
__export(build_multiple_exports, {
|
|
1823
|
-
tool: () =>
|
|
1885
|
+
tool: () => tool21
|
|
1824
1886
|
});
|
|
1825
1887
|
function generateBatchCode(parts, groupName, groupParent) {
|
|
1826
1888
|
const lines = [];
|
|
@@ -1879,11 +1941,11 @@ function generateBatchCode(parts, groupName, groupParent) {
|
|
|
1879
1941
|
lines.push(`print("Built ${parts.length} parts${groupName ? ` in ${groupName}` : ""}")`);
|
|
1880
1942
|
return lines.join("\n");
|
|
1881
1943
|
}
|
|
1882
|
-
var
|
|
1944
|
+
var tool21;
|
|
1883
1945
|
var init_build_multiple = __esm({
|
|
1884
1946
|
"src/tools/studio/build-multiple.ts"() {
|
|
1885
1947
|
init_protocol();
|
|
1886
|
-
|
|
1948
|
+
tool21 = {
|
|
1887
1949
|
name: "build_multiple",
|
|
1888
1950
|
description: "Create multiple parts/instances in one batch call. Send an array of specs and they all get created in a single Studio round-trip. Ideal for building trees, houses, vehicles, terrain \u2014 anything composed of multiple parts.",
|
|
1889
1951
|
parameters: {
|
|
@@ -1952,16 +2014,234 @@ var init_build_multiple = __esm({
|
|
|
1952
2014
|
}
|
|
1953
2015
|
});
|
|
1954
2016
|
|
|
2017
|
+
// src/tools/studio/serialize-ui.ts
|
|
2018
|
+
var serialize_ui_exports = {};
|
|
2019
|
+
__export(serialize_ui_exports, {
|
|
2020
|
+
tool: () => tool22
|
|
2021
|
+
});
|
|
2022
|
+
var tool22;
|
|
2023
|
+
var init_serialize_ui = __esm({
|
|
2024
|
+
"src/tools/studio/serialize-ui.ts"() {
|
|
2025
|
+
init_protocol();
|
|
2026
|
+
tool22 = {
|
|
2027
|
+
name: "serialize_ui",
|
|
2028
|
+
description: "Serialize an existing UI tree from Roblox Studio into create_ui-compatible JSON. This is the reverse of create_ui \u2014 read any instance and get back a declarative JSON tree you can modify and pass back to create_ui. Perfect for cloning, templating, or refactoring UIs.\n\nThe output includes ClassName, Name, all non-default properties (with values converted to JSON-friendly formats like [r,g,b] for Color3, [xS,xO,yS,yO] for UDim2, etc.), and Children.",
|
|
2029
|
+
parameters: {
|
|
2030
|
+
type: "object",
|
|
2031
|
+
properties: {
|
|
2032
|
+
path: {
|
|
2033
|
+
type: "string",
|
|
2034
|
+
description: 'Instance path to serialize (e.g. "StarterGui.MyScreenGui")'
|
|
2035
|
+
},
|
|
2036
|
+
maxDepth: {
|
|
2037
|
+
type: "number",
|
|
2038
|
+
description: "Maximum depth to serialize (default: 50)"
|
|
2039
|
+
}
|
|
2040
|
+
},
|
|
2041
|
+
required: ["path"]
|
|
2042
|
+
},
|
|
2043
|
+
async execute(params, ctx) {
|
|
2044
|
+
if (!ctx.isStudioConnected()) {
|
|
2045
|
+
return { success: false, error: "Studio is not connected" };
|
|
2046
|
+
}
|
|
2047
|
+
const response = await ctx.sendToStudio(CliMsg.SERIALIZE_UI, {
|
|
2048
|
+
path: params.path,
|
|
2049
|
+
maxDepth: params.maxDepth ?? 50
|
|
2050
|
+
}, 1e3 * 60 * 2);
|
|
2051
|
+
const payload = response.payload;
|
|
2052
|
+
if (!payload.success) {
|
|
2053
|
+
return { success: false, error: payload.error ?? "Serialization failed" };
|
|
2054
|
+
}
|
|
2055
|
+
return { success: true, data: payload.tree };
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
});
|
|
2060
|
+
|
|
2061
|
+
// src/tools/studio/move-instance.ts
|
|
2062
|
+
var move_instance_exports = {};
|
|
2063
|
+
__export(move_instance_exports, {
|
|
2064
|
+
tool: () => tool23
|
|
2065
|
+
});
|
|
2066
|
+
var tool23;
|
|
2067
|
+
var init_move_instance = __esm({
|
|
2068
|
+
"src/tools/studio/move-instance.ts"() {
|
|
2069
|
+
init_protocol();
|
|
2070
|
+
tool23 = {
|
|
2071
|
+
name: "move_instance",
|
|
2072
|
+
description: "Move (reparent) an instance to a new parent in Roblox Studio. This preserves the instance and all its descendants \u2014 much better than delete + recreate. Sets a ChangeHistoryService waypoint so the move can be undone.",
|
|
2073
|
+
parameters: {
|
|
2074
|
+
type: "object",
|
|
2075
|
+
properties: {
|
|
2076
|
+
path: {
|
|
2077
|
+
type: "string",
|
|
2078
|
+
description: 'Full path of the instance to move (e.g. "Workspace.OldFolder.MyPart")'
|
|
2079
|
+
},
|
|
2080
|
+
newParent: {
|
|
2081
|
+
type: "string",
|
|
2082
|
+
description: 'Full path of the new parent (e.g. "Workspace.NewFolder")'
|
|
2083
|
+
}
|
|
2084
|
+
},
|
|
2085
|
+
required: ["path", "newParent"]
|
|
2086
|
+
},
|
|
2087
|
+
async execute(params, ctx) {
|
|
2088
|
+
if (!ctx.isStudioConnected()) {
|
|
2089
|
+
return { success: false, error: "Studio is not connected" };
|
|
2090
|
+
}
|
|
2091
|
+
const response = await ctx.sendToStudio(CliMsg.MOVE_INSTANCE, {
|
|
2092
|
+
path: params.path,
|
|
2093
|
+
newParent: params.newParent
|
|
2094
|
+
});
|
|
2095
|
+
const payload = response.payload;
|
|
2096
|
+
if (!payload.success) {
|
|
2097
|
+
return { success: false, error: payload.error ?? "Move failed" };
|
|
2098
|
+
}
|
|
2099
|
+
return { success: true, data: { newPath: payload.newPath } };
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
});
|
|
2104
|
+
|
|
2105
|
+
// src/tools/studio/bulk-set-properties.ts
|
|
2106
|
+
var bulk_set_properties_exports = {};
|
|
2107
|
+
__export(bulk_set_properties_exports, {
|
|
2108
|
+
tool: () => tool24
|
|
2109
|
+
});
|
|
2110
|
+
var tool24;
|
|
2111
|
+
var init_bulk_set_properties = __esm({
|
|
2112
|
+
"src/tools/studio/bulk-set-properties.ts"() {
|
|
2113
|
+
init_protocol();
|
|
2114
|
+
tool24 = {
|
|
2115
|
+
name: "bulk_set_properties",
|
|
2116
|
+
description: 'Set properties on multiple instances at once in a single call. Much faster than calling set_properties repeatedly. Supports two modes:\n1. Explicit paths: provide an array of {path, properties} objects\n2. By class filter: provide a root path, className filter, and properties to apply to all matches\n\nExample (explicit): [{"path": "StarterGui.HUD.Title", "properties": {"TextColor3": "#fff"}}, ...]\nExample (filter): root="StarterGui.HUD", className="TextLabel", properties={"Font": "GothamBold"}',
|
|
2117
|
+
parameters: {
|
|
2118
|
+
type: "object",
|
|
2119
|
+
properties: {
|
|
2120
|
+
targets: {
|
|
2121
|
+
type: "array",
|
|
2122
|
+
description: "Array of {path, properties} objects for explicit mode.",
|
|
2123
|
+
items: {
|
|
2124
|
+
type: "object",
|
|
2125
|
+
description: "A target with path and properties to set."
|
|
2126
|
+
}
|
|
2127
|
+
},
|
|
2128
|
+
root: {
|
|
2129
|
+
type: "string",
|
|
2130
|
+
description: "Root instance path to search under (filter mode)"
|
|
2131
|
+
},
|
|
2132
|
+
className: {
|
|
2133
|
+
type: "string",
|
|
2134
|
+
description: "Only apply to instances of this ClassName (filter mode)"
|
|
2135
|
+
},
|
|
2136
|
+
properties: {
|
|
2137
|
+
type: "object",
|
|
2138
|
+
description: "Properties to set on all matched instances (filter mode)"
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
},
|
|
2142
|
+
async execute(params, ctx) {
|
|
2143
|
+
if (!ctx.isStudioConnected()) {
|
|
2144
|
+
return { success: false, error: "Studio is not connected" };
|
|
2145
|
+
}
|
|
2146
|
+
const response = await ctx.sendToStudio(CliMsg.BULK_SET_PROPERTIES, {
|
|
2147
|
+
targets: params.targets,
|
|
2148
|
+
root: params.root,
|
|
2149
|
+
className: params.className,
|
|
2150
|
+
properties: params.properties
|
|
2151
|
+
}, 1e3 * 60);
|
|
2152
|
+
const payload = response.payload;
|
|
2153
|
+
if (!payload.success) {
|
|
2154
|
+
return { success: false, error: payload.error ?? "Bulk set failed" };
|
|
2155
|
+
}
|
|
2156
|
+
return {
|
|
2157
|
+
success: true,
|
|
2158
|
+
data: {
|
|
2159
|
+
modified: payload.modified,
|
|
2160
|
+
errors: payload.errors?.length ? payload.errors : void 0
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
|
|
2168
|
+
// src/tools/studio/find-replace-scripts.ts
|
|
2169
|
+
var find_replace_scripts_exports = {};
|
|
2170
|
+
__export(find_replace_scripts_exports, {
|
|
2171
|
+
tool: () => tool25
|
|
2172
|
+
});
|
|
2173
|
+
var tool25;
|
|
2174
|
+
var init_find_replace_scripts = __esm({
|
|
2175
|
+
"src/tools/studio/find-replace-scripts.ts"() {
|
|
2176
|
+
init_protocol();
|
|
2177
|
+
tool25 = {
|
|
2178
|
+
name: "find_replace_scripts",
|
|
2179
|
+
description: 'Find and replace text across all scripts in the game (or under a root path). Supports plain text and Lua pattern matching. Returns a list of modified scripts with match counts.\n\nUse dryRun=true to preview changes without applying them.\nExample: find="workspace" replace="Workspace" root="ServerScriptService"',
|
|
2180
|
+
parameters: {
|
|
2181
|
+
type: "object",
|
|
2182
|
+
properties: {
|
|
2183
|
+
find: {
|
|
2184
|
+
type: "string",
|
|
2185
|
+
description: "Text or Lua pattern to search for"
|
|
2186
|
+
},
|
|
2187
|
+
replace: {
|
|
2188
|
+
type: "string",
|
|
2189
|
+
description: "Replacement text (supports Lua pattern captures like %1, %2)"
|
|
2190
|
+
},
|
|
2191
|
+
root: {
|
|
2192
|
+
type: "string",
|
|
2193
|
+
description: "Root path to limit search scope (optional, searches entire game if omitted)"
|
|
2194
|
+
},
|
|
2195
|
+
usePattern: {
|
|
2196
|
+
type: "boolean",
|
|
2197
|
+
description: "If true, treat find/replace as Lua patterns. Default: false (plain text)"
|
|
2198
|
+
},
|
|
2199
|
+
dryRun: {
|
|
2200
|
+
type: "boolean",
|
|
2201
|
+
description: "If true, report matches but do not modify scripts. Default: false"
|
|
2202
|
+
}
|
|
2203
|
+
},
|
|
2204
|
+
required: ["find", "replace"]
|
|
2205
|
+
},
|
|
2206
|
+
async execute(params, ctx) {
|
|
2207
|
+
if (!ctx.isStudioConnected()) {
|
|
2208
|
+
return { success: false, error: "Studio is not connected" };
|
|
2209
|
+
}
|
|
2210
|
+
const response = await ctx.sendToStudio(CliMsg.FIND_REPLACE_SCRIPTS, {
|
|
2211
|
+
find: params.find,
|
|
2212
|
+
replace: params.replace,
|
|
2213
|
+
root: params.root,
|
|
2214
|
+
usePattern: params.usePattern ?? false,
|
|
2215
|
+
dryRun: params.dryRun ?? false
|
|
2216
|
+
}, 1e3 * 60 * 5);
|
|
2217
|
+
const payload = response.payload;
|
|
2218
|
+
if (!payload.success) {
|
|
2219
|
+
return { success: false, error: payload.error ?? "Find/replace failed" };
|
|
2220
|
+
}
|
|
2221
|
+
return {
|
|
2222
|
+
success: true,
|
|
2223
|
+
data: {
|
|
2224
|
+
totalFiles: payload.totalFiles,
|
|
2225
|
+
totalMatches: payload.totalMatches,
|
|
2226
|
+
modified: payload.modified,
|
|
2227
|
+
dryRun: params.dryRun ?? false
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
2234
|
+
|
|
1955
2235
|
// src/tools/memory/recall.ts
|
|
1956
2236
|
var recall_exports = {};
|
|
1957
2237
|
__export(recall_exports, {
|
|
1958
|
-
tool: () =>
|
|
2238
|
+
tool: () => tool26
|
|
1959
2239
|
});
|
|
1960
|
-
var
|
|
2240
|
+
var tool26;
|
|
1961
2241
|
var init_recall = __esm({
|
|
1962
2242
|
"src/tools/memory/recall.ts"() {
|
|
1963
2243
|
init_store();
|
|
1964
|
-
|
|
2244
|
+
tool26 = {
|
|
1965
2245
|
name: "recall_memory",
|
|
1966
2246
|
description: "Search your memory for previously learned facts about the current project. Use this to recall architecture decisions, patterns, known bugs, or user preferences.",
|
|
1967
2247
|
parameters: {
|
|
@@ -2000,13 +2280,13 @@ var init_recall = __esm({
|
|
|
2000
2280
|
// src/tools/memory/remember.ts
|
|
2001
2281
|
var remember_exports = {};
|
|
2002
2282
|
__export(remember_exports, {
|
|
2003
|
-
tool: () =>
|
|
2283
|
+
tool: () => tool27
|
|
2004
2284
|
});
|
|
2005
|
-
var
|
|
2285
|
+
var tool27;
|
|
2006
2286
|
var init_remember = __esm({
|
|
2007
2287
|
"src/tools/memory/remember.ts"() {
|
|
2008
2288
|
init_store();
|
|
2009
|
-
|
|
2289
|
+
tool27 = {
|
|
2010
2290
|
name: "remember",
|
|
2011
2291
|
description: "Store a fact about the current project in persistent memory. Use this to remember architecture decisions, patterns, bugs found, or user preferences so you can recall them in future sessions.",
|
|
2012
2292
|
parameters: {
|
|
@@ -2046,12 +2326,12 @@ var init_remember = __esm({
|
|
|
2046
2326
|
// src/tools/cloud/upload-asset.ts
|
|
2047
2327
|
var upload_asset_exports = {};
|
|
2048
2328
|
__export(upload_asset_exports, {
|
|
2049
|
-
tool: () =>
|
|
2329
|
+
tool: () => tool28
|
|
2050
2330
|
});
|
|
2051
|
-
var
|
|
2331
|
+
var tool28;
|
|
2052
2332
|
var init_upload_asset = __esm({
|
|
2053
2333
|
"src/tools/cloud/upload-asset.ts"() {
|
|
2054
|
-
|
|
2334
|
+
tool28 = {
|
|
2055
2335
|
name: "upload_asset",
|
|
2056
2336
|
description: "Upload a local file (image, model, audio) to Roblox via the Open Cloud API. Requires an Open Cloud API key configured in dominus.",
|
|
2057
2337
|
parameters: {
|
|
@@ -2147,6 +2427,57 @@ var init_upload_asset = __esm({
|
|
|
2147
2427
|
}
|
|
2148
2428
|
});
|
|
2149
2429
|
|
|
2430
|
+
// src/tools/studio/undo-redo.ts
|
|
2431
|
+
var undo_redo_exports = {};
|
|
2432
|
+
__export(undo_redo_exports, {
|
|
2433
|
+
redoTool: () => redoTool,
|
|
2434
|
+
undoTool: () => undoTool
|
|
2435
|
+
});
|
|
2436
|
+
var undoTool, redoTool;
|
|
2437
|
+
var init_undo_redo = __esm({
|
|
2438
|
+
"src/tools/studio/undo-redo.ts"() {
|
|
2439
|
+
init_protocol();
|
|
2440
|
+
undoTool = {
|
|
2441
|
+
name: "undo",
|
|
2442
|
+
description: "Undo the last action in Roblox Studio using ChangeHistoryService. Works just like Ctrl+Z. Use this to roll back mistakes.",
|
|
2443
|
+
parameters: {
|
|
2444
|
+
type: "object",
|
|
2445
|
+
properties: {}
|
|
2446
|
+
},
|
|
2447
|
+
async execute(_params, ctx) {
|
|
2448
|
+
if (!ctx.isStudioConnected()) {
|
|
2449
|
+
return { success: false, error: "Studio is not connected" };
|
|
2450
|
+
}
|
|
2451
|
+
const response = await ctx.sendToStudio(CliMsg.UNDO, {});
|
|
2452
|
+
const payload = response.payload;
|
|
2453
|
+
if (!payload.success) {
|
|
2454
|
+
return { success: false, error: payload.error ?? "Undo failed" };
|
|
2455
|
+
}
|
|
2456
|
+
return { success: true, data: "Undo performed" };
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
redoTool = {
|
|
2460
|
+
name: "redo",
|
|
2461
|
+
description: "Redo the last undone action in Roblox Studio using ChangeHistoryService. Works just like Ctrl+Y.",
|
|
2462
|
+
parameters: {
|
|
2463
|
+
type: "object",
|
|
2464
|
+
properties: {}
|
|
2465
|
+
},
|
|
2466
|
+
async execute(_params, ctx) {
|
|
2467
|
+
if (!ctx.isStudioConnected()) {
|
|
2468
|
+
return { success: false, error: "Studio is not connected" };
|
|
2469
|
+
}
|
|
2470
|
+
const response = await ctx.sendToStudio(CliMsg.REDO, {});
|
|
2471
|
+
const payload = response.payload;
|
|
2472
|
+
if (!payload.success) {
|
|
2473
|
+
return { success: false, error: payload.error ?? "Redo failed" };
|
|
2474
|
+
}
|
|
2475
|
+
return { success: true, data: "Redo performed" };
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
|
|
2150
2481
|
// src/mcp/server.ts
|
|
2151
2482
|
var server_exports = {};
|
|
2152
2483
|
__export(server_exports, {
|
|
@@ -2228,6 +2559,19 @@ async function startMcpServer() {
|
|
|
2228
2559
|
return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
|
|
2229
2560
|
}
|
|
2230
2561
|
);
|
|
2562
|
+
mcp.tool(
|
|
2563
|
+
"get_descendants_properties",
|
|
2564
|
+
"Easily get all descendants of a UI instance (or any instance) and all of their properties.",
|
|
2565
|
+
{
|
|
2566
|
+
path: z.string().describe('Full instance path (e.g., "Workspace.Part", "StarterGui.ScreenGui")'),
|
|
2567
|
+
outputFile: z.string().optional().describe("Optional file path to write to prevent limit errors/timeouts")
|
|
2568
|
+
},
|
|
2569
|
+
async ({ path: path5, outputFile }) => {
|
|
2570
|
+
assertConnected();
|
|
2571
|
+
const res = await bridge.sendRequest(CliMsg.GET_DESCENDANTS_PROPERTIES, { path: path5, outputFile }, 1e3 * 60 * 5);
|
|
2572
|
+
return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
|
|
2573
|
+
}
|
|
2574
|
+
);
|
|
2231
2575
|
mcp.tool(
|
|
2232
2576
|
"set_properties",
|
|
2233
2577
|
"Set properties on an instance in Roblox Studio",
|
|
@@ -2327,13 +2671,14 @@ async function startMcpServer() {
|
|
|
2327
2671
|
parent: z.string().optional().describe('Parent: "StarterGui", "PlayerGui", or path. Default: StarterGui'),
|
|
2328
2672
|
tree: z.record(z.unknown()).describe(
|
|
2329
2673
|
'Root UI node: {ClassName, Name, properties..., Children: [{...}, ...]}. UDim2 as [xScale, xOffset, yScale, yOffset], Color3 as "#hex", Font as "GothamBold"'
|
|
2330
|
-
)
|
|
2674
|
+
),
|
|
2675
|
+
animate: z.boolean().optional().describe("If true, task.wait() will be injected between creation of nodes so that the UI writes out dynamically.")
|
|
2331
2676
|
},
|
|
2332
|
-
async ({ parent, tree }) => {
|
|
2677
|
+
async ({ parent, tree, animate }) => {
|
|
2333
2678
|
assertConnected();
|
|
2334
2679
|
const res = await bridge.sendRequest(
|
|
2335
2680
|
CliMsg.BUILD_UI,
|
|
2336
|
-
{ parent: parent ?? "StarterGui", tree }
|
|
2681
|
+
{ parent: parent ?? "StarterGui", tree, animate }
|
|
2337
2682
|
);
|
|
2338
2683
|
return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
|
|
2339
2684
|
}
|
|
@@ -3775,13 +4120,13 @@ var AIProvider = class {
|
|
|
3775
4120
|
yield { type: "done" };
|
|
3776
4121
|
}
|
|
3777
4122
|
};
|
|
3778
|
-
function toolToOpenAI(
|
|
4123
|
+
function toolToOpenAI(tool29) {
|
|
3779
4124
|
return {
|
|
3780
4125
|
type: "function",
|
|
3781
4126
|
function: {
|
|
3782
|
-
name:
|
|
3783
|
-
description:
|
|
3784
|
-
parameters:
|
|
4127
|
+
name: tool29.name,
|
|
4128
|
+
description: tool29.description,
|
|
4129
|
+
parameters: tool29.parameters
|
|
3785
4130
|
}
|
|
3786
4131
|
};
|
|
3787
4132
|
}
|
|
@@ -4237,11 +4582,11 @@ var AgentLoop = class {
|
|
|
4237
4582
|
// src/tools/registry.ts
|
|
4238
4583
|
var ToolRegistry = class {
|
|
4239
4584
|
tools = /* @__PURE__ */ new Map();
|
|
4240
|
-
register(
|
|
4241
|
-
if (this.tools.has(
|
|
4242
|
-
throw new Error(`Tool already registered: ${
|
|
4585
|
+
register(tool29) {
|
|
4586
|
+
if (this.tools.has(tool29.name)) {
|
|
4587
|
+
throw new Error(`Tool already registered: ${tool29.name}`);
|
|
4243
4588
|
}
|
|
4244
|
-
this.tools.set(
|
|
4589
|
+
this.tools.set(tool29.name, tool29);
|
|
4245
4590
|
}
|
|
4246
4591
|
get(name) {
|
|
4247
4592
|
return this.tools.get(name);
|
|
@@ -4253,12 +4598,12 @@ var ToolRegistry = class {
|
|
|
4253
4598
|
return this.getAll();
|
|
4254
4599
|
}
|
|
4255
4600
|
async execute(call, ctx) {
|
|
4256
|
-
const
|
|
4257
|
-
if (!
|
|
4601
|
+
const tool29 = this.tools.get(call.name);
|
|
4602
|
+
if (!tool29) {
|
|
4258
4603
|
return { success: false, error: `Unknown tool: ${call.name}` };
|
|
4259
4604
|
}
|
|
4260
4605
|
try {
|
|
4261
|
-
return await
|
|
4606
|
+
return await tool29.execute(call.arguments, ctx);
|
|
4262
4607
|
} catch (err) {
|
|
4263
4608
|
const message = err instanceof Error ? err.message : String(err);
|
|
4264
4609
|
return { success: false, error: message };
|
|
@@ -4276,6 +4621,7 @@ function createDefaultRegistry() {
|
|
|
4276
4621
|
Promise.resolve().then(() => (init_run_code(), run_code_exports)),
|
|
4277
4622
|
Promise.resolve().then(() => (init_get_explorer(), get_explorer_exports)),
|
|
4278
4623
|
Promise.resolve().then(() => (init_get_properties(), get_properties_exports)),
|
|
4624
|
+
Promise.resolve().then(() => (init_get_descendants_properties(), get_descendants_properties_exports)),
|
|
4279
4625
|
Promise.resolve().then(() => (init_set_properties(), set_properties_exports)),
|
|
4280
4626
|
Promise.resolve().then(() => (init_insert_instance(), insert_instance_exports)),
|
|
4281
4627
|
Promise.resolve().then(() => (init_delete_instance(), delete_instance_exports)),
|
|
@@ -4291,6 +4637,10 @@ function createDefaultRegistry() {
|
|
|
4291
4637
|
Promise.resolve().then(() => (init_clone_instance(), clone_instance_exports)),
|
|
4292
4638
|
Promise.resolve().then(() => (init_group_instances(), group_instances_exports)),
|
|
4293
4639
|
Promise.resolve().then(() => (init_build_multiple(), build_multiple_exports)),
|
|
4640
|
+
Promise.resolve().then(() => (init_serialize_ui(), serialize_ui_exports)),
|
|
4641
|
+
Promise.resolve().then(() => (init_move_instance(), move_instance_exports)),
|
|
4642
|
+
Promise.resolve().then(() => (init_bulk_set_properties(), bulk_set_properties_exports)),
|
|
4643
|
+
Promise.resolve().then(() => (init_find_replace_scripts(), find_replace_scripts_exports)),
|
|
4294
4644
|
Promise.resolve().then(() => (init_recall(), recall_exports)),
|
|
4295
4645
|
Promise.resolve().then(() => (init_remember(), remember_exports)),
|
|
4296
4646
|
Promise.resolve().then(() => (init_upload_asset(), upload_asset_exports))
|
|
@@ -4301,6 +4651,10 @@ function createDefaultRegistry() {
|
|
|
4301
4651
|
if (m.tool) registry.register(m.tool);
|
|
4302
4652
|
})
|
|
4303
4653
|
);
|
|
4654
|
+
void Promise.resolve().then(() => (init_undo_redo(), undo_redo_exports)).then((m) => {
|
|
4655
|
+
registry.register(m.undoTool);
|
|
4656
|
+
registry.register(m.redoTool);
|
|
4657
|
+
});
|
|
4304
4658
|
return registry;
|
|
4305
4659
|
}
|
|
4306
4660
|
|
|
@@ -4398,7 +4752,62 @@ function formatUpdateMessage(info) {
|
|
|
4398
4752
|
""
|
|
4399
4753
|
].join("\n");
|
|
4400
4754
|
}
|
|
4401
|
-
|
|
4755
|
+
async function autoUpdate(currentVersion) {
|
|
4756
|
+
const info = await checkForUpdate(currentVersion);
|
|
4757
|
+
if (!info?.updateAvailable) {
|
|
4758
|
+
return { success: true, from: currentVersion, to: currentVersion };
|
|
4759
|
+
}
|
|
4760
|
+
const managers = [
|
|
4761
|
+
{ name: "pnpm", check: "pnpm list -g dominus-cli", cmd: "pnpm add -g dominus-cli@latest" },
|
|
4762
|
+
{ name: "npm", check: "npm list -g dominus-cli", cmd: "npm install -g dominus-cli@latest" },
|
|
4763
|
+
{ name: "yarn", check: "yarn global list --pattern dominus-cli", cmd: "yarn global add dominus-cli@latest" }
|
|
4764
|
+
];
|
|
4765
|
+
let installCmd = null;
|
|
4766
|
+
for (const mgr of managers) {
|
|
4767
|
+
try {
|
|
4768
|
+
const out = execSync(mgr.check, { stdio: "pipe", timeout: 1e4 }).toString();
|
|
4769
|
+
if (out.includes("dominus-cli")) {
|
|
4770
|
+
installCmd = mgr.cmd;
|
|
4771
|
+
break;
|
|
4772
|
+
}
|
|
4773
|
+
} catch {
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
if (!installCmd) {
|
|
4777
|
+
try {
|
|
4778
|
+
const out = execSync("pnpm list -g dominus-cli", { stdio: "pipe", timeout: 1e4 }).toString();
|
|
4779
|
+
if (out.includes("link:")) {
|
|
4780
|
+
const pkgPath = path4.resolve(__dirname$1, "..");
|
|
4781
|
+
if (fs5.existsSync(path4.join(pkgPath, ".git"))) {
|
|
4782
|
+
try {
|
|
4783
|
+
execSync("git pull", { cwd: pkgPath, stdio: "pipe", timeout: 3e4 });
|
|
4784
|
+
execSync("pnpm install", { cwd: pkgPath, stdio: "pipe", timeout: 6e4 });
|
|
4785
|
+
execSync("pnpm build", { cwd: pkgPath, stdio: "pipe", timeout: 6e4 });
|
|
4786
|
+
execSync("pnpm build:plugin", { cwd: pkgPath, stdio: "pipe", timeout: 3e4 });
|
|
4787
|
+
return { success: true, from: currentVersion, to: info.latestVersion };
|
|
4788
|
+
} catch (err) {
|
|
4789
|
+
return { success: false, from: currentVersion, to: info.latestVersion, error: `Git pull/rebuild failed: ${err}` };
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
return { success: false, from: currentVersion, to: info.latestVersion, error: 'Linked install without git \u2014 run "pnpm build" manually in the repo' };
|
|
4793
|
+
}
|
|
4794
|
+
} catch {
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
if (!installCmd) {
|
|
4798
|
+
installCmd = `npm install -g ${NPM_PACKAGE}@latest`;
|
|
4799
|
+
}
|
|
4800
|
+
try {
|
|
4801
|
+
execSync(installCmd, { stdio: "pipe", timeout: 12e4 });
|
|
4802
|
+
writeCache({ lastCheck: 0, latestVersion: null });
|
|
4803
|
+
return { success: true, from: currentVersion, to: info.latestVersion };
|
|
4804
|
+
} catch (err) {
|
|
4805
|
+
return { success: false, from: currentVersion, to: info.latestVersion, error: `Update command failed: ${err}` };
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
var __dirname$1 = path4.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1"));
|
|
4809
|
+
var __dirname2 = path4.dirname(fileURLToPath(import.meta.url));
|
|
4810
|
+
var VERSION = JSON.parse(fs5.readFileSync(path4.join(__dirname2, "..", "package.json"), "utf-8")).version;
|
|
4402
4811
|
var program = new Command();
|
|
4403
4812
|
program.name("dominus").description("AI-powered autonomous agent for Roblox Studio development").version(VERSION);
|
|
4404
4813
|
program.command("start", { isDefault: true }).description("Launch the Dominus TUI agent").option("-p, --port <port>", "WebSocket server port", "18088").action(async (opts) => {
|
|
@@ -4510,9 +4919,24 @@ rulesCmd.command("list").description("List all rules").action(() => {
|
|
|
4510
4919
|
}
|
|
4511
4920
|
console.log("");
|
|
4512
4921
|
});
|
|
4922
|
+
program.command("update").description("Auto-update Dominus to the latest version").action(async () => {
|
|
4923
|
+
console.log(` Current version: ${VERSION}`);
|
|
4924
|
+
console.log(" Checking for updates...");
|
|
4925
|
+
const result = await autoUpdate(VERSION);
|
|
4926
|
+
if (result.from === result.to && result.success) {
|
|
4927
|
+
console.log(" Already on the latest version.");
|
|
4928
|
+
return;
|
|
4929
|
+
}
|
|
4930
|
+
if (result.success) {
|
|
4931
|
+
console.log(` \u2714 Updated: ${result.from} \u2192 ${result.to}`);
|
|
4932
|
+
console.log(" Restart dominus to use the new version.");
|
|
4933
|
+
} else {
|
|
4934
|
+
console.error(` \u2718 Update failed: ${result.error}`);
|
|
4935
|
+
}
|
|
4936
|
+
});
|
|
4513
4937
|
program.command("install-plugin").description("Build & install the Dominus plugin into Roblox Studio").action(async () => {
|
|
4514
|
-
const { execSync:
|
|
4515
|
-
const packageRoot = path4.resolve(import.meta.dirname ??
|
|
4938
|
+
const { execSync: execSync3 } = await import('child_process');
|
|
4939
|
+
const packageRoot = path4.resolve(import.meta.dirname ?? __dirname2, "..");
|
|
4516
4940
|
const pluginDir = path4.join(packageRoot, "plugin");
|
|
4517
4941
|
const pluginProject = path4.join(pluginDir, "default.project.json");
|
|
4518
4942
|
const studioPlugins = path4.join(
|
|
@@ -4533,14 +4957,14 @@ program.command("install-plugin").description("Build & install the Dominus plugi
|
|
|
4533
4957
|
}
|
|
4534
4958
|
let rojoAvailable = false;
|
|
4535
4959
|
try {
|
|
4536
|
-
|
|
4960
|
+
execSync3("rojo --version", { stdio: "pipe" });
|
|
4537
4961
|
rojoAvailable = true;
|
|
4538
4962
|
} catch {
|
|
4539
4963
|
}
|
|
4540
4964
|
if (rojoAvailable) {
|
|
4541
4965
|
try {
|
|
4542
4966
|
console.log(" Building plugin with Rojo...");
|
|
4543
|
-
|
|
4967
|
+
execSync3(`rojo build "${pluginDir}" -o "${destFile}"`, { stdio: "pipe" });
|
|
4544
4968
|
console.log(` \u2714 Plugin built and installed to: ${destFile}`);
|
|
4545
4969
|
console.log(" Restart Roblox Studio to load the plugin.");
|
|
4546
4970
|
} catch (err) {
|