
File Search
- 337 installs
- 31 repo stars
- Updated August 2, 2026
- netresearch/file-search-skill
file-search is a Claude Code skill that equips coding agents with fast, reliable project file discovery when developers navigate unfamiliar repos, locate symbols, or gather context before edits.
About
file-search is an agent skill from netresearch/file-search-skill that improves how coding agents find files, symbols, and project context in large or unfamiliar codebases. Instead of relying on slow or incomplete directory walks, the skill gives agents a dependable search workflow for locating definitions, configs, and related modules before proposing changes. Developers reach for file-search when onboarding to a new repository, debugging cross-package references, or preparing an agent session that needs accurate file paths and symbol maps. It reduces missed references and wrong-file edits during agent-assisted refactors, reviews, and feature work across monorepos and polyglot projects.
- Cross-repo file discovery
- Symbol and path lookup
- Agent context grounding
- Reduces blind edits
- Works with large codebases
File Search by the numbers
- 337 all-time installs (skills.sh)
- +15 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #2,178 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/netresearch/file-search-skill --skill file-searchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 337 |
|---|---|
| repo stars | ★ 31 |
| Last updated | August 2, 2026 |
| Repository | netresearch/file-search-skill ↗ |
How do coding agents find files in large repos?
Equip coding agents with fast, reliable project file discovery when navigating unfamiliar repos, locating symbols, or gathering context before edits.
Who is it for?
Developers using coding agents on large or unfamiliar repositories who need reliable file and symbol discovery.
Skip if: Developers who already know exact file paths or only need to edit a single known source file.
When should I use this skill?
User or agent needs to navigate an unfamiliar repo, find a symbol definition, or gather file context before editing.
What you get
Ranked file paths, symbol locations, and gathered repository context for targeted edits
- File path matches
- Symbol location map
- Repository context summary
Files
File Search Skill
Efficient CLI search tools for AI agents.
Tool Selection Guide
| Task | Use | Instead of |
|---|---|---|
| Search text in code files | rg (ripgrep) | grep, grep -r |
| Find files by name/path | fd | find, ls -R |
| Structural/syntax-aware code search | sg (ast-grep) | regex hacks |
| Apply rule packs (security/lint, taint) | semgrep | regex CI checks |
| Search PDFs, Office docs, archives | rga (ripgrep-all) | manual extraction |
| Count lines of code by language | tokei | cloc, wc -l |
| Code stats with complexity metrics | scc | cloc, tokei |
Decision flow: text → rg | structural → sg | rule packs → semgrep | filenames → fd | PDFs/archives → rga | LOC → tokei/scc
Quick Examples
rg 'def \w+\(' -t py src/ # rg: text search in Python files
rg -c 'TODO' -t js | wc -l # rg: count first, then drill down
sg --pattern 'console.log($$$)' --rewrite 'logger.info($$$)' --lang js # sg: structural replace
fd -g '*.test.ts' --changed-within 1d # fd: -g for compound suffixes (NOT -e)
fd -g '*_test.go' -X rg 'func Test' # fd+rg: find files, verify contents
rga 'quarterly revenue' docs/ # rga: search inside PDFs/archives
tokei --sort code # tokei: language stats
scc --wide # scc: complexity + COCOMOBest Practices
1. Start narrow. Specify types (-t, --lang, -e), scope dirs, count first (rg -c). 2. Exclude noise (-g '!vendor/', fd -E node_modules). 3. Batch independent queries. Union patterns with rg -e P1 -e P2 -e P3 (one walk, one process), or issue distinct queries as parallel tool calls in a single message — never sequential && chains for independent searches. 4. `--json` for programmatic processing. 5. rg ≠ fd types. rg -t ts includes .tsx; fd -e ts does NOT. No -t tsx in rg.
See references/search-strategies.md.
Beyond Local Files
If local search finds nothing and context lives in issues/PRs/external docs — hand off (gh, Jira, WebFetch). Issue keys in comments signal this.
See references/remote-handoff.md.
References
| Topic | File |
|---|---|
| rg flags, patterns, recipes | references/ripgrep-patterns.md |
| ast-grep patterns by language | references/ast-grep-patterns.md |
| semgrep rules and taint mode | references/semgrep-patterns.md |
| fd flags, usage, fd+rg combos | references/fd-guide.md |
| rga formats, usage, caching | references/rga-guide.md |
| tokei and scc usage | references/code-metrics.md |
| Search targeting strategies | references/search-strategies.md |
| Tool comparison and decision guide | references/tool-comparison.md |
| Remote context handoff guide | references/remote-handoff.md |
{
"skill_name": "file-search",
"evals": [
{
"id": 1,
"eval_name": "find-todo-comments",
"prompt": "Find all TODO comments in this codebase. Give me a count per file and show the top files with the most TODOs.",
"expected_output": "Uses rg with -c flag to count TODO occurrences per file, sorted by count.",
"files": [],
"assertions": [
"Uses rg (ripgrep), not grep — rg respects .gitignore automatically",
"Uses -c flag for per-file counts rather than full-line output (reduces token consumption)",
"Sorts or presents results by frequency",
"Avoids multi-stage grep|sed|sort|uniq pipelines — rg -c handles counting natively"
]
},
{
"id": 2,
"eval_name": "search-function-definition",
"prompt": "Search for the definition of a function called 'processEvent' in this Go codebase. I need to find where it's defined, not where it's called.",
"expected_output": "Uses rg with Go function definition pattern or sg with structural AST search to find func processEvent.",
"files": [],
"assertions": [
"Uses rg with -t go or sg --lang go (not find or grep)",
"Pattern targets function definitions (func keyword), not calls",
"Limits search to Go files only",
"Command is syntactically valid"
]
},
{
"id": 3,
"eval_name": "find-files-by-extension",
"prompt": "Find all TypeScript test files (*.test.ts and *.spec.ts) that were modified in the last week.",
"expected_output": "Uses fd with glob patterns and time filters to locate recent test files.",
"files": [],
"assertions": [
"Uses fd (not find or ls -R)",
"Filters by .test.ts and .spec.ts using -g glob patterns (NOT -e, which only matches the final extension)",
"Uses --changed-within flag for time-based filtering",
"Command is syntactically valid"
]
},
{
"id": 4,
"eval_name": "search-pdf-documents",
"prompt": "Search all PDF files in the docs/ directory for mentions of 'quarterly revenue'.",
"expected_output": "Uses rga in a single command to search inside PDFs — replaces multi-line pdftotext+grep loops.",
"files": [],
"assertions": [
"Uses a single rga command (not for-loop+pdftotext+grep pipelines)",
"Targets the docs/ directory specifically",
"Does not attempt manual PDF text extraction (no pdftotext, no python scripts, no bash loops)",
"Command is concise — no shell loops or multi-step extraction"
]
},
{
"id": 5,
"eval_name": "structural-search-empty-catch",
"prompt": "Find all empty catch blocks in this JavaScript/TypeScript codebase. I want to find places where exceptions are silently swallowed.",
"expected_output": "Uses sg (ast-grep) with a structural pattern to find empty catch blocks, or rg with multiline flag.",
"files": [],
"assertions": [
"Uses sg --lang js/ts or rg -U for multiline matching (not grep -Pzo which is fragile and macOS-incompatible)",
"Pattern targets catch blocks with empty or minimal bodies",
"Specifies JavaScript or TypeScript language filter",
"Command works reliably (sg AST is immune to whitespace/formatting variations; grep -Pzo often fails)"
]
},
{
"id": 6,
"eval_name": "codebase-size-analysis",
"prompt": "Give me an overview of this codebase: how many lines of code per language, and which language dominates?",
"expected_output": "Uses tokei or scc to produce a language breakdown with line counts in a single command.",
"files": [],
"assertions": [
"Uses tokei or scc (not cloc or wc -l) — single command vs multi-step find|wc|sort pipeline",
"Does not manually count lines with shell commands (wc -l gives no language breakdown)",
"Output includes per-language breakdown with code/comments/blanks separation",
"Requires only 1 tool call (not 3+ for find+wc+sort or pip install+radon+manual formula)"
]
},
{
"id": 7,
"eval_name": "security-scan-hardcoded-secrets",
"prompt": "Scan this codebase for hardcoded passwords, API keys, and secrets. Exclude lock files and markdown.",
"expected_output": "Uses rg with case-insensitive patterns for common secret indicators, excluding lock files and docs.",
"files": [],
"assertions": [
"Uses rg (not grep) for the search — rg respects .gitignore by default, avoiding false positives in vendored code",
"Uses -i flag or case-insensitive pattern for password/secret/key/token",
"Excludes lock files (-g '!*.lock' or similar)",
"Excludes markdown or documentation files"
]
},
{
"id": 8,
"eval_name": "progressive-refinement-strategy",
"prompt": "I need to find all uses of the 'requests' library in this large Python project, but there might be thousands of matches. How should I approach this?",
"expected_output": "Demonstrates progressive refinement: count first with rg -c, then narrow scope, then view with context.",
"files": [],
"assertions": [
"Starts with counting matches (rg -c) before viewing full results — avoids flooding context with thousands of lines",
"Uses -t py to limit to Python files",
"Suggests narrowing by directory or pattern if count is high",
"Does not dump all results into context — count-first prevents token waste on thousands of matches"
]
},
{
"id": 9,
"eval_name": "find-and-search-combination",
"prompt": "Find all Go test files and verify each one actually contains test functions.",
"expected_output": "Combines fd to find test files with rg to verify they contain test functions.",
"files": [],
"assertions": [
"Uses fd to find test files (by name pattern like *_test.go or -e go)",
"Uses rg or sg to verify test files contain 'func Test' functions",
"Pipes or combines fd output with rg/sg (-X flag or xargs)",
"Commands are syntactically valid"
]
},
{
"id": 10,
"eval_name": "structural-refactor-preview",
"prompt": "I want to replace all console.log calls with logger.info in this JavaScript project. Can you show me what would change without actually modifying files?",
"expected_output": "Uses sg with --pattern and --rewrite for structural replacement preview, or rg with -r for text preview.",
"files": [],
"assertions": [
"Uses sg --rewrite or rg -r/--passthru — not grep+sed pipe (sg catches structural variants grep misses)",
"Does not modify files (preview only, no --update-all or in-place flags)",
"Specifies --lang js/ts or -t js for JavaScript targeting",
"Single command shows replacements (vs grep|sed pipe = 2 subprocesses + no structural awareness)"
]
},
{
"id": 11,
"eval_name": "complexity-analysis",
"prompt": "I need a complexity analysis of this codebase with COCOMO cost estimates. Which files are the most complex?",
"expected_output": "Uses scc with --wide flag for complexity metrics and COCOMO estimates.",
"files": [],
"assertions": [
"Uses scc (not tokei, which lacks complexity metrics) — 1 command replaces pip install + radon + manual COCOMO formula",
"Uses --wide, --by-file, or --sort complexity to show complexity/COCOMO data",
"Does not use cloc, wc -l, or manual COCOMO calculations",
"Single tool call produces complexity + cost estimate (vs 3+ tool calls without skill)"
]
},
{
"id": 12,
"eval_name": "exclude-noise-directories",
"prompt": "Search for 'useState' across this React project but exclude node_modules, dist, build, and coverage directories.",
"expected_output": "Uses rg with glob exclusion patterns to skip noise directories.",
"files": [],
"assertions": [
"Uses rg (not grep) for the search",
"Excludes node_modules with -g '!node_modules/' or equivalent",
"Excludes at least dist and build directories",
"Uses -t js, -t ts, or glob patterns for frontend file types"
]
},
{
"id": 13,
"eval_name": "search-archive-contents",
"prompt": "Search inside all .tar.gz backup files in /backups/ for configuration entries containing 'database_host'.",
"expected_output": "Uses rga to search inside compressed archives for the pattern.",
"files": [],
"assertions": [
"Uses rga (not rg) — 1 command vs for-loop+tar -xzf+grep pipeline",
"Targets the /backups/ directory",
"Searches for 'database_host' pattern",
"Does not attempt manual tar extraction (no tar -xzf, no for loops)"
]
},
{
"id": 14,
"eval_name": "find-large-files",
"prompt": "Find all JavaScript files larger than 500KB in this project. These might be bundled or minified files we should investigate.",
"expected_output": "Uses fd with size filter and extension filter to find large JS files.",
"files": [],
"assertions": [
"Uses fd (not find or ls) for file discovery",
"Uses -S or --size flag with +500k or equivalent size threshold",
"Filters by -e js for JavaScript files",
"Command is syntactically valid"
]
},
{
"id": 15,
"eval_name": "extract-import-dependencies",
"prompt": "List all unique Python package imports in this project. I want to know which external libraries we depend on.",
"expected_output": "Uses rg with capture groups and replacement to extract import names, piped through sort -u.",
"files": [],
"assertions": [
"Uses rg with -o flag to output only matches",
"Uses capture group with -r to extract package names",
"Uses --no-filename to clean output",
"Pipes through sort -u or equivalent for deduplication"
]
},
{
"id": 16,
"eval_name": "json-output-for-processing",
"prompt": "I need to programmatically process all function definitions in this Python project. Give me machine-readable output I can feed into a script.",
"expected_output": "Uses rg --json or sg --json to produce structured, machine-readable output.",
"files": [],
"assertions": [
"Uses --json flag on rg or sg for machine-readable output",
"Targets Python files with -t py or --lang py",
"Pattern matches function definitions (def keyword)",
"Output format is JSON, not plain text piped through jq"
]
},
{
"id": 17,
"eval_name": "context-gathering-for-refactor",
"prompt": "I need to add rate limiting to our API endpoints. Help me get started.",
"expected_output": "Before implementing, uses search tools to find existing API endpoint definitions, middleware patterns, and any existing rate limiting code.",
"files": [],
"assertions": [
"Uses rg or sg to locate API route/endpoint definitions before writing code",
"Searches for existing middleware or decorator patterns in the codebase",
"Uses file-type filtering (-t or --lang) appropriate to the project language",
"Does not start implementing without first understanding the existing code structure"
]
},
{
"id": 18,
"eval_name": "context-gathering-for-bugfix",
"prompt": "Users are reporting that login sometimes fails silently. Can you investigate?",
"expected_output": "Uses search tools to find authentication/login code, error handling patterns, and logging around auth flows.",
"files": [],
"assertions": [
"Uses rg or sg to find login/auth-related code (not just reading random files)",
"Searches for error handling patterns near authentication code",
"Searches for logging or error suppression patterns (empty catch, silent failures)",
"Uses targeted searches rather than reading entire files sequentially"
]
},
{
"id": 19,
"eval_name": "context-gathering-for-upgrade",
"prompt": "We need to upgrade this project from Express 4 to Express 5. What would be affected?",
"expected_output": "Uses search tools to find Express-specific patterns, middleware usage, and deprecated API calls across the codebase.",
"files": [],
"assertions": [
"Uses rg to find Express import/require statements and usage patterns",
"Searches for middleware patterns (app.use, router.use) to assess scope",
"Uses tokei or scc to understand codebase size before planning the upgrade",
"Searches broadly across the project, not just a single file"
]
},
{
"id": 20,
"eval_name": "codebase-orientation",
"prompt": "I just joined this project. Help me understand how it's structured and what technologies it uses.",
"expected_output": "Uses tokei/scc for language breakdown, fd to find key files (configs, entry points), and rg to identify frameworks and patterns.",
"files": [],
"assertions": [
"Uses tokei or scc to get a language/size overview of the codebase",
"Uses fd to find configuration files, entry points, or project manifests",
"Uses rg or fd to identify frameworks, dependencies, or tech-debt hotspots",
"Provides a structured multi-step orientation rather than dumping raw output"
]
},
{
"id": 21,
"eval_name": "impact-analysis-before-change",
"prompt": "I want to rename the 'UserProfile' class. How many places would be affected?",
"expected_output": "Uses rg to count all references to UserProfile across the codebase, broken down by usage type.",
"files": [],
"assertions": [
"Uses rg (not grep) to find all references to UserProfile",
"Uses -c or -l to assess impact scope (file count or match count) before changing anything",
"Counts or lists affected files across the codebase to understand blast radius",
"Does not start renaming without first understanding the impact"
]
},
{
"id": 22,
"eval_name": "handoff-to-remote-issue-context",
"prompt": "The code has a comment saying '// Workaround for PROJ-1234, remove after fix ships'. What's the status of that issue? Should we remove the workaround?",
"expected_output": "Recognizes that PROJ-1234 is an issue tracker reference. Uses local search to find the workaround code, then hands off to issue tracker (Jira, GitHub Issues) to check status rather than guessing.",
"files": [],
"assertions": [
"Uses rg to find the workaround code and comment in the codebase",
"Recognizes PROJ-1234 as an external issue tracker reference",
"Attempts to check the issue status via an appropriate tool (gh, Jira API, or asks the user)",
"Does not guess the issue status based solely on the code comment"
]
},
{
"id": 23,
"eval_name": "handoff-to-remote-pr-context",
"prompt": "Why was the retry logic in src/client.py changed last month? The current implementation seems wrong.",
"expected_output": "Uses local search and git log to find the change, then checks PR/MR context for the rationale rather than guessing why it was changed.",
"files": [],
"assertions": [
"Uses rg to find the retry logic in src/client.py",
"Uses git log or git blame to identify the commit that changed it",
"Checks commit messages for rationale, and if PR/issue refs are found, follows up with gh/glab",
"Does not fabricate a rationale — seeks evidence from commit history or linked PRs"
]
},
{
"id": 24,
"eval_name": "large-codebase-efficient-search",
"prompt": "This monorepo has 200,000 files across 50 services. Find all places where we call the deprecated 'sendEmail' function, but only in the billing service under services/billing/.",
"expected_output": "Uses rg with directory scoping and file-type filters. Does NOT search the entire repo.",
"files": [],
"assertions": [
"Uses rg (not grep) — rg is parallel and respects .gitignore, critical at 200K files",
"Scopes search to services/billing/ directory, does not search from root",
"Uses -t or -g flags to limit to relevant source file types",
"Does not pipe find/grep combinations that would scan the entire repo"
]
},
{
"id": 25,
"eval_name": "gitignore-awareness",
"prompt": "Search for 'API_KEY' across this Node.js project. Make sure you don't get flooded with results from dependencies.",
"expected_output": "Uses rg which respects .gitignore by default, automatically excluding node_modules. May also add explicit exclusions.",
"files": [],
"assertions": [
"Uses rg (not grep) — rg automatically skips .gitignore'd paths like node_modules",
"Does not need manual --exclude-dir=node_modules (rg handles this by default)",
"Uses -t js or -t ts to further scope to source files",
"Command is concise — no long find|xargs|grep pipelines"
]
},
{
"id": 26,
"eval_name": "php-empty-catch-blocks",
"prompt": "Find all empty catch blocks in this PHP codebase. I want to find places where exceptions are silently swallowed.",
"expected_output": "Uses sg (ast-grep) with --lang php and a structural pattern targeting empty catch blocks.",
"files": [],
"assertions": [
"Uses sg (ast-grep), not grep or rg — structural matching handles whitespace/formatting variations that regex cannot",
"Specifies --lang php for PHP language targeting",
"Pattern targets catch blocks with empty bodies, e.g. 'catch ($EXCEPTION) { }' or similar structural pattern",
"Command is syntactically valid and would produce meaningful results on a PHP codebase"
],
"model_notes": "Smaller models may default to rg -U multiline regex instead of sg. The skill's ast-grep-patterns.md reference must be loaded to know --lang php is supported and to pick the right pattern syntax."
},
{
"id": 27,
"eval_name": "php-class-definitions",
"prompt": "List all class definitions in this PHP project, including those that extend a base class or implement an interface.",
"expected_output": "Uses sg (ast-grep) with --lang php and structural patterns for class declarations with optional inheritance.",
"files": [],
"assertions": [
"Uses sg (ast-grep) with --lang php, not grep/rg — AST search captures all class variants (plain, extends, implements) structurally",
"Specifies --lang php for PHP language targeting",
"Pattern covers class definitions, e.g. 'class $NAME { $$$ }' or broader patterns for extends/implements",
"Does not rely on fragile regex that would miss multi-line class declarations or unusual formatting"
]
},
{
"id": 28,
"eval_name": "python-decorated-functions",
"prompt": "Find all decorated functions in this Python project. I want to see which decorators are being used and on which functions.",
"expected_output": "Uses sg (ast-grep) with --lang py and a structural pattern for decorated function definitions.",
"files": [],
"assertions": [
"Uses sg (ast-grep), not grep or rg — structural matching captures decorator+function pairs as a unit, regardless of decorator complexity",
"Specifies --lang py for Python language targeting",
"Pattern captures decorated functions, e.g. '@$DECORATOR def $NAME($$$): $$$' or equivalent structural pattern",
"Command is syntactically valid and handles single-line and multi-line decorators"
],
"model_notes": "Smaller models may attempt rg '@\\w+' which finds decorator lines but misses the associated function. The skill's ast-grep-patterns.md shows the correct multi-line pattern with newline between decorator and def."
},
{
"id": 29,
"eval_name": "python-bare-except",
"prompt": "Find all bare except blocks in this Python codebase — places where 'except:' is used without specifying an exception type. These catch SystemExit and KeyboardInterrupt which is usually a bug.",
"expected_output": "Uses sg (ast-grep) with --lang py and a structural pattern for bare except clauses.",
"files": [],
"assertions": [
"Uses sg (ast-grep) with --lang py, not rg — AST matching distinguishes bare 'except:' from 'except SomeError:' structurally",
"Specifies --lang py for Python language targeting",
"Pattern targets bare except blocks: 'try: $$$ except: $$$' or equivalent without an exception type",
"Does not use regex that would false-positive on 'except Exception:' or 'except ValueError:'"
],
"model_notes": "Smaller models often suggest rg 'except:' which also matches 'except Exception:' lines. The skill's ast-grep pattern 'try:\\n $$$\\nexcept:\\n $$$' is structurally precise and avoids false positives."
},
{
"id": 30,
"eval_name": "go-unwrapped-errors",
"prompt": "Find all places in this Go codebase where errors are returned without wrapping — 'return err' instead of 'return fmt.Errorf(\"...: %w\", err)'. These lose context about where the error originated.",
"expected_output": "Uses sg (ast-grep) with --lang go and a structural pattern for bare error returns without wrapping.",
"files": [],
"assertions": [
"Uses sg (ast-grep) with --lang go, not rg — structural pattern 'if $ERR != nil { return $ERR }' matches the exact unwrapped-error idiom",
"Specifies --lang go for Go language targeting",
"Pattern targets the unwrapped error return pattern: 'if $ERR != nil { return $ERR }' or similar",
"Does not false-positive on properly wrapped errors using fmt.Errorf with %w"
],
"model_notes": "Smaller models may suggest rg 'return err' which matches variable names like 'return errorCode' and misses multi-return patterns like 'return nil, err'. The sg pattern is structurally precise."
},
{
"id": 31,
"eval_name": "multi-tool-php-import-size",
"prompt": "Find all PHP files that import the 'GuzzleHttp\\Client' class, then check if any of those files are larger than 10KB. Large files using HTTP clients might need refactoring.",
"expected_output": "Combines rg to find import statements with fd -S for size filtering, or uses a pipeline approach.",
"files": [],
"assertions": [
"Uses rg to find 'use GuzzleHttp\\Client' or equivalent import pattern in PHP files",
"Uses fd with -S or --size flag to check file sizes against the 10KB threshold",
"Combines the tools in a pipeline or sequential approach (e.g., rg -l | xargs fd -S, or fd -S + rg)",
"Targets PHP files specifically with -t php or -e php"
]
},
{
"id": 32,
"eval_name": "multi-tool-tokei-then-search",
"prompt": "Get a language breakdown of this project, then search the dominant language for any functions marked as deprecated (via comments or annotations).",
"expected_output": "Uses tokei or scc for language stats first, then rg or sg with appropriate language filter for deprecated markers.",
"files": [],
"assertions": [
"Uses tokei or scc as the first step to determine the dominant language",
"Uses rg or sg as the second step to search for deprecated markers (@deprecated, @Deprecated, #[deprecated], etc.)",
"Applies language-specific file type filter (-t or --lang) based on the tokei/scc output",
"Executes as a deliberate two-step workflow rather than guessing the dominant language"
],
"model_notes": "Smaller models may skip the tokei step and guess the dominant language, or search all languages. The skill teaches the count-first-then-drill-down pattern explicitly."
}
]
}
ast-grep Pattern Recipes
Structural search patterns for sg (ast-grep), organized by language.
ast-grep matches code by AST structure, not text. This means patterns are immune to whitespace differences, comment variations, and formatting styles.
---
Metavariable Reference
| Syntax | Description | Example |
|---|---|---|
$VAR | Single AST node (expression, identifier, literal) | console.log($MSG) |
$$$ | Zero or more AST nodes (variadic) | function($$$) |
$$_ | Zero or more unnamed nodes (wildcard) | [$$$] |
$_ | Any single node (unnamed wildcard) | if ($_) { $$$ } |
Key rules:
- Named metavariables (
$VAR) capture their match and must match consistently
within a pattern (same $VAR = same value).
$$$is greedy and matches any number of arguments, statements, etc.- Use
$_when you do not care about capturing the value.
---
JavaScript / TypeScript
React Patterns
# useState hooks
sg --pattern 'const [$STATE, $SETTER] = useState($$$)' --lang tsx
# useEffect with dependency array
sg --pattern 'useEffect(() => { $$$ }, [$$$])' --lang tsx
# useEffect without dependencies (runs every render)
sg --pattern 'useEffect(() => { $$$ })' --lang tsx
# Component definitions (function)
sg --pattern 'function $NAME($$$) { $$$ return $$$ }' --lang tsx
# Arrow function components
sg --pattern 'const $NAME = ($$$) => { $$$ }' --lang tsx
# JSX with specific prop
sg --pattern '<$COMP className={$$$} />' --lang tsx
sg --pattern '<$COMP onClick={$$$}>$$$</$COMP>' --lang tsx
# Custom hook calls
sg --pattern 'const $RESULT = use$HOOK($$$)' --lang tsxAsync/Await Patterns
# Async function declarations
sg --pattern 'async function $NAME($$$) { $$$ }' --lang js
# Await expressions
sg --pattern 'await $EXPR' --lang js
# Try/catch around await
sg --pattern 'try { $$$ await $EXPR $$$ } catch ($ERR) { $$$ }' --lang ts
# Promise.all usage
sg --pattern 'await Promise.all([$$$])' --lang ts
# Unhandled promise (missing await)
sg --pattern '$VAR.$METHOD($$$).then($$$)' --lang tsError Handling
# Empty catch blocks
sg --pattern 'try { $$$ } catch ($ERR) { }' --lang js
# Console.log in catch (often should be proper logging)
sg --pattern 'catch ($ERR) { $$$ console.log($$$) $$$ }' --lang js
# Throw new Error
sg --pattern 'throw new Error($MSG)' --lang ts
# Throw non-Error objects
sg --pattern 'throw $MSG' --lang tsModule Patterns
# Default exports
sg --pattern 'export default $EXPR' --lang ts
# Named exports
sg --pattern 'export const $NAME = $VALUE' --lang ts
sg --pattern 'export function $NAME($$$) { $$$ }' --lang ts
# Dynamic imports
sg --pattern 'import($PATH)' --lang ts
sg --pattern 'await import($PATH)' --lang tsCommon Anti-Patterns
# Direct DOM manipulation in React
sg --pattern 'document.getElementById($$$)' --lang tsx
sg --pattern 'document.querySelector($$$)' --lang tsx
# setState in useEffect without cleanup
sg --pattern 'useEffect(() => { $$$ $SETTER($$$) $$$ })' --lang tsx
# Object mutation
sg --pattern '$OBJ.$PROP = $VALUE' --lang ts---
Python
Function and Class Patterns
# Function definitions
sg --pattern 'def $NAME($$$):
$$$' --lang py
# Async function definitions
sg --pattern 'async def $NAME($$$):
$$$' --lang py
# Class with inheritance
sg --pattern 'class $NAME($BASE):
$$$' --lang py
# Static methods
sg --pattern '@staticmethod
def $NAME($$$):
$$$' --lang py
# Class methods
sg --pattern '@classmethod
def $NAME(cls, $$$):
$$$' --lang py
# Property definitions
sg --pattern '@property
def $NAME(self):
$$$' --lang pyDecorator Patterns
# Any decorated function
sg --pattern '@$DECORATOR
def $NAME($$$):
$$$' --lang py
# Specific decorator
sg --pattern '@app.route($$$)
def $NAME($$$):
$$$' --lang py
# Decorator with arguments
sg --pattern '@$DECORATOR($$$)
def $NAME($$$):
$$$' --lang py
# Pytest fixtures
sg --pattern '@pytest.fixture($$$)
def $NAME($$$):
$$$' --lang pyError Handling
# Bare except (catches everything including SystemExit)
sg --pattern 'try:
$$$
except:
$$$' --lang py
# Except with pass (silenced errors)
sg --pattern 'except $EXCEPTION:
pass' --lang py
# Broad exception catching
sg --pattern 'except Exception as $ERR:
$$$' --lang py
# Context managers
sg --pattern 'with $EXPR as $VAR:
$$$' --lang pyType Annotation Patterns
# Typed function signatures
sg --pattern 'def $NAME($$$) -> $RETURN:
$$$' --lang py
# Optional types
sg --pattern 'Optional[$TYPE]' --lang py
# Union types
sg --pattern 'Union[$$$]' --lang py---
PHP
Class and Method Patterns
# Class definitions
sg --pattern 'class $NAME { $$$ }' --lang php
sg --pattern 'class $NAME extends $BASE { $$$ }' --lang php
sg --pattern 'class $NAME implements $IFACE { $$$ }' --lang php
# Method definitions
sg --pattern 'public function $NAME($$$) { $$$ }' --lang php
sg --pattern 'protected function $NAME($$$) { $$$ }' --lang php
sg --pattern 'private function $NAME($$$) { $$$ }' --lang php
# Static methods
sg --pattern 'public static function $NAME($$$) { $$$ }' --lang php
# Constructor
sg --pattern 'public function __construct($$$) { $$$ }' --lang php
# Constructor property promotion (PHP 8+)
sg --pattern 'public function __construct(
$$$
) { $$$ }' --lang phpCommon PHP Patterns
# Array operations
sg --pattern 'array_map($CALLBACK, $$$)' --lang php
sg --pattern 'array_filter($ARRAY, $$$)' --lang php
# Method chaining
sg --pattern '$OBJ->$METHOD1($$$)->$METHOD2($$$)' --lang php
# Type-hinted parameters
sg --pattern 'function $NAME($TYPE $PARAM) { $$$ }' --lang php
# Null coalescing
sg --pattern '$EXPR ?? $DEFAULT' --lang php
# Match expression (PHP 8+)
sg --pattern 'match ($EXPR) { $$$ }' --lang phpTYPO3/Symfony/Laravel Patterns
# Dependency injection
sg --pattern 'public function __construct($TYPE $PARAM) { $$$ }' --lang php
# Route annotations
sg --pattern '#[Route($$$)]' --lang php
# Doctrine annotations
sg --pattern '#[ORM\Column($$$)]' --lang php---
Go
Function and Method Patterns
# Function definitions
sg --pattern 'func $NAME($$$) $RETURN { $$$ }' --lang go
# Methods with receiver
sg --pattern 'func ($RECV $TYPE) $NAME($$$) $RETURN { $$$ }' --lang go
# Pointer receiver methods
sg --pattern 'func ($RECV *$TYPE) $NAME($$$) $RETURN { $$$ }' --lang go
# Init functions
sg --pattern 'func init() { $$$ }' --lang go
# Main functions
sg --pattern 'func main() { $$$ }' --lang goError Handling
# Standard error check
sg --pattern 'if $ERR != nil { $$$ }' --lang go
# Error return without wrapping
sg --pattern 'if $ERR != nil { return $ERR }' --lang go
# Error wrapping with fmt.Errorf
sg --pattern 'fmt.Errorf($FMT, $$$)' --lang go
# errors.Is / errors.As
sg --pattern 'errors.Is($ERR, $TARGET)' --lang go
sg --pattern 'errors.As($ERR, $TARGET)' --lang goConcurrency
# Goroutine launches
sg --pattern 'go $FUNC($$$)' --lang go
# Channel operations
sg --pattern '$CH <- $VALUE' --lang go
sg --pattern '<-$CH' --lang go
# Select statements
sg --pattern 'select { $$$ }' --lang go
# Mutex usage
sg --pattern '$MU.Lock()' --lang go
sg --pattern 'defer $MU.Unlock()' --lang go
# WaitGroup
sg --pattern '$WG.Add($N)' --lang go
sg --pattern 'defer $WG.Done()' --lang goStruct and Interface Patterns
# Struct definitions
sg --pattern 'type $NAME struct { $$$ }' --lang go
# Interface definitions
sg --pattern 'type $NAME interface { $$$ }' --lang go
# Struct literal initialization
sg --pattern '$TYPE{ $$$ }' --lang goTesting
# Test functions
sg --pattern 'func Test$NAME(t *testing.T) { $$$ }' --lang go
# Benchmark functions
sg --pattern 'func Benchmark$NAME(b *testing.B) { $$$ }' --lang go
# Table-driven tests
sg --pattern 'for $_, $TC := range $CASES { $$$ }' --lang go
# t.Run subtests
sg --pattern 't.Run($NAME, func(t *testing.T) { $$$ })' --lang go---
Rust
Function Patterns
# Function definitions
sg --pattern 'fn $NAME($$$) -> $RETURN { $$$ }' --lang rust
# Async functions
sg --pattern 'async fn $NAME($$$) -> $RETURN { $$$ }' --lang rust
# Impl blocks
sg --pattern 'impl $TYPE { $$$ }' --lang rust
# Trait implementations
sg --pattern 'impl $TRAIT for $TYPE { $$$ }' --lang rustError Handling
# unwrap calls (potential panics)
sg --pattern '$EXPR.unwrap()' --lang rust
# expect calls
sg --pattern '$EXPR.expect($MSG)' --lang rust
# Match on Result
sg --pattern 'match $EXPR { Ok($VAL) => $$$, Err($ERR) => $$$ }' --lang rust
# Question mark operator
sg --pattern '$EXPR?' --lang rust---
Tips for Writing ast-grep Patterns
1. Start simple. Begin with a small pattern and add complexity. If $FUNC($$$) matches too broadly, add more structure.
2. Use `--json` output for programmatic processing. The JSON includes file path, line/column ranges, and matched metavariable bindings.
3. Test patterns interactively at https://ast-grep.github.io/playground to verify they match what you expect.
4. Language matters. Always specify --lang -- the same text may parse differently in different languages.
5. Metavariable consistency. Within a single pattern, the same $NAME must match the same text. Use different names ($A, $B) for different captures.
6. Whitespace is ignored in pattern matching. func ( $ARG ) matches func(arg), func( arg ), etc.
---
When to Use ast-grep Over ripgrep
- Matching code structure regardless of formatting/whitespace
- Finding function calls with specific argument patterns
- Matching patterns that span multiple lines unpredictably
- Refactoring patterns (find + replace structurally)
- When regex would be too fragile for the code pattern
---
Basic Usage
# Search with a pattern in a language
sg --pattern 'console.log($$$)' --lang js
# Search in specific directory
sg --pattern 'fmt.Errorf($$$)' --lang go src/
# JSON output for programmatic use
sg --pattern '$FUNC($$$)' --lang py --json
# Find and replace structurally
sg --pattern 'console.log($$$)' --rewrite 'logger.info($$$)' --lang jsCode Metrics: tokei and scc
Fast codebase analysis tools for counting lines of code, comments, and blanks by language.
---
tokei
# Basic usage -- counts all code in current directory
tokei
# Count specific directory
tokei src/
# Sort by lines of code
tokei --sort code
# Specific languages only
tokei --type=Python,JavaScript
# Output as JSON for processing
tokei --output json
# Exclude directories
tokei --exclude='vendor/*' --exclude='node_modules/*'---
scc
Like tokei but adds complexity estimation and COCOMO cost modeling.
# Basic usage
scc
# Specific directory
scc src/
# Sort by lines of code
scc --sort-by code
# Include complexity and COCOMO
scc --wide
# Specific languages
scc --include-ext py,js,ts
# Exclude directories
scc --exclude-dir vendor,node_modules
# Output as JSON
scc --format json---
When to Use Which
| Need | Tool |
|---|---|
| Quick language breakdown | tokei |
| Complexity estimates / cost modeling | scc |
| CI integration / badge generation | scc (has badge output) |
| Fastest possible count | tokei |
fd Guide
Fast, user-friendly file finder. Replaces find with sane defaults: respects .gitignore, colorized output, regex by default, smart case.
---
Key Flags
-e EXT Filter by extension (-e py, -e rs)
-t TYPE Filter by type: f (file), d (directory), l (symlink), x (executable)
-H Include hidden files
-I Do not respect .gitignore
-E PATTERN Exclude glob pattern
-d DEPTH Limit directory depth
-x CMD Execute command for each result
-X CMD Execute command with all results at once
-0 Null-byte separator (for xargs -0)
-a Show absolute paths
-L Follow symlinks
-g GLOB Use glob pattern instead of regex
-p Match against full path (not just filename)
--changed-within TIME Files modified within duration (e.g., 1h, 2d, 1w)
--changed-before TIME Files modified before duration
-S / --size Filter by size (e.g., +1m for >1MB)---
Important: -e Matches the Literal Final Extension Only
fd -e ts matches *.ts — files whose extension is exactly .ts. Unlike rg -t ts (which includes .tsx), fd -e is a literal extension match:
fd -e ts # matches *.ts only (NOT *.tsx)
fd -e js # matches *.js only (NOT *.jsx or *.mjs)
fd -e ts -e tsx # matches *.ts AND *.tsx
# WRONG: compound suffixes don't work with -e
fd -e test.ts # matches nothing useful
# RIGHT: use glob patterns for compound suffixes
fd -g '*.test.ts' # matches foo.test.ts
fd -g '*.{test,spec}.ts' # matches foo.test.ts and foo.spec.ts---
Common Usage
# Find Python files
fd -e py
# Find all test files
fd 'test_.*\.py$'
fd -g '*_test.go'
# Find files modified in the last day
fd -e js --changed-within 1d
# Find large files
fd -S +10m
# Find and delete all .pyc files
fd -e pyc -x rm {}
# Find directories named "test" or "tests"
fd -t d '^tests?$'
# Find executable files
fd -t x
# Find files excluding certain directories
fd -e ts -E node_modules -E dist
# Find hidden config files
fd -H '^\.' -t f
# Find files and pipe to rg for content search
fd -0 -e py | xargs -0 rg 'import os'---
fd + rg Combinations
# Find Python files, then search for a pattern
fd -e py -x rg -l 'async def'
# Find recently changed files and search them
fd --changed-within 2h -e ts -X rg 'TODO'
# Find config files and search for a key
fd -g '*.{yml,yaml,toml,json}' -X rg 'database'Beyond Local Files — Remote Handoff Guide
This skill covers local CLI search tools. When the context you need lives outside the repository, hand off to the appropriate tool or skill.
---
Handoff Table
| Need | Hand off to |
|---|---|
| Issue/ticket context (Jira, GitHub Issues) | jira-communication skill or gh issue view |
| PR/MR discussion, review comments | git-workflow skill or gh pr view |
| Wiki pages, project documentation sites | WebFetch or documentation skills |
| Upstream/fork differences | git log, git diff, gh repo view |
| Live API responses, external services | WebFetch, Playwright |
---
When to Hand Off
rg/fdreturn no results and the answer likely lives in issues, PRs,
or external docs
- Code comments reference issue keys (
#123,PROJ-456,JIRA-789) - You need the rationale behind a change (check the PR/MR, not just the diff)
- The user asks about deployment status, CI results, or external service state
---
Combining Local and Remote Search
A common pattern is local-first, then remote:
1. Local: rg 'PROJ-1234' — find where the issue is referenced in code 2. Local: git log --grep='PROJ-1234' — find commits mentioning it 3. Remote: gh issue view 1234 or Jira API — get current status 4. Decision: combine local context with remote status to answer the question
---
Signals That Context Is Remote
- Comments like
// TODO: see #456,// Workaround for PROJ-123 - Commit messages referencing PRs (
Merge pull request #89) - Configuration referencing external services (URLs, API endpoints)
- Questions about "why" something was done (rationale lives in PRs/issues)
rga (ripgrep-all) Guide
Extends ripgrep to search inside PDFs, Word/Excel/PowerPoint, SQLite databases, compressed archives, and more.
---
When to Use rga Instead of rg
- Searching PDF documents for text
- Searching Word (.docx), Excel (.xlsx), PowerPoint (.pptx) files
- Searching inside .zip, .tar.gz, .tar.bz2 archives
- Searching SQLite database contents
- Searching EPUB ebooks
---
Supported Formats
| Format | Extension |
|---|---|
.pdf | |
| Word | .docx, .doc |
| Excel | .xlsx, .xls |
| PowerPoint | .pptx, .ppt |
| OpenDocument | .odt, .ods, .odp |
| Archive | .zip, .tar, .tar.gz, .tar.bz2, .tar.xz |
| SQLite | .db, .sqlite, .sqlite3 |
| EPUB | .epub |
---
Usage
# Search PDFs for a term
rga 'quarterly revenue' docs/
# Search all document types
rga 'confidential' --rga-adapters=+pdfpages,poppler /path/to/docs/
# Search inside archives
rga 'config' backups/*.tar.gz
# Search with ripgrep flags (most rg flags work)
rga -i -c 'error' logs/
# List matching files only
rga -l 'password' /shared/docs/---
Dependencies
rga requires adapters to process different file types:
- PDF:
poppler(pdftotext) — usually pre-installed on Linux - Office (.docx, .xlsx, .pptx): built-in via
zip+ XML parsing - Legacy Office (.doc, .xls): requires
libreofficeorcatdoc - Archives: requires standard tools (
tar,unzip, etc.)
Install adapters: sudo apt install poppler-utils (Debian/Ubuntu) or brew install poppler (macOS).
---
Cache Behavior
rga caches extracted text for faster subsequent searches. The cache is stored in the system cache directory. Use --rga-no-cache to disable.
ripgrep Pattern Recipes
Practical rg patterns organized by use case. All examples assume you are in the project root directory.
---
Security Scanning
# Hardcoded passwords/secrets
rg -i '(password|passwd|secret|api_key|apikey|token)\s*[:=]' -g '!*.lock'
rg -i '(password|secret|key)\s*=\s*["\x27][^"\x27]{8,}' -g '!*.lock' -g '!*.md'
# AWS credentials
rg '(AKIA|ASIA)[A-Z0-9]{16}'
rg 'aws_secret_access_key\s*[:=]'
# Private keys
rg 'BEGIN (RSA |DSA |EC |OPENSSH )?PRIVATE KEY'
# JWT tokens
rg 'eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}'
# Dangerous functions
rg '\beval\s*\(' -t py -t js -t php -t ruby
rg '\bexec\s*\(' -t py -t php
rg 'innerHTML\s*=' -t js -t ts
# SQL injection vectors
rg '(query|execute)\s*\(.*["\x27]\s*\+' -t py -t js -t java
rg '\$\w+.*->query\(\s*["\x27]' -t php
rg 'raw\s*\(\s*f["\x27]' -t py # f-string in raw SQL (Python)
# Insecure HTTP
rg 'http://' -t py -t js -t go -t java -g '!*.lock' -g '!*.md'
rg 'verify\s*=\s*False' -t py # disabled SSL verification
rg 'rejectUnauthorized.*false' -t js -t ts
# Debug/dev artifacts left in code
rg '(console\.log|print\(|fmt\.Print)' -t js -t py -t go --count-matches
rg 'binding\.pry' -t ruby
rg 'debugger;' -t js -t ts
rg 'dd\(' -t php---
Dependency Analysis
# Python imports
rg '^from (\S+) import' -t py -o -r '$1' --no-filename | sort -u
rg '^import (\S+)' -t py -o -r '$1' --no-filename | sort -u
# JavaScript/TypeScript imports
rg "^import .+ from ['\"]([^'\"]+)" -t js -t ts -o -r '$1' --no-filename | sort -u
rg "require\(['\"]([^'\"]+)" -t js -o -r '$1' --no-filename | sort -u
# Go imports
rg '"([\w./]+)"' -t go -o -r '$1' --no-filename | sort -u
# PHP use statements
rg '^use\s+([\w\\\\]+)' -t php -o -r '$1' --no-filename | sort -u
# Java imports
rg '^import\s+([\w.]+)' -t java -o -r '$1' --no-filename | sort -u
# Rust use statements
rg '^use\s+([\w:]+)' -t rust -o -r '$1' --no-filename | sort -u
# Find unused imports (files that import X but never reference it again)
# Step 1: find files importing the module
rg -l 'import someModule' -t js
# Step 2: check if the module is used beyond the import line
rg -c 'someModule' -t js # files with count=1 likely only have the import---
Code Quality
# TODO/FIXME/HACK/XXX with context
rg '(TODO|FIXME|HACK|XXX|WARN|DEPRECATED):?\s' -n --trim
rg '(TODO|FIXME)\((\w+)\)' -o -r '$1 by $2' --no-filename # extract assignee
# Deprecated usage
rg '@deprecated' -n -t py -t js -t ts -t java -t php
rg '@Deprecated' -t java
# Magic numbers (numeric literals in logic, not in declarations)
rg 'if.*[^=!<>]==[^=].*\d{2,}' -t py -t js -t go
# Long functions (find function start, count lines until next function)
rg -n 'def \w+\(' -t py # list all function definitions with line numbers
# Empty exception handling
rg -U 'except.*:\s*\n\s*(pass|\.\.\.)\s*$' -t py
rg -U 'catch\s*\(.*\)\s*\{\s*\}' -t js -t ts -t java
# Commented-out code (heuristic)
rg '^\s*//\s*(if|for|while|return|function|var|let|const)\b' -t js -t ts
rg '^\s*#\s*(if|for|while|return|def|class|import)\b' -t py
# Dead code indicators
rg 'noinspection' -t java -t py
rg '@ts-ignore' -t ts
rg '// eslint-disable' -t js -t ts
rg '# type: ignore' -t py
rg '# noqa' -t py
rg '# noinspection' -t py---
Refactoring
# Find all usages of a function
rg '\bfunctionName\b' -t py -n
# Find function definitions (various languages)
rg 'def functionName\(' -t py
rg 'function functionName\(' -t js
rg 'func functionName\(' -t go
rg 'fn functionName\(' -t rust
# Find class definitions
rg 'class ClassName' -t py -t java -t ts
# Find method calls on a specific type
rg '\.methodName\(' -t go -t java -t ts
# Find variable assignments
rg '\bvarName\s*[:=]' -t py -t js -t ts
# Find all files referencing a module (across languages)
rg -l '(import|require|use|from).*moduleName'
# Rename preview (show what would change)
rg 'oldName' -t py -r 'newName' # shows replacements, does not write
# Find interface implementations (Go)
rg 'func \(.*\) MethodName\(' -t go
# Find all routes/endpoints
rg '(@app\.(get|post|put|delete|patch)|router\.(get|post|put|delete|patch))' -t py
rg 'app\.(get|post|put|delete|patch)\(' -t js -t ts
rg '(GET|POST|PUT|DELETE|PATCH)\s+/' -t go---
Configuration and Infrastructure
# Find all config files
rg -l '.' -g '*.{yml,yaml,toml,ini,cfg,conf,json,env,env.*}'
# Find Docker-related configs
rg -l '.' -g 'Dockerfile*' -g 'docker-compose*' -g '.dockerignore'
# Find port bindings
rg '(port|PORT)\s*[:=]\s*\d+' -g '*.{yml,yaml,toml,json,env,py,js,go}'
rg ':\d{4,5}' -g '*.{yml,yaml,toml,env}'
# Find database connection strings
rg '(postgres|mysql|mongo|redis|sqlite)://' -g '!*.lock' -g '!*.md'
# Find environment variable references
rg 'os\.environ\[' -t py
rg 'os\.Getenv\(' -t go
rg 'process\.env\.' -t js -t ts
rg '\$_ENV\[' -t php
rg 'getenv\(' -t php
# Find CI/CD references
rg -l '.' -g '.github/workflows/*.yml'
rg -l '.' -g '.gitlab-ci.yml' -g 'Jenkinsfile' -g '.circleci/*'---
Advanced Flags and Techniques
# Invert match (show lines NOT matching)
rg -v 'pattern' file.txt
# Show only the matched portion
rg -o 'pattern' -t py
# Capture groups with replacement (extract data)
rg 'version:\s*"([^"]+)"' -o -r '$1' --no-filename
# Search binary files
rg --binary 'pattern'
# Search compressed files (use rga instead for full support)
rg -z 'pattern' archive.gz
# Null-byte separated output (for xargs -0)
rg -l0 'pattern' | xargs -0 wc -l
# Limit number of results
rg 'pattern' --max-count 5 # max 5 matches per file
rg 'pattern' -l | head -20 # first 20 matching files
# Search with lookahead/lookbehind (PCRE2)
rg -P '(?<=import\s)\w+' -t py # word after "import "
rg -P 'def \w+\((?=.*self)' -t py # methods (have self param)
# Stats about the search
rg 'pattern' --stats
# Sorted output by file path
rg 'pattern' --sort path
# Glob patterns for includes
rg 'pattern' -g '*.{ts,tsx}' -g '!*.test.*' -g '!*.spec.*'---
Performance Tips
- Always specify file types (
-t) when possible. This avoids scanning
irrelevant files entirely.
- Limit directory scope to the relevant subtree.
- Use `-l` (list files) when you only need to know which files match, not
the matching lines.
- Use `--max-count N` to stop searching a file after N matches.
- Exclude large directories like
vendor/,node_modules/,dist/,
.git/ with -g '!dir/'.
- Use `-F` (fixed string) when your pattern has no regex metacharacters.
This is faster than regex matching.
- Pipe to `head` when exploring. You do not need all 10,000 results to
understand the pattern.
---
Key Flags Reference
-i Case-insensitive search
-w Match whole words only
-l List matching file paths only (no content)
-c Count matching lines per file
-n Show line numbers (default)
-t TYPE Restrict to file type (e.g., -t py, -t js, -t go)
-T TYPE Exclude file type
-g 'GLOB' Filter by glob pattern (e.g., -g '*.tsx')
--json Machine-readable JSON output
-A N / -B N Show N lines after/before match
-C N Show N lines of context (before + after)
-U Enable multiline matching
--hidden Include hidden files (dotfiles)
--no-ignore Search files ignored by .gitignore
-F Treat pattern as fixed string (no regex)
-e PATTERN Specify pattern (useful for multiple patterns or leading dashes)
--count-matches Count individual matches per file (vs -c which counts matching lines)
-r REPLACEMENT Replace matches in output (preview, does not modify files)---
Progressive Refinement Strategy
Start narrow, widen only if needed:
# 1. Count matches first to gauge scope
rg -c 'pattern' -t py
# 2. If too many, narrow by directory
rg -c 'pattern' -t py src/core/
# 3. View results with context
rg -n -C 2 'pattern' -t py src/core/
# 4. If still too many, add word boundaries or refine regex
rg -nw 'exactFunction' -t py src/core/---
Common Patterns
# Find function definitions in Python
rg 'def \w+\(' -t py
# Find class definitions in TypeScript
rg 'class \w+' -t ts
# Find all imports of a module
rg "from ['\"](react|vue)" -t js -t ts
# Find TODO/FIXME comments
rg '(TODO|FIXME|HACK|XXX):' -n
# Find environment variable usage
rg '\$\{?\w+\}?' -t sh
# Find SQL injection risks
rg 'execute\(.*\+.*\)' -t py
rg '\$\w+.*->query\(' -t php
# Search with multiple patterns
rg -e 'pattern1' -e 'pattern2' -t js
# Search for multiline patterns (e.g., function with decorator)
rg -U '@deprecated\n.*def \w+' -t py
# Exclude directories
rg 'pattern' -g '!vendor/' -g '!node_modules/'
# Fixed string search (no regex interpretation)
rg -F 'array_map($callback, $items)' -t php---
File Type Targeting
ripgrep has built-in type definitions. List all with rg --type-list.
Common types: py, js, ts, go, rust, java, php, ruby, css, html, json, yaml, toml, md, sh, sql, c, cpp.
Note: -t ts typically matches .ts AND .tsx. Run rg --type-list | grep ts to check your version — some builds include a separate tsx type. Similarly, -t js usually covers .js, .jsx, .mjs. Always verify with rg --type-list.
# Multiple types
rg 'pattern' -t js -t ts
# Custom type definition (one-off)
rg --type-add 'web:*.{html,css,js}' -t web 'pattern'Search Strategies
Unfocused searches produce overwhelming output. Always scope searches deliberately.
---
1. Specify File Types
# Good: targeted by type
rg 'pattern' -t py
sg --pattern '$FUNC($$$)' --lang go
# Bad: search everything
rg 'pattern'---
2. Limit Directory Scope
# Good: specific directory
rg 'pattern' src/api/
fd -e ts src/components/
# Bad: search from root
rg 'pattern' /---
3. Count Before Viewing
# First: how many matches?
rg -c 'pattern' -t py | wc -l # number of files
rg --count-matches 'pattern' -t py # total matches per file
# Then: view if manageable
rg -n 'pattern' -t py---
4. Progressive Refinement
# Start: broad count
rg -c 'import' -t py | wc -l
# => 847 files -- too many
# Narrow: specific module
rg -c 'from requests import' -t py | wc -l
# => 23 files -- manageable
# View: with context
rg -n -C 1 'from requests import' -t py---
5. Exclude Noise
# Exclude generated/vendor code
rg 'pattern' -g '!vendor/' -g '!node_modules/' -g '!*.min.js' -g '!dist/'
fd -e py -E __pycache__ -E .venv -E '*.pyc'---
6. Batch & Parallelize Independent Queries
rg walks the filesystem once per invocation. N sequential calls = N walks + N startup costs. Two ways to collapse that:
6a. Union patterns in one process (rg -e ... -e ...)
When you want any of several patterns from the same scope, pass them all to one rg:
# Good: one walk, one process
rg -t php -e 'RequestHandlerInterface' -e 'MiddlewareInterface' -e 'PSR-15'
# Bad: three walks
rg -t php 'RequestHandlerInterface'
rg -t php 'MiddlewareInterface'
rg -t php 'PSR-15'Use -f patterns.txt for many patterns. Note: ripgrep does not annotate output with which -e/-f pattern matched — neither plain text nor --json exposes a stable pattern index. If you need provenance, run separate searches or post-process by re-matching the captured text.
rg also accepts multiple -t flags and multiple path arguments, so prefer batching scope into a single call:
# Good: one walk, multiple types
rg -t php -t js -t ts 'pattern'
# Good: one walk, multiple paths
rg 'pattern' src/ tests/ docs/6b. Parallel tool calls for distinct intents
Use parallel tool calls when the queries can't collapse into one rg invocation — different patterns in different scopes, different tools, or different post-processing. Issue them as parallel tool calls in a single message; the agent harness runs them concurrently and total wall time ≈ slowest single call.
Good candidates for parallel calls:
- Different patterns in different scopes:
rg 'Error' logs/+rg 'TODO' src/ - Different tools on same target:
rg X+fd -g '*X*'+tokei - Different search modes:
rg 'pattern'+sg --pattern 'func($$$)'
Anti-pattern: chaining independent greps with && in one Bash call — the shell still runs them sequentially.
semgrep Pattern Recipes
Rule-driven structural code search with semgrep, focused on security and lint patterns. Use when sg (ast-grep) is the wrong tool: semgrep ships with curated rule packs, supports taint analysis (source → sink dataflow), and is the standard for security CI gates.
semgrep vs ast-grep
sg (ast-grep) | semgrep | |
|---|---|---|
| Primary use | Structural search & rewrite | Security/lint rules at scale |
| Patterns live in | CLI flag or YAML | YAML rules (file or registry) |
| Rewrite support | Yes (first-class) | Limited (autofix:) |
| Taint tracking | No | Yes (mode: taint) |
| Rule registry | No | https://semgrep.dev/r |
| Per-language config | --lang flag | languages: in rule |
| CI integration | Manual | semgrep ci (native) |
Rule of thumb: reach for sg when you have a structural pattern in your head; reach for semgrep when you want to apply a catalog of patterns (OWASP, CWE, framework-specific lints).
Quick Examples
# Apply the curated security rule pack (no local rules needed)
semgrep --config=auto .
# Run a specific rule pack
semgrep --config=p/security-audit .
semgrep --config=p/owasp-top-ten .
semgrep --config=p/r2c-ci .
# Single inline pattern (ad-hoc, no YAML)
semgrep -e 'eval(...)' --lang python .
semgrep -e 'exec($CMD)' -e 'os.system($CMD)' --lang python .
# JSON output for pipelines
semgrep --config=auto --json --quiet .
# Only report severity ERROR (skip WARNING/INFO)
semgrep --config=auto --severity=ERROR .
# Limit scope
semgrep --config=auto --include='*.py' src/Inline Patterns by Language
# Python: dangerous deserialization
semgrep -e 'pickle.loads($X)' --lang python .
semgrep -e 'yaml.load($X)' --lang python . # missing SafeLoader
# Python: SQL string concatenation
semgrep -e 'cursor.execute("..." + $X)' --lang python .
# JavaScript/TypeScript: dangerous DOM sinks
semgrep -e '$EL.innerHTML = $X' --lang js .
semgrep -e 'document.write($X)' --lang js .
# Go: ignored errors (both assignment and short-declaration forms)
semgrep -e '_, _ = $F(...)' -e '_, _ := $F(...)' --lang go .
# PHP: unsafe shell
semgrep -e 'shell_exec($X)' --lang php .
semgrep -e 'eval($X)' --lang php .Custom Rule (YAML)
For anything you want to reuse, write a rule file:
# .semgrep/no-eval.yml
rules:
- id: no-eval
message: "Avoid eval() — use ast.literal_eval or a parser."
severity: ERROR
languages: [python]
pattern: eval(...)Run it:
semgrep --config=.semgrep/ .Taint Mode (Dataflow)
Use when you need to track that user input flows into a dangerous sink. Pattern-only matches miss this; taint mode tracks it across assignments and function calls.
# .semgrep/sql-injection.yml
rules:
- id: sql-from-request
mode: taint
message: "User input flows into raw SQL."
severity: ERROR
languages: [python]
pattern-sources:
- pattern: request.args.get($X)
- pattern: request.form.get($X)
pattern-sinks:
- pattern: cursor.execute($Q)CI Integration
# Fail the build on findings of severity ERROR
semgrep --config=auto --severity=ERROR --error .
# Diff-aware: only scan changed lines (fast CI mode)
semgrep ci --baseline-ref=origin/mainWhen NOT to Use semgrep
- Unstructured text search → use
rginstead. semgrep parses code
(and structured formats like YAML/JSON); it cannot match log lines or free-form prose.
- One-off structural refactor with a rewrite → use
sginstead. Its
--rewrite is first-class; semgrep autofix is more constrained.
- Files semgrep cannot parse → semgrep reports parse errors on
stderr but produces no findings for those files. If a file is unsupported or unparseable, fall back to rg/sg.
Further Reading
- semgrep docs: https://semgrep.dev/docs
- Rule registry: https://semgrep.dev/r
- Writing rules: https://semgrep.dev/docs/writing-rules/overview
Tool Comparison and Decision Guide
Detailed comparison of the file search tools covered by this skill, with guidance on when to use each one.
---
Quick Decision Flowchart
What are you trying to do?
|
|-- Search for TEXT PATTERNS in files?
| |
| |-- In source code files?
| | --> Use rg (ripgrep)
| |
| |-- In PDFs, Word docs, Excel, archives?
| | --> Use rga (ripgrep-all)
| |
| |-- Need to match CODE STRUCTURE (not just text)?
| |
| |-- One-off structural pattern / refactor?
| | --> Use sg (ast-grep)
| |
| |-- Apply a CATALOG of security/lint rules (with dataflow)?
| --> Use semgrep
|
|-- Find FILES by name, path, or attributes?
| --> Use fd
|
|-- Count lines of code / analyze codebase?
|
|-- Just language breakdown and line counts?
| --> Use tokei
|
|-- Need complexity metrics or cost estimates?
--> Use scc---
Feature Comparison: Text Search Tools
| Feature | rg (ripgrep) | rga (ripgrep-all) | sg (ast-grep) |
|---|---|---|---|
| Primary use | Text/regex search in files | Text search in any document | Structural code search |
| Search method | Regex (PCRE2) | Regex (via rg) | AST pattern matching |
| Speed | Extremely fast | Fast (with caching) | Fast (per language) |
| Respects .gitignore | Yes (default) | Yes (default) | Yes |
| Multi-language | Via file type filters | Via file type filters | Per-language parsers |
| Format awareness | Plain text only | PDF, Office, archives, SQLite | Source code ASTs |
| Multiline | With -U flag | With -U flag | Natural (AST-based) |
| Replace support | Preview only (-r) | No | Yes (structural rewrite) |
| JSON output | Yes (--json) | Yes | Yes (--json) |
| Whitespace sensitive | Yes | Yes | No (AST-based) |
| Comment aware | No | No | Yes (can skip comments) |
When to Choose Which
Choose rg when:
- Searching for string literals, log messages, error codes
- Searching for simple patterns like function names, variable names
- Searching across all file types simultaneously
- You need maximum speed on large codebases
- The pattern is straightforward text or regex
Choose rga when:
- Searching inside PDF documents
- Searching inside Word, Excel, PowerPoint files
- Searching inside compressed archives (.zip, .tar.gz)
- Searching SQLite database contents
- You need rg features but on non-plaintext files
Choose sg when:
- Matching function calls with specific argument patterns
- Finding code structures regardless of formatting
- Matching patterns that span multiple lines unpredictably
- Finding anti-patterns or code smells structurally
- You need to ignore comments and whitespace in matches
- Regex would be too fragile for the code pattern
Choose semgrep when:
- Applying a curated rule pack (OWASP, CWE, framework-specific)
- You need taint analysis (source → sink dataflow), not just pattern match
- Wiring a security/lint gate into CI (
semgrep ci) - Maintaining a reusable rule library in YAML across the team
- The pattern is meaningless without severity/message metadata
For inline patterns and recipes, see references/semgrep-patterns.md.
---
Feature Comparison: File Finding
| Feature | fd | find (system) |
|---|---|---|
| Speed | Very fast (parallel) | Slower (single-threaded) |
| Respects .gitignore | Yes (default) | No |
| Regex support | Yes (default mode) | Limited (-regex) |
| Glob support | Yes (-g) | Yes (-name) |
| Smart case | Yes (default) | No |
| Colored output | Yes | No |
| Syntax | Intuitive | Verbose |
| Execution | -x (each), -X (batch) | -exec, -exec + |
| Size filter | -S | -size |
| Time filter | --changed-within/before | -mtime, -newer |
fd vs find: Syntax Comparison
| Task | fd | find |
|---|---|---|
| Find .py files | fd -e py | find . -name '*.py' |
| Find by regex | fd 'test_.*' | find . -regex '.*test_.*' |
| Find directories | fd -t d | find . -type d |
| Find + delete | fd -e pyc -x rm {} | find . -name '*.pyc' -exec rm {} \; |
| Exclude dir | fd -E vendor | find . -path ./vendor -prune -o -print |
| Modified today | fd --changed-within 1d | find . -mtime 0 |
| Size > 1MB | fd -S +1m | find . -size +1M |
Always use fd instead of find. It is faster, has better defaults, and requires less typing.
---
Feature Comparison: Code Statistics
| Feature | tokei | scc | cloc | wc -l |
|---|---|---|---|---|
| Speed | Very fast | Very fast | Slow | Fast |
| Language detection | Yes (200+) | Yes (200+) | Yes (200+) | No |
| Separates code/comments/blanks | Yes | Yes | Yes | No |
| Complexity metrics | No | Yes (per file) | No | No |
| COCOMO estimates | No | Yes | Yes | No |
| Badge generation | No | Yes | No | No |
| Respects .gitignore | Yes | Yes | No | No |
| JSON output | Yes | Yes | Yes | No |
| Binary detection | Yes | Yes | Yes | No |
When to Choose Which
Choose tokei when:
- You need a fast, accurate language breakdown
- You want lines of code separated by code, comments, blanks
- You need reliable .gitignore support
- You want the fastest possible count
Choose scc when:
- You need complexity estimates alongside line counts
- You need COCOMO cost modeling
- You want to generate badges for a README
- You need per-file breakdowns with complexity
Do NOT use:
cloc-- significantly slower, no advantage over tokei/sccwc -l-- counts all lines including blanks and comments, does not detect
languages, gives misleading results
---
Performance Characteristics
Approximate performance on a large codebase (~500K files, 50M lines):
| Tool | Typical Time | Notes |
|---|---|---|
| rg (targeted, -t py) | < 1s | File type filter is key |
| rg (unfiltered) | 2-5s | Searches all text files |
| fd (by extension) | < 0.5s | Very fast for file listing |
| sg (single language) | 1-3s | Parses ASTs per file |
| tokei | 1-2s | Parallel counting |
| scc | 1-2s | Parallel counting |
| rga (with cache) | 2-5s | Depends on document count |
| rga (no cache) | 10-60s | First run extracts text |
| grep -r (same search as rg) | 30-120s | Single-threaded, no .gitignore |
| find (same search as fd) | 5-20s | Single-threaded |
| cloc (same as tokei) | 60-300s | Much slower |
---
Tool Combinations
These tools work well together. Common combinations:
# Find files, then search contents
fd -e py --changed-within 1d -X rg 'TODO'
# Search for files, then analyze structurally
rg -l 'deprecated' -t py | xargs sg --pattern '@deprecated' --lang py
# Get codebase overview, then search largest language
tokei --sort code # see which language dominates
rg 'pattern' -t py # search that language
# Find config files, search for setting
fd -g '*.{yml,yaml}' -X rg 'database:'
# Find test files by name, verify they test something
fd -g '*_test.go' -X rg 'func Test'
# Combine fd size filter with rg
fd -S +100k -e js -X rg 'TODO' # TODOs in large JS files---
Installation
All tools are available through common package managers:
# Ubuntu/Debian
sudo apt install ripgrep fd-find
# Note: fd binary is 'fdfind' on Debian/Ubuntu, alias to 'fd'
# macOS (Homebrew)
brew install ripgrep fd ast-grep ripgrep-all tokei scc semgrep
# Cargo (Rust)
cargo install ripgrep fd-find ast-grep tokei
# Go
go install github.com/boyter/scc/v3@latest
# npm (ast-grep)
npm install -g @ast-grep/cli
# pipx (semgrep — Python-based)
pipx install semgrep---
Further Reading
- ripgrep: https://github.com/BurntSushi/ripgrep
- ast-grep: https://github.com/ast-grep/ast-grep
- semgrep: https://semgrep.dev/docs
- fd: https://github.com/sharkdp/fd
- rga: https://github.com/phiresky/ripgrep-all
- tokei: https://github.com/XAMPPRocky/tokei
- scc: https://github.com/boyter/scc
Related skills
FAQ
What problem does file-search solve?
file-search solves unreliable project navigation when coding agents work in unfamiliar repositories. The skill provides fast file discovery and symbol location so agents gather accurate context before proposing edits.
When should developers invoke file-search?
file-search fits onboarding to new repos, cross-package debugging, and pre-edit context gathering. Invoke it when agents need dependable paths and symbol maps instead of guessing directory structure.