fix: improve LSP diagnostics when no servers detected (#1082) (#1086)

* docs: add Node LTS pinning guide for macOS Homebrew users

New doc (docs/node-lts-macos.md) explains how to pin Node 24 LTS
via Homebrew to avoid running on odd-numbered development releases.
Covers brew install/link/pin, version managers as alternatives,
and verification steps.

Added notice banner in README linking to the guide.

* fix: improve LSP diagnostics when no servers detected (#1082)

When lsp status returns 'No language servers configured', the output
now includes diagnostics:
- Which project markers were detected (e.g. package.json found)
- Which server commands are missing (e.g. typescript-language-server)
- Install instructions

Also added LSP troubleshooting section to docs/troubleshooting.md
with common install commands per language.
This commit is contained in:
Tom Boucher 2026-03-17 23:45:11 -04:00 committed by GitHub
parent 94be09482f
commit 792b166ce6
4 changed files with 132 additions and 5 deletions

View file

@ -18,6 +18,8 @@ One command. Walk away. Come back to a built project with clean git history.
<pre><code>npm install -g gsd-pi</code></pre>
> **📋 NOTICE: New to Node on Mac?** If you installed Node.js via Homebrew, you may be running a development release instead of LTS. **[Read this guide](./docs/node-lts-macos.md)** to pin Node 24 LTS and avoid compatibility issues.
</div>
---

75
docs/node-lts-macos.md Normal file
View file

@ -0,0 +1,75 @@
# Pinning Node.js LTS on macOS with Homebrew
If you installed Node.js via Homebrew (`brew install node`), you're tracking the **latest current release** — which can include odd-numbered development versions (e.g. 23.x, 25.x). These aren't LTS and may have breaking changes or instability.
GSD requires Node.js **v22 or later** and works best on an **LTS (even-numbered) release**. This guide shows how to pin Node 24 LTS using Homebrew.
## Check your current version
```bash
node --version
```
If this shows an odd number (e.g. `v23.x`, `v25.x`), you're on a development release.
## Install Node 24 LTS
Homebrew provides versioned formulas for LTS releases:
```bash
# Unlink the current (possibly non-LTS) version
brew unlink node
# Install Node 24 LTS
brew install node@24
# Link it as the default
brew link --overwrite node@24
```
Verify:
```bash
node --version
# Should show v24.x.x
```
## Why pin to LTS?
- **Stability** — LTS releases receive bug fixes and security patches for 30 months
- **Compatibility** — npm packages (including GSD) test against LTS versions
- **No surprises**`brew upgrade` won't jump you to an unstable development release
## Prevent accidental upgrades
By default, `brew upgrade` will upgrade all packages, which could move you off the pinned version. Pin the formula:
```bash
brew pin node@24
```
To unpin later:
```bash
brew unpin node@24
```
## Switching between versions
If you need multiple Node versions (e.g. 22 and 24), consider using a version manager instead:
- **[nvm](https://github.com/nvm-sh/nvm)** — `nvm install 24 && nvm use 24`
- **[fnm](https://github.com/Schniz/fnm)** — `fnm install 24 && fnm use 24` (faster, Rust-based)
- **[mise](https://mise.jdx.dev/)** — `mise use node@24` (polyglot version manager)
These let you set per-project Node versions via `.node-version` or `.nvmrc` files.
## Verify GSD works
After pinning:
```bash
node --version # v24.x.x
npm install -g gsd-pi
gsd --version
```

View file

@ -123,3 +123,27 @@ Doctor rebuilds `STATE.md` from plan and roadmap files on disk and fixes detecte
- **Dashboard:** `Ctrl+Alt+G` or `/gsd status` for real-time diagnostics
- **Forensics:** `/gsd forensics` for structured post-mortem analysis of auto-mode failures
- **Session logs:** `.gsd/activity/` contains JSONL session dumps for crash forensics
## LSP (Language Server Protocol)
### "LSP isn't available in this workspace"
GSD auto-detects language servers based on project files (e.g. `package.json` → TypeScript, `Cargo.toml` → Rust, `go.mod` → Go). If no servers are detected, the agent skips LSP features.
**Check status:**
```
lsp status
```
This shows which servers are active and, if none are found, diagnoses why — including which project markers were detected but which server commands are missing.
**Common fixes:**
| Project type | Install command |
|-------------|-----------------|
| TypeScript/JavaScript | `npm install -g typescript-language-server typescript` |
| Python | `pip install pyright` or `pip install python-lsp-server` |
| Rust | `rustup component add rust-analyzer` |
| Go | `go install golang.org/x/tools/gopls@latest` |
After installing, run `lsp reload` to restart detection without restarting GSD.

View file

@ -14,7 +14,7 @@ import {
setIdleTimeout,
WARMUP_TIMEOUT_MS,
} from "./client.js";
import { getServersForFile, type LspConfig, loadConfig } from "./config.js";
import { getServersForFile, type LspConfig, loadConfig, hasRootMarkers, resolveCommand } from "./config.js";
import { applyTextEdits, applyWorkspaceEdit } from "./edits.js";
import { ToolAbortError, clampTimeout, throwIfAborted } from "./helpers.js";
import { detectLspmux } from "./lspmux.js";
@ -370,10 +370,36 @@ export function createLspTool(cwd: string): AgentTool<typeof lspSchema, LspToolD
: "lspmux: installed but server not running"
: "";
const serverStatus =
servers.length > 0
? `Active language servers: ${servers.join(", ")}`
: "No language servers configured for this project";
let serverStatus: string;
if (servers.length > 0) {
serverStatus = `Active language servers: ${servers.join(", ")}`;
} else {
// Diagnose why no servers were detected
const DEFAULTS = (await import("./defaults.json", { with: { type: "json" } })).default as Record<string, { command: string; rootMarkers: string[] }>;
const diagnostics: string[] = ["No language servers configured for this project."];
const matchedButMissing: string[] = [];
const noMarkers: string[] = [];
for (const [name, def] of Object.entries(DEFAULTS)) {
if (hasRootMarkers(cwd, def.rootMarkers)) {
const resolved = resolveCommand(def.command, cwd);
if (!resolved) {
matchedButMissing.push(` ${name}: project detected (${def.rootMarkers[0]}) but '${def.command}' not found — install it with npm/pip/brew`);
}
}
}
if (matchedButMissing.length > 0) {
diagnostics.push("\nDetected projects missing language servers:");
diagnostics.push(...matchedButMissing);
diagnostics.push("\nInstall the missing server command and restart GSD, or run: lsp reload");
} else {
diagnostics.push("No recognized project markers found in the working directory.");
diagnostics.push("LSP auto-detects projects via files like package.json, Cargo.toml, go.mod, pyproject.toml, etc.");
}
serverStatus = diagnostics.join("\n");
}
const output = lspmuxStatus ? `${serverStatus}\n${lspmuxStatus}` : serverStatus;
return {