import { ToolDefinition, ToolResponse, ToolExecutor, ConsoleMessage, PerformanceStats, ValidationResult, ValidationIssue } from '../types'; export class DebugTools implements ToolExecutor { private consoleMessages: ConsoleMessage[] = []; private readonly maxMessages = 1000; constructor() { this.setupConsoleCapture(); } private setupConsoleCapture(): void { // Intercept Editor console messages // Note: Editor.Message.addBroadcastListener may not be available in all versions // This is a placeholder for console capture implementation console.log('Console capture setup - implementation depends on Editor API availability'); } private addConsoleMessage(message: any): void { this.consoleMessages.push({ timestamp: new Date().toISOString(), ...message }); // Keep only latest messages if (this.consoleMessages.length > this.maxMessages) { this.consoleMessages.shift(); } } getTools(): ToolDefinition[] { return [ { name: 'get_console_logs', description: 'Get editor console logs', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Number of recent logs to retrieve', default: 100 }, filter: { type: 'string', description: 'Filter logs by type', enum: ['all', 'log', 'warn', 'error', 'info'], default: 'all' } } } }, { name: 'clear_console', description: 'Clear editor console', inputSchema: { type: 'object', properties: {} } }, { name: 'execute_script', description: 'Execute JavaScript in scene context', inputSchema: { type: 'object', properties: { script: { type: 'string', description: 'JavaScript code to execute' } }, required: ['script'] } }, { name: 'get_node_tree', description: 'Get detailed node tree for debugging', inputSchema: { type: 'object', properties: { rootUuid: { type: 'string', description: 'Root node UUID (optional, uses scene root if not provided)' }, maxDepth: { type: 'number', description: 'Maximum tree depth', default: 10 } } } }, { name: 'get_performance_stats', description: 'Get performance statistics', inputSchema: { type: 'object', properties: {} } }, { name: 'validate_scene', description: 'Validate current scene for issues', inputSchema: { type: 'object', properties: { checkMissingAssets: { type: 'boolean', description: 'Check for missing asset references', default: true }, checkPerformance: { type: 'boolean', description: 'Check for performance issues', default: true } } } }, { name: 'get_editor_info', description: 'Get editor and environment information', inputSchema: { type: 'object', properties: {} } } ]; } async execute(toolName: string, args: any): Promise { switch (toolName) { case 'get_console_logs': return await this.getConsoleLogs(args.limit, args.filter); case 'clear_console': return await this.clearConsole(); case 'execute_script': return await this.executeScript(args.script); case 'get_node_tree': return await this.getNodeTree(args.rootUuid, args.maxDepth); case 'get_performance_stats': return await this.getPerformanceStats(); case 'validate_scene': return await this.validateScene(args); case 'get_editor_info': return await this.getEditorInfo(); default: throw new Error(`Unknown tool: ${toolName}`); } } private async getConsoleLogs(limit: number = 100, filter: string = 'all'): Promise { let logs = this.consoleMessages; if (filter !== 'all') { logs = logs.filter(log => log.type === filter); } const recentLogs = logs.slice(-limit); return { success: true, data: { total: logs.length, returned: recentLogs.length, logs: recentLogs } }; } private async clearConsole(): Promise { this.consoleMessages = []; try { // Note: Editor.Message.send may not return a promise in all versions Editor.Message.send('console', 'clear'); return { success: true, message: 'Console cleared successfully' }; } catch (err: any) { return { success: false, error: err.message }; } } private async executeScript(script: string): Promise { return new Promise((resolve) => { Editor.Message.request('scene', 'execute-script', { script: script }).then((result: any) => { resolve({ success: true, data: { result: result, message: 'Script executed successfully' } }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async getNodeTree(rootUuid?: string, maxDepth: number = 10): Promise { return new Promise((resolve) => { const buildTree = async (nodeUuid: string, depth: number = 0): Promise => { if (depth >= maxDepth) { return { truncated: true }; } try { const nodeData = await Editor.Message.request('scene', 'query-node', nodeUuid); const tree = { uuid: nodeData.uuid, name: nodeData.name, active: nodeData.active, components: (nodeData as any).components ? (nodeData as any).components.map((c: any) => c.__type__) : [], childCount: nodeData.children ? nodeData.children.length : 0, children: [] as any[] }; if (nodeData.children && nodeData.children.length > 0) { for (const childId of nodeData.children) { const childTree = await buildTree(childId, depth + 1); tree.children.push(childTree); } } return tree; } catch (err: any) { return { error: err.message }; } }; if (rootUuid) { buildTree(rootUuid).then(tree => { resolve({ success: true, data: tree }); }); } else { Editor.Message.request('scene', 'query-hierarchy').then(async (hierarchy: any) => { const trees = []; for (const rootNode of hierarchy.children) { const tree = await buildTree(rootNode.uuid); trees.push(tree); } resolve({ success: true, data: trees }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); } }); } private async getPerformanceStats(): Promise { return new Promise((resolve) => { Editor.Message.request('scene', 'query-performance').then((stats: any) => { const perfStats: PerformanceStats = { nodeCount: stats.nodeCount || 0, componentCount: stats.componentCount || 0, drawCalls: stats.drawCalls || 0, triangles: stats.triangles || 0, memory: stats.memory || {} }; resolve({ success: true, data: perfStats }); }).catch(() => { // Fallback to basic stats resolve({ success: true, data: { message: 'Performance stats not available in edit mode' } }); }); }); } private async validateScene(options: any): Promise { const issues: ValidationIssue[] = []; try { // Check for missing assets if (options.checkMissingAssets) { const assetCheck = await Editor.Message.request('scene', 'check-missing-assets'); if (assetCheck && assetCheck.missing) { issues.push({ type: 'error', category: 'assets', message: `Found ${assetCheck.missing.length} missing asset references`, details: assetCheck.missing }); } } // Check for performance issues if (options.checkPerformance) { const hierarchy = await Editor.Message.request('scene', 'query-hierarchy'); const nodeCount = this.countNodes(hierarchy.children); if (nodeCount > 1000) { issues.push({ type: 'warning', category: 'performance', message: `High node count: ${nodeCount} nodes (recommended < 1000)`, suggestion: 'Consider using object pooling or scene optimization' }); } } const result: ValidationResult = { valid: issues.length === 0, issueCount: issues.length, issues: issues }; return { success: true, data: result }; } catch (err: any) { return { success: false, error: err.message }; } } private countNodes(nodes: any[]): number { let count = nodes.length; for (const node of nodes) { if (node.children) { count += this.countNodes(node.children); } } return count; } private async getEditorInfo(): Promise { const info = { editor: { version: (Editor as any).versions?.editor || 'Unknown', cocosVersion: (Editor as any).versions?.cocos || 'Unknown', platform: process.platform, arch: process.arch, nodeVersion: process.version }, project: { name: Editor.Project.name, path: Editor.Project.path, uuid: Editor.Project.uuid }, memory: process.memoryUsage(), uptime: process.uptime() }; return { success: true, data: info }; } }