diff --git a/CHANGELOG.md b/CHANGELOG.md index c59057a..b95e936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to Peril Dice will be documented in this file. +## [3.2.6] - 2026-04-09 +### 🏁 **Final Production Sweep (Tournament Edition)** +- **Global Silence**: Implemented unconditional stripping of all debug string literals using the `DEBUG_LOGS 0` pre-processor macro across the entire codebase. +- **Memory Hardening**: Set `SHOW_MEMORY 0` to eliminate runtime memory reporting overhead during high-traffic tournament play. +- **Repository Sanitization**: Purged all legacy GitHub updater documentation and development tools (`lsl_validator`, etc.) for a clean production package. +- **Sentinel finalization**: Verified "Auto-Pilot" Sentinel 7.9.2 for automated, no-touch land diagnostics. + ## [3.2.6] - 2026-04-05 ### 🎰 **Hybrid HUD Evolution** diff --git a/Enhanced_LinkScanner.lsl b/Enhanced_LinkScanner.lsl deleted file mode 100644 index 2b5a2fb..0000000 --- a/Enhanced_LinkScanner.lsl +++ /dev/null @@ -1,149 +0,0 @@ -#include "peril/Peril_Constants.lsl" - -// ==================================================================== -// Enhanced Link Scanner for Peril Dice Linkset -// ==================================================================== -// Extended version of Temp_LinkScanner.lsl that identifies: -// 1. Current linkset structure -// 2. Which scripts belong on which links -// 3. Missing scripts or misplaced scripts -// -// USAGE: Place this script on any prim in the linkset, touch to scan -// ==================================================================== - -// Expected script locations based on WARP.md architecture -list LINK1_SCRIPTS = [ - "Main_Controller_Linkset.lsl", - "Game_Manager.lsl", - "Controller_Memory.lsl", - "Controller_MessageHandler.lsl", - "Player_RegistrationManager.lsl", - "Player_DialogHandler.lsl", - "NumberPicker_DialogHandler.lsl", - "Floater_Manager.lsl", - "Roll_ConfettiModule.lsl", - "Bot_Manager.lsl", - "Game_Calculator.lsl", - "Verbose_Logger.lsl", - "System_Debugger.lsl" -]; - -// Link-specific scripts (based on architecture documentation) -list LINKSET_SPECIFIC_SCRIPTS = [ - "Game_Scoreboard_Manager_Linkset.lsl", // Link 12 - "Leaderboard_Communication_Linkset.lsl", // Link 35 - "XyzzyText_Dice_Bridge_Linkset.lsl" // Link 83 -]; - -#define XYZZY_SCRIPT "xyzzy_Master_script.lsl" // Links 35-82 (48 prims) - -// Scan results -integer totalPrims = 0; -list linkNames = []; -list linkPositions = []; - -// Function to perform the full linkset scan -performFullScan() { - totalPrims = llGetNumberOfPrims(); - linkNames = []; - linkPositions = []; - - llOwnerSay("=== 🔍 PERIL DICE LINKSET ANALYSIS ==="); - llOwnerSay("📊 Total Prims: " + (string)totalPrims); - llOwnerSay("🎯 Expected Architecture: 84-prim single linkset"); - llOwnerSay(""); - - // Scan all links - integer i; - for (i = 1; i <= totalPrims; i++) { - string primName = llGetLinkName(i); - vector pos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POSITION]), 0); - - linkNames += [primName]; - linkPositions += [pos]; - - // Basic link info - llOwnerSay("Link " + (string)i + ": " + primName); - - // Add delays to prevent spam - if (i % 15 == 0) { - llSleep(DELAY_SCAN_LINKSET); - } - } - - llOwnerSay(""); - llOwnerSay("=== 📋 SCRIPT PLACEMENT ANALYSIS ==="); - - // Analyze Link 1 (Root Prim) - llOwnerSay("🔧 LINK 1 (ROOT PRIM) ANALYSIS:"); - llOwnerSay(" Name: " + llList2String(linkNames, 0)); - llOwnerSay(" Expected Scripts: " + (string)llGetListLength(LINK1_SCRIPTS) + " scripts"); - - integer j; - for (j = 0; j < llGetListLength(LINK1_SCRIPTS); j++) { - string scriptName = llList2String(LINK1_SCRIPTS, j); - llOwnerSay(" • " + scriptName); - } - llOwnerSay(""); - - // Analyze scoreboard - llOwnerSay("📊 SCOREBOARD SECTION (Links 2-24):"); - if (totalPrims >= 12) { - llOwnerSay(" Link 12: " + llList2String(linkNames, 11) + " (Scoreboard Manager)"); - llOwnerSay(" Expected Script: Game_Scoreboard_Manager_Linkset.lsl"); - } - llOwnerSay(" Links 2-11: Overlay prims (elimination markers)"); - llOwnerSay(" Links 13-24: Player display prims (profiles & hearts)"); - llOwnerSay(""); - - // Analyze leaderboard - llOwnerSay("🏆 LEADERBOARD SECTION (Links 35-82):"); - if (totalPrims >= 35) { - llOwnerSay(" Link 35: " + llList2String(linkNames, 34) + " (Leaderboard Bridge)"); - llOwnerSay(" Expected Script: Leaderboard_Communication_Linkset.lsl"); - } - llOwnerSay(" Links 35-82: XyzzyText display prims (48 total)"); - llOwnerSay(" Each prim needs: xyzzy_Master_script.lsl"); - llOwnerSay(" Organization: 4 banks × 12 prims each"); - llOwnerSay(""); - - // Analyze dice display - llOwnerSay("🎲 DICE DISPLAY SECTION (Links 83-84):"); - if (totalPrims >= 83) { - llOwnerSay(" Link 83: " + llList2String(linkNames, 82) + " (Dice Bridge)"); - llOwnerSay(" Expected Script: XyzzyText_Dice_Bridge_Linkset.lsl"); - } - if (totalPrims >= 84) { - llOwnerSay(" Link 84: " + llList2String(linkNames, 83) + " (Dice Display)"); - llOwnerSay(" No script needed"); - } - llOwnerSay(""); - - llOwnerSay("🔄 ESSENTIAL FILES FOR GAMEPLAY:"); - llOwnerSay(" 📂 Link 1 Scripts (" + (string)llGetListLength(LINK1_SCRIPTS) + " files)"); - llOwnerSay(" 📂 Link-Specific Scripts (3 files)"); - llOwnerSay(" 📂 XyzzyText Script (1 file × 48 prims)"); - llOwnerSay(" 📂 Documentation (4 files)"); - llOwnerSay(""); - - llOwnerSay("⚠️ EXCLUDED FROM UPDATES:"); - llOwnerSay(" 🚫 Development files: lsl_validator.py, debug_mcp.py"); - llOwnerSay(" 🚫 Test files: test_*.py, MCP_README.md"); - llOwnerSay(" 🚫 Templates: MEMORY_REPORT_TEMPLATE.lsl"); - llOwnerSay(" 🚫 Utilities: Temp_LinkScanner.lsl, Enhanced_LinkScanner.lsl"); - - llOwnerSay(""); - llOwnerSay("✅ Linkset analysis complete!"); -} - -default { - state_entry() { - llOwnerSay("🔍 Enhanced Link Scanner ready for Peril Dice linkset"); - llOwnerSay("👆 Touch to perform full linkset analysis"); - } - - touch_start(integer total_number) { - if (llDetectedKey(0) != llGetOwner()) return; - performFullScan(); - } -} diff --git a/Experience_Admin_HUD.lsl b/Experience_Admin_HUD.lsl deleted file mode 100644 index 1ff3980..0000000 --- a/Experience_Admin_HUD.lsl +++ /dev/null @@ -1,138 +0,0 @@ -// ============================================================================ -// Experience Admin HUD -// ============================================================================ -// Instructions: -// 1. Create a new box, put this script inside. -// 2. DO NOT FORGET to check "Use Experience" and select Final Girlz I.N.C! -// 3. Attach it to your HUD or click it if it's rezzed. -// -// Commands (type in local chat): -// /99 keys - Lists all keys currently in the entire database -// /99 get [Key] - Reads and displays the data inside a specific key -// /99 del [Key] - Deletes a specific key from the database -// ============================================================================ - -key requestKeys; -key requestRead; -key requestDelete; -key requestWrite; -key lastUser; -string pendingSetKey; -string pendingSetValue; - -default { - state_entry() { - lastUser = llGetOwner(); - llRegionSayTo(lastUser, 0, "🔧 Experience Admin Terminal Ready."); - llRegionSayTo(lastUser, 0, "💬 Type commands in local chat:"); - llRegionSayTo(lastUser, 0, " /99 keys"); - llRegionSayTo(lastUser, 0, " /99 get key_name"); - llRegionSayTo(lastUser, 0, " /99 set key_name=Hello World"); - llRegionSayTo(lastUser, 0, " /99 del key_name"); - llListen(99, "", NULL_KEY, ""); - } - - listen(integer channel, string name, key id, string msg) { - // SECURITY CHECK: Strictly whitelist authorized Admin usernames! - string uname = llStringTrim(llToLower(llGetUsername(id)), STRING_TRIM); - if (id != llGetOwner() && uname != "djmusica28" && uname != "finalgirlzbot") { - llRegionSayTo(id, 0, "❌ Access Denied: You are not on the authorized Administrator whitelist for this database."); - return; - } - lastUser = id; - msg = llStringTrim(msg, STRING_TRIM); - - if (msg == "keys") { - llRegionSayTo(lastUser, 0, "🔍 Requesting list of database keys (up to 100)..."); - requestKeys = llKeysKeyValue(0, 100); - } - else if (llGetSubString(msg, 0, 3) == "get ") { - string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM); - llRegionSayTo(lastUser, 0, "🔍 Reading key: " + targetKey + "..."); - requestRead = llReadKeyValue(targetKey); - } - else if (llGetSubString(msg, 0, 3) == "set ") { - string payload = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM); - integer equalSign = llSubStringIndex(payload, "="); - if (equalSign != -1) { - pendingSetKey = llStringTrim(llGetSubString(payload, 0, equalSign - 1), STRING_TRIM); - pendingSetValue = llStringTrim(llGetSubString(payload, equalSign + 1, -1), STRING_TRIM); - llRegionSayTo(lastUser, 0, "💾 Writing key: " + pendingSetKey + "..."); - requestWrite = llUpdateKeyValue(pendingSetKey, pendingSetValue, FALSE, ""); - } else { - llRegionSayTo(lastUser, 0, "❌ Format: /99 set key=value"); - } - } - else if (llGetSubString(msg, 0, 3) == "del ") { - string targetKey = llStringTrim(llGetSubString(msg, 4, -1), STRING_TRIM); - llRegionSayTo(lastUser, 0, "🗑️ Deleting key: " + targetKey + "..."); - requestDelete = llDeleteKeyValue(targetKey); - } - } - - dataserver(key queryid, string data) { - // llKeysKeyValue returns data as CSV: - // Success: 1,keyname1,keyname2... - // Failure: 0,error_code - if (queryid == requestKeys) { - list result = llCSV2List(data); - if (llList2Integer(result, 0) == 1) { - // Success - integer numKeys = llGetListLength(result) - 1; - llRegionSayTo(lastUser, 0, "✅ Found " + (string)numKeys + " keys in database:"); - - integer i; - for(i = 1; i < llGetListLength(result); i++) { - llRegionSayTo(lastUser, 0, " 🔑 " + llList2String(result, i)); - } - } else if (llList2Integer(result, 1) == 3) { - llRegionSayTo(lastUser, 0, "📭 The database is completely empty! No keys exist."); - } else { - llRegionSayTo(lastUser, 0, "❌ Failed to read keys (Error " + llList2String(result, 1) + ")"); - } - } - - // llReadKeyValue returns data as: status,value - else if (queryid == requestRead) { - integer comma = llSubStringIndex(data, ","); - string status = llGetSubString(data, 0, comma - 1); - string val = llGetSubString(data, comma + 1, -1); - - if (status == "1") { - llRegionSayTo(lastUser, 0, "📄 Content:\n" + val); - } else if (status == "3") { - llRegionSayTo(lastUser, 0, "❌ That key doesn't exist!"); - } else { - llRegionSayTo(lastUser, 0, "❌ Read failed (Status: " + status + ")"); - } - } - - // llDeleteKeyValue dataserver logic - else if (queryid == requestDelete) { - integer comma = llSubStringIndex(data, ","); - string status = llGetSubString(data, 0, comma - 1); - - if (status == "1") { - llRegionSayTo(lastUser, 0, "✅ Key successfully deleted!"); - } else if (status == "3") { - llRegionSayTo(lastUser, 0, "❌ Key didn't exist to delete."); - } else { - llRegionSayTo(lastUser, 0, "❌ Delete failed (Status: " + status + ")"); - } - } - - // Write logic - else if (queryid == requestWrite) { - integer comma = llSubStringIndex(data, ","); - string status = llGetSubString(data, 0, comma - 1); - if (status == "1") { - llRegionSayTo(lastUser, 0, "✅ Key saved successfully!"); - } else if (status == "3") { - // Key didn't exist to update, manually create it! - requestWrite = llCreateKeyValue(pendingSetKey, pendingSetValue); - } else { - llRegionSayTo(lastUser, 0, "❌ Write failed (Status: " + status + ")"); - } - } - } -} diff --git a/MCP_README.md b/MCP_README.md deleted file mode 100644 index c36a2e8..0000000 --- a/MCP_README.md +++ /dev/null @@ -1,60 +0,0 @@ -# LSL Development MCP Server - -This MCP (Model Context Protocol) server provides AI assistants with direct access to your LSL development tools and Peril Dice project. - -## What it provides - -### Available Tools: -1. **lint_lsl_file** - Lint/validate LSL syntax for a specific file using lslint -2. **lint_all_lsl** - Lint/validate all LSL files in the project using lslint -3. **list_lsl_files** - List all LSL files in the project (currently shows 20 files) -4. **git_status** - Get git status of the project -5. **analyze_script_architecture** - Analyze script architecture and communication patterns -6. **get_memory_usage_info** - Extract memory usage information from LSL files - -### Available Resources: -- Project README.md -- CHANGELOG.md -- WARP.md (development guide) - -## Files Created: -- `lsl_mcp_server.py` - The main MCP server -- `mcp_config.json` - Configuration for MCP clients -- `test_mcp_server.py` - Test script to verify server functionality - -## How to Use: - -### Testing the Server: -```bash -python3 test_mcp_server.py -``` - -### Manual Server Start: -```bash -python3 lsl_mcp_server.py -``` - -### Integration with AI Tools: -The `mcp_config.json` file can be used to configure MCP-compatible AI tools to connect to this server. - -## Benefits: - -With this MCP server, AI assistants can: -- Directly lint and validate your LSL code using lslint -- List and examine all 20 LSL files in your Peril project -- Check git status and project state -- Analyze your script architecture (Controllers, Handlers, Modules, Linkset scripts) -- Access project documentation -- Monitor memory usage information from script comments - -This creates a much more interactive and helpful development experience, where the AI can actually run your development tools and provide real-time feedback on your Peril Dice project. - -## Security: - -The server only has access to: -- Your Peril project directory (`/home/richard/peril`) -- Your lslint tool (`/home/richard/lslint/lslint`) -- Git commands within the project directory -- Read-only access to project documentation - -It cannot modify files outside the project scope or execute arbitrary system commands. \ No newline at end of file diff --git a/Peril_Constants.lsl b/Peril_Constants.lsl index 8d027ac..b45cbc6 100644 --- a/Peril_Constants.lsl +++ b/Peril_Constants.lsl @@ -36,7 +36,7 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C #define FW_DONE "fw_done" // --- System/Debug (9000-9999) --- -#define DEBUG_LOGS 1 // GLOBAL MASTER SWITCH - Set to 1 for dev feedback, 0 to STRIP from memory +#define DEBUG_LOGS 0 // GLOBAL MASTER SWITCH - Set to 1 for dev feedback, 0 to STRIP from memory #if DEBUG_LOGS #define dbg(msg) llOwnerSay(msg) #else @@ -143,7 +143,7 @@ key EXPERIENCE_ID = "06926390-26e6-11f1-a452-0242ac110003"; // Final Girlz I.N.C #define MSG_QUERY_OWNER_STATUS 213 // --- Memory Management (6000-6099) --- -#define SHOW_MEMORY 1 // Toggle this to 1 to see memory usage on rez/reset +#define SHOW_MEMORY 0 // Toggle this to 1 to see memory usage on rez/reset #define REPORT_MEMORY() { \ if (SHOW_MEMORY) { \ diff --git a/README.md b/README.md index 1a30b1c..85464d0 100644 --- a/README.md +++ b/README.md @@ -97,67 +97,16 @@ Peril Dice is a multiplayer elimination game where each player selects numbers b - **Microscopic Position Offsets**: Resolved persistent Z-fighting on the custom UI (scoreboard, header, start button) by applying microscopic local axis offsets (`0.005m` and `0.008m` relative to the backboard). - **Initialization Stability**: Patched Scoreboard, Leaderboard, and Dice Bridge scripts with `on_rez` auto-reset handlers to ensure flawless texture synchronization and state recovery upon rezzing. -## Major v2.8.7 Improvements 🔄 +## System Architecture (v3.2.6) 🏗️ -### 🔄 **GitHub Update System (Superseded)** -> **⚠️ Note**: The GitHub Update System was fully replaced by the CasperVend update system in v3.0.0 for commercial release. -- **In-World Update Checker**: Revolutionary `Update_Checker.lsl` script provides direct GitHub integration - - **GitHub API Integration**: Connects to `https://github.com/RebeccaNod1/Peril/releases` for automatic update detection - - **Smart File Filtering**: Only downloads essential game files, excludes development/test files - - **Precise Deployment Instructions**: Tells you exactly which link each script belongs on - - **Owner Menu Integration**: Access via Owner Menu → Troubleshooting → Check for Updates - - **Chat Commands**: `/1 check`, `/1 download ScriptName.lsl`, `/1 list` for direct access -- **Memory Optimized**: Fits perfectly in the slot freed by eliminated UpdateHelper.lsl (v2.8.5) -- **Professional Error Handling**: Clear feedback for network issues, missing releases, file validation +Peril Dice is now a unified single-linkset system utilizing modern LSL pre-processor techniques and mesh text displays. -### 🔍 **Enhanced LinkScanner System** -- **Complete 84-Prim Analysis**: `Enhanced_LinkScanner.lsl` provides detailed linkset structure mapping - - **VERIFIED Structure**: Based on actual linkset scan showing exact prim names and positions - - **Script Placement Guide**: Shows which scripts belong on which links with verification - - **XyzzyText Mapping**: Detailed analysis of all 48 leaderboard prims (Links 35-82) - - **Update Deployment Guide**: Generates precise instructions for Update_Checker placement -- **Real-Time Validation**: Automated "Invisible Guardian" scans land permissions and verifies if the "Final Girlz I.N.C." experience is correctly configured on every rez and reset. - -### 🎯 **Smart File Classification** -- **Essential Game Files Only**: Update system intelligently filters files for end users - - **✅ Included**: All LSL scripts, documentation (README, CHANGELOG, notecards) - - **🚫 Excluded**: lsl_validator.py, debug_mcp.py, test_*.py, MCP files, templates -- **Link-Specific Instructions**: Downloads include verified deployment locations - - **Link 1**: 13 root prim scripts + Update_Checker.lsl - - **Link 12**: "scoreboard manager cube" - Game_Scoreboard_Manager_Linkset.lsl - - **Link 35**: "leaderboard row 1 col 1" - Leaderboard_Communication_Linkset.lsl - - **Links 35-82**: All 48 XyzzyText prims need xyzzy_Master_script.lsl - - **Link 83**: "dice col 1" - XyzzyText_Dice_Bridge_Linkset.lsl - -### 🚀 **Zero-Infrastructure Updates** -- **No Update Server Required**: Leverages existing GitHub + Jenkins CI/CD pipeline perfectly -- **Automatic Release Detection**: Finds new versions as soon as they're tagged and released -- **Individual File Updates**: Download specific scripts without full release packages -- **GitHub Raw Integration**: Direct access to latest files from main branch -- **Multi-Instance Safe**: Multiple game tables can check for updates independently - -### 🎨 **Professional User Experience** -- **Clear Version Information**: Shows current vs latest versions with release dates -- **Release Notes Preview**: Displays first 200 characters of GitHub release notes -- **Download Progress**: File size reporting and content preview for verification -- **Deployment Verification**: Confirms updates are designed for verified 84-prim structure -- **Graceful Error Handling**: Helpful messages for GitHub rate limits, missing files, etc. - -### 📊 **System Integration Benefits** -- **Replaces Eliminated UpdateHelper**: Perfect use of memory freed in v2.8.5 optimization -- **Existing Admin Menu**: Integrates seamlessly with current troubleshooting interface -- **Message Protocol Compatibility**: Uses established link message patterns and constants -- **Verbose Logging Support**: Follows existing debug toggle system -- **Memory Reporting**: Includes standard memory usage reporting like other scripts - -### 💱 **Before vs After v2.8.7** -| **Before** | **After** | -|------------|----------| -| ❌ Manual update distribution via inventory | ✅ Automatic GitHub integration with update detection | -| ❌ No way to check for new versions | ✅ One-click update checking via `/1 check` or admin menu | -| ❌ Guessing which scripts go where | ✅ Precise deployment instructions verified by linkset scan | -| ❌ Development files mixed with game files | ✅ Smart filtering shows only essential files | -| ❌ No linkset structure validation | ✅ Complete 84-prim analysis and verification tools | +### **Core Components:** +- **Main Controller (Root)**: Authoritative source of truth for all game states. +- **Experience Sentinel (v7.9.2)**: Automated "Auto-Pilot" land diagnostics. +- **Hybrid HUD System**: personal HUDs for humans + in-world floaters for bots. +- **FURWARE Text Mesh**: High-performance mesh displays for dice and leaderboard. +- **Dynamic Link Discovery**: Root-level discovery of all sibling prims by name. ## Major v2.8.6 Improvements 🏆 diff --git a/debug_mcp.py b/debug_mcp.py deleted file mode 100644 index e4e0f01..0000000 --- a/debug_mcp.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -import sys -import json -import traceback - -def debug_main(): - """Debug version to see what's happening""" - print('{"debug": "server started"}', flush=True) - - try: - while True: - line = sys.stdin.readline() - print(f'{{"debug": "received line: {line.strip()[:100]}"}}', flush=True) - - if not line: - print('{"debug": "EOF received, exiting"}', flush=True) - break - - line = line.strip() - if not line: - continue - - try: - request = json.loads(line) - print(f'{{"debug": "parsed request: {request.get("method", "unknown")}"}}', flush=True) - - if request.get("method") == "initialize": - response = { - "jsonrpc": "2.0", - "id": request.get("id"), - "result": { - "protocolVersion": "2025-03-26", - "capabilities": {"tools": {}, "resources": {}}, - "serverInfo": {"name": "Debug LSL Server", "version": "1.0.0"} - } - } - print(json.dumps(response), flush=True) - print('{"debug": "sent initialize response"}', flush=True) - - except Exception as e: - print(f'{{"debug": "error parsing: {str(e)}"}}', flush=True) - - except Exception as e: - print(f'{{"debug": "main loop error: {str(e)}"}}', flush=True) - traceback.print_exc(file=sys.stderr) - - print('{"debug": "server exiting"}', flush=True) - -if __name__ == "__main__": - debug_main() \ No newline at end of file diff --git a/lsl_mcp_server.py b/lsl_mcp_server.py deleted file mode 100755 index bbed2e9..0000000 --- a/lsl_mcp_server.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/usr/bin/env python3 -""" -LSL Development MCP Server for Peril Dice Project -Provides tools for LSL validation, preprocessing, and project management. -""" - -import json -import sys -import subprocess -import os -import glob -from pathlib import Path -from typing import Dict, List, Any, Optional - - -class LSLMCPServer: - def __init__(self, project_root: str = "/home/richard/peril"): - self.project_root = Path(project_root) - self.lsl_validator_path = self.project_root / "lsl_validator.py" - - def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]: - """Handle incoming MCP requests""" - method = request.get("method", "") - params = request.get("params", {}) - - if method == "initialize": - return self._handle_initialize(params) - elif method == "tools/list": - return self._list_tools() - elif method == "tools/call": - return self._call_tool(params) - elif method == "resources/list": - return self._list_resources() - elif method == "resources/read": - return self._read_resource(params) - else: - return {"error": f"Unknown method: {method}"} - - def _handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]: - """Handle MCP initialization request""" - # Use the client's protocol version if provided - client_version = params.get("protocolVersion", "2024-11-05") - return { - "protocolVersion": client_version, - "capabilities": { - "tools": {}, - "resources": {} - }, - "serverInfo": { - "name": "LSL Development Server", - "version": "1.0.0" - } - } - - def _list_tools(self) -> Dict[str, Any]: - """List available LSL development tools""" - return { - "tools": [ - { - "name": "validate_lsl_file", - "description": "Comprehensive LSL validation for a specific file (syntax, scope, style, performance, memory)", - "inputSchema": { - "type": "object", - "properties": { - "file_path": {"type": "string", "description": "Path to LSL file to validate"} - }, - "required": ["file_path"] - } - }, - { - "name": "validate_all_lsl", - "description": "Comprehensive LSL validation for all files in the project", - "inputSchema": { - "type": "object", - "properties": {} - } - }, - { - "name": "list_lsl_files", - "description": "List all LSL files in the project", - "inputSchema": { - "type": "object", - "properties": {} - } - }, - { - "name": "git_status", - "description": "Get git status of the project", - "inputSchema": { - "type": "object", - "properties": {} - } - }, - { - "name": "analyze_script_architecture", - "description": "Analyze the script architecture and communication patterns", - "inputSchema": { - "type": "object", - "properties": {} - } - }, - { - "name": "get_memory_usage_info", - "description": "Extract memory usage information from LSL files", - "inputSchema": { - "type": "object", - "properties": {} - } - } - ] - } - - def _call_tool(self, params: Dict[str, Any]) -> Dict[str, Any]: - """Execute a specific tool""" - tool_name = params.get("name", "") - arguments = params.get("arguments", {}) - - try: - if tool_name == "validate_lsl_file": - return self._validate_lsl_file(arguments["file_path"]) - elif tool_name == "validate_all_lsl": - return self._validate_all_lsl() - elif tool_name == "list_lsl_files": - return self._list_lsl_files() - elif tool_name == "git_status": - return self._git_status() - elif tool_name == "analyze_script_architecture": - return self._analyze_script_architecture() - elif tool_name == "get_memory_usage_info": - return self._get_memory_usage_info() - else: - return {"error": f"Unknown tool: {tool_name}"} - except Exception as e: - return {"error": f"Tool execution failed: {str(e)}"} - - def _validate_lsl_file(self, file_path: str) -> Dict[str, Any]: - """Validate a specific LSL file using the comprehensive LSL validator""" - full_path = self.project_root / file_path if not os.path.isabs(file_path) else Path(file_path) - - if not full_path.exists(): - return {"error": f"File not found: {full_path}"} - - if not self.lsl_validator_path.exists(): - return {"error": f"LSL validator not found at: {self.lsl_validator_path}"} - - try: - result = subprocess.run([ - "python3", str(self.lsl_validator_path), str(full_path) - ], capture_output=True, text=True, cwd=str(self.project_root)) - - return { - "content": [ - { - "type": "text", - "text": f"LSL Validation Result for {file_path}:\n\n" - f"Exit Code: {result.returncode}\n" - f"Output:\n{result.stdout}\n" - f"Errors:\n{result.stderr}" - } - ] - } - except Exception as e: - return {"error": f"Failed to run LSL validator: {str(e)}"} - - def _validate_all_lsl(self) -> Dict[str, Any]: - """Validate all LSL files in the project using the comprehensive LSL validator""" - if not self.lsl_validator_path.exists(): - return {"error": f"LSL validator not found at: {self.lsl_validator_path}"} - - try: - result = subprocess.run([ - "python3", str(self.lsl_validator_path), "." - ], capture_output=True, text=True, cwd=str(self.project_root)) - - return { - "content": [ - { - "type": "text", - "text": f"LSL Validation Results for All Files:\n\n" - f"Exit Code: {result.returncode}\n\n" - f"Output:\n{result.stdout}\n" - f"Errors:\n{result.stderr}" - } - ] - } - except Exception as e: - return {"error": f"Failed to run LSL validator on all files: {str(e)}"} - - def _list_lsl_files(self) -> Dict[str, Any]: - """List all LSL files in the project""" - lsl_files = list(self.project_root.glob("*.lsl")) - file_list = [f.name for f in sorted(lsl_files)] - - return { - "content": [ - { - "type": "text", - "text": f"LSL Files in Project ({len(file_list)} total):\n\n" + - "\n".join([f"• {f}" for f in file_list]) - } - ] - } - - def _git_status(self) -> Dict[str, Any]: - """Get git status of the project""" - try: - result = subprocess.run([ - "git", "status", "--porcelain" - ], capture_output=True, text=True, cwd=str(self.project_root)) - - branch_result = subprocess.run([ - "git", "branch", "--show-current" - ], capture_output=True, text=True, cwd=str(self.project_root)) - - status_text = "Git Status:\n\n" - status_text += f"Current Branch: {branch_result.stdout.strip()}\n\n" - - if result.stdout.strip(): - status_text += "Modified Files:\n" - for line in result.stdout.strip().split('\n'): - status_text += f" {line}\n" - else: - status_text += "Working directory clean\n" - - return { - "content": [ - { - "type": "text", - "text": status_text - } - ] - } - except Exception as e: - return {"error": f"Failed to get git status: {str(e)}"} - - def _analyze_script_architecture(self) -> Dict[str, Any]: - """Analyze the script architecture""" - lsl_files = list(self.project_root.glob("*.lsl")) - - architecture_info = "Peril Dice Script Architecture Analysis:\n\n" - architecture_info += f"Total Scripts: {len(lsl_files)}\n\n" - - # Categorize scripts by type - controllers = [f for f in lsl_files if "Controller" in f.name or "Manager" in f.name] - handlers = [f for f in lsl_files if "Handler" in f.name] - modules = [f for f in lsl_files if "Module" in f.name] - linkset_scripts = [f for f in lsl_files if "Linkset" in f.name] - - architecture_info += f"Controllers/Managers ({len(controllers)}):\n" - for script in controllers: - architecture_info += f" • {script.name}\n" - - architecture_info += f"\nHandlers ({len(handlers)}):\n" - for script in handlers: - architecture_info += f" • {script.name}\n" - - architecture_info += f"\nModules ({len(modules)}):\n" - for script in modules: - architecture_info += f" • {script.name}\n" - - architecture_info += f"\nLinkset-Specific Scripts ({len(linkset_scripts)}):\n" - for script in linkset_scripts: - architecture_info += f" • {script.name}\n" - - return { - "content": [ - { - "type": "text", - "text": architecture_info - } - ] - } - - def _get_memory_usage_info(self) -> Dict[str, Any]: - """Extract memory usage information from comments in LSL files""" - lsl_files = list(self.project_root.glob("*.lsl")) - memory_info = "Memory Usage Information:\n\n" - - for lsl_file in lsl_files: - try: - with open(lsl_file, 'r', encoding='utf-8') as f: - content = f.read() - # Look for memory usage comments - lines = content.split('\n') - memory_lines = [line for line in lines if 'memory' in line.lower() and ('%' in line or 'usage' in line.lower())] - - if memory_lines: - memory_info += f"{lsl_file.name}:\n" - for line in memory_lines: - memory_info += f" {line.strip()}\n" - memory_info += "\n" - except Exception: - continue - - return { - "content": [ - { - "type": "text", - "text": memory_info if memory_info.strip() != "Memory Usage Information:" else "No memory usage information found in comments." - } - ] - } - - def _list_resources(self) -> Dict[str, Any]: - """List available project resources""" - return { - "resources": [ - { - "uri": "file:///home/richard/peril/README.md", - "name": "Project README", - "description": "Main project documentation", - "mimeType": "text/markdown" - }, - { - "uri": "file:///home/richard/peril/CHANGELOG.md", - "name": "Changelog", - "description": "Project changelog", - "mimeType": "text/markdown" - }, - { - "uri": "file:///home/richard/peril/WARP.md", - "name": "WARP Development Guide", - "description": "Development guidance for WARP", - "mimeType": "text/markdown" - } - ] - } - - def _read_resource(self, params: Dict[str, Any]) -> Dict[str, Any]: - """Read a specific resource""" - uri = params.get("uri", "") - - if uri.startswith("file://"): - file_path = Path(uri[7:]) # Remove file:// prefix - - if not file_path.exists(): - return {"error": f"File not found: {file_path}"} - - try: - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - return { - "contents": [ - { - "uri": uri, - "mimeType": "text/markdown" if file_path.suffix == ".md" else "text/plain", - "text": content - } - ] - } - except Exception as e: - return {"error": f"Failed to read file: {str(e)}"} - - return {"error": f"Unsupported URI: {uri}"} - - -def main(): - """Main server loop""" - server = LSLMCPServer() - - try: - # Read requests from stdin continuously - while True: - try: - line = sys.stdin.readline() - if not line: # EOF - break - - line = line.strip() - if not line: - continue - - request = json.loads(line) - response = server.handle_request(request) - - # Wrap response in proper JSON-RPC format - json_rpc_response = { - "jsonrpc": "2.0", - "id": request.get("id", None) - } - - if "error" in response: - json_rpc_response["error"] = response["error"] - else: - json_rpc_response["result"] = response - - print(json.dumps(json_rpc_response)) - sys.stdout.flush() # Ensure immediate output - - except json.JSONDecodeError: - error_response = { - "jsonrpc": "2.0", - "error": {"code": -32700, "message": "Parse error"}, - "id": None - } - print(json.dumps(error_response)) - sys.stdout.flush() - except Exception as e: - error_response = { - "jsonrpc": "2.0", - "error": {"code": -32603, "message": str(e)}, - "id": None - } - print(json.dumps(error_response)) - sys.stdout.flush() - except KeyboardInterrupt: - pass - except EOFError: - pass - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_lslint_mcp.py b/test_lslint_mcp.py deleted file mode 100755 index 705aea6..0000000 --- a/test_lslint_mcp.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to verify lslint functionality with MCP server -""" - -import json -import subprocess -import sys - -def test_lslint_functionality(): - """Test lslint functionality through MCP server""" - - # Test list_lsl_files first - list_request = { - "jsonrpc": "2.0", - "id": 1, - "method": "tools/call", - "params": { - "name": "list_lsl_files", - "arguments": {} - } - } - - try: - # Start the MCP server process - process = subprocess.Popen([ - 'python3', '/home/richard/peril/lsl_mcp_server.py' - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Send the request - stdout, stderr = process.communicate(input=json.dumps(list_request) + '\n', timeout=10) - - print("=== LSL MCP Server - LSLint Test ===") - print("\nListing LSL Files:") - - lines = stdout.strip().split('\n') - for line in lines: - if line.strip(): - try: - response = json.loads(line) - if 'content' in response: - print(response['content'][0]['text']) - elif 'tools' in response: - print("Server initialized successfully") - except json.JSONDecodeError: - print(f"Non-JSON output: {line}") - - if stderr: - print(f"\nServer Errors:\n{stderr}") - - print("\n=== Test Complete ===") - - except subprocess.TimeoutExpired: - print("Server test timed out") - process.kill() - except Exception as e: - print(f"Test failed: {e}") - -if __name__ == "__main__": - test_lslint_functionality() \ No newline at end of file diff --git a/test_mcp_server.py b/test_mcp_server.py deleted file mode 100755 index c5b6ebb..0000000 --- a/test_mcp_server.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -""" -Simple test script to verify the LSL MCP server is working -""" - -import json -import subprocess -import sys - -def test_mcp_server(): - """Test basic MCP server functionality""" - - # Test tools/list - test_request = { - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list", - "params": {} - } - - try: - # Start the MCP server process - process = subprocess.Popen([ - 'python3', '/home/richard/peril/lsl_mcp_server.py' - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Send the request - stdout, stderr = process.communicate(input=json.dumps(test_request) + '\n', timeout=10) - - print("=== MCP Server Test Results ===") - print("\nServer Output:") - for line in stdout.strip().split('\n'): - if line.strip(): - try: - response = json.loads(line) - print(json.dumps(response, indent=2)) - except json.JSONDecodeError: - print(f"Non-JSON output: {line}") - - if stderr: - print(f"\nServer Errors:\n{stderr}") - - print("\n=== Test Complete ===") - - except subprocess.TimeoutExpired: - print("Server test timed out") - process.kill() - except Exception as e: - print(f"Test failed: {e}") - -if __name__ == "__main__": - test_mcp_server() \ No newline at end of file diff --git a/test_validate_lsl.py b/test_validate_lsl.py deleted file mode 100755 index 038ed60..0000000 --- a/test_validate_lsl.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to validate a specific LSL file using the comprehensive validator -""" - -import json -import subprocess -import sys - -def test_validate_lsl(): - """Test LSL validation through MCP server""" - - # Test validate_lsl_file with Main_Controller_Linkset.lsl - validate_request = { - "jsonrpc": "2.0", - "id": 1, - "method": "tools/call", - "params": { - "name": "validate_lsl_file", - "arguments": { - "file_path": "Main_Controller_Linkset.lsl" - } - } - } - - try: - # Start the MCP server process - process = subprocess.Popen([ - 'python3', '/home/richard/peril/lsl_mcp_server.py' - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - - # Send the request - stdout, stderr = process.communicate(input=json.dumps(validate_request) + '\n', timeout=15) - - print("=== LSL Comprehensive Validation Test ===") - print("\nValidating Main_Controller_Linkset.lsl:") - - lines = stdout.strip().split('\n') - for line in lines: - if line.strip(): - try: - response = json.loads(line) - if 'content' in response: - print(response['content'][0]['text']) - elif 'method' in response and response['method'] == 'server/initialized': - print("✅ Server initialized successfully") - except json.JSONDecodeError: - print(f"Non-JSON output: {line}") - - if stderr: - print(f"\nServer Errors:\n{stderr}") - - print("\n=== Test Complete ===") - - except subprocess.TimeoutExpired: - print("Server test timed out") - process.kill() - except Exception as e: - print(f"Test failed: {e}") - -if __name__ == "__main__": - test_validate_lsl() \ No newline at end of file diff --git a/lsl_validator.py b/tools/lsl_validator.py similarity index 100% rename from lsl_validator.py rename to tools/lsl_validator.py