@weppy/roblox-mcp 0.1.11 → 1.0.0

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.
Files changed (38) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +45 -10
  3. package/README.md +6 -0
  4. package/docs/en/installation/roblox-plugin.md +10 -5
  5. package/docs/en/pro-upgrade.md +36 -30
  6. package/docs/en/tools/overview.md +255 -95
  7. package/docs/es/README.md +6 -0
  8. package/docs/es/installation/README.md +6 -5
  9. package/docs/es/installation/roblox-plugin.md +29 -24
  10. package/docs/es/pro-upgrade.md +39 -33
  11. package/docs/es/tools/overview.md +4 -0
  12. package/docs/id/README.md +6 -0
  13. package/docs/id/installation/roblox-plugin.md +28 -23
  14. package/docs/id/pro-upgrade.md +36 -30
  15. package/docs/id/tools/overview.md +4 -0
  16. package/docs/ja/README.md +6 -0
  17. package/docs/ja/installation/README.md +5 -5
  18. package/docs/ja/installation/roblox-plugin.md +32 -27
  19. package/docs/ja/pro-upgrade.md +38 -32
  20. package/docs/ja/tools/overview.md +4 -0
  21. package/docs/ko/README.md +6 -0
  22. package/docs/ko/installation/roblox-plugin.md +10 -5
  23. package/docs/ko/pro-upgrade.md +36 -30
  24. package/docs/ko/tools/overview.md +255 -95
  25. package/docs/pt-br/README.md +6 -0
  26. package/docs/pt-br/installation/roblox-plugin.md +28 -23
  27. package/docs/pt-br/pro-upgrade.md +39 -33
  28. package/docs/pt-br/tools/overview.md +4 -0
  29. package/package.json +4 -2
  30. package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
  31. package/plugins/weppy-roblox-mcp/dist/index.js +70 -98
  32. package/plugins/weppy-roblox-mcp/skills/roblox-game-dev/SKILL.md +0 -155
  33. package/plugins/weppy-roblox-mcp/skills/roblox-game-dev/references/animations.json +0 -34
  34. package/plugins/weppy-roblox-mcp/skills/roblox-game-dev/references/mcp-tools.md +0 -220
  35. package/plugins/weppy-roblox-mcp/skills/roblox-sync/SKILL.md +0 -492
  36. package/plugins/weppy-roblox-mcp/skills/roblox-sync/scripts/post-verify.sh +0 -162
  37. package/plugins/weppy-roblox-mcp/skills/roblox-sync/scripts/pre-check.sh +0 -307
  38. package/plugins/weppy-roblox-mcp/skills/roblox-sync/scripts/update-metadata.sh +0 -27
@@ -1,492 +0,0 @@
1
- ---
2
- name: roblox-sync
3
- description: Synchronize Roblox Studio state with local cache. BLOCKING - does not return until sync is verified complete.
4
- argument-hint: "[full|verify]"
5
- user-invocable: false
6
- hooks:
7
- PostToolUse:
8
- - matcher: "mcp__robloxstudio__(create|delete|set|move|rename|clone|edit|insert|terrain).*"
9
- hooks:
10
- - type: command
11
- command: |
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
19
- timestamp=$(date +"%Y-%m-%d %H:%M:%S")
20
- echo "[$timestamp] Modified: $TOOL_NAME" >> "$SYNC_DIR/changes.log"
21
- fi
22
- ---
23
-
24
- # Roblox Sync Skill
25
-
26
- **CRITICAL: This skill BLOCKS until sync is fully verified. Do NOT return early.**
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
-
55
- ## Cache Structure (Roblox Studio Mirror)
56
-
57
- The explorer folder mirrors Roblox Studio's hierarchy:
58
-
59
- ```
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)
64
- ├── explorer/ # Mirrors Studio hierarchy
65
- │ ├── Workspace/
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
75
- │ ├── ServerScriptService/
76
- │ │ ├── _tree.json
77
- │ │ └── GameManager.server.luau
78
- │ ├── ReplicatedStorage/
79
- │ │ ├── _tree.json
80
- │ │ └── SharedModule.module.luau
81
- │ ├── ServerStorage/
82
- │ │ └── _tree.json
83
- │ ├── StarterGui/
84
- │ │ └── _tree.json
85
- │ ├── StarterPlayer/
86
- │ │ ├── _tree.json
87
- │ │ └── StarterPlayerScripts/
88
- │ │ └── _tree.json
89
- │ └── Lighting/
90
- │ ├── _tree.json
91
- │ └── Atmosphere/
92
- │ └── _props.json
93
- └── snapshots/ # Point-in-time snapshots (.tar.gz)
94
- └── _index.json # Snapshot registry
95
- ```
96
-
97
- ### File Formats
98
-
99
- **_tree.json (Service/Container Tree Summary):**
100
- ```json
101
- {
102
- "name": "Workspace",
103
- "className": "Workspace",
104
- "childCount": 5,
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"
110
- }
111
- ```
112
-
113
- **_props.json (Instance Properties):**
114
- ```json
115
- {
116
- "name": "SpawnLocation",
117
- "className": "SpawnLocation",
118
- "properties": {
119
- "Position": { "x": 160, "y": 26, "z": -40 },
120
- "Size": { "x": 6, "y": 1, "z": 6 },
121
- "Anchored": true,
122
- "Neutral": true
123
- },
124
- "attributes": { "SpawnPriority": 1 },
125
- "tags": ["PlayerSpawn"]
126
- }
127
- ```
128
-
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)
151
- - Contains full script source code
152
-
153
- ---
154
-
155
- ## Mandatory Workflow
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
-
174
- ### Step 1: Run Pre-Check Script (BLOCKING)
175
-
176
- ```bash
177
- ./.claude/skills/roblox-sync/scripts/pre-check.sh
178
- ```
179
-
180
- **Exit codes:**
181
- | Code | Meaning | Action |
182
- |------|---------|--------|
183
- | 0 | Already synced | Skip to Step 6 (verify only) |
184
- | 100 | Connection OK, sync needed | Proceed to Step 2 |
185
- | 11 | Studio not connected | Script WAITS for user |
186
- | 10/12/13 | Error | Report and STOP |
187
-
188
- ### Step 2: Load MCP Tools (Legacy Mode Only)
189
-
190
- Skip this step if Project Sync is active.
191
-
192
- ```
193
- ToolSearch: "+robloxstudio project structure"
194
- ToolSearch: "+robloxstudio get_script_source"
195
- ```
196
-
197
- ### Step 3: Fetch Service Structures (Legacy Mode Only)
198
-
199
- Skip this step if Project Sync is active.
200
-
201
- Call `get_project_structure` for ALL services in parallel:
202
-
203
- ```javascript
204
- get_project_structure({ rootPath: "game.Workspace", depth: 10 })
205
- get_project_structure({ rootPath: "game.Lighting", depth: 5 })
206
- get_project_structure({ rootPath: "game.ReplicatedStorage", depth: 10 })
207
- get_project_structure({ rootPath: "game.ServerStorage", depth: 10 })
208
- get_project_structure({ rootPath: "game.ServerScriptService", depth: 10 })
209
- get_project_structure({ rootPath: "game.StarterGui", depth: 10 })
210
- get_project_structure({ rootPath: "game.StarterPlayer", depth: 10 })
211
- ```
212
-
213
- ### Step 4: Create Folder Hierarchy (Legacy Mode Only)
214
-
215
- Skip this step if Project Sync is active.
216
-
217
- For each service response, create folder structure under `roblox-project-sync/explorer/`:
218
-
219
- ```typescript
220
- function syncService(serviceName: string, data: any) {
221
- const baseDir = `roblox-project-sync/explorer/${serviceName}`;
222
- mkdir(baseDir);
223
-
224
- // Write _tree.json
225
- Write(`${baseDir}/_tree.json`, JSON.stringify({
226
- name: data.structure.name,
227
- className: data.structure.className,
228
- childCount: data.structure.childCount,
229
- children: data.structure.children?.map(c => ({
230
- name: c.name, className: c.className, childCount: c.childCount
231
- })) || [],
232
- syncedAt: new Date().toISOString()
233
- }, null, 2));
234
-
235
- // Process children recursively
236
- for (const child of data.structure.children || []) {
237
- syncInstance(baseDir, child);
238
- }
239
- }
240
-
241
- async function syncInstance(parentDir: string, instance: any) {
242
- const isScript = ["Script", "LocalScript", "ModuleScript"].includes(instance.className);
243
- const isContainer = instance.childCount > 0 || ["Folder", "Model"].includes(instance.className);
244
-
245
- if (isScript) {
246
- const source = await get_script_source({ path: instance.path });
247
- const ext = instance.className === "Script" ? ".server.luau"
248
- : instance.className === "LocalScript" ? ".client.luau"
249
- : ".module.luau";
250
- Write(`${parentDir}/${instance.name}${ext}`, source.source);
251
- } else if (isContainer) {
252
- const subDir = `${parentDir}/${instance.name}`;
253
- mkdir(subDir);
254
-
255
- // Write _props.json for the container
256
- Write(`${subDir}/_props.json`, JSON.stringify({
257
- name: instance.name,
258
- className: instance.className,
259
- properties: instance.properties || {}
260
- }, null, 2));
261
-
262
- for (const child of instance.children || []) {
263
- syncInstance(subDir, child);
264
- }
265
- } else {
266
- // Instance with properties
267
- const subDir = `${parentDir}/${instance.name}`;
268
- mkdir(subDir);
269
- Write(`${subDir}/_props.json`, JSON.stringify({
270
- name: instance.name,
271
- className: instance.className,
272
- properties: instance.properties || {}
273
- }, null, 2));
274
- }
275
- }
276
- ```
277
-
278
- ### Step 5: Update Metadata
279
-
280
- ```bash
281
- ./.claude/skills/roblox-sync/scripts/update-metadata.sh roblox-project-sync
282
- ```
283
-
284
- ### Step 6: Verify Completion (REQUIRED)
285
-
286
- ```bash
287
- ./.claude/skills/roblox-sync/scripts/post-verify.sh
288
- ```
289
-
290
- **Verification checks:**
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)
295
-
296
- ### Step 7: Report Success
297
-
298
- ```
299
- Sync Complete
300
- - Mode: Project Sync (automatic) / Legacy (MCP calls)
301
- - Services: Workspace, Lighting, ReplicatedStorage, ServerStorage, ServerScriptService, StarterGui, StarterPlayer
302
- - Scripts synced: {count}
303
- - Instances: {count}
304
- - Last sync: {timestamp}
305
- ```
306
-
307
- ---
308
-
309
- ## Data Validation Rules
310
-
311
- ### Critical Services (MUST have content)
312
-
313
- | Service | Minimum Children |
314
- |---------|------------------|
315
- | Workspace | At least 1 child |
316
- | Lighting | (any) |
317
- | StarterPlayer | StarterPlayerScripts |
318
-
319
- ### Allowed Empty Services
320
-
321
- These can be empty for new projects:
322
- - ReplicatedStorage
323
- - ServerStorage
324
- - ServerScriptService
325
- - StarterGui
326
-
327
- ### Validation Logic
328
-
329
- ```typescript
330
- function validateSync(): boolean {
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
- }
346
-
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");
351
- }
352
-
353
- // Check all required _tree.json files exist
354
- const required = ["Workspace", "Lighting", "ReplicatedStorage",
355
- "ServerStorage", "ServerScriptService", "StarterGui", "StarterPlayer"];
356
- for (const svc of required) {
357
- if (!fileExists(`${syncDir}/explorer/${svc}/_tree.json`)) {
358
- errors.push(`Missing ${svc}/_tree.json`);
359
- }
360
- }
361
-
362
- return errors.length === 0;
363
- }
364
- ```
365
-
366
- ---
367
-
368
- ## Script Sync Details
369
-
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:
386
-
387
- ```typescript
388
- const scriptSource = await mcp__robloxstudio__get_script_source({
389
- path: instance.path
390
- });
391
-
392
- const ext = {
393
- "Script": ".server.luau",
394
- "LocalScript": ".client.luau",
395
- "ModuleScript": ".module.luau"
396
- }[instance.className];
397
-
398
- Write(`${parentDir}/${instance.name}${ext}`, scriptSource.source);
399
- ```
400
-
401
- ---
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
-
413
- ## Error Handling
414
-
415
- | Error | Action |
416
- |-------|--------|
417
- | MCP not responding | Wait and guide user |
418
- | Studio not connected | Wait and guide user |
419
- | Session ID mismatch | Guide user to reconnect plugin |
420
- | Service sync failed | Retry up to 3 times |
421
- | Script fetch failed | Log warning, continue |
422
- | Verification failed | Retry full sync |
423
-
424
- **Never silently fail. Always report specific errors.**
425
-
426
- ### Session ID Debugging
427
-
428
- When the user reports "plugin shows connected but sync doesn't work":
429
-
430
- 1. Call `get_connection_info()` and note `server.sessionId` (e.g., "A3F2-B1C7")
431
- 2. Ask user to check plugin UI for "ID: XXXX-XXXX" next to server URL
432
- 3. If IDs don't match:
433
- - Multiple MCP servers may be running
434
- - Ask user to disconnect and reconnect the plugin
435
- - Or restart Claude Code session
436
-
437
- ---
438
-
439
- ## Arguments
440
-
441
- | Argument | Action |
442
- |----------|--------|
443
- | `full` | Execute full sync workflow (Steps 0-7) |
444
- | `verify` | Run verification only (Step 6) |
445
- | (none) | Same as `full` |
446
-
447
- ---
448
-
449
- ## Exit Criteria
450
-
451
- **This skill is NOT complete until ALL conditions are true:**
452
-
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
459
-
460
- **If ANY fails, DO NOT return success.**
461
-
462
- ---
463
-
464
- ## Plugin Logs
465
-
466
- When `enableLogSync` is enabled in plugin settings, logs are synced to the MCP server and saved to:
467
-
468
- ```
469
- ~/.roblox-studio-sync/logs/
470
- ├── current.log # Rolling log (last 500 lines)
471
- └── plugin-YYYY-MM-DD.log # Daily log files
472
- ```
473
-
474
- ### Log Format
475
-
476
- ```
477
- [2026-01-29 14:30:45] [INFO] Plugin initialized
478
- [2026-01-29 14:30:46] [DEBUG] Health check OK - Latency: 15ms
479
- [2026-01-29 14:31:02] [ERROR] Connection failed: timeout
480
- ```
481
-
482
- ### Log Levels
483
-
484
- | Level | Description |
485
- |-------|-------------|
486
- | DEBUG | Detailed debugging information |
487
- | INFO | General operational information |
488
- | WARN | Warning messages for potential issues |
489
- | ERROR | Error messages for failures |
490
- | FATAL | Critical errors that may crash the plugin |
491
-
492
- The `logLevel` setting in the plugin controls the minimum level displayed and synced.
@@ -1,162 +0,0 @@
1
- #!/bin/bash
2
- # =============================================================================
3
- # verify-sync-complete.sh - Post-sync verification script
4
- # =============================================================================
5
- # Verifies the folder-based sync structure is complete and valid.
6
- # Explorer mirrors Roblox Studio hierarchy with _index.json per folder.
7
- #
8
- # Exit codes:
9
- # 0 = Sync fully verified
10
- # 1 = Verification failed
11
- # =============================================================================
12
-
13
- set -euo pipefail
14
-
15
- SYNC_DIR="${SYNC_DIR:-roblox-project-sync}"
16
- EXPLORER_DIR="$SYNC_DIR/explorer"
17
-
18
- # Required service folders
19
- REQUIRED_SERVICES=("Workspace" "ReplicatedStorage" "ServerScriptService" "StarterPlayer" "StarterGui" "Lighting" "ServerStorage")
20
-
21
- RED='\033[0;31m'
22
- GREEN='\033[0;32m'
23
- YELLOW='\033[1;33m'
24
- NC='\033[0m'
25
-
26
- log_success() { echo -e "${GREEN}[PASS]${NC} $1"; }
27
- log_fail() { echo -e "${RED}[FAIL]${NC} $1"; }
28
- log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
29
-
30
- verify_service() {
31
- local service="$1"
32
- local service_dir="$EXPLORER_DIR/$service"
33
- # Check folder exists
34
- if [ ! -d "$service_dir" ]; then
35
- log_fail "$service: Folder not found"
36
- return 1
37
- fi
38
-
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
44
- if [ ! -f "$index_file" ]; then
45
- log_fail "$service: _tree.json not found"
46
- return 1
47
- fi
48
-
49
- # Check file has content
50
- local size
51
- size=$(stat -f %z "$index_file" 2>/dev/null || stat -c %s "$index_file" 2>/dev/null || echo "0")
52
- if [ "$size" -lt 20 ]; then
53
- log_fail "$service: $(basename $index_file) too small (${size} bytes)"
54
- return 1
55
- fi
56
-
57
- # Check JSON has required fields
58
- if ! grep -q '"name"' "$index_file" 2>/dev/null; then
59
- log_fail "$service: $(basename $index_file) missing 'name' field"
60
- return 1
61
- fi
62
-
63
- if ! grep -q '"className"' "$index_file" 2>/dev/null; then
64
- log_fail "$service: $(basename $index_file) missing 'className' field"
65
- return 1
66
- fi
67
-
68
- # Workspace must have children (Camera, Terrain always exist)
69
- if [[ "$service" == "Workspace" ]]; then
70
- local child_count
71
- child_count=$(grep -o '"childCount":\s*[0-9]*' "$index_file" | grep -o '[0-9]*' || echo "0")
72
- if [ "$child_count" -lt 2 ]; then
73
- log_fail "$service: Must have children (childCount: $child_count, expected >= 2)"
74
- return 1
75
- fi
76
- fi
77
-
78
- # Count files in service folder (excluding _index.json)
79
- local file_count
80
- file_count=$(find "$service_dir" -type f ! -name '_index.json' 2>/dev/null | wc -l | tr -d ' ')
81
-
82
- # For services that may be empty (new project), just warn
83
- if [[ "$service" != "Workspace" && "$service" != "Lighting" && "$service" != "StarterPlayer" ]]; then
84
- local child_count
85
- child_count=$(grep -o '"childCount":\s*[0-9]*' "$index_file" | grep -o '[0-9]*' || echo "0")
86
- if [ "$child_count" -eq 0 ]; then
87
- log_warn "$service: Empty (childCount: 0) - normal for new project"
88
- fi
89
- fi
90
-
91
- log_success "$service: Verified (_index.json: ${size}B, files: ${file_count})"
92
- return 0
93
- }
94
-
95
- main() {
96
- echo "========================================"
97
- echo " Post-Sync Verification (Folder Mode)"
98
- echo "========================================"
99
- echo ""
100
-
101
- # Check explorer directory exists
102
- if [ ! -d "$EXPLORER_DIR" ]; then
103
- log_fail "Explorer directory not found: $EXPLORER_DIR"
104
- echo ""
105
- echo "VERIFICATION=failed"
106
- echo "REASON=explorer_dir_missing"
107
- exit 1
108
- fi
109
-
110
- local failed=0
111
- local passed=0
112
-
113
- # Check each required service
114
- for service in "${REQUIRED_SERVICES[@]}"; do
115
- if verify_service "$service"; then
116
- passed=$((passed + 1))
117
- else
118
- failed=$((failed + 1))
119
- fi
120
- done
121
-
122
- echo ""
123
- echo "========================================"
124
-
125
- # Check timestamp
126
- if [ -f "$SYNC_DIR/last-sync.txt" ]; then
127
- local last_sync
128
- last_sync=$(cat "$SYNC_DIR/last-sync.txt")
129
- echo "Last sync: $last_sync"
130
- else
131
- log_fail "No timestamp found"
132
- failed=$((failed + 1))
133
- fi
134
-
135
- # Count total scripts synced
136
- local script_count=0
137
- if [ -d "$EXPLORER_DIR" ]; then
138
- script_count=$(find "$EXPLORER_DIR" \( -name "*.luau" -o -name "*.lua" \) -type f 2>/dev/null | wc -l | tr -d ' ')
139
- fi
140
- echo "Scripts synced: $script_count"
141
-
142
- # Final result
143
- echo ""
144
- if [ "$failed" -eq 0 ]; then
145
- echo -e "${GREEN}SYNC VERIFIED: $passed/${#REQUIRED_SERVICES[@]} services OK${NC}"
146
- echo ""
147
- echo "VERIFICATION=passed"
148
- echo "SERVICES_OK=$passed"
149
- echo "SERVICES_TOTAL=${#REQUIRED_SERVICES[@]}"
150
- echo "SCRIPTS_SYNCED=$script_count"
151
- exit 0
152
- else
153
- echo -e "${RED}SYNC INCOMPLETE: $failed services failed${NC}"
154
- echo ""
155
- echo "VERIFICATION=failed"
156
- echo "SERVICES_FAILED=$failed"
157
- echo "SERVICES_TOTAL=${#REQUIRED_SERVICES[@]}"
158
- exit 1
159
- fi
160
- }
161
-
162
- main "$@"