Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"enabledPlugins": {
"typescript-lsp@claude-plugins-official": false,
"context7@claude-plugins-official": false,
"typescript-lsp-volta@skills-dir": true
},
"permissions": {
"allow": [
"Bash",
"Edit",
"Glob",
"Grep",
"PowerShell",
"mcp__ide__*",
"NotebookEdit",
"Read",
"WebFetch",
"WebSearch",
"Write"
],
"deny": [
"Bash(chmod -R 777 /)",
"Bash(git commit --no-verify:*)",
"Bash(git commit -n:*)",
"Bash(git push --force:*)",
"Bash(git push -f:*)",
"Bash(mkfs:*)",
"Bash(npm publish:*)",
"Bash(rm -rf /)",
"Bash(rm -rf /*)",
"PowerShell(git commit --no-verify:*)",
"PowerShell(git commit -n:*)",
"PowerShell(git push --force:*)",
"PowerShell(git push -f:*)",
"PowerShell(npm publish:*)",
"PowerShell(rm -rf /)",
"PowerShell(rm -rf /*)",
"PowerShell(rm -r -fo /)",
"PowerShell(Remove-Item -Recurse -Force /:*)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(**/dist/**/*.js)",
"Read(**/dist/**/*.map)"
],
"ask": [
"Bash(chmod -R:*)",
"Bash(chown -R:*)",
"Bash(eval:*)",
"Bash(git clean -f:*)",
"Bash(git push --force-with-lease:*)",
"Bash(git reset --hard:*)",
"Bash(rm -rf:*)",
"Bash(rm -r:*)",
"Bash(sudo:*)",
"Edit(.claude/settings.json)",
"Edit(.claude/settings.local.json)",
"PowerShell(del:*)",
"PowerShell(erase:*)",
"PowerShell(git clean -f:*)",
"PowerShell(git push --force-with-lease:*)",
"PowerShell(git reset --hard:*)",
"PowerShell(Invoke-Expression:*)",
"PowerShell(iex:*)",
"PowerShell(rd:*)",
"PowerShell(ri:*)",
"PowerShell(rm -rf:*)",
"PowerShell(rm -r:*)",
"PowerShell(Remove-Item:*)",
"PowerShell(sudo:*)"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "typescript-lsp-volta",
"displayName": "TypeScript LSP (Volta-compatible)",
"version": "1.0.0",
"description": "TypeScript/JavaScript language server for Claude Code, launched via node against the repo-local typescript-language-server dependency. Works with Volta on Windows, macOS, and Linux. Use instead of the official typescript-lsp plugin, whose shell-free spawn cannot launch Volta's .cmd shim on Windows (ENOENT).",
"lspServers": "./.lsp.json"
}
19 changes: 19 additions & 0 deletions .claude/skills/typescript-lsp-volta/.lsp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"typescript": {
"command": "node",
"args": [
"${CLAUDE_PROJECT_DIR}/node_modules/typescript-language-server/lib/cli.mjs",
"--stdio"
],
"extensionToLanguage": {
".ts": "typescript",
".tsx": "typescriptreact",
".js": "javascript",
".jsx": "javascriptreact",
".mts": "typescript",
".cts": "typescript",
".mjs": "javascript",
".cjs": "javascript"
}
}
}
36 changes: 36 additions & 0 deletions .claude/skills/typescript-lsp-volta/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# typescript-lsp-volta

A committed, cross-platform replacement for the official `typescript-lsp` Claude Code plugin. It
loads automatically for everyone who starts Claude Code from the repo root (a "skills-directory
plugin": `<repo>/.claude/skills/<name>/.claude-plugin/plugin.json`), so there is no install step
beyond `npm install`.

## Why this exists

The official `typescript-lsp` plugin has two problems here:

1. It expects `typescript-language-server` to be installed **globally** (`npm i -g`), which nothing
in this repo does.
2. It spawns that binary from `PATH` **without a shell** (libuv `uv_spawn`). On Windows, Volta (and
npm) provide only a `.cmd` shim for it — no native `.exe` — and a shell-free spawn cannot launch
a `.cmd`, so it fails with `ENOENT: uv_spawn 'typescript-language-server'`. macOS and Linux are
unaffected, because Volta's shims are real executables there.

## How it avoids the bug

`.lsp.json` runs the language server as:

```
node ${CLAUDE_PROJECT_DIR}/node_modules/typescript-language-server/lib/cli.mjs --stdio
```

`node` is a real executable under Volta on every OS, so it spawns shell-free without trouble, and
the server itself comes from the repo-local `typescript-language-server` devDependency rather than a
global install.

## Keeping it working

- `typescript-language-server` must stay in `devDependencies` — it is dev-only tooling and is never
published (`package.json` `files` ships `dist` and `src` only).
- `.claude/settings.json` sets `typescript-lsp@claude-plugins-official` to `false` on purpose. Don't
re-enable it alongside this one, or two language servers will race for the same files.
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,20 @@ dist
.yarn/install-state.gz
.pnp.*

# Claude Code
# Claude Code - files that are supposed to be local-only and not shared with the team
.claude/settings.local.json
.claude/todos/
.claude/memory/
.claude/worktrees
.superpowers
docs/superpowers
.claude/*
# settings.json and skills/ are shared team config (tracked in git), so keep them out of the
# .claude ignore above. The /* glob (rather than a bare `.claude`) is what lets these negations
# re-include the paths.
#
# NOTE `.claude/*` ignores EVERYTHING else under .claude/, including paths that would be shared team
# config if you added them — e.g. agents/, commands/, hooks/. If you add one, add a negation here
# too, or it will silently never be committed.
!.claude/settings.json
!.claude/skills/
9 changes: 9 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mcpServers": {
"context7": {
"type": "stdio",
"command": "node",
"args": ["scripts/mcp-launcher.mjs", "-y", "@upstash/context7-mcp@latest"]
}
}
}
Loading
Loading