Skip to main content
Glama
diagnostics-manager.ts3.07 kB
import { readFile } from 'fs/promises'; import { resolve, relative } from 'path'; import fg from 'fast-glob'; import { LSPClient } from './lsp-client.js'; import { Diagnostic } from './types.js'; /** * Manages workspace-wide diagnostic operations */ export class DiagnosticsManager { private workspacePath: string | null = null; constructor(private lspClient: LSPClient) { // Listen for workspace changes from Godot lspClient.on('workspaceChange', (params: unknown) => { const oldPath = this.workspacePath; if (params && typeof params === 'object' && 'path' in params) { this.workspacePath = params.path as string; if (oldPath !== this.workspacePath) { console.error(`[DiagnosticsManager] Workspace changed: ${oldPath || '(none)'} -> ${this.workspacePath}`); } } else { console.error(`[DiagnosticsManager] Invalid workspace change params:`, params); } }); } /** * Set workspace path (from gdscript_client/changeWorkspace) */ setWorkspace(path: string): void { this.workspacePath = path; } /** * Get diagnostics for a specific file */ async getFileDiagnostics(filePath: string): Promise<Diagnostic[]> { if (!this.workspacePath) { throw new Error('Workspace path not set. Ensure Godot is running with a project open.'); } // Validate path is within workspace to prevent path traversal const normalizedPath = resolve(filePath); const relativePath = relative(this.workspacePath, normalizedPath); if (relativePath.startsWith('..') || !relativePath) { throw new Error(`File path must be within workspace: ${filePath}`); } const content = await readFile(normalizedPath, 'utf-8'); await this.lspClient.openFile(normalizedPath, content); return this.lspClient.getDiagnostics(normalizedPath); } /** * Scan entire workspace for diagnostics */ async scanWorkspace(): Promise<Record<string, Diagnostic[]>> { if (!this.workspacePath) { throw new Error('Workspace path not set. Ensure Godot is running with a project open.'); } console.error(`[SCAN] Workspace path: ${this.workspacePath}`); // Find all .gd files const files = await fg('**/*.gd', { cwd: this.workspacePath, absolute: true, ignore: ['**/addons/**', '**/.godot/**'], }); console.error(`[SCAN] Found ${files.length} .gd files`); if (files.length <= 5) { console.error(`[SCAN] Files found:`, files); } // Open all files in batches const BATCH_SIZE = 50; for (let i = 0; i < files.length; i += BATCH_SIZE) { const batch = files.slice(i, i + BATCH_SIZE); await Promise.all( batch.map((file) => this.getFileDiagnostics(file).catch((err) => { console.error(`[SCAN] Failed: ${file}:`, err.message); }) ) ); await new Promise((resolve) => setTimeout(resolve, 100)); } // Return all diagnostics (LSPClient already caches them) return this.lspClient.getAllDiagnostics(); } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ryanmazzolini/minimal-godot-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server