dominus-cli 0.5.4 → 0.5.6

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/mcp.js CHANGED
@@ -703,10 +703,19 @@ async function startMcpServer() {
703
703
  }
704
704
  const mcp = new McpServer({
705
705
  name: "dominus",
706
- version: "0.2.1"
706
+ version: "0.5.5"
707
707
  }, {
708
708
  instructions: INTERNAL_MEMORY
709
709
  });
710
+ function luaStr(s) {
711
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r")}"`;
712
+ }
713
+ function resolvePath(dotPath) {
714
+ const parts = dotPath.split(".");
715
+ let r = "game";
716
+ for (const p of parts) r += `[${luaStr(p)}]`;
717
+ return r;
718
+ }
710
719
  mcp.tool(
711
720
  "read_script",
712
721
  "Read the source code of a script in Roblox Studio",
@@ -758,11 +767,14 @@ async function startMcpServer() {
758
767
  );
759
768
  mcp.tool(
760
769
  "get_properties",
761
- "Get all properties of an instance in Roblox Studio",
762
- { path: z.string().describe("Full instance path") },
763
- async ({ path: path2 }) => {
770
+ "Get all properties of a single instance in Roblox Studio. Returns property name, value, type, and category. For inspecting UI trees, prefer serialize_ui (structured tree) or get_descendants_properties (flat list with compact filtering).",
771
+ {
772
+ path: z.string().describe('Full instance path (e.g. "Workspace.Part", "StarterGui.MyUI.Frame")'),
773
+ compact: z.boolean().optional().default(true).describe("Strip read-only, nil, deprecated, and default-valued properties (default true). Set false for full dump.")
774
+ },
775
+ async ({ path: path2, compact }) => {
764
776
  assertConnected();
765
- const res = await bridge.sendRequest(CliMsg.GET_PROPERTIES, { path: path2 });
777
+ const res = await bridge.sendRequest(CliMsg.GET_PROPERTIES, { path: path2, compact });
766
778
  return { content: [{ type: "text", text: JSON.stringify(res.payload, null, 2) }] };
767
779
  }
768
780
  );
@@ -978,16 +990,10 @@ async function startMcpServer() {
978
990
  },
979
991
  async (params) => {
980
992
  assertConnected();
981
- const parts = params.path.split(".");
982
- let resolve = "game";
983
- for (const p of parts) resolve += `["${p}"]`;
984
- const lines = [`local orig = ${resolve}`, `local clone = orig:Clone()`];
985
- if (params.newName) lines.push(`clone.Name = "${params.newName}"`);
993
+ const lines = [`local orig = ${resolvePath(params.path)}`, `local clone = orig:Clone()`];
994
+ if (params.newName) lines.push(`clone.Name = ${luaStr(params.newName)}`);
986
995
  if (params.newParent) {
987
- const pp = params.newParent.split(".");
988
- let pr = "game";
989
- for (const p of pp) pr += `["${p}"]`;
990
- lines.push(`clone.Parent = ${pr}`);
996
+ lines.push(`clone.Parent = ${resolvePath(params.newParent)}`);
991
997
  } else {
992
998
  lines.push(`clone.Parent = orig.Parent`);
993
999
  }
@@ -1003,7 +1009,7 @@ async function startMcpServer() {
1003
1009
  );
1004
1010
  mcp.tool(
1005
1011
  "group_instances",
1006
- "Group multiple instances into a Model or Folder",
1012
+ "Group multiple instances into a Model or Folder. All listed instances become children of the new group.",
1007
1013
  {
1008
1014
  paths: z.array(z.string()).describe("Array of instance paths to group"),
1009
1015
  groupName: z.string().describe("Name for the group"),
@@ -1014,20 +1020,11 @@ async function startMcpServer() {
1014
1020
  assertConnected();
1015
1021
  const gc = params.groupClass ?? "Model";
1016
1022
  const parent = params.parent ?? "Workspace";
1017
- const parentParts = parent.split(".");
1018
- let parentResolve = "game";
1019
- for (const p of parentParts) parentResolve += `["${p}"]`;
1020
- const resolves = params.paths.map((path2) => {
1021
- const parts = path2.split(".");
1022
- let r = "game";
1023
- for (const part of parts) r += `["${part}"]`;
1024
- return r;
1025
- });
1026
1023
  const code = [
1027
- `local group = Instance.new("${gc}")`,
1028
- `group.Name = "${params.groupName}"`,
1029
- `group.Parent = ${parentResolve}`,
1030
- ...resolves.map((r) => `${r}.Parent = group`),
1024
+ `local group = Instance.new(${luaStr(gc)})`,
1025
+ `group.Name = ${luaStr(params.groupName)}`,
1026
+ `group.Parent = ${resolvePath(parent)}`,
1027
+ ...params.paths.map((p) => `${resolvePath(p)}.Parent = group`),
1031
1028
  `print("Grouped ${params.paths.length} instances into: " .. group:GetFullName())`
1032
1029
  ].join("\n");
1033
1030
  const res = await bridge.sendRequest(CliMsg.RUN_CODE, { code });
@@ -1036,7 +1033,7 @@ async function startMcpServer() {
1036
1033
  );
1037
1034
  mcp.tool(
1038
1035
  "build_multiple",
1039
- "Create many parts/instances in one batch. Send an array of specs \u2014 all created in a single round-trip. Ideal for building trees, houses, vehicles.",
1036
+ "Create many parts/instances in one batch. Send an array of specs \u2014 all created in a single round-trip. Ideal for building trees, houses, vehicles. Supports hex colors, rgb() colors, and BrickColor names.",
1040
1037
  {
1041
1038
  parts: z.array(z.object({
1042
1039
  name: z.string(),
@@ -1044,10 +1041,11 @@ async function startMcpServer() {
1044
1041
  parent: z.string().optional(),
1045
1042
  position: z.object({ x: z.number(), y: z.number(), z: z.number() }).optional(),
1046
1043
  size: z.object({ x: z.number(), y: z.number(), z: z.number() }).optional(),
1047
- color: z.string().optional().describe('Hex "#RRGGBB" or BrickColor name'),
1048
- material: z.string().optional(),
1044
+ color: z.string().optional().describe('Hex "#RRGGBB", "rgb(r,g,b)", or BrickColor name'),
1045
+ material: z.string().optional().describe("Enum.Material name: Plastic, Wood, Neon, Glass, etc."),
1049
1046
  transparency: z.number().optional(),
1050
1047
  anchored: z.boolean().optional(),
1048
+ canCollide: z.boolean().optional(),
1051
1049
  shape: z.enum(["Block", "Ball", "Cylinder", "Wedge"]).optional()
1052
1050
  })).describe("Array of part specifications"),
1053
1051
  groupName: z.string().optional().describe("Group all parts into a Model with this name"),
@@ -1058,35 +1056,36 @@ async function startMcpServer() {
1058
1056
  const parts = params.parts;
1059
1057
  const groupName = params.groupName;
1060
1058
  const groupParent = params.groupParent ?? "Workspace";
1061
- function resolveP(path2) {
1062
- const segs = path2.split(".");
1063
- let r = "game";
1064
- for (const s of segs) r += `["${s}"]`;
1065
- return r;
1066
- }
1067
1059
  const lines = [];
1068
1060
  if (groupName) {
1069
1061
  lines.push(`local _group = Instance.new("Model")`);
1070
- lines.push(`_group.Name = "${groupName}"`);
1071
- lines.push(`_group.Parent = ${resolveP(groupParent)}`);
1062
+ lines.push(`_group.Name = ${luaStr(groupName)}`);
1063
+ lines.push(`_group.Parent = ${resolvePath(groupParent)}`);
1072
1064
  }
1073
1065
  for (let i = 0; i < parts.length; i++) {
1074
1066
  const p = parts[i];
1075
1067
  const v = `_p${i}`;
1076
1068
  const cn = p.className ?? (p.shape === "Wedge" ? "WedgePart" : "Part");
1077
- lines.push(`local ${v} = Instance.new("${cn}")`);
1078
- lines.push(`${v}.Name = "${p.name}"`);
1069
+ lines.push(`local ${v} = Instance.new(${luaStr(cn)})`);
1070
+ lines.push(`${v}.Name = ${luaStr(p.name)}`);
1079
1071
  if (p.position) lines.push(`${v}.Position = Vector3.new(${p.position.x}, ${p.position.y}, ${p.position.z})`);
1080
1072
  if (p.size) lines.push(`${v}.Size = Vector3.new(${p.size.x}, ${p.size.y}, ${p.size.z})`);
1081
1073
  if (p.color) {
1082
- if (p.color.startsWith("#")) lines.push(`${v}.Color = Color3.fromHex("${p.color}")`);
1083
- else lines.push(`${v}.BrickColor = BrickColor.new("${p.color}")`);
1074
+ if (p.color.startsWith("#")) {
1075
+ lines.push(`${v}.Color = Color3.fromHex(${luaStr(p.color)})`);
1076
+ } else if (p.color.startsWith("rgb(")) {
1077
+ const m = p.color.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
1078
+ if (m) lines.push(`${v}.Color = Color3.fromRGB(${m[1]}, ${m[2]}, ${m[3]})`);
1079
+ } else {
1080
+ lines.push(`${v}.BrickColor = BrickColor.new(${luaStr(p.color)})`);
1081
+ }
1084
1082
  }
1085
1083
  if (p.material) lines.push(`${v}.Material = Enum.Material.${p.material}`);
1086
1084
  if (p.transparency !== void 0) lines.push(`${v}.Transparency = ${p.transparency}`);
1087
1085
  lines.push(`${v}.Anchored = ${p.anchored ?? true}`);
1086
+ if (p.canCollide !== void 0) lines.push(`${v}.CanCollide = ${p.canCollide}`);
1088
1087
  if (p.shape && p.shape !== "Block" && cn === "Part") lines.push(`${v}.Shape = Enum.PartType.${p.shape}`);
1089
- lines.push(`${v}.Parent = ${groupName ? "_group" : resolveP(p.parent ?? "Workspace")}`);
1088
+ lines.push(`${v}.Parent = ${groupName ? "_group" : resolvePath(p.parent ?? "Workspace")}`);
1090
1089
  }
1091
1090
  lines.push(`print("Built ${parts.length} parts${groupName ? ` in ${groupName}` : ""}")`);
1092
1091
  const res = await bridge.sendRequest(CliMsg.RUN_CODE, { code: lines.join("\n") });