diff --git a/README.md b/README.md index 9063c68cb..869acfadb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ One command. Walk away. Come back to a built project with clean git history.
npm install -g gsd-pi
+> **📋 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. + --- diff --git a/docs/node-lts-macos.md b/docs/node-lts-macos.md new file mode 100644 index 000000000..67582aec1 --- /dev/null +++ b/docs/node-lts-macos.md @@ -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 +``` diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 022688338..452ca8ccb 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -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. diff --git a/packages/pi-coding-agent/src/core/lsp/index.ts b/packages/pi-coding-agent/src/core/lsp/index.ts index b2dbf2c6d..9bcfa66f7 100644 --- a/packages/pi-coding-agent/src/core/lsp/index.ts +++ b/packages/pi-coding-agent/src/core/lsp/index.ts @@ -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 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; + 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 {