[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-1259":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":30,"readmeContent":31,"aiSummary":32,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":33,"discoverSource":34},1259,"claude-code-lsp-enforcement-kit","nesaminua\u002Fclaude-code-lsp-enforcement-kit","nesaminua","Hooks that force Claude Code to use LSP instead of Grep for code navigation. Saves   ~80% tokens","",null,"JavaScript",310,20,1,3,0,2,23,47.27,"MIT License",false,"main",true,[25,26,27,28,29],"claude","claude-ai","claude-code","claude-hook","claude-hooks","2026-06-12 04:00:08","\u003Ch1 align=\"center\">LSP Enforcement Kit\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\n  \u003Cstrong>Physical enforcement of LSP-first navigation in Claude Code.\u003C\u002Fstrong>\n  \u003Cbr>\n  Stop burning tokens on Grep. Make Claude navigate code like an IDE — 100% of the time.\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit\u002Freleases\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fv\u002Frelease\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit?style=for-the-badge&color=6366f1\" alt=\"Release\">\u003C\u002Fa>\n  \u003Ca href=\"LICENSE\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit?style=for-the-badge&color=10b981\" alt=\"License\">\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit\u002Fstargazers\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit?style=for-the-badge&color=f59e0b\" alt=\"Stars\">\u003C\u002Fa>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FClaude%20Code-compatible-8b5cf6?style=for-the-badge\" alt=\"Claude Code compatible\">\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"#-quick-start\">Quick Start\u003C\u002Fa> &bull;\n  \u003Ca href=\"#-the-problem\">Why\u003C\u002Fa> &bull;\n  \u003Ca href=\"#-token-savings-grep-vs-lsp-per-operation\">Savings\u003C\u002Fa> &bull;\n  \u003Ca href=\"#-architecture-6-hooks--1-tracker\">Architecture\u003C\u002Fa> &bull;\n  \u003Ca href=\"#-how-each-hook-works\">Hooks\u003C\u002Fa> &bull;\n  \u003Ca href=\"CHANGELOG.md\">Changelog\u003C\u002Fa>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Ftoken-savings.png\" alt=\"LSP vs Grep token savings — 73% per week\" width=\"720\">\n\u003C\u002Fp>\n\n---\n\n## In Action\n\nWhen Claude tries to `Grep` for a code symbol, the hook blocks with a copy-pasteable LSP command:\n\n```\n⛔ LSP-FIRST BLOCK: Pattern contains code symbol(s) — use LSP instead\nSymbols: handleSubmit, UserService\n\nLSP tools:\n  handleSubmit:\n    mcp__cclsp__find_references(\"handleSubmit\")  (cclsp)\n\n  UserService:\n    mcp__cclsp__find_workspace_symbols(\"UserService\")  (cclsp)\n```\n\nWhen Claude tries to `Read` a code file without warming up LSP, the progressive gate blocks:\n\n```\n🛡️  LSP-FIRST READ GATE — Gate 1: warmup required\n\n  Call one of these first:\n    mcp__cclsp__get_diagnostics(\"src\u002Fpage.tsx\")  (cclsp)\n\n  CONCRETE CALL FOR THIS FILE (works in any project):\n    mcp__cclsp__get_diagnostics(\"src\u002Fpage.tsx\")\n\n  After warmup: 2 free Reads, then need LSP navigation.\n```\n\nNo generic advice. Every block message is parametrized by the actual file Claude tried to touch.\n\n---\n\n## ⚡ Quick Start\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit.git\ncd claude-code-lsp-enforcement-kit\nbash install.sh\n# Windows: pwsh .\u002Finstall.ps1\n```\n\nRestart Claude Code. Done. The installer is idempotent — safe to re-run on upgrades.\n\nVerify:\n\n```bash\nbash scripts\u002Flsp-status.sh\n```\n\n---\n\n## 🎯 The Problem\n\nClaude Code defaults to **Grep + Read** for code navigation. This works, but it's wasteful:\n\n```\n\"Where is handleSubmit defined?\"\n\nGrep approach:\n  Grep(\"handleSubmit\") → 23 matches, ~1500 tokens of output\n  Read file1.tsx (wrong) → 2500 tokens\n  Read file2.tsx (still wrong) → 2500 tokens\n  Read file3.tsx (found it) → 2500 tokens\n  ─────────────────────────────────────\n  Total: ~9,000 tokens, 4 tool calls\n\nLSP approach:\n  find_definition(\"handleSubmit\") → form-actions.ts:42, ~80 tokens\n  Read form-actions.ts:35-55 → ~150 tokens\n  ─────────────────────────────────────\n  Total: ~230 tokens, 2 tool calls\n```\n\n**~40x fewer tokens. Same answer.**\n\nA rule in CLAUDE.md saying \"use LSP\" helps ~60% of the time. Hooks make it 100%.\n\n## 💰 Token Savings: Grep vs LSP Per Operation\n\n| Task | Grep approach | LSP approach | Saved |\n|------|--------------|--------------|-------|\n| Find definition of `handleSubmit` | Grep → 23 matches (~1500 tok) + 2 wrong Reads (~5000 tok) = **~6500 tok** | `find_definition` → file:line (~80 tok) + 1 targeted Read (~500 tok) = **~580 tok** | **91%** |\n| Find all usages of `UserService` | Grep → 15 matches (~1200 tok), scan results (~300 tok) = **~1500 tok** | `find_references` → 8 file:line pairs (~150 tok) = **~150 tok** | **90%** |\n| Check type of `formData` | Read full file (~2500 tok), search visually = **~2500 tok** | `get_hover` → type signature (~60 tok) = **~60 tok** | **98%** |\n| Find component `InviteForm` | Glob (~200 tok) + Grep (~800 tok) + Read wrong file (~2500 tok) = **~3500 tok** | `find_workspace_symbols` → exact location (~100 tok) = **~100 tok** | **97%** |\n| Who calls `validateToken`? | Grep → noisy results (~1500 tok) + 3 Reads to verify (~6000 tok) = **~7500 tok** | `get_incoming_calls` → caller list (~200 tok) + 1 Read (~500 tok) = **~700 tok** | **91%** |\n\n## 📊 Real-World Data: 1 Week, 2 Projects\n\nAggregate from a week of development across 2 TypeScript projects:\n\n| Metric | With LSP | Without LSP (estimated) |\n|--------|----------|------------------------|\n| LSP navigation calls | 39 | — |\n| Grep calls on code symbols | 0 (blocked) | ~120 |\n| Unique code files Read | 53 | ~180 |\n| Estimated navigation tokens | **~85k** | **~320k** |\n| **Tokens saved** | | **~235k (~73%)** |\n\n**How the estimate works:**\n- Each blocked Grep saves ~1200 tokens of noisy output\n- Each avoided Read saves ~1500 tokens of file content loaded into context\n- 39 LSP calls cost ~4k tokens total (precise, compact results)\n- Without LSP: ~120 Greps + ~180 Reads = ~315k tokens for the same navigation work\n- With LSP: 39 nav calls + 53 targeted Reads = ~84k tokens\n\n## 🔌 Works with any LSP MCP server\n\nv2.1 introduces **provider-aware block messages**. The kit detects which LSP MCP server(s) you have installed and tailors its suggestions accordingly:\n\n- [**cclsp**](https:\u002F\u002Fgithub.com\u002Fktnyt\u002Fcclsp) — standalone MCP server or bundled via the `typescript-lsp` Claude Code plugin. Suggestions use `mcp__cclsp__find_definition`, `find_references`, `find_workspace_symbols`, etc.\n- [**Serena**](https:\u002F\u002Fgithub.com\u002Foraios\u002Fserena) — high-level symbol MCP server (MIT, by Oraios AI). Multi-language support (Python, Go, Rust, Java, TypeScript, Vue, and more via its bundled `solidlsp` wrapper). Suggestions use `mcp__serena__find_symbol`, `find_referencing_symbols`, `get_symbols_overview`.\n- **Both installed** — suggestions show entries for both providers.\n- **Neither installed** — generic fallback with install hints for both.\n\nDetection reads user-level Claude Code config (`~\u002F.claude.json`, `~\u002F.claude\u002Fsettings.json`) and matches known server names. The shared helper is in `hooks\u002Flib\u002Fdetect-lsp-provider.js` — adding a new provider means adding one entry to its `PROVIDERS` registry, with no changes to the individual hooks.\n\n## 🏗️ Architecture: 6 Hooks + 1 Tracker\n\n```\n                    PreToolUse                          PostToolUse\n                    ──────────                          ───────────\n\n Grep call ──→ [lsp-first-guard.js] ──→ BLOCK\n                  detects code symbols,\n                  suggests LSP equivalent\n\n Glob call ──→ [lsp-first-glob-guard.js] ──→ BLOCK\n                  blocks *UserService*, **\u002FhandleFoo*.ts;\n                  allows *.ts, *subdomain*, src\u002F**\n\n Bash(grep) ──→ [bash-grep-block.js] ──→ BLOCK\n                  catches grep\u002Frg\u002Fag\u002Fack\n                  in shell commands\n\n Read(.tsx) ──→ [lsp-first-read-guard.js] ──→ GATE\n                  5 progressive gates\n                  (warmup → orient → nav → surgical)\n\n Agent(impl) ─→ [lsp-pre-delegation.js] ──→ BLOCK\n                  subagents can't access MCP,\n                  orchestrator must pre-resolve\n\n LSP call ─────────────────────────────────────→ [lsp-usage-tracker.js]\n                                                   tracks nav_count,\n                                                   read_count, state\n\n                    SessionStart\n                    ────────────\n\n New session ──→ [lsp-session-reset.js] ──→ WIPE\n                    clears stale nav_count for current cwd,\n                    forces fresh warmup + re-enforces gates\n```\n\n> **v2 note:** versions before v2 had two silent bypass routes that let\n> Claude read code files without ever calling LSP:\n> (1) `Glob(\"*SymbolName*\")` had no guard, and (2) `nav_count` persisted\n> for 24 h across sessions, so a new session inherited \"surgical mode\"\n> (unlimited reads) from yesterday's LSP work. Both are closed in v2 by\n> `lsp-first-glob-guard.js` and `lsp-session-reset.js`. If you installed\n> v1, re-run `bash install.sh` — it merges the new hooks without touching\n> your existing settings.\n\n## 🔧 How Each Hook Works\n\n### 1. `lsp-first-guard.js` — Grep Blocker\n\n**Hook type:** PreToolUse | **Matcher:** `Grep`\n\nIntercepts every Grep call. Detects code symbols in the pattern. Blocks with a suggestion to use the correct LSP tool.\n\n| Pattern | Detected as | Action |\n|---------|------------|--------|\n| `getUserById` | camelCase symbol | BLOCK |\n| `UserService` | PascalCase symbol | BLOCK |\n| `router.refresh` | dotted symbol | BLOCK |\n| `write_audit_log` | snake_case function | BLOCK |\n| `create-folder-modal` | component filename | BLOCK |\n| `TODO` | keyword | allow |\n| `NEXT_PUBLIC_URL` | env var (SCREAMING_SNAKE) | allow |\n| `flex-col` | CSS class | allow |\n| `*.md`, `*.json`, `*.sql` | non-code file glob | allow |\n| `.task\u002F`, `node_modules\u002F` | non-code path | allow |\n\n**Block message example** (with both cclsp and Serena detected):\n```\n⛔ LSP-FIRST BLOCK: 1 code symbol(s) in Grep — use LSP instead\nSymbols: handleSubmit\nLSP tools:\n  handleSubmit:\n    mcp__cclsp__find_references(\"handleSubmit\")  (cclsp)\n    mcp__serena__find_referencing_symbols(\"handleSubmit\")  (Serena)\n```\nIf only one provider is installed, only that suggestion appears.\n\n### 2. `lsp-first-glob-guard.js` — Glob Symbol Blocker\n\n**Hook type:** PreToolUse | **Matcher:** `Glob`\n\nCloses the gap where Claude searches for a symbol by *filename pattern* instead of content. Without this hook, `Glob(\"*UserService*\")` silently returns the file, Claude reads it, and LSP enforcement never fires.\n\nThe guard parses the glob pattern, extracts alphabetic tokens, and blocks if any token looks like a code symbol (PascalCase, camelCase, or snake_case with 3+ parts). Lowercase-only tokens and short generic words are always allowed.\n\n| Pattern | Detected as | Action |\n|---------|------------|--------|\n| `*UserService*` | PascalCase symbol | BLOCK |\n| `**\u002FAuthProvider.tsx` | PascalCase in path | BLOCK |\n| `*createOrder*` | camelCase symbol | BLOCK |\n| `*handleSubmit*` | camelCase handler | BLOCK |\n| `*get_user_sessions*` | snake_case function | BLOCK |\n| `src\u002F**\u002F*.ts` | extension pattern | allow |\n| `*.tsx`, `**\u002F*.json` | extension pattern | allow |\n| `*subdomain*`, `*auth*` | lowercase concept | allow |\n| `**\u002Fmiddleware*` | file concept | allow |\n| `tsconfig.json`, `next.config.ts` | framework config | allow |\n| `README.md` | docs | allow |\n\n**Allowed by design:** lowercase concept searches (`*auth*`, `*subdomain*`) are legitimate file discovery by topic. Only symbol-shaped tokens (casing patterns) are blocked, because those should use `find_workspace_symbols` instead.\n\n### 3. `bash-grep-block.js` — Shell Grep Blocker\n\n**Hook type:** PreToolUse | **Matcher:** `Bash`\n\nSame detection logic, but for `Bash(grep \"UserService\" src\u002F)`, `Bash(rg handleSubmit)`, etc. Claude sometimes tries to bypass the Grep hook by shelling out.\n\nAllows: `git grep` (history search), non-code paths, non-code file type filters.\n\n### 4. `lsp-first-read-guard.js` — Progressive Read Gate\n\n**Hook type:** PreToolUse | **Matcher:** `Read`\n\nThe most sophisticated hook. Forces a \"navigate first, read targeted\" workflow through 5 gates:\n\n```\nGate 1 — Warmup Required\n  No LSP state file → BLOCK\n  Must call get_diagnostics(\u003Cany .ts file>) first\n\nGate 2 — Free Orientation (reads 1-2)\n  ALLOW — explore freely, no restrictions\n\nGate 3 — Warning (read 3)\n  WARN if no LSP nav calls yet\n  \"Next Read will be BLOCKED\"\n\nGate 4 — Navigation Required (reads 4-5)\n  BLOCK if nav_count \u003C 1\n  Must use at least 1 LSP navigation call\n\nGate 5 — Surgical Mode (reads 6+)\n  BLOCK if nav_count \u003C 2\n  After 2 nav calls → unlimited reads forever\n```\n\n**Session flow:**\n```\nSession starts\n  │\n  ├─ Read(page.tsx) → Gate 1 BLOCKS → \"warmup required\"\n  │\n  ├─ get_diagnostics(file.ts) → tracker writes warmup_done=true\n  │\n  ├─ Read(page.tsx) → Gate 2 allows (1 of 2 free)\n  ├─ Read(actions.ts) → Gate 2 allows (2 of 2 free)\n  ├─ Read(types.ts) → Gate 3 WARNS\n  ├─ Read(helpers.ts) → Gate 4 BLOCKS\n  │\n  ├─ find_workspace_symbols(\"MyFunc\") → tracker: nav_count=1\n  │\n  ├─ Read(helpers.ts) → unlocked (reads 4-5)\n  ├─ Read(utils.ts) → unlocked\n  ├─ Read(service.ts) → Gate 5 BLOCKS\n  │\n  ├─ find_references(\"MyFunc\") → tracker: nav_count=2\n  │\n  └─ SURGICAL MODE — all Reads unlimited\n```\n\n**Always allowed (no gate):**\n- Non-code files: `.md`, `.json`, `.yaml`, `.env`, `.sql`, `.css`, `.html`\n- Config files: `tsconfig.json`, `next.config.ts`, `package.json`\n- Test files: `*.test.ts`, `*.spec.tsx`\n- Non-code paths: `.task\u002F`, `.claude\u002F`, `node_modules\u002F`, `__tests__\u002F`\n\n**Dedup:** Reading the same file at different line ranges counts as 1 Read.\n\n### 5. `lsp-pre-delegation.js` — Agent Pre-Resolution\n\n**Hook type:** PreToolUse | **Matcher:** `Agent`\n\nClaude Code subagents **cannot access MCP tools** — this is an architectural limitation of the platform. Without this hook, every delegated agent falls back to Grep+Read, bypassing all LSP enforcement.\n\n```\n\u002F\u002F BLOCKED — no LSP context\nAgent({\n  prompt: \"Fix handleSubmit in the form component\",\n  isolation: \"worktree\"\n})\n\n\u002F\u002F ALLOWED — pre-resolved LSP context\nAgent({\n  prompt: `Fix handleSubmit error handling.\n\n    ## LSP CONTEXT (pre-resolved by orchestrator)\n    - handleSubmit: defined at form-actions.ts:42, called from page.tsx:15\n    - FormComponent: defined at form.tsx:8, used in page.tsx:120`,\n  isolation: \"worktree\"\n})\n```\n\n**Three enforcement tiers:**\n\n| Tier | Agents | Enforcement |\n|------|--------|-------------|\n| Force | `frontend-explorer`, `backend-explorer`, `db-explorer` | Always BLOCK without LSP context |\n| Standard | Implementation agents, worktree-isolated agents | BLOCK during implement phase |\n| Exempt | Reviewers, testers, planners, auditors | Never enforced (read-only) |\n\n### 6. `lsp-session-reset.js` — Stale State Wiper\n\n**Hook type:** SessionStart | **Matcher:** `true` (runs on every session start)\n\nThe Read guard's state file (`~\u002F.claude\u002Fstate\u002Flsp-ready-\u003Ccwd-hash>`) has a 24-hour expiry. Without this hook, a new session inherits yesterday's `nav_count` — and if that count was ≥ 2, the guard is permanently in **surgical mode** for today's session: unlimited Reads with zero LSP calls required. A full bypass of the enforcement chain.\n\nThis hook runs once on session start and deletes the state file for the current cwd. The next Read triggers Gate 1 (warmup required), forcing at least one `get_diagnostics` call before any code file can be opened. After warmup, the standard progression kicks in (Gate 2 → 3 → 4 → 5) requiring real LSP navigation calls before surgical mode unlocks.\n\n**Session lifecycle with reset:**\n```\nSession start\n  │\n  ├─ lsp-session-reset.js → unlinks lsp-ready-\u003Chash>\n  │\n  ├─ Read(page.tsx) → Gate 1 BLOCKS → \"warmup required\"\n  │\n  ├─ get_diagnostics(file.ts) → tracker writes warmup_done=true\n  │\n  ├─ Read × 2 (free) → Gate 3 warn → Gate 4 block → LSP nav → …\n  │\n  └─ (2 nav calls later) SURGICAL MODE unlocked\n```\n\n**Safety:** the hook only deletes the flag for the current cwd — other projects' state files are left alone. Failure is silent (never blocks session start).\n\n### 7. `lsp-usage-tracker.js` — State Tracker\n\n**Hook type:** PostToolUse | **Matcher:** all `mcp__cclsp__*` tools\n\nTracks successful LSP calls in a per-project state file. Other hooks read this state to make gate decisions.\n\n**State file:** `~\u002F.claude\u002Fstate\u002Flsp-ready-\u003Cmd5-hash-of-cwd>`\n\n```json\n{\n  \"cwd\": \"\u002Fpath\u002Fto\u002Fproject\",\n  \"warmup_done\": true,\n  \"nav_count\": 25,\n  \"read_count\": 38,\n  \"read_files\": [\"src\u002Fpage.tsx\", \"src\u002Factions.ts\"],\n  \"timestamp\": 1775818285727,\n  \"last_tool\": \"mcp__cclsp__find_references\"\n}\n```\n\n**Cold start handling:** Detects the cclsp \"No Project\" error (upstream bug where `find_workspace_symbols` doesn't prime the TypeScript project). Emits a `systemMessage` with the correct fix — call a file-based tool first. It's an ordering bug, not a timing issue.\n\n## 📦 Installation\n\n### Option 1: Give the repo to Claude Code (recommended)\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit.git\ncd claude-code-lsp-enforcement-kit\n```\n\nThen tell Claude Code:\n\n```\nRun bash install.sh in this repo to set up LSP enforcement hooks.\n```\n\nThe install script:\n- Copies 7 hooks + shared `lib\u002Fdetect-lsp-provider.js` helper to `~\u002F.claude\u002Fhooks\u002F`\n- Copies the LSP-first rule to `~\u002F.claude\u002Frules\u002F`\n- **Merges** hook registrations into your existing `~\u002F.claude\u002Fsettings.json` (won't overwrite your other hooks)\n- Enables the built-in `typescript-lsp` plugin\n- Creates `~\u002F.claude\u002Fstate\u002F` for tracking\n- Verifies everything at the end\n- Safe to re-run: entries are deduped by command path, so upgrading from v1\u002Fv2.0 to v2.1 just adds what's missing without touching anything else\n\n### Option 2: Run the script yourself\n\n**macOS \u002F Linux:**\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit.git\ncd claude-code-lsp-enforcement-kit\nbash install.sh\n```\n\n**Windows (PowerShell):**\n```powershell\ngit clone https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit.git\ncd claude-code-lsp-enforcement-kit\npwsh .\u002Finstall.ps1\n# or: powershell -ExecutionPolicy Bypass -File .\u002Finstall.ps1\n```\n\nOutput:\n```\n=== LSP Enforcement Kit — Install ===\n\n[1\u002F4] Directories ready\n[2\u002F4] Copied 7 hooks + lib + 1 rule\n[3\u002F4] settings.json updated (merged, not overwritten)\n[4\u002F4] Verifying...\n\n  Hooks installed:  7\u002F7\n  Rule installed:   yes\n  Plugin enabled:   yes\n  State directory:  yes\n\nDone. Restart Claude Code to activate.\n```\n\n### Option 3: Manual setup\n\n\u003Cdetails>\n\u003Csummary>Click to expand manual steps\u003C\u002Fsummary>\n\n#### Prerequisites\n\n- Claude Code (CLI, Desktop, or IDE extension)\n- TypeScript\u002FJavaScript project\n\n#### Step 1: Copy files\n\n```bash\nmkdir -p ~\u002F.claude\u002Fhooks ~\u002F.claude\u002Fstate ~\u002F.claude\u002Frules\ncp hooks\u002F*.js ~\u002F.claude\u002Fhooks\u002F\ncp rules\u002Flsp-first.md ~\u002F.claude\u002Frules\u002F\n```\n\n#### Step 2: Enable the plugin\n\nIn `~\u002F.claude\u002Fsettings.json`, add to `enabledPlugins`:\n\n```json\n{\n  \"enabledPlugins\": {\n    \"typescript-lsp@claude-plugins-official\": true\n  }\n}\n```\n\n#### Step 3: Register hooks in settings.json\n\n**IMPORTANT:** If you already have hooks, **add** these entries to your existing arrays — don't replace them.\n\nAdd to `PreToolUse` array:\n\n```json\n{\n  \"matcher\": \"Grep\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Flsp-first-guard.js\" }]\n},\n{\n  \"matcher\": \"Glob\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Flsp-first-glob-guard.js\" }]\n},\n{\n  \"matcher\": \"Bash\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Fbash-grep-block.js\" }]\n},\n{\n  \"matcher\": \"Read\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Flsp-first-read-guard.js\" }]\n},\n{\n  \"matcher\": \"Agent\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Flsp-pre-delegation.js\" }]\n}\n```\n\nAdd to `PostToolUse` array:\n\n```json\n{\n  \"matcher\": \"mcp__cclsp__find_definition|mcp__cclsp__find_references|mcp__cclsp__find_workspace_symbols|mcp__cclsp__find_implementation|mcp__cclsp__get_hover|mcp__cclsp__get_diagnostics|mcp__cclsp__get_incoming_calls|mcp__cclsp__get_outgoing_calls\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Flsp-usage-tracker.js\" }]\n}\n```\n\nAdd to `SessionStart` array (create it if missing):\n\n```json\n{\n  \"matcher\": \"true\",\n  \"hooks\": [{ \"type\": \"command\", \"command\": \"node ~\u002F.claude\u002Fhooks\u002Flsp-session-reset.js\" }]\n}\n```\n\n\u003C\u002Fdetails>\n\n### Verify\n\nRun the health-check script:\n\n```bash\nbash scripts\u002Flsp-status.sh\n# or from anywhere after install:\nbash ~\u002F.claude\u002Fscripts\u002Flsp-status.sh\n```\n\nExpected output:\n\n```\nLSP Enforcement Kit — Status\n============================\n\n  Hook files:          ✓ 7\u002F7\n  Shared lib\u002Fhelper:   ✓ yes\n  Settings registered: ✓ PreToolUse(5) PostToolUse(1) SessionStart(1)\n  Detected providers:  ✓ cclsp\n\nState for current cwd (\u002Fpath\u002Fto\u002Fproject)\n------------------------\n  Warmup done:         yes\n  nav_count:           5 (LSP navigation calls)\n  read_count:          7 (unique code files read)\n  Last tool:           mcp__cclsp__find_references (2min ago)\n\n  ✓ Surgical mode active — all Reads unlimited for this session.\n\nDiagnostic summary\n------------------\n  All checks passed. Enforcement is active.\n```\n\nOr restart Claude Code and ask \"Where is handleSubmit defined?\" — Claude should use `find_definition`, not Grep.\n\n## 📚 LSP Tool Reference\n\n| Tool | Question It Answers | Output |\n|------|-------------------|--------|\n| `find_definition` | Where is X defined? | file:line of definition |\n| `find_references` | Where is X used? | All file:line usages |\n| `find_workspace_symbols` | Find anything named X | All matching symbols in project |\n| `find_implementation` | What implements this interface? | Concrete implementations |\n| `get_incoming_calls` | What calls X? | All callers with file:line |\n| `get_outgoing_calls` | What does X call? | All callees with file:line |\n| `get_hover` | What type is X? | Type signature + docs |\n| `get_diagnostics` | Any errors in this file? | TypeScript errors\u002Fwarnings |\n\n## 🐍 Optional: Python, Go, Rust Support\n\nThe built-in plugin only covers TypeScript\u002FJavaScript. For other languages, install `cclsp` — a standalone MCP server that connects Claude Code to any Language Server:\n\n```bash\nnpm install -g cclsp\n```\n\nThen install the language server for your language:\n\n```bash\n# Python\npip install python-lsp-server\n\n# Go\ngo install golang.org\u002Fx\u002Ftools\u002Fgopls@latest\n\n# Rust\nrustup component add rust-analyzer\n```\n\nCreate `~\u002F.config\u002Fclaude\u002Fcclsp.json`:\n\n```json\n{\n  \"servers\": [\n    {\n      \"extensions\": [\"py\", \"pyi\"],\n      \"command\": [\"pylsp\"]\n    },\n    {\n      \"extensions\": [\"go\"],\n      \"command\": [\"gopls\", \"serve\"]\n    },\n    {\n      \"extensions\": [\"rs\"],\n      \"command\": [\"rust-analyzer\"]\n    }\n  ]\n}\n```\n\nAdd to your Claude Code MCP config (`~\u002F.claude.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"cclsp\": {\n      \"type\": \"stdio\",\n      \"command\": \"cclsp\",\n      \"args\": []\n    }\n  }\n}\n```\n\nThe hooks work identically — they detect code symbols by naming convention, not by language. Once `cclsp` is connected, `find_definition`, `find_references`, etc. work across all configured languages.\n\n## ❓ FAQ\n\n**Q: Does this work with Python\u002FGo\u002FRust?**\nOut of the box — TypeScript\u002FJavaScript only (built-in plugin). For other languages, install `cclsp` + the language server (see section above). The hooks themselves are language-agnostic.\n\n**Q: What if LSP gives wrong results?**\nThe hooks don't eliminate Grep — they block Grep for *code symbols*. If LSP returns empty, Claude can still Grep with non-symbol patterns or search non-code files. The Read guard also gives 2 free reads before requiring navigation.\n\n**Q: Won't the Read gate slow down simple tasks?**\nAfter 2 LSP navigation calls, all gates open permanently (surgical mode). This happens within the first 30 seconds of a session. Non-code files (config, tests, docs) are never gated.\n\n**Q: Why block Agent delegation without LSP context?**\nClaude Code subagents cannot access MCP tools (architectural limitation). Without pre-resolved context, every delegated agent falls back to exploratory Grep+Read, burning thousands of tokens and bypassing all enforcement.\n\n**Q: Known issues?**\n`find_workspace_symbols` fails with \"No Project\" if called before any file-based LSP tool (cclsp upstream bug). The tracker detects this and tells Claude to call `get_diagnostics` first. Not a timing issue — ordering issue.\n\n**Q: I installed v1 and shared it with my team — should I upgrade?**\nYes. v1 had two silent bypass routes (Glob symbol search and stale session state) that let Claude navigate code without ever calling LSP. Both are closed in v2. Just re-run `bash install.sh` — it's idempotent and only adds the missing hook entries to your `settings.json`. No existing configuration is touched.\n\n**Q: Does this work with Serena?**\nYes. Since **v2.1**, the kit detects your LSP MCP provider and tailors its block-message suggestions. If you run [Serena](https:\u002F\u002Fgithub.com\u002Foraios\u002Fserena) (the multi-language MCP symbol toolkit by Oraios AI — MIT), the hooks will point you at `mcp__serena__find_symbol`, `find_referencing_symbols`, and `get_symbols_overview` instead of cclsp tools. The enforcement logic (Grep\u002FGlob\u002FRead\u002FAgent gates, session reset) is provider-agnostic — it works the same for both. You can also run cclsp and Serena side-by-side; suggestions then show both.\n\nThis is pure interop — the kit ships no Serena code, uses only their public tool names in suggestion strings, and reads only your own config to detect which provider is active.\n\n**Q: What about Python\u002FGo\u002FRust? cclsp is TypeScript-only.**\nTwo options:\n1. Install a standalone `cclsp` MCP server with multi-language config (see the \"Optional\" section above), OR\n2. Install [Serena](https:\u002F\u002Fgithub.com\u002Foraios\u002Fserena) — it bundles `solidlsp`, a unified wrapper around language servers for Python, Go, Rust, Java, TypeScript, Vue, PHP, Ruby, Swift, Elixir, Clojure, Bash, PowerShell, and more. The kit will detect Serena automatically and adapt its suggestions.\n\nThe hook detection logic itself is language-agnostic — it works on naming conventions (PascalCase, camelCase, snake_case), not language-specific ASTs.\n\n## 📄 License\n\nMIT — see [LICENSE](LICENSE)\n\n---\n\n\u003Cp align=\"center\">\n  Made for Claude Code power users who care about token efficiency.\n  \u003Cbr>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit\u002Fissues\">Report an issue\u003C\u002Fa> &bull;\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnesaminua\u002Fclaude-code-lsp-enforcement-kit\u002Freleases\">Releases\u003C\u002Fa> &bull;\n  \u003Ca href=\"CHANGELOG.md\">Changelog\u003C\u002Fa>\n\u003C\u002Fp>\n","该项目通过强制Claude Code使用LSP而非Grep进行代码导航，显著减少了Token消耗。其核心功能是通过一系列钩子拦截Claude Code的Grep操作，并提供LSP命令作为替代方案，从而节省约80%的Token。技术上，项目采用JavaScript编写，利用了6个钩子和1个追踪器来实现对LSP的强制使用。适用于需要高效代码导航且希望降低Token消耗的开发场景，尤其适合频繁使用Claude Code的开发者。","2026-06-11 02:42:38","CREATED_QUERY"]