diff --git a/src/cli/doctor/checks/tools-lsp.ts b/src/cli/doctor/checks/tools-lsp.ts index d55b5ec46..3add74053 100644 --- a/src/cli/doctor/checks/tools-lsp.ts +++ b/src/cli/doctor/checks/tools-lsp.ts @@ -1,6 +1,46 @@ +import { readFileSync } from "node:fs" +import { join } from "node:path" import { createLspMcpConfig } from "../../../mcp/lsp" +import { detectPluginConfigFile, getOpenCodeConfigDir, parseJsonc } from "../../../shared" + +type OmoConfigForDoctor = { + disabled_mcps?: string[] +} + +const PROJECT_CONFIG_DIR = join(process.cwd(), ".opencode") + +function readOmoConfig(configDirectory: string): OmoConfigForDoctor | null { + const detected = detectPluginConfigFile(configDirectory) + if (detected.format === "none") { + return null + } + + try { + const content = readFileSync(detected.path, "utf-8") + return parseJsonc(content) + } catch { + return null + } +} + +function isLspMcpDisabled(): boolean { + const userConfigDirectory = getOpenCodeConfigDir({ binary: "opencode" }) + const userConfig = readOmoConfig(userConfigDirectory) + const projectConfig = readOmoConfig(PROJECT_CONFIG_DIR) + + const disabledMcps = new Set([ + ...(userConfig?.disabled_mcps ?? []), + ...(projectConfig?.disabled_mcps ?? []), + ]) + + return disabledMcps.has("lsp") +} export function getInstalledLspServers(): Array<{ id: string; extensions: string[] }> { + if (isLspMcpDisabled()) { + return [] + } + const lspMcpConfig = createLspMcpConfig() if (!lspMcpConfig) { diff --git a/src/mcp/lsp.ts b/src/mcp/lsp.ts index de7a82a85..f43e66106 100644 --- a/src/mcp/lsp.ts +++ b/src/mcp/lsp.ts @@ -13,21 +13,35 @@ export type LocalMcpConfig = { environment?: Record } +function addCliPathCandidates(startDirectory: string, maxParentDepth: number, target: Set): void { + let currentDirectory = startDirectory + + for (let depth = 0; depth <= maxParentDepth; depth += 1) { + target.add(resolve(currentDirectory, SUBMODULE_REL, CLI_REL)) + + const parentDirectory = resolve(currentDirectory, "..") + if (parentDirectory === currentDirectory) { + return + } + + currentDirectory = parentDirectory + } +} + function resolveLspCliPathCandidates(): string[] { - const candidates: string[] = [] + const candidates = new Set() try { const currentFilePath = fileURLToPath(import.meta.url) - candidates.push(resolve(currentFilePath, "..", "..", "..", SUBMODULE_REL, CLI_REL)) - candidates.push(resolve(currentFilePath, "..", "..", SUBMODULE_REL, CLI_REL)) - candidates.push(resolve(currentFilePath, "..", SUBMODULE_REL, CLI_REL)) + const currentDirectory = resolve(currentFilePath, "..") + addCliPathCandidates(currentDirectory, 6, candidates) } catch { - // ignore and fall through to cwd-based candidate + // ignore and fall through to cwd-based candidates } - candidates.push(resolve(process.cwd(), SUBMODULE_REL, CLI_REL)) + addCliPathCandidates(process.cwd(), 4, candidates) - return candidates + return [...candidates] } export function createLspMcpConfig(): LocalMcpConfig | null {