Implements a comprehensive 'doctor' command that diagnoses oh-my-opencode installation health with a beautiful TUI output. Checks performed: - OpenCode installation (version, path, binary) - Plugin registration in opencode.json - Configuration file validity (oh-my-opencode.json) - Auth providers (Anthropic, OpenAI, Google) - Dependencies (ast-grep CLI/NAPI, comment-checker) - LSP servers availability - MCP servers (builtin and user) - Version status and updates Features: - Beautiful TUI with symbols and colors - --verbose flag for detailed output - --json flag for machine-readable output - --category flag for running specific checks - Exit code 1 on failures for CI integration Closes #333 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
import type { CheckResult, CheckDefinition, LspServerInfo } from "../types"
|
|
import { CHECK_IDS, CHECK_NAMES } from "../constants"
|
|
|
|
const DEFAULT_LSP_SERVERS: Array<{
|
|
id: string
|
|
binary: string
|
|
extensions: string[]
|
|
}> = [
|
|
{ id: "typescript-language-server", binary: "typescript-language-server", extensions: [".ts", ".tsx", ".js", ".jsx"] },
|
|
{ id: "pyright", binary: "pyright-langserver", extensions: [".py"] },
|
|
{ id: "rust-analyzer", binary: "rust-analyzer", extensions: [".rs"] },
|
|
{ id: "gopls", binary: "gopls", extensions: [".go"] },
|
|
]
|
|
|
|
async function checkBinaryExists(binary: string): Promise<boolean> {
|
|
try {
|
|
const proc = Bun.spawn(["which", binary], { stdout: "pipe", stderr: "pipe" })
|
|
await proc.exited
|
|
return proc.exitCode === 0
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export async function getLspServersInfo(): Promise<LspServerInfo[]> {
|
|
const servers: LspServerInfo[] = []
|
|
|
|
for (const server of DEFAULT_LSP_SERVERS) {
|
|
const installed = await checkBinaryExists(server.binary)
|
|
servers.push({
|
|
id: server.id,
|
|
installed,
|
|
extensions: server.extensions,
|
|
source: "builtin",
|
|
})
|
|
}
|
|
|
|
return servers
|
|
}
|
|
|
|
export function getLspServerStats(servers: LspServerInfo[]): { installed: number; total: number } {
|
|
const installed = servers.filter((s) => s.installed).length
|
|
return { installed, total: servers.length }
|
|
}
|
|
|
|
export async function checkLspServers(): Promise<CheckResult> {
|
|
const servers = await getLspServersInfo()
|
|
const stats = getLspServerStats(servers)
|
|
const installedServers = servers.filter((s) => s.installed)
|
|
const missingServers = servers.filter((s) => !s.installed)
|
|
|
|
if (stats.installed === 0) {
|
|
return {
|
|
name: CHECK_NAMES[CHECK_IDS.LSP_SERVERS],
|
|
status: "warn",
|
|
message: "No LSP servers detected",
|
|
details: [
|
|
"LSP tools will have limited functionality",
|
|
...missingServers.map((s) => `Missing: ${s.id}`),
|
|
],
|
|
}
|
|
}
|
|
|
|
const details = [
|
|
...installedServers.map((s) => `Installed: ${s.id}`),
|
|
...missingServers.map((s) => `Not found: ${s.id} (optional)`),
|
|
]
|
|
|
|
return {
|
|
name: CHECK_NAMES[CHECK_IDS.LSP_SERVERS],
|
|
status: "pass",
|
|
message: `${stats.installed}/${stats.total} servers available`,
|
|
details,
|
|
}
|
|
}
|
|
|
|
export function getLspCheckDefinition(): CheckDefinition {
|
|
return {
|
|
id: CHECK_IDS.LSP_SERVERS,
|
|
name: CHECK_NAMES[CHECK_IDS.LSP_SERVERS],
|
|
category: "tools",
|
|
check: checkLspServers,
|
|
critical: false,
|
|
}
|
|
}
|