Skip to main content
WarpGrep is a subagent your coding agent calls as a tool. The flow:
  1. Your agent (Claude, Codex, GPT-4o, Gemini) needs to find code
  2. It calls WarpGrep with a natural language query
  3. WarpGrep searches in its own isolated context window, multiple turns, 8 parallel tool calls per turn
  4. It returns only the relevant file/line-range spans
  5. Your agent continues with clean context
The parent agent never sees the intermediate search steps, the rejected files, or the dead-end greps. On SWE-Bench Pro, this makes Opus 15.6% cheaper and 28% faster than searching on its own. SDK adapters for Anthropic, OpenAI, Vercel AI SDK, and Gemini.
See complete agent examples for each framework — copy-paste ready.

Quick Start

import Anthropic from '@anthropic-ai/sdk';
import { MorphClient } from '@morphllm/morphsdk';

const morph = new MorphClient({ apiKey: process.env.MORPH_API_KEY });
const anthropic = new Anthropic();

// Create the tool
const warpGrepSubagent = morph.anthropic.createWarpGrepTool({ repoRoot: '.' });

// Use it with your agent
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250929',
  max_tokens: 12000,
  tools: [warpGrepSubagent],
  messages: [{ role: 'user', content: 'Find authentication middleware' }]
});

// Execute the tool call
const toolUse = response.content.find(c => c.type === 'tool_use');
if (toolUse) {
  const result = await warpGrepSubagent.execute(toolUse.input);
  console.log(warpGrepSubagent.formatResult(result));
}

Platform Examples

import { Sandbox } from "@e2b/code-interpreter";
import { MorphClient } from '@morphllm/morphsdk';

const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
const sandbox = await Sandbox.create();
const repoDir = "/home/user/repo";

// Clone repo and install ripgrep
await sandbox.commands.run(`git clone --depth 1 https://github.com/example/repo ${repoDir}`);
await sandbox.commands.run("apt-get update && apt-get install -y ripgrep");

const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
  repoRoot: repoDir,
  remoteCommands: {
    grep: async (pattern, path) => {
      const r = await sandbox.commands.run(
        `rg --no-heading --line-number '${pattern}' '${path}'`,
        { cwd: repoDir }
      );
      return r.stdout || '';
    },
    read: async (path, start, end) => {
      const r = await sandbox.commands.run(`sed -n '${start},${end}p' '${path}'`);
      return r.stdout || '';
    },
    listDir: async (path, maxDepth) => {
      const r = await sandbox.commands.run(
        `find '${path}' -maxdepth ${maxDepth} -not -path '*/node_modules/*'`
      );
      return r.stdout || '';
    },
  },
});

// Use with Anthropic
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250929',
  tools: [warpGrepSubagent],
  messages: [{ role: 'user', content: 'Find authentication middleware' }]
});
Your sandbox needs ripgrep (rg) installed for the grep function. Most sandbox providers support apt-get install ripgrep (Ubuntu/Debian). On Amazon Linux 2023 (Vercel Sandbox), download the static binary instead.

Configuration

const warpGrepSubagent = morph.openai.createWarpGrepTool({
  repoRoot: '.',
  excludes: ['dist', '*.test.ts'],
  includes: ['src/**/*.ts'],
});
OptionDefaultDescription
repoRoot(required)Root directory of the repository to search
excludes(see below)Glob patterns to exclude
includes(all files)Glob patterns to include (e.g., ['src/**/*.ts', 'lib/**/*.js'])
namecodebase_searchTool name exposed to the LLM
description(see SDK)Tool description for the LLM
remoteCommands(local)Functions for remote sandbox execution
morphApiUrlhttps://api.morphllm.comOverride API base URL
timeout30000Timeout in ms (also via MORPH_WARP_GREP_TIMEOUT env var)

Default Excludes

WarpGrep excludes common non-source directories by default:
  • Dependencies: node_modules, bower_components, .pnpm, .yarn, vendor, Pods, .bundle
  • Build output: dist, build, .next, .nuxt, out, target, .output
  • Python: __pycache__, .pytest_cache, .mypy_cache, .ruff_cache, .venv, venv, site-packages
  • Version control: .git, .svn, .hg
  • Lock files, minified files, source maps, and common binary formats
Pass excludes to override these defaults. Your list replaces the defaults entirely — it does not merge with them.

Searching node_modules

By default, node_modules is excluded. To search inside dependencies (e.g., debugging a library or finding how a package implements something), pass an empty excludes list:
const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
  repoRoot: '.',
  excludes: [],
});
WarpGrep runs faster with less context to search over. Only include node_modules in your search when you need it. See the node_modules example.

GitHub Search Options

createGitHubSearchTool() accepts:
OptionDefaultDescription
morphApiKeyMORPH_API_KEY env varAPI key for Morph
morphApiUrlhttps://api.morphllm.comOverride API base URL
codeSearchUrlhttps://morphllm.comCode storage service URL
timeout30000Timeout in ms
Search public GitHub repositories. No local clone or ripgrep needed — Morph indexes the repo remotely. See the GitHub search example.

Client API

const morph = new MorphClient({ apiKey: process.env.MORPH_API_KEY });

const result = await morph.warpGrep.searchGitHub({
  searchTerm: 'Find authentication middleware',
  github: 'vercel/next.js',       // or full URL: 'https://github.com/vercel/next.js'
  branch: 'canary',               // optional, defaults to repo's default branch
});

if (result.success) {
  for (const ctx of result.contexts) {
    console.log(`${ctx.file}: ${ctx.content}`);
  }
}

Agent Tool

Each SDK adapter provides createGitHubSearchTool():
// Same pattern as createWarpGrepTool — just swap the factory:
const githubTool = morph.anthropic.createGitHubSearchTool();
// Or: morph.openai.createGitHubSearchTool()
// Or: morph.vercel.createGitHubSearchTool()
Usage is identical to the Quick Start above — pass githubTool in your tools array.
GitHub search returns the same WarpGrepResult format as local search.

Remote Execution (Sandboxes)

When your code lives in a remote sandbox (E2B, Modal, Daytona, Docker), provide three functions that execute commands remotely. The SDK handles all parsing.
const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
  repoRoot: '/home/user/repo',
  remoteCommands: {
    grep: async (pattern, path, glob) => {
      const cmd = `rg --no-heading --line-number --color never -C 1 ${glob ? `--glob '${glob}'` : ''} '${pattern}' '${path}'`;
      const r = await sandbox.run(cmd);
      return r.stdout;
    },
    read: async (path, start, end) => {
      const r = await sandbox.run(`sed -n '${start},${end}p' '${path}'`);
      return r.stdout;
    },
    listDir: async (path, maxDepth) => {
      const r = await sandbox.run(`find '${path}' -maxdepth ${maxDepth}`);
      return r.stdout;
    },
  },
});
Replace sandbox.run(...) with your provider’s exec method (E2B’s sandbox.commands.run, Modal’s sandbox.exec, etc).
The SDK parses the raw output for you:
  • grep expects ripgrep format (path:line:content) with -C 1 context lines
  • read expects raw file content (SDK adds line numbers)
  • listDir expects one path per line (from find command)
Your sandbox needs ripgrep (rg) installed. Most providers support apt-get install ripgrep.

API Reference

Input (WarpGrepInput):
{
  searchTerm: string,         // Natural language search query
  repoRoot: string,           // Root directory to search
  excludes?: string[],        // Glob patterns to exclude
  includes?: string[],        // Glob patterns to include
  streamSteps?: boolean,      // Stream progress (see Streaming page)
  provider?: WarpGrepProvider, // Custom file system provider (see Custom Providers page)
  remoteCommands?: {          // For sandbox environments
    grep: (pattern, path, glob?) => Promise<string>,
    read: (path, start, end) => Promise<string>,
    listDir: (path, maxDepth) => Promise<string>,
  },
}
Returns (WarpGrepResult):
{
  success: boolean,
  contexts?: Array<{
    file: string,     // File path relative to repo root
    content: string,  // Relevant code section
  }>,
  summary?: string,   // Summary of findings
  error?: string,     // Error message if failed
}
Tool methods (when using as agent tool):
// Execute the tool with the LLM's input
const result = await warpGrepSubagent.execute(toolInput);

// Format the result as a string to send back to the LLM
// (converts the structured result into a readable text block)
const formatted = warpGrepSubagent.formatResult(result);

Error Handling

const result = await warpGrepSubagent.execute(toolUse.input);

if (!result.success) {
  console.error(result.error);
  // Common errors:
  // - "Search did not complete" — the model did not call finish within 4 turns
  // - "API error" — authentication or network issue
  // - "timeout" — search took longer than the configured timeout
}

Type Exports

import {
  warpGrepInputSchema,
  executeToolCall,
  WARP_GREP_TOOL_NAME,
  WARP_GREP_DESCRIPTION,
} from '@morphllm/morphsdk/tools/warp-grep';

import type {
  WarpGrepResult,
  WarpGrepContext,
  WarpGrepInput,
  WarpGrepToolConfig,
  GitHubSearchInput,
  GitHubSearchToolConfig,
  RemoteCommands,
} from '@morphllm/morphsdk';