> ## Documentation Index
> Fetch the complete documentation index at: https://code.storage/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# grep()

> Search for patterns within repository content using regular expressions.

<Badge color="green">Beta</Badge>

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  // Basic pattern search
  const results = await repo.grep({
    query: { pattern: 'TODO' },
  });

  console.log(`Found ${results.matches.length} files with matches`);
  results.matches.forEach((match) => {
    console.log(`${match.path}:`);
    match.lines.forEach((line) => {
      if (line.type === 'match') {
        console.log(`  ${line.lineNumber}: ${line.text}`);
      }
    });
  });

  // Search inside an ephemeral ref
  const previewResults = await repo.grep({
    ref: 'preview/pr-123',
    ephemeral: true,
    query: { pattern: 'TODO' },
  });

  // Advanced search with filtering and context
  const advancedResults = await repo.grep({
    ref: 'main',
    query: {
      pattern: 'function\\s+\\w+Error',
      caseSensitive: true,
    },
    paths: ['src/'],
    fileFilters: {
      includeGlobs: ['**/*.js', '**/*.ts'],
      excludeGlobs: ['**/node_modules/**', '**/*.test.js'],
    },
    context: { before: 2, after: 2 },
    limits: { maxLines: 200, maxMatchesPerFile: 10 },
    pagination: { limit: 50 },
  });

  // Handle pagination
  let cursor = advancedResults.nextCursor;
  while (cursor && advancedResults.hasMore) {
    const moreResults = await repo.grep({
      query: { pattern: 'TODO' },
      pagination: { cursor, limit: 50 },
    });
    console.log('Next page:', moreResults.matches);
    cursor = moreResults.nextCursor;
  }

  // Paging is done, but the search itself may have hit the server cap
  if (advancedResults.incompleteResults) {
    console.log('Results capped at maxLines, narrow the search to see more');
  }
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  # Basic pattern search
  results = await repo.grep(pattern="TODO")

  print(f"Found {len(results['matches'])} files with matches")
  for match in results['matches']:
      print(f"{match['path']}:")
      for line in match['lines']:
          if line['type'] == 'match':
              print(f"  {line['line_number']}: {line['text']}")

  # Search inside an ephemeral ref
  preview_results = await repo.grep(
      pattern="TODO",
      ref="preview/pr-123",
      ephemeral=True,
  )

  # Advanced search with all options
  advanced_results = await repo.grep(
      pattern=r"function\s+\w+Error",
      ref="main",
      paths=["src/"],
      case_sensitive=True,
      file_filters={
          "include_globs": ["**/*.py", "**/*.js"],
          "exclude_globs": ["**/tests/**", "**/node_modules/**"],
          "extension_filters": [".py", ".js", ".ts"]
      },
      context={"before": 2, "after": 2},
      limits={"max_lines": 200, "max_matches_per_file": 10},
      pagination={"limit": 50}
  )

  # Handle pagination
  cursor = advanced_results.get('next_cursor')
  while cursor and advanced_results['has_more']:
      more_results = await repo.grep(
          pattern="TODO",
          pagination={"cursor": cursor, "limit": 50}
      )
      print('Next page:', more_results['matches'])
      cursor = more_results.get('next_cursor')

  # Paging is done, but the search itself may have hit the server cap
  if advanced_results['incomplete_results']:
      print('Results capped at max_lines, narrow the search to see more')
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  // Basic pattern search
  results, err := repo.Grep(context.Background(), storage.GrepOptions{
  	Query: storage.GrepQuery{Pattern: "TODO"},
  })
  fmt.Printf("Found %d files with matches", len(results.Matches))

  // Search inside an ephemeral ref
  ephemeral := true
  preview, err := repo.Grep(context.Background(), storage.GrepOptions{
  	Ref:       "preview/pr-123",
  	Ephemeral: &ephemeral,
  	Query:     storage.GrepQuery{Pattern: "TODO"},
  })

  caseSensitive := true
  before := 2
  after := 2
  maxLines := 200
  maxMatches := 10
  pageLimit := 50

  // Advanced search with filters
  advanced, err := repo.Grep(context.Background(), storage.GrepOptions{
  	Ref: "main",
  	Query: storage.GrepQuery{
  		Pattern:       `function\s+\w+Error`,
  		CaseSensitive: &caseSensitive,
  	},
  	Paths: []string{"src/"},
  	FileFilters: &storage.GrepFileFilters{
  		IncludeGlobs:     []string{"**/*.go", "**/*.ts"},
  		ExcludeGlobs:     []string{"**/node_modules/**", "**/*.test.ts"},
  		ExtensionFilters: []string{".go", ".ts"},
  	},
  	Context: &storage.GrepContext{Before: &before, After: &after},
  	Limits:  &storage.GrepLimits{MaxLines: &maxLines, MaxMatchesPerFile: &maxMatches},
  	Pagination: &storage.GrepPagination{Limit: &pageLimit},
  })

  // Handle pagination
  cursor := advanced.NextCursor
  for cursor != "" && advanced.HasMore {
  	moreLimit := 50
  	more, err := repo.Grep(context.Background(), storage.GrepOptions{
  		Query:      storage.GrepQuery{Pattern: "TODO"},
  		Pagination: &storage.GrepPagination{Cursor: cursor, Limit: &moreLimit},
  	})
  	fmt.Println("Next page:", more.Matches)
  	cursor = more.NextCursor
  }

  // Paging is done, but the search itself may have hit the server cap
  if advanced.IncompleteResults {
  	fmt.Println("Results capped at MaxLines, narrow the search to see more")
  }
  ```
</CodeGroup>

## Options

<ParamField path="query.pattern" type="string" required>
  Regular expression pattern to search for
</ParamField>

<ParamField path="query.caseSensitive" type="string">
  Whether search should be case sensitive (default: true)
</ParamField>

<ParamField path="ref" type="string">
  Git reference to search in (defaults to repository's default branch)
</ParamField>

<ParamField path="ephemeral" type="boolean">
  Resolve `ref` against the ephemeral namespace instead of the persistent one (default: `false`)
</ParamField>

<ParamField path="paths" type="string">
  Array/List of Git pathspecs to limit search scope
</ParamField>

<ParamField path="fileFilters.includeGlobs" type="string">
  Glob patterns for files to include
</ParamField>

<ParamField path="fileFilters.excludeGlobs" type="string">
  Glob patterns for files to exclude
</ParamField>

<ParamField path="fileFilters.extensionFilters" type="string">
  File extensions to filter by (e.g., `['.js', '.py']`)
</ParamField>

<ParamField path="context.before" type="string">
  Number of lines to include before each match
</ParamField>

<ParamField path="context.after" type="string">
  Number of lines to include after each match
</ParamField>

<ParamField path="limits.maxLines" type="string">
  Maximum total output lines the server will collect for a search (default: 2000, max: 2000 - the
  default is already the maximum). You can lower it for cheap previews, but you cannot raise it. If
  a search produces more lines than this cap, the result is capped and the response sets
  `incomplete_results` to `true`.
</ParamField>

<ParamField path="limits.maxMatchesPerFile" type="string">
  Maximum matches per file (default: 200)
</ParamField>

<ParamField path="pagination.cursor" type="string">
  Pagination cursor from previous response
</ParamField>

<ParamField path="pagination.limit" type="string">
  Maximum results per page (default: 200)
</ParamField>

<ParamField path="ttl" type="string">
  Token TTL. Token TTL in seconds.
</ParamField>

## Response

The grep response includes:

* `query`: Echo of search parameters with resolved defaults
* `repo`: Information about the searched ref and commit
* `matches`: Array of files with matching lines
  * Each match contains `path` and `lines` array
  * **TypeScript**: Lines include `lineNumber`, `text`, and `type` (`'match'` or `'context'`)
  * **Python**: Lines include `line_number`, `text`, and `type` (`'match'` or `'context'`)
* **TypeScript**: `hasMore` and `nextCursor` for pagination, `incompleteResults` for the search cap
* **Python**: `has_more` and `next_cursor` for pagination, `incomplete_results` for the search cap

## Incomplete results vs. pagination

Two different fields report two different things:

* `has_more` is about **pagination**. The server collected more matches than fit in this page. Keep
  requesting with `next_cursor` until `has_more` is `false`. you will receive everything the server
  collected.
* `incomplete_results` is about the **search cap** from raw output of `git grep`. The search
  produced more than `limits.maxLines` output lines (default and maximum: 2000), so the server
  stopped collecting at the cap. Matches beyond the cap exist in the repository but **cannot be
  retrieved by paging**.

The two combine independently: a capped result can still span several pages
(`has_more: true, incomplete_results: true`), and paging it to the end (`has_more: false`) does not
necessarily make it complete.

When `incomplete_results` is `true`, narrow the search instead of paging harder: use a more specific
`pattern`, restrict `paths`, or add `fileFilters`.
