by nesaminua
Hooks that force Claude Code to use LSP instead of Grep for code navigation. Saves ~80% tokens
# Add to your Claude Code skills
git clone https://github.com/nesaminua/claude-code-lsp-enforcement-kitGuides for using ai agents skills like claude-code-lsp-enforcement-kit.
Last scanned: 5/30/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-05-30T15:03:49.670Z",
"npmAuditRan": true,
"pipAuditRan": true
}claude-code-lsp-enforcement-kit is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by nesaminua. Hooks that force Claude Code to use LSP instead of Grep for code navigation. Saves ~80% tokens. It has 322 GitHub stars.
Yes. claude-code-lsp-enforcement-kit passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/nesaminua/claude-code-lsp-enforcement-kit" and add it to your Claude Code skills directory (see the Installation section above).
claude-code-lsp-enforcement-kit is primarily written in JavaScript. It is open-source under nesaminua on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh claude-code-lsp-enforcement-kit against similar tools.
No comments yet. Be the first to share your thoughts!
When Claude tries to Grep for a code symbol, the hook blocks with a copy-pasteable LSP command:
⛔ LSP-FIRST BLOCK: Pattern contains code symbol(s) — use LSP instead
Symbols: handleSubmit, UserService
LSP tools:
handleSubmit:
mcp__cclsp__find_references("handleSubmit") (cclsp)
UserService:
mcp__cclsp__find_workspace_symbols("UserService") (cclsp)
When Claude tries to Read a code file without warming up LSP, the progressive gate blocks:
🛡️ LSP-FIRST READ GATE — Gate 1: warmup required
Call one of these first:
mcp__cclsp__get_diagnostics("src/page.tsx") (cclsp)
CONCRETE CALL FOR THIS FILE (works in any project):
mcp__cclsp__get_diagnostics("src/page.tsx")
After warmup: 2 free Reads, then need LSP navigation.
No generic advice. Every block message is parametrized by the actual file Claude tried to touch.
git clone https://github.com/nesaminua/claude-code-lsp-enforcement-kit.git
cd claude-code-lsp-enforcement-kit
bash install.sh
# Windows: pwsh ./install.ps1
Restart Claude Code. Done. The installer is idempotent — safe to re-run on upgrades.
Verify:
bash scripts/lsp-status.sh
Claude Code defaults to Grep + Read for code navigation. This works, but it's wasteful:
"Where is handleSubmit defined?"
Grep approach:
Grep("handleSubmit") → 23 matches, ~1500 tokens of output
Read file1.tsx (wrong) → 2500 tokens
Read file2.tsx (still wrong) → 2500 tokens
Read file3.tsx (found it) → 2500 tokens
─────────────────────────────────────
Total: ~9,000 tokens, 4 tool calls
LSP approach:
find_definition("handleSubmit") → form-actions.ts:42, ~80 tokens
Read form-actions.ts:35-55 → ~150 tokens
─────────────────────────────────────
Total: ~230 tokens, 2 tool calls
~40x fewer tokens. Same answer.
A rule in CLAUDE.md saying "use LSP" helps ~60% of the time. Hooks make it 100%.
| Task | Grep approach | LSP approach | Saved |
|---|---|---|---|
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% |
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% |
Check type of formData |
Read full file (~2500 tok), search visually = ~2500 tok | get_hover → type signature (~60 tok) = ~60 tok |
98% |
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% |
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% |
Aggregate from a week of development across 2 TypeScript projects:
| Metric | With LSP | Without LSP (estimated) |
|---|---|---|
| LSP navigation calls | 39 | — |
| Grep calls on code symbols | 0 (blocked) | ~120 |
| Unique code files Read | 53 | ~180 |
| Estimated navigation tokens | ~85k | ~320k |
| Tokens saved | ~235k (~73%) |
How the estimate works:
v2.1 introduces provider-aware block messages. The kit detects which LSP MCP server(s) you have installed and tailors its suggestions accordingly:
typescript-lsp Claude Code plugin. Suggestions use mcp__cclsp__find_definition, find_references, find_workspace_symbols, etc.solidlsp wrapper). Suggestions use mcp__serena__find_symbol, find_referencing_symbols, get_symbols_overview.Detection reads user-level Claude Code config (~/.claude.json, ~/.claude/settings.json) and matches known server names. The shared helper is in hooks/lib/detect-lsp-provider.js — adding a new provider means adding one entry to its PROVIDERS registry, with no changes to the individual hooks.
PreToolUse PostToolUse
────────── ───────────
Grep call ──→ [lsp-first-guard.js] ──→ BLOCK
detects code symbols,
suggests LSP equivalent
Glob call ──→ [lsp-first-glob-guard.js] ──→ BLOCK
blocks *UserService*, **/handleFoo*.ts;
allows *.ts, *subdomain*, src/**
Bash(grep) ──→ [bash-grep-block.js] ──→ BLOCK
catches grep/rg/ag/ack
in shell commands
Read(.tsx) ──→ [lsp-first-read-guard.js] ──→ GATE
5 progressive gates
(warmup → orient → nav → surgical)
Agent(impl) ─→ [lsp-pre-delegation.js] ──→ BLOCK
subagents can't access MCP,
orchestrator must pre-resolve
LSP call ─────────────────────────────────────→ [lsp-usage-tracker.js]
tracks nav_count,
read_count, state
SessionStart
────────────
New session ──→ [lsp-session-reset.js] ──→ WIPE
clears stale nav_count for current cwd,
forces fresh warmup + re-enforces gates
v2 note: versions before v2 had two silent bypass routes that let Claude read code files without ever calling LSP: (1)
Glob("*SymbolName*")had no guard, and (2)nav_countpersisted for 24 h across sessions, so a new session inherited "surgical mode" (unlimited reads) from yesterday's LSP work. Both are closed in v2 bylsp-first-glob-guard.jsandlsp-session-reset.js. If you installed v1, re-runbash install.sh— it merges the new hooks without touching your existing settings.
lsp-first-guard.js — Grep BlockerHook type: PreToolUse | Matcher: Grep
Intercepts every Grep call. Detects code symbols in the pattern. Blocks with a suggestion to use the correct LSP tool.
| Pattern | Detected as | Action |
|---|---|---|
getUserById |
camelCase symbol | BLOCK |
UserService |
PascalCase symbol | BLOCK |
router.refresh |
dotted symbol | BLOCK |
write_audit_log |
snake_case function | BLOCK |
create-folder-modal |
component filename | BLOCK |
TODO |
keyword | allow |
NEXT_PUBLIC_URL |
env var (SCREAMING_SNAKE) | allow |
flex-col |
CSS class | allow |
*.md, *.json, *.sql |
non-code file glob | allow |
.task/, node_modules/ |
non-code path | allow |
Block message example (with both cclsp and Serena detected):
⛔ LSP-FIRST BLOCK: 1 code symbol(s) in Grep — use LSP instead
Symbols: handleSubmit
LSP tools:
handleSubmit:
mcp__cclsp__find_references("handleSubmit") (cclsp)
mcp__serena__find_referencing_symbols("handleSubmit") (Serena)
If only one provider is installed, only that suggestion appears.
lsp-first-glob-guard.js — Glob Symbol BlockerHook type: PreToolUse | Matcher: Glob
Closes 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.
The 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+ pa