Code Search skill for Claude Code
Find every usage, and prove you found them all. Claude picks the right search tool for the question, from ripgrep with types, globs and word boundaries to ast-grep for call shapes, git grep for history and LSP for type-aware references. It searches every spelling, includes config, templates, SQL and infra, follows re-exports and dynamic access, cross-checks counts, and reports results with file and line evidence plus an explanation of false positives.
- name
code-search - size150 lines · ≈1,650 tokens
- files1
- licenseMIT
- updated
What Claude sees before loading it
This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.
description: Finds anything in a large codebase fast and completely by choosing the right tool per question, ripgrep with type filters, globs, fixed strings, word boundaries and multiline, ast-grep structural patterns, git grep across history and branches, LSP or ctags symbol navigation, and file-name search, then narrowing iteratively and proving completeness before acting on the results. Use when the user asks where something is defined or used, to find all call sites before a change, to locate the code behind an error message, URL, or config key, or when a grep returns too much or too little. Try asking
- Find every call site of fetchUser before I change its signature
- Where is the code that produces this error: 'could not acquire lock'?
- Find all useEffect hooks with an empty dependency array in the web app
Use it when
- You need all usages of a symbol, key, or route before changing it
- An error message, log line, or URL must be traced to code
- A text search returns too many false positives
- You are working in a large or unfamiliar codebase
Skip it when
- You want an overall map of an unfamiliar repo — use codebase-onboarding instead
- You already found the call sites and need to rewrite them all — use codemods instead
How to check the result
The answer lists file:line evidence, and the completeness checklist is ticked, including spellings, non-code files, ignored directories and an LSP or second-tool count comparison.
The common mistake it prevents
Searching only src/ for one spelling, then renaming and breaking config files, SQL, templates and scripts that referenced the old name in another case style.
A search is done when you can state the answer with file:line evidence and you know it is complete: every spelling, layer, and file type where the thing can appear was covered, and the false positives were explained. For “find all usages before changing X”, completeness matters more than speed.
Pick the tool
| Question | Tool | Example |
|---|---|---|
| Where is this text/identifier? | ripgrep | rg -n -w 'fetchUser' src |
| Where is this exact string with regex chars? | ripgrep fixed string | rg -nF 'user?.id ?? 0' |
| Calls with a certain shape (args, nesting) | ast-grep | ast-grep run -l ts -p 'fetchUser($A, { cache: true })' |
| Where is it defined / all references, type-aware | LSP (IDE, code-intelligence plugin) | Go to definition / find references |
| When did it appear, on which branch? | git grep / git log -S | git grep -n 'flagName' $(git rev-list --all -n 200) |
| Which file is called something like X? | file search | rg --files | rg -i 'invoice.*service' |
| Code behind an error message or log line | ripgrep on a stable fragment | rg -nF 'could not acquire lock' |
| Code behind a URL/route | ripgrep on path segments | rg -n "['\"]/api/v1/invoices" |
Workflow
- [ ] 1. Write the question and the target names in every plausible spelling
- [ ] 2. Start broad with counts, then narrow by path/type/word
- [ ] 3. Switch to structural search when text matches are noisy
- [ ] 4. Cover non-code: config, templates, SQL, infra, docs, tests
- [ ] 5. Prove completeness (checklist below); explain false positives
- [ ] 6. Report results with file:line, grouped by meaning
1. Spellings
Identifiers change shape across layers. For a concept invoice status:
invoiceStatus, InvoiceStatus, invoice_status, INVOICE_STATUS, invoice-status,
"invoiceStatus" in JSON, invoice.status in templates. One regex covers most:
rg -n -i 'invoice[_-]?status' .
2. Broad, then narrow
rg -c 'fetchUser' | sort -t: -k2 -nr | head # where are the hits concentrated?
rg -n -w 'fetchUser' -t ts -g '!**/*.test.ts' src # whole word, TS only, no tests
rg -n 'fetchUser\(' -g 'src/server/**' # call sites in one area
rg -l 'fetchUser' | xargs rg -l 'cache' # files containing both terms
ripgrep essentials:
| Flag | Effect |
|---|---|
-n / -l / -c | Line numbers / file names only / counts per file |
-w | Whole-word match |
-F | Fixed string, no regex |
-i / -S | Case-insensitive / smart case |
-t ts / -T ts | Include / exclude a file type (rg --type-list) |
-g 'glob' / -g '!glob' | Include / exclude paths |
-A 3 -B 3 / -C 3 | Context lines |
-U --multiline-dotall | Patterns spanning lines |
--hidden / -uu | Search dotfiles / also ignore .gitignore rules |
--pcre2 | Lookarounds and backreferences |
-o | Print only the matched part (pipe to sort | uniq -c) |
ripgrep respects .gitignore by default. Generated or vendored code that is ignored will
not appear: use -uu when the answer might be there (built bundles, vendor/).
3. Structural search with ast-grep
Use when text search matches comments, strings, or similar names, or when the shape of the call matters:
ast-grep run -l ts -p 'useEffect($FN, [])' # effects with empty deps
ast-grep run -l ts -p 'await $X.findMany({ $$$ })' src # Prisma calls, any args
ast-grep run -l py -p 'requests.$METHOD($URL, verify=False)' # insecure requests
ast-grep run -l go -p 'if err != nil { return nil }' # swallowed errors
$X matches one node; $$$ matches zero or more. Add --json for machine-readable
output. For repeated checks, write a rule file and run ast-grep scan.
4. Beyond source code
Usages hide in: YAML/JSON config, .env.example, SQL migrations and raw queries,
templates (Jinja, ERB, Handlebars), GraphQL schemas, OpenAPI specs, infra (Terraform,
Helm, Kubernetes), CI workflows, docs, fixtures, feature-flag definitions, and other
repositories. Search without type filters at least once:
rg -n --hidden -g '!.git' -g '!node_modules' -g '!dist' 'invoice[_-]?status'
Dynamic references defeat every text search: string concatenation ("get" + name),
reflection, getattr, DI by name, routes built from config. Search for the fragments and
for the registry that builds them.
5. Completeness checklist
- All spellings and casings searched
- Tests, config, templates, SQL, infra, and docs included
- Ignored and generated directories considered (
-uu) - Re-exports and aliases followed (
export { fetchUser as getUser },import * as api) - Dynamic access patterns searched
- Other repos or packages that depend on this one considered
- For type-aware languages, LSP “find references” count compared with grep count
If grep and LSP disagree, investigate the difference before acting; it is usually an alias, a dynamic call, or a stale index.
Large repos and history
git grep -n 'oldFlag' main -- '*.ts' # a branch without checking it out
git log -S'oldFlag' --oneline --all # commits adding/removing it
git ls-files | rg -i 'billing' | head # tracked files only, very fast
For very large monorepos, scope by package first (rg ... packages/billing), and use
--max-columns 200 to avoid minified lines flooding output.
Gotchas
- Unanchored patterns overmatch (
idmatcheswidth); use-wor\b. - Regex special characters in literal searches (
.,?,() silently change meaning; use-F. - Shell quoting: single-quote patterns;
$in double quotes is expanded by the shell. - Searching only
src/misses scripts, tests, and config that will break on rename. - Reading hundreds of matches into context wastes tokens: count first (
-c), then open the files that matter, or delegate a wide sweep to a subagent and keep its summary.
Output
**Question:** all call sites of fetchUser() before changing its signature
**Result:** 14 call sites in 9 files (LSP references: 14, rg -w: 17; 3 extra were a comment, a test name, a string in docs)
- src/server/routes/users.ts:42, :88 — direct calls
- src/web/hooks/useUser.ts:19 — via re-export `getUser` (src/api/index.ts:7)
- scripts/backfill.ts:33 — script, not covered by tests
**Also found:** dynamic call via handlers["fetch" + entity] in src/server/registry.ts:51 (covers User)
**Completeness:** spellings, tests, config, scripts, -uu search done; no other repos depend on this package About this skill
Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.