DEV Community

Zhengxin
Zhengxin

Posted on

Claude Code Tools Deep Dive (4): Grep + Glob

This is the fourth article in my series on Claude Code tools. The first three covered the “interaction primitive trio”: AskUserQuestion, EnterPlanMode, and ExitPlanMode. Together, those tools solve one problem: how the AI and the user align with each other.

Starting with this article, we enter the world of execution primitives—how Claude turns an agreed-upon plan into actual code changes. But before it can read, edit, or write anything, it first needs to know where to look. That makes the first execution primitives worth examining the search duo: Glob finds by path; Grep finds by content.

This series begins with a prerequisite article explaining what tools are and how Claude uses them. Like the other articles, this one follows the four-layer framework introduced there.

Grep + Glob

When Claude enters a new project, it does not know the file paths. Questions such as “Where is the authentication code?”, “Which files use useState?”, and “Which files changed recently?” are impossible to answer reliably without search tools. Claude would otherwise have to guess from its training data—which is unreliable—or ask the user to list files manually—which is tedious.

Claude Code provides two complementary search tools: Glob searches by path; Grep searches by content. I am covering them together because their roles are tightly coupled, they are frequently used in combination, and treating them separately would create a lot of repetition.

They share one central philosophy: perceive on demand, and send only what Claude truly needs into the context window. They are also the prerequisites for the next three file-operation primitives: Read, Edit, and Write.

What they do

Glob finds files using a path pattern. It accepts a shell glob such as **/*.ts or src/**/api-*.js, then returns matching file paths sorted by modification time in descending order.

Grep finds files or lines by content. It is powered by ripgrep, accepts a regular expression, and can return matching file paths, matching lines, or match counts, depending on the selected output mode.

Together, they solve the core problem of helping Claude locate the files it needs inside a large codebase:

  1. No need to read the entire project—locate relevant files first, then use Read, saving context.
  2. No need to guess where files are—search exposes the truth on disk rather than relying on training-time assumptions.
  3. No need to construct Bash commands—dedicated tools avoid shell escaping, path dependencies, and permission issues.
  4. Controllable output—Grep in particular offers three output modes so Claude can request only the level of detail it needs.

A concrete example

Scenario: The user says, “Help me understand how the auth code is organized. I want to refactor it.”

Claude has no idea where the auth code lives. It could be under src/auth/, server/middleware/, or lib/security/, or scattered inside files such as pages/api/login.ts.

The bad alternative: Read alone

Without search tools, Claude would have only a few poor options:

  • Guess from training-time patterns—“Auth middleware in a Node.js project is probably at src/middleware/auth.js.” It tries to read the file and discovers that it does not exist.
  • Ask the user to list files—“Can you tell me the paths of all auth-related files?” The user has to do tedious manual work.
  • Read the entire src/ directory—a medium-sized project may contain 200 files and hundreds of thousands of tokens, overwhelming the context window.

The core problem is simple: without search, Claude cannot see the shape of the codebase. It must rely on indirect information or brute-force reading.

How Grep + Glob solve it

Step 1: Use Glob to sketch the file landscape

Claude calls Glob with:

  • pattern: **/*{auth,login,session,jwt}* (matching paths or filenames containing those keywords)

It returns:

src/auth/middleware.ts    (2h ago)
src/auth/routes.ts        (2h ago)
src/lib/session-store.ts  (3d ago)
src/pages/api/login.ts    (1w ago)
tests/auth.test.ts        (2h ago)
Enter fullscreen mode Exit fullscreen mode

The results are ordered by modification time, so recently modified files appear first. Those files are often where the active work is happening.

Step 2: Use Grep to investigate specific calls

Claude now wants to know where jwt.verify is used:

  • pattern: jwt\.verify
  • output_mode: content (return matching lines, file paths, and line numbers)
  • -C: 2 (include two lines of context before and after each match)
  • type: ts

It returns:

src/auth/middleware.ts:8:      const decoded = jwt.verify(token, process.env.JWT_SECRET);
src/auth/middleware.ts:9:      req.user = decoded;
--
src/services/api-client.ts:42:  return jwt.verify(token, PUBLIC_KEY);
--
Enter fullscreen mode Exit fullscreen mode

Every result is a precise coordinate that Claude can immediately pass to Read or Edit.

Step 3: Combine modes according to the question

If Claude only wants to know how many files use jwt.verify, rather than the exact locations, it can use:

  • pattern: jwt\.verify
  • output_mode: count

The result might be 4 files. This call uses only a few dozen tokens because it does not pull every matching line into the context window.

If it only wants to know which files contain the call, without seeing the lines themselves, it can use:

  • pattern: jwt\.verify
  • output_mode: files_with_matches

The result is simply a list of file paths.

Key insight: Grep’s three output modes—content, files_with_matches, and count—let Claude choose the right precision on demand. It can fetch matching lines for deep investigation, paths for narrowing the scope, or a count for estimating impact.

When they are triggered

Use Glob when:

  • Searching by filename or path—“all .tsx files,” “everything under src/api/,” or “where are the tests?”
  • Looking for recently modified files—Glob sorts results by modification time in descending order.
  • Narrowing the scope before Grep—first identify relevant files, then search their contents.

Use Grep when:

  • Searching files or lines by content—“where is useEffect used?” or “where is UserBadge defined?”
  • Investigating API usage—“every place that calls db.query.”
  • Tracing an error message—when a user supplies an error, search the codebase for the place that may throw it.

Use both when:

  • Locating a module in a large codebase—first Glob for **/*auth*, then Grep for a specific call.
  • Restricting by language or file type—if you only need to search .ts files, Grep’s type: "ts" can do that directly without a preceding Glob call.

Do not use them when:

  • You already know the exact path—use Read directly instead of taking a detour through Grep or Glob.
  • You merely need to list a directory—use ls; Glob matches patterns rather than browsing directories.
  • You need fuzzy semantic search—for a request such as “find all code that performs authentication,” Grep can only perform literal or regex matching. It does not understand semantics; an Agent should investigate instead.

Technical design

Grep and Glob are sibling tools. Their responsibilities are distinct, but their design philosophies are shared. Let us examine each through the four layers, then compare their symmetry.


Glob

1. Naming

Glob

The name comes directly from the convention used by shells and Python’s glob library. “Glob” is the standard term for finding files with path patterns. The fields pattern and path are immediately intuitive to anyone familiar with a shell.

Calling it FindByPath or SearchFiles would actually weaken its central promise: the input uses glob syntax, not regular expressions. The name itself signals the syntax.

2. Tool-level description

Glob’s description is extremely short—only five bullet points—centered on two concerns: usage constraints and delegation at the boundary.

The pattern uses glob syntax, not regex

Supports glob patterns like **/*.js or src/**/*.ts

It provides two examples and no regex examples. This is demonstration instead of prohibition. Rather than saying “do not use regex,” it shows Claude canonical glob shapes such as **/*.js, preventing it from passing something like .*\.ts as the pattern.

Results are sorted by modification time

Returns matching file paths sorted by modification time

This declares a total ordering for the output. It gives Claude a useful intuition: the first file returned by Glob is the most recently modified. For questions such as “where is the project’s active work?” or “which module was just refactored?”, inspecting the first few results may be enough.

The explicit purpose is filename search

Use this tool when you need to find files by name patterns

Grep also has a glob field, but that field is a filter, not a search operation. Glob treats the filename as the primary search target.

Open-ended searches are delegated to Agent

When you are doing an open-ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead

This is the most interesting part: the tool explicitly acknowledges its own boundary. If a task needs repeated cycles of Glob and Grep—for example, “find which module a recent change broke”—the description tells Claude to switch to Agent rather than forcing the task into a single Glob call.

3. Field-level descriptions

  • pattern—a shell glob expression such as **/*.js or src/**/*.{ts,tsx}, not a regular expression.
  • path—an optional search directory; defaults to the current working directory.

The field set is minimal. The modification-time ordering is a subtle but valuable design choice. When developers want to know which area is currently active, their instinct is often to run ls -lt. Glob effectively provides the same signal by default: cold code sinks, hot code rises.

4. Schema validation

The schema is minimal. Only pattern is required; path is optional, and there are no additional numeric constraints.

Almost all of Glob’s behavioral signals live in its name and tool description. The schema adds little restriction because glob syntax is already narrow enough.


Grep

1. Naming

Grep

This name also borrows an industry convention. In the Unix world, “grep” universally means “match by content.” Under the hood, however, the tool uses ripgrep (rg) rather than traditional grep. The familiar name reduces cognitive load while the implementation uses a faster engine.

2. Tool-level description

Grep’s description is one level more detailed than Glob’s: seven bullet points plus a declaration, centered on four concerns—locking in correct usage, explaining syntax, exposing filter dimensions, and delegating at the boundary.

ALWAYS + NEVER: locking usage from both directions

ALWAYS use Grep for search tasks. NEVER invoke grep or rg as a Bash command. The Grep tool has been optimized for correct permissions and access.

This is the strongest sentence in the entire description. “ALWAYS” and “NEVER” constrain behavior from both directions: it says what to use, forbids the tempting shortcut, and explains why with “optimized for correct permissions and access.” It prevents Claude—already comfortable with the shell—from instinctively running rg through Bash, where output is unstructured and permission handling may differ.

The pattern uses ripgrep regex syntax

Supports full regex syntax (e.g., log.*Error, function\s+\w+)

This is the direct opposite of Glob. Grep’s pattern is a regular expression. Two realistic examples—searching for logged errors and function declarations—immediately communicate the syntax.

Two filtering dimensions: glob vs. type

Filter files with glob parameter (e.g., *.js, **/*.tsx) or type parameter (e.g., js, py, rust)

Claude gets two parallel options: use glob for a precise path pattern or type for a language shortcut from ripgrep’s built-in type table. One type: rust is far more concise than spelling out a collection of extensions.

The default output mode is files_with_matches

Output modes: content shows matching lines, files_with_matches shows only file paths (default), count shows match counts

Notice that “default” is attached to files_with_matches, not content. Why? Because content consumes the most context. Making it the default could flood the context window. A path list lets Claude decide whether a deeper read is necessary. This is a default designed around the token budget.

Open-ended search is delegated to Agent

Use Agent tool for open-ended searches requiring multiple rounds

This mirrors Glob. Both tools declare their own boundary: if the task requires repeated iterations, use Agent.

ripgrep is not grep; literals may need escaping

Pattern syntax: Uses ripgrep (not grep)—literal braces need escaping (use interface\{\} to find interface{} in Go code)

This is a concrete example of a real failure mode. In a regular expression, braces express repetition ranges—a{2,3} means two or three occurrences—so searching for Go’s interface{} syntax requires escaping the braces. One realistic example replaces a long syntax lecture.

Multiline mode is off by default and must be enabled explicitly

Multiline matching: By default patterns match within single lines only. For cross-line patterns like struct \{[\s\S]*?field, use multiline: true

This prevents Claude from writing a cross-line expression, receiving no matches, and not understanding why. It also illustrates a recurring design pattern: expensive behavior stays off by default and requires an explicit switch.

3. Field-level descriptions

Grep has far more fields than Glob:

  • pattern—a regular expression using ripgrep syntax.
  • path—an optional directory in which to search.
  • glob—an optional file glob, such as *.ts.
  • type—an optional language filter such as ts, py, or rust.
  • output_modecontent, files_with_matches (default), or count.
  • head_limit—limits the number of output entries.
  • -i—case-insensitive matching.
  • -n—shows line numbers; enabled by default in content mode.
  • -A, -B, and -C—lines of context after, before, or around a match; only relevant in content mode.
  • multiline—allows patterns to match across lines.
  • Format flags such as -c, -l, -L, -o, and -Z—pass output handling through to native grep behavior.

Several design choices stand out.

Three output modes

This is Grep’s most elegant feature. A single search can return three levels of precision:

  • content—all matching lines, when you need to inspect or fix exact locations.
  • files_with_matches—file paths only, when you are scoping a refactor.
  • count—counts only, when you are estimating impact.

These map to three common intents: “I need to fix it,” “I need to refactor it,” and “I need to assess it.” Grep lets Claude choose precision according to intent instead of always retrieving the maximum amount of data.

head_limit as a safety net

A search for console.log might return 1,000 lines. Without a limit, that could overwhelm the context window. head_limit: 50 returns only the first 50 entries—enough to work with without flooding the model.

One subtle detail: Grep orders by file-path lexicographic order, whereas Glob orders by modification time. Neither is a relevance ranking; the limit simply truncates that ordering.

type vs. glob for narrowing scope

type uses ripgrep’s language recognition based on extensions and file types, covering common languages such as Python, Rust, and TypeScript. glob is pure path matching and can express special layouts such as **/legacy/**/*.js. type is more concise; glob is more flexible.

Native format flags as an escape hatch

When the normalized tool output is not enough, Claude can fall back to native ripgrep capabilities. The designers recognize that no wrapper can cover every use case, so they leave an escape hatch.

4. Schema validation

Grep’s schema also has almost no hard constraints such as numeric ranges or string-length limits. Most constraints are enums or types:

Field Type Constraint
output_mode string content, files_with_matches, or count; defaults to files_with_matches
-i, -n, multiline boolean default to false
-A, -B, -C integer only apply when output_mode is content
head_limit integer no default; unlimited when omitted

Defaults are central to Grep’s design. The output mode defaults to files_with_matches, multiline mode is off, and the flags are off unless requested. Every default leans toward less output and simpler behavior, making the tool context-efficient even when Claude accepts all defaults.


Why dedicated Grep and Glob tools instead of Bash + rg?

Bash is a catch-all: in theory, it can do everything. But invoking rg directly creates several problems:

  • Shell escaping—characters such as $, !, and ( inside regular expressions may be interpreted by the shell.
  • Path dependencies—is rg installed, and which version is available?
  • Output parsing—Bash returns a large text blob that Claude must parse itself.
  • No output-mode abstraction—Claude has to remember and combine raw rg flags.

Dedicated tools solve these problems with typed parameters, normalized output, no shell quoting traps, and a single structured call. That is the technical foundation behind the instruction: “ALWAYS use Grep… NEVER invoke grep or rg as a Bash command.”


Division of responsibility among neighboring tools

Here is where Grep + Glob sit within Claude Code’s execution primitives. Later articles will fill in the rest of the map:

Dimension Interaction trio Grep + Glob Read Edit Write
Role Collaborative alignment Locate coordinates Perceive content Precise execution Full execution
Frequency Key moments High-frequency High-frequency High-frequency Medium-frequency
Input Structured / empty Pattern; path need not be known Known path Known path + old_string Known path + full content
Output User decision Paths / matching lines / counts Complete file content Modified diff New or overwritten file
Conservative bias “When uncertain, plan” “Search on demand before reading everything” “When uncertain, read” “When uncertain, read first” “Prefer Edit when possible”

A complete investigation chain:

User: Help me refactor the auth-related code
    ↓
Glob (**/*{auth,login,session}*)                 ← This article
    → Relevant file paths, ordered by mtime
    ↓
Grep (pattern: "jwt\.verify", output_mode: files_with_matches)  ← This article
    → Files that actually use the API
    ↓
Read (each relevant file)                       ← Next article
    → Full contents establish a perception commitment
    ↓
Edit / Write                                    ← Later articles
    → Precise or full changes based on that perception
Enter fullscreen mode Exit fullscreen mode

The trust chain of execution primitives:

  • Glob / Grep—location: Which files are relevant to this task?
  • Read—perception: What do those files look like right now?
  • Edit / Write—execution: How should they be changed, precisely or in full, based on that perception?

Every step is enforced at runtime, accepts typed parameters, and produces normalized output. A vague user request gradually converges into a precise file change, and the entire process remains predictable, reviewable, and composable.


Summary

The elegance of Grep + Glob is not merely that they let an AI search. It lies in how their signals are highly symmetrical yet deliberately different:

  • Glob relies on its industry-standard name for the core semantics, uses a minimal field set, and imposes almost no schema constraints. Its entire complexity is contained in one job: finding paths with glob syntax.
  • Grep has a much richer set of fields and flags, yet almost no numeric hard constraints. Instead, its defaults converge on the most context-efficient behavior.

Their most compelling symmetry is that both tools explicitly acknowledge their limits in their descriptions. When a task requires repeated rounds of globbing and grepping, they tell Claude to switch to Agent. The tools know what they are good at—and what they are not. That restraint is a sign of a mature tool ecosystem.

This reflects a core philosophy of Claude Code: do not give the AI one universal shell and ask it to improvise; turn each step into a primitive that is sufficient, safe, and composable.

The next article will examine Read: once Claude has the coordinates, how does it accurately perceive the current state of a file and establish the “perception commitment” that makes Edit and Write trustworthy?

Top comments (0)