@weppy/roblox-mcp 0.1.9 → 0.1.11
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +15 -0
- package/docs/assets/screenshots/antigravity/antigravity_mcp_raw.png +3 -0
- package/docs/assets/screenshots/antigravity/antigravity_mcp_services_menu.png +3 -0
- package/docs/assets/screenshots/antigravity/antigravity_raw_config_menu.png +3 -0
- package/docs/en/installation/ai-apps/antigravity.md +26 -67
- package/docs/en/installation/ai-apps/gemini-cli.md +24 -31
- package/docs/en/installation/roblox-plugin.md +1 -1
- package/docs/es/installation/ai-apps/antigravity.md +36 -77
- package/docs/es/installation/ai-apps/gemini-cli.md +51 -58
- package/docs/es/installation/roblox-plugin.md +1 -1
- package/docs/id/installation/ai-apps/antigravity.md +34 -75
- package/docs/id/installation/ai-apps/gemini-cli.md +42 -49
- package/docs/id/installation/roblox-plugin.md +1 -1
- package/docs/ja/installation/ai-apps/antigravity.md +22 -63
- package/docs/ja/installation/ai-apps/gemini-cli.md +39 -46
- package/docs/ja/installation/roblox-plugin.md +1 -1
- package/docs/ko/installation/ai-apps/antigravity.md +21 -63
- package/docs/ko/installation/ai-apps/gemini-cli.md +24 -31
- package/docs/ko/installation/roblox-plugin.md +1 -1
- package/docs/pt-br/installation/ai-apps/antigravity.md +36 -77
- package/docs/pt-br/installation/ai-apps/gemini-cli.md +47 -54
- package/docs/pt-br/installation/roblox-plugin.md +1 -1
- package/package.json +1 -1
- package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
- package/plugins/weppy-roblox-mcp/dist/index.js +51 -47
- package/plugins/weppy-roblox-mcp/skills/roblox-sync/SKILL.md +208 -105
- package/plugins/weppy-roblox-mcp/skills/roblox-sync/scripts/post-verify.sh +12 -10
- package/plugins/weppy-roblox-mcp/skills/roblox-sync/scripts/pre-check.sh +8 -5
- package/plugins/weppy-roblox-mcp/skills/roblox-sync/scripts/update-metadata.sh +1 -1
|
@@ -9,9 +9,15 @@ hooks:
|
|
|
9
9
|
hooks:
|
|
10
10
|
- type: command
|
|
11
11
|
command: |
|
|
12
|
-
|
|
12
|
+
SYNC_DIR=""
|
|
13
|
+
if [ -d "roblox-project-sync" ]; then
|
|
14
|
+
SYNC_DIR="roblox-project-sync"
|
|
15
|
+
elif [ -d "roblox-studio-sync" ]; then
|
|
16
|
+
SYNC_DIR="roblox-studio-sync"
|
|
17
|
+
fi
|
|
18
|
+
if [ -n "$SYNC_DIR" ]; then
|
|
13
19
|
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
|
14
|
-
echo "[$timestamp] Modified: $TOOL_NAME" >> "
|
|
20
|
+
echo "[$timestamp] Modified: $TOOL_NAME" >> "$SYNC_DIR/changes.log"
|
|
15
21
|
fi
|
|
16
22
|
---
|
|
17
23
|
|
|
@@ -19,88 +25,152 @@ hooks:
|
|
|
19
25
|
|
|
20
26
|
**CRITICAL: This skill BLOCKS until sync is fully verified. Do NOT return early.**
|
|
21
27
|
|
|
28
|
+
## Sync Mode Detection
|
|
29
|
+
|
|
30
|
+
This skill supports two sync modes:
|
|
31
|
+
|
|
32
|
+
### Project Sync Mode (Automatic, Preferred)
|
|
33
|
+
|
|
34
|
+
When `.sync-meta.json` exists in `roblox-project-sync/`, the Roblox Studio plugin automatically syncs the instance tree to the local filesystem. **No MCP tool calls needed.**
|
|
35
|
+
|
|
36
|
+
**Detection:**
|
|
37
|
+
```bash
|
|
38
|
+
if [ -f "roblox-project-sync/.sync-meta.json" ]; then
|
|
39
|
+
# Project Sync active -- skip MCP calls, just read files
|
|
40
|
+
fi
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**When Project Sync is active:**
|
|
44
|
+
1. Skip Steps 2-4 (no MCP tool calls needed)
|
|
45
|
+
2. Read files directly from `roblox-project-sync/explorer/`
|
|
46
|
+
3. Verify sync freshness via `.sync-meta.json` timestamps
|
|
47
|
+
4. All script sources are already on disk as `.luau` files
|
|
48
|
+
|
|
49
|
+
### Legacy MCP Mode (Fallback)
|
|
50
|
+
|
|
51
|
+
When Project Sync is NOT active, fall back to MCP tool calls (`get_project_structure`, `get_script_source`) to populate `roblox-project-sync/explorer/`.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
22
55
|
## Cache Structure (Roblox Studio Mirror)
|
|
23
56
|
|
|
24
|
-
The explorer folder mirrors Roblox Studio's hierarchy
|
|
57
|
+
The explorer folder mirrors Roblox Studio's hierarchy:
|
|
25
58
|
|
|
26
59
|
```
|
|
27
|
-
roblox-
|
|
28
|
-
├──
|
|
29
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
├── changes.log # MCP tool usage log
|
|
32
|
-
├── activity.log # All MCP activity
|
|
60
|
+
roblox-project-sync/
|
|
61
|
+
├── .sync-meta.json # Sync metadata (placeId, timestamps, config)
|
|
62
|
+
├── .sync-index.json # Per-file hash tracking
|
|
63
|
+
├── changes.log # Change history (auto-flushed)
|
|
33
64
|
├── explorer/ # Mirrors Studio hierarchy
|
|
34
65
|
│ ├── Workspace/
|
|
35
|
-
│ │ ├──
|
|
36
|
-
│ │ ├──
|
|
37
|
-
│ │ ├──
|
|
38
|
-
│ │
|
|
39
|
-
│ │
|
|
40
|
-
│ │
|
|
41
|
-
│ │
|
|
66
|
+
│ │ ├── _tree.json # Service tree summary
|
|
67
|
+
│ │ ├── _props.json # Service properties
|
|
68
|
+
│ │ ├── MyModel/ # Model becomes folder
|
|
69
|
+
│ │ │ ├── _props.json # Model properties
|
|
70
|
+
│ │ │ ├── Part1/
|
|
71
|
+
│ │ │ │ └── _props.json
|
|
72
|
+
│ │ │ └── MyScript.server.luau # Script SOURCE (full text)
|
|
73
|
+
│ │ └── SpawnLocation/
|
|
74
|
+
│ │ └── _props.json
|
|
42
75
|
│ ├── ServerScriptService/
|
|
43
|
-
│ │ ├──
|
|
44
|
-
│ │ └── GameManager.server.
|
|
76
|
+
│ │ ├── _tree.json
|
|
77
|
+
│ │ └── GameManager.server.luau
|
|
45
78
|
│ ├── ReplicatedStorage/
|
|
46
|
-
│ │ ├──
|
|
47
|
-
│ │ └──
|
|
48
|
-
│ │ └── _index.json
|
|
79
|
+
│ │ ├── _tree.json
|
|
80
|
+
│ │ └── SharedModule.module.luau
|
|
49
81
|
│ ├── ServerStorage/
|
|
50
|
-
│ │ └──
|
|
82
|
+
│ │ └── _tree.json
|
|
51
83
|
│ ├── StarterGui/
|
|
52
|
-
│ │ └──
|
|
84
|
+
│ │ └── _tree.json
|
|
53
85
|
│ ├── StarterPlayer/
|
|
54
|
-
│ │ ├──
|
|
86
|
+
│ │ ├── _tree.json
|
|
55
87
|
│ │ └── StarterPlayerScripts/
|
|
56
|
-
│ │ └──
|
|
88
|
+
│ │ └── _tree.json
|
|
57
89
|
│ └── Lighting/
|
|
58
|
-
│ ├──
|
|
59
|
-
│
|
|
60
|
-
│
|
|
61
|
-
|
|
62
|
-
└──
|
|
90
|
+
│ ├── _tree.json
|
|
91
|
+
│ └── Atmosphere/
|
|
92
|
+
│ └── _props.json
|
|
93
|
+
└── snapshots/ # Point-in-time snapshots (.tar.gz)
|
|
94
|
+
└── _index.json # Snapshot registry
|
|
63
95
|
```
|
|
64
96
|
|
|
65
97
|
### File Formats
|
|
66
98
|
|
|
67
|
-
**
|
|
99
|
+
**_tree.json (Service/Container Tree Summary):**
|
|
68
100
|
```json
|
|
69
101
|
{
|
|
70
102
|
"name": "Workspace",
|
|
71
103
|
"className": "Workspace",
|
|
72
|
-
"path": "game.Workspace",
|
|
73
104
|
"childCount": 5,
|
|
74
|
-
"children": [
|
|
75
|
-
|
|
105
|
+
"children": [
|
|
106
|
+
{ "name": "SpawnLocation", "className": "SpawnLocation", "childCount": 0 },
|
|
107
|
+
{ "name": "MyModel", "className": "Model", "childCount": 3, "children": [...] }
|
|
108
|
+
],
|
|
109
|
+
"syncedAt": "2026-02-09T12:00:00.000Z"
|
|
76
110
|
}
|
|
77
111
|
```
|
|
78
112
|
|
|
79
|
-
**
|
|
113
|
+
**_props.json (Instance Properties):**
|
|
80
114
|
```json
|
|
81
115
|
{
|
|
82
116
|
"name": "SpawnLocation",
|
|
83
117
|
"className": "SpawnLocation",
|
|
84
|
-
"path": "game.Workspace.SpawnLocation",
|
|
85
118
|
"properties": {
|
|
86
|
-
"Position":
|
|
87
|
-
"Size":
|
|
119
|
+
"Position": { "x": 160, "y": 26, "z": -40 },
|
|
120
|
+
"Size": { "x": 6, "y": 1, "z": 6 },
|
|
88
121
|
"Anchored": true,
|
|
89
122
|
"Neutral": true
|
|
90
|
-
}
|
|
123
|
+
},
|
|
124
|
+
"attributes": { "SpawnPriority": 1 },
|
|
125
|
+
"tags": ["PlayerSpawn"]
|
|
91
126
|
}
|
|
92
127
|
```
|
|
93
128
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
129
|
+
**.sync-meta.json (Sync Metadata):**
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"version": 1,
|
|
133
|
+
"placeId": 12345,
|
|
134
|
+
"placeName": "My Game",
|
|
135
|
+
"lastFullSync": "2026-02-09T12:00:00.000Z",
|
|
136
|
+
"lastIncrementalSync": "2026-02-09T12:05:30.000Z",
|
|
137
|
+
"instanceCount": 1234,
|
|
138
|
+
"scriptCount": 56,
|
|
139
|
+
"syncMode": "mirror",
|
|
140
|
+
"excludePatterns": ["*.Terrain", "*.Camera"],
|
|
141
|
+
"propertyMode": "common"
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Script Files (.luau):**
|
|
146
|
+
- `Name.server.luau` -- Server Script
|
|
147
|
+
- `Name.client.luau` -- Local Script
|
|
148
|
+
- `Name.module.luau` -- Module Script
|
|
149
|
+
- Scripts with children: `Name/init.server.luau` (directory with init file)
|
|
150
|
+
- Scripts without children: `Name.server.luau` (flat file in parent dir)
|
|
98
151
|
- Contains full script source code
|
|
99
152
|
|
|
100
153
|
---
|
|
101
154
|
|
|
102
155
|
## Mandatory Workflow
|
|
103
156
|
|
|
157
|
+
### Step 0: Detect Sync Mode
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Check for Project Sync first
|
|
161
|
+
if [ -f "roblox-project-sync/.sync-meta.json" ]; then
|
|
162
|
+
echo "Project Sync active"
|
|
163
|
+
# Check freshness
|
|
164
|
+
LAST_SYNC=$(grep -o '"lastFullSync":"[^"]*"' roblox-project-sync/.sync-meta.json | cut -d'"' -f4)
|
|
165
|
+
if [ -n "$LAST_SYNC" ]; then
|
|
166
|
+
echo "Last sync: $LAST_SYNC"
|
|
167
|
+
# Skip to Step 6 (verify only)
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If Project Sync is active AND data is fresh (< 30 minutes old), skip directly to verification.
|
|
173
|
+
|
|
104
174
|
### Step 1: Run Pre-Check Script (BLOCKING)
|
|
105
175
|
|
|
106
176
|
```bash
|
|
@@ -115,14 +185,18 @@ roblox-studio-sync/
|
|
|
115
185
|
| 11 | Studio not connected | Script WAITS for user |
|
|
116
186
|
| 10/12/13 | Error | Report and STOP |
|
|
117
187
|
|
|
118
|
-
### Step 2: Load MCP Tools
|
|
188
|
+
### Step 2: Load MCP Tools (Legacy Mode Only)
|
|
189
|
+
|
|
190
|
+
Skip this step if Project Sync is active.
|
|
119
191
|
|
|
120
192
|
```
|
|
121
193
|
ToolSearch: "+robloxstudio project structure"
|
|
122
194
|
ToolSearch: "+robloxstudio get_script_source"
|
|
123
195
|
```
|
|
124
196
|
|
|
125
|
-
### Step 3: Fetch Service Structures
|
|
197
|
+
### Step 3: Fetch Service Structures (Legacy Mode Only)
|
|
198
|
+
|
|
199
|
+
Skip this step if Project Sync is active.
|
|
126
200
|
|
|
127
201
|
Call `get_project_structure` for ALL services in parallel:
|
|
128
202
|
|
|
@@ -136,24 +210,25 @@ get_project_structure({ rootPath: "game.StarterGui", depth: 10 })
|
|
|
136
210
|
get_project_structure({ rootPath: "game.StarterPlayer", depth: 10 })
|
|
137
211
|
```
|
|
138
212
|
|
|
139
|
-
### Step 4: Create Folder Hierarchy
|
|
213
|
+
### Step 4: Create Folder Hierarchy (Legacy Mode Only)
|
|
140
214
|
|
|
141
|
-
|
|
215
|
+
Skip this step if Project Sync is active.
|
|
216
|
+
|
|
217
|
+
For each service response, create folder structure under `roblox-project-sync/explorer/`:
|
|
142
218
|
|
|
143
219
|
```typescript
|
|
144
220
|
function syncService(serviceName: string, data: any) {
|
|
145
|
-
const baseDir = `roblox-
|
|
146
|
-
|
|
147
|
-
// Create service folder
|
|
221
|
+
const baseDir = `roblox-project-sync/explorer/${serviceName}`;
|
|
148
222
|
mkdir(baseDir);
|
|
149
223
|
|
|
150
|
-
// Write
|
|
151
|
-
Write(`${baseDir}/
|
|
224
|
+
// Write _tree.json
|
|
225
|
+
Write(`${baseDir}/_tree.json`, JSON.stringify({
|
|
152
226
|
name: data.structure.name,
|
|
153
227
|
className: data.structure.className,
|
|
154
|
-
path: data.structure.path,
|
|
155
228
|
childCount: data.structure.childCount,
|
|
156
|
-
children: data.structure.children?.map(c =>
|
|
229
|
+
children: data.structure.children?.map(c => ({
|
|
230
|
+
name: c.name, className: c.className, childCount: c.childCount
|
|
231
|
+
})) || [],
|
|
157
232
|
syncedAt: new Date().toISOString()
|
|
158
233
|
}, null, 2));
|
|
159
234
|
|
|
@@ -168,35 +243,33 @@ async function syncInstance(parentDir: string, instance: any) {
|
|
|
168
243
|
const isContainer = instance.childCount > 0 || ["Folder", "Model"].includes(instance.className);
|
|
169
244
|
|
|
170
245
|
if (isScript) {
|
|
171
|
-
// Fetch and save script source
|
|
172
246
|
const source = await get_script_source({ path: instance.path });
|
|
173
|
-
const ext = instance.className === "Script" ? ".server.
|
|
174
|
-
: instance.className === "LocalScript" ? ".client.
|
|
175
|
-
: ".
|
|
247
|
+
const ext = instance.className === "Script" ? ".server.luau"
|
|
248
|
+
: instance.className === "LocalScript" ? ".client.luau"
|
|
249
|
+
: ".module.luau";
|
|
176
250
|
Write(`${parentDir}/${instance.name}${ext}`, source.source);
|
|
177
251
|
} else if (isContainer) {
|
|
178
|
-
// Create subfolder
|
|
179
252
|
const subDir = `${parentDir}/${instance.name}`;
|
|
180
253
|
mkdir(subDir);
|
|
181
|
-
|
|
254
|
+
|
|
255
|
+
// Write _props.json for the container
|
|
256
|
+
Write(`${subDir}/_props.json`, JSON.stringify({
|
|
182
257
|
name: instance.name,
|
|
183
258
|
className: instance.className,
|
|
184
|
-
|
|
185
|
-
childCount: instance.childCount,
|
|
186
|
-
children: instance.children?.map(c => c.name) || []
|
|
259
|
+
properties: instance.properties || {}
|
|
187
260
|
}, null, 2));
|
|
188
261
|
|
|
189
|
-
// Recurse into children
|
|
190
262
|
for (const child of instance.children || []) {
|
|
191
263
|
syncInstance(subDir, child);
|
|
192
264
|
}
|
|
193
265
|
} else {
|
|
194
|
-
//
|
|
195
|
-
|
|
266
|
+
// Instance with properties
|
|
267
|
+
const subDir = `${parentDir}/${instance.name}`;
|
|
268
|
+
mkdir(subDir);
|
|
269
|
+
Write(`${subDir}/_props.json`, JSON.stringify({
|
|
196
270
|
name: instance.name,
|
|
197
271
|
className: instance.className,
|
|
198
|
-
|
|
199
|
-
childCount: instance.childCount
|
|
272
|
+
properties: instance.properties || {}
|
|
200
273
|
}, null, 2));
|
|
201
274
|
}
|
|
202
275
|
}
|
|
@@ -205,7 +278,7 @@ async function syncInstance(parentDir: string, instance: any) {
|
|
|
205
278
|
### Step 5: Update Metadata
|
|
206
279
|
|
|
207
280
|
```bash
|
|
208
|
-
./.claude/skills/roblox-sync/scripts/update-metadata.sh
|
|
281
|
+
./.claude/skills/roblox-sync/scripts/update-metadata.sh roblox-project-sync
|
|
209
282
|
```
|
|
210
283
|
|
|
211
284
|
### Step 6: Verify Completion (REQUIRED)
|
|
@@ -215,19 +288,20 @@ async function syncInstance(parentDir: string, instance: any) {
|
|
|
215
288
|
```
|
|
216
289
|
|
|
217
290
|
**Verification checks:**
|
|
218
|
-
1.
|
|
219
|
-
2.
|
|
220
|
-
3.
|
|
221
|
-
4.
|
|
291
|
+
1. All required service folders exist
|
|
292
|
+
2. Each folder has valid `_tree.json`
|
|
293
|
+
3. Workspace has children (in _tree.json)
|
|
294
|
+
4. Timestamp is current (from .sync-meta.json or last-sync.txt)
|
|
222
295
|
|
|
223
296
|
### Step 7: Report Success
|
|
224
297
|
|
|
225
298
|
```
|
|
226
|
-
|
|
299
|
+
Sync Complete
|
|
300
|
+
- Mode: Project Sync (automatic) / Legacy (MCP calls)
|
|
227
301
|
- Services: Workspace, Lighting, ReplicatedStorage, ServerStorage, ServerScriptService, StarterGui, StarterPlayer
|
|
228
302
|
- Scripts synced: {count}
|
|
229
|
-
-
|
|
230
|
-
-
|
|
303
|
+
- Instances: {count}
|
|
304
|
+
- Last sync: {timestamp}
|
|
231
305
|
```
|
|
232
306
|
|
|
233
307
|
---
|
|
@@ -238,7 +312,7 @@ async function syncInstance(parentDir: string, instance: any) {
|
|
|
238
312
|
|
|
239
313
|
| Service | Minimum Children |
|
|
240
314
|
|---------|------------------|
|
|
241
|
-
| Workspace |
|
|
315
|
+
| Workspace | At least 1 child |
|
|
242
316
|
| Lighting | (any) |
|
|
243
317
|
| StarterPlayer | StarterPlayerScripts |
|
|
244
318
|
|
|
@@ -255,29 +329,36 @@ These can be empty for new projects:
|
|
|
255
329
|
```typescript
|
|
256
330
|
function validateSync(): boolean {
|
|
257
331
|
const errors: string[] = [];
|
|
332
|
+
const syncDir = "roblox-project-sync";
|
|
333
|
+
|
|
334
|
+
// Check .sync-meta.json for Project Sync mode
|
|
335
|
+
const metaPath = `${syncDir}/.sync-meta.json`;
|
|
336
|
+
if (fileExists(metaPath)) {
|
|
337
|
+
const meta = readJSON(metaPath);
|
|
338
|
+
// Check freshness via lastFullSync
|
|
339
|
+
if (meta.lastFullSync) {
|
|
340
|
+
const syncAge = Date.now() - Date.parse(meta.lastFullSync);
|
|
341
|
+
if (syncAge > 30 * 60 * 1000) {
|
|
342
|
+
errors.push("Sync data is stale (>30 minutes old)");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
258
346
|
|
|
259
|
-
// Check Workspace
|
|
260
|
-
const
|
|
261
|
-
if (!
|
|
262
|
-
errors.push("Workspace must have at least
|
|
347
|
+
// Check Workspace tree
|
|
348
|
+
const wsTree = readJSON(`${syncDir}/explorer/Workspace/_tree.json`);
|
|
349
|
+
if (!wsTree || wsTree.childCount < 1) {
|
|
350
|
+
errors.push("Workspace must have at least 1 child");
|
|
263
351
|
}
|
|
264
352
|
|
|
265
|
-
// Check all required
|
|
353
|
+
// Check all required _tree.json files exist
|
|
266
354
|
const required = ["Workspace", "Lighting", "ReplicatedStorage",
|
|
267
355
|
"ServerStorage", "ServerScriptService", "StarterGui", "StarterPlayer"];
|
|
268
356
|
for (const svc of required) {
|
|
269
|
-
if (!fileExists(
|
|
270
|
-
errors.push(`Missing ${svc}/
|
|
357
|
+
if (!fileExists(`${syncDir}/explorer/${svc}/_tree.json`)) {
|
|
358
|
+
errors.push(`Missing ${svc}/_tree.json`);
|
|
271
359
|
}
|
|
272
360
|
}
|
|
273
361
|
|
|
274
|
-
// Check timestamp freshness (max 30 minutes)
|
|
275
|
-
const lastSync = readFile("last-sync.txt");
|
|
276
|
-
const syncAge = Date.now() - Date.parse(lastSync);
|
|
277
|
-
if (syncAge > 30 * 60 * 1000) {
|
|
278
|
-
errors.push("Sync data is stale (>30 minutes old)");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
362
|
return errors.length === 0;
|
|
282
363
|
}
|
|
283
364
|
```
|
|
@@ -286,27 +367,49 @@ function validateSync(): boolean {
|
|
|
286
367
|
|
|
287
368
|
## Script Sync Details
|
|
288
369
|
|
|
289
|
-
|
|
370
|
+
In **Project Sync mode**, scripts are already on disk. Just read them directly:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
// Scripts are at:
|
|
374
|
+
// - Without children: ParentDir/ScriptName.server.luau
|
|
375
|
+
// - With children: ScriptName/init.server.luau
|
|
376
|
+
|
|
377
|
+
// Extension mapping:
|
|
378
|
+
const ext = {
|
|
379
|
+
"Script": ".server.luau",
|
|
380
|
+
"LocalScript": ".client.luau",
|
|
381
|
+
"ModuleScript": ".module.luau"
|
|
382
|
+
}[instance.className];
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
In **Legacy mode**, fetch via MCP:
|
|
290
386
|
|
|
291
387
|
```typescript
|
|
292
|
-
// For each Script/LocalScript/ModuleScript found:
|
|
293
388
|
const scriptSource = await mcp__robloxstudio__get_script_source({
|
|
294
389
|
path: instance.path
|
|
295
390
|
});
|
|
296
391
|
|
|
297
|
-
// Determine file extension
|
|
298
392
|
const ext = {
|
|
299
|
-
"Script": ".server.
|
|
300
|
-
"LocalScript": ".client.
|
|
301
|
-
"ModuleScript": ".
|
|
393
|
+
"Script": ".server.luau",
|
|
394
|
+
"LocalScript": ".client.luau",
|
|
395
|
+
"ModuleScript": ".module.luau"
|
|
302
396
|
}[instance.className];
|
|
303
397
|
|
|
304
|
-
// Save to file
|
|
305
398
|
Write(`${parentDir}/${instance.name}${ext}`, scriptSource.source);
|
|
306
399
|
```
|
|
307
400
|
|
|
308
401
|
---
|
|
309
402
|
|
|
403
|
+
## Migration from roblox-studio-sync
|
|
404
|
+
|
|
405
|
+
If `roblox-studio-sync/` exists but `roblox-project-sync/` does not:
|
|
406
|
+
1. The old folder uses `_index.json` and `.lua` extensions
|
|
407
|
+
2. Create `roblox-project-sync/` with the new structure
|
|
408
|
+
3. Do NOT delete `roblox-studio-sync/` (user may want to keep it)
|
|
409
|
+
4. Future syncs will use `roblox-project-sync/` exclusively
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
310
413
|
## Error Handling
|
|
311
414
|
|
|
312
415
|
| Error | Action |
|
|
@@ -337,7 +440,7 @@ When the user reports "plugin shows connected but sync doesn't work":
|
|
|
337
440
|
|
|
338
441
|
| Argument | Action |
|
|
339
442
|
|----------|--------|
|
|
340
|
-
| `full` | Execute full sync workflow (Steps
|
|
443
|
+
| `full` | Execute full sync workflow (Steps 0-7) |
|
|
341
444
|
| `verify` | Run verification only (Step 6) |
|
|
342
445
|
| (none) | Same as `full` |
|
|
343
446
|
|
|
@@ -347,12 +450,12 @@ When the user reports "plugin shows connected but sync doesn't work":
|
|
|
347
450
|
|
|
348
451
|
**This skill is NOT complete until ALL conditions are true:**
|
|
349
452
|
|
|
350
|
-
1.
|
|
351
|
-
2.
|
|
352
|
-
3.
|
|
353
|
-
4.
|
|
354
|
-
5.
|
|
355
|
-
6.
|
|
453
|
+
1. MCP connection verified (or Project Sync active)
|
|
454
|
+
2. All 7+ service folders created with `_tree.json`
|
|
455
|
+
3. Workspace has children (childCount >= 1)
|
|
456
|
+
4. All scripts have `.luau` source files
|
|
457
|
+
5. `post-verify.sh` returns exit 0
|
|
458
|
+
6. Timestamps are current
|
|
356
459
|
|
|
357
460
|
**If ANY fails, DO NOT return success.**
|
|
358
461
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
set -euo pipefail
|
|
14
14
|
|
|
15
|
-
SYNC_DIR="${SYNC_DIR:-roblox-
|
|
15
|
+
SYNC_DIR="${SYNC_DIR:-roblox-project-sync}"
|
|
16
16
|
EXPLORER_DIR="$SYNC_DIR/explorer"
|
|
17
17
|
|
|
18
18
|
# Required service folders
|
|
@@ -30,36 +30,38 @@ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
|
30
30
|
verify_service() {
|
|
31
31
|
local service="$1"
|
|
32
32
|
local service_dir="$EXPLORER_DIR/$service"
|
|
33
|
-
local index_file="$service_dir/_index.json"
|
|
34
|
-
|
|
35
33
|
# Check folder exists
|
|
36
34
|
if [ ! -d "$service_dir" ]; then
|
|
37
35
|
log_fail "$service: Folder not found"
|
|
38
36
|
return 1
|
|
39
37
|
fi
|
|
40
38
|
|
|
41
|
-
# Check _index.json exists
|
|
39
|
+
# Check _tree.json (new format) or _index.json (legacy) exists
|
|
40
|
+
local index_file="$service_dir/_tree.json"
|
|
41
|
+
if [ ! -f "$index_file" ]; then
|
|
42
|
+
index_file="$service_dir/_index.json"
|
|
43
|
+
fi
|
|
42
44
|
if [ ! -f "$index_file" ]; then
|
|
43
|
-
log_fail "$service:
|
|
45
|
+
log_fail "$service: _tree.json not found"
|
|
44
46
|
return 1
|
|
45
47
|
fi
|
|
46
48
|
|
|
47
|
-
# Check
|
|
49
|
+
# Check file has content
|
|
48
50
|
local size
|
|
49
51
|
size=$(stat -f %z "$index_file" 2>/dev/null || stat -c %s "$index_file" 2>/dev/null || echo "0")
|
|
50
52
|
if [ "$size" -lt 20 ]; then
|
|
51
|
-
log_fail "$service:
|
|
53
|
+
log_fail "$service: $(basename $index_file) too small (${size} bytes)"
|
|
52
54
|
return 1
|
|
53
55
|
fi
|
|
54
56
|
|
|
55
57
|
# Check JSON has required fields
|
|
56
58
|
if ! grep -q '"name"' "$index_file" 2>/dev/null; then
|
|
57
|
-
log_fail "$service:
|
|
59
|
+
log_fail "$service: $(basename $index_file) missing 'name' field"
|
|
58
60
|
return 1
|
|
59
61
|
fi
|
|
60
62
|
|
|
61
63
|
if ! grep -q '"className"' "$index_file" 2>/dev/null; then
|
|
62
|
-
log_fail "$service:
|
|
64
|
+
log_fail "$service: $(basename $index_file) missing 'className' field"
|
|
63
65
|
return 1
|
|
64
66
|
fi
|
|
65
67
|
|
|
@@ -133,7 +135,7 @@ main() {
|
|
|
133
135
|
# Count total scripts synced
|
|
134
136
|
local script_count=0
|
|
135
137
|
if [ -d "$EXPLORER_DIR" ]; then
|
|
136
|
-
script_count=$(find "$EXPLORER_DIR" -name "*.lua" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
138
|
+
script_count=$(find "$EXPLORER_DIR" \( -name "*.luau" -o -name "*.lua" \) -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
137
139
|
fi
|
|
138
140
|
echo "Scripts synced: $script_count"
|
|
139
141
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
set -euo pipefail
|
|
19
19
|
|
|
20
20
|
# Configuration
|
|
21
|
-
SYNC_DIR="${SYNC_DIR:-roblox-
|
|
21
|
+
SYNC_DIR="${SYNC_DIR:-roblox-project-sync}"
|
|
22
22
|
MCP_PORT="${MCP_PORT:-3002}"
|
|
23
23
|
MAX_RETRIES="${MAX_RETRIES:-60}" # Max retries for each phase (5 minutes at 5s intervals)
|
|
24
24
|
RETRY_INTERVAL=5
|
|
@@ -154,19 +154,22 @@ verify_sync_data() {
|
|
|
154
154
|
# Check each required service has a folder with _index.json
|
|
155
155
|
for service in "${REQUIRED_SERVICES[@]}"; do
|
|
156
156
|
local service_dir="$explorer_dir/${service}"
|
|
157
|
-
local index_file="$service_dir/_index.json"
|
|
158
|
-
|
|
159
157
|
if [ ! -d "$service_dir" ]; then
|
|
160
158
|
missing_services+=("$service")
|
|
161
159
|
continue
|
|
162
160
|
fi
|
|
163
161
|
|
|
162
|
+
# Check for _tree.json (new format) or _index.json (legacy)
|
|
163
|
+
local index_file="$service_dir/_tree.json"
|
|
164
|
+
if [ ! -f "$index_file" ]; then
|
|
165
|
+
index_file="$service_dir/_index.json"
|
|
166
|
+
fi
|
|
164
167
|
if [ ! -f "$index_file" ]; then
|
|
165
|
-
invalid_services+=("$service (missing
|
|
168
|
+
invalid_services+=("$service (missing _tree.json)")
|
|
166
169
|
continue
|
|
167
170
|
fi
|
|
168
171
|
|
|
169
|
-
# Check
|
|
172
|
+
# Check file is not empty and has valid structure
|
|
170
173
|
local file_size
|
|
171
174
|
file_size=$(stat -f %z "$index_file" 2>/dev/null || stat -c %s "$index_file" 2>/dev/null || echo "0")
|
|
172
175
|
if [ "$file_size" -lt 20 ]; then
|