#!/usr/bin/env -S npx tsx import * as path from "node:path"; import { startClientForFile } from "./src/client.ts"; import { isLspCommand, listCommands, runCommand } from "./src/commands.ts"; import { pickServer } from "./src/root.ts"; // Usage function usage(): never { process.stderr.write( `Usage: cli.ts \n` + `Commands: ${listCommands().join(", ")}\n` + `Example:\n` + ` cli.ts backend/api/server.go hover '{"position":{"line":223,"character":22}}'\n`, ); process.exit(2); } async function main() { const [, , fileArg, cmdArg, jsonArg] = process.argv; if (!fileArg || !cmdArg || jsonArg === undefined) usage(); if (!isLspCommand(cmdArg)) { process.stderr.write( `Unknown command "${cmdArg}". Known: ${listCommands().join(", ")}\n`, ); process.exit(2); } // Parse Request JSON let params: Record; try { params = jsonArg.trim() === "" ? {} : JSON.parse(jsonArg); } catch (err) { process.stderr.write( `Invalid JSON for req_data_json: ${(err as Error).message}\n`, ); process.exit(2); } const filePath = path.resolve(fileArg); const server = pickServer(filePath); const { client, uri } = await startClientForFile(server, filePath); try { const result = await runCommand(cmdArg, client, uri, params); process.stdout.write(JSON.stringify(result, null, 2) + "\n"); } finally { // Fire-And-Forget Shutdown - With `gopls -remote=auto` (and similar) // the spawned process is a thin client to a background daemon; a // graceful shutdown can hang the parent. Kick it off but don't wait. void client.dispose(); } // Hard Exit - Any lingering handles (LSP stdio, daemon stubs) would keep // the event loop alive. For a short-lived CLI we just exit. process.exit(0); } main().catch((err) => { process.stderr.write(`${(err as Error).stack ?? err}\n`); process.exit(1); });