package com.claudecode.tool.impl; import com.claudecode.tool.Tool; import com.claudecode.tool.ToolContext; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; /** * Grep 搜索工具 —— 对应 claude-code/src/tools/grep/GrepTool.ts。 *
* 在文件中搜索文本模式(正则),优先使用 ripgrep(rg),降级为系统 grep。
* 支持多种输出模式、大小写、上下文行、多行匹配等参数。
*/
public class GrepTool implements Tool {
private static final int DEFAULT_MAX_RESULTS = 100;
@Override
public String name() {
return "Grep";
}
@Override
public String description() {
return """
A powerful search tool built on ripgrep. Searches for a regex pattern in file contents \
and returns matching lines with file paths and line numbers.
IMPORTANT: ALWAYS use this Grep tool for searching file content. NEVER invoke `grep`, \
`rg`, or `findstr` as a Bash command — using this dedicated tool allows the user to \
better understand and review your searches.
Uses ripgrep (rg) if available, falls back to system grep/findstr. Supports full regex \
syntax. Pattern syntax uses ripgrep — literal braces need escaping (e.g., interface\\{\\}).
Output modes:
- "content": Shows matching lines with context (default). Supports context flags.
- "files_with_matches": Shows only file paths containing matches. Use for broad discovery.
- "count": Shows match counts per file.
When doing open-ended searches requiring multiple rounds, use the Agent tool instead.""";
}
@Override
public String inputSchema() {
return """
{
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "Regular expression pattern to search for"
},
"path": {
"type": "string",
"description": "Directory or file to search in (default: working directory)"
},
"include": {
"type": "string",
"description": "File glob pattern to include (e.g., '*.java')"
},
"type": {
"type": "string",
"description": "File type filter (e.g., 'java', 'py', 'ts', 'js'). Only works with ripgrep."
},
"output_mode": {
"type": "string",
"enum": ["content", "files_with_matches", "count"],
"description": "Output format. 'content' shows matching lines (default), 'files_with_matches' shows only file paths, 'count' shows match counts per file."
},
"case_insensitive": {
"type": "boolean",
"description": "Case insensitive search (default: false)"
},
"multiline": {
"type": "boolean",
"description": "Enable multiline mode where patterns can span lines (default: false)"
},
"context_before": {
"type": "integer",
"description": "Lines of context before each match"
},
"context_after": {
"type": "integer",
"description": "Lines of context after each match"
},
"head_limit": {
"type": "integer",
"description": "Limit output to first N results"
}
},
"required": ["pattern"]
}""";
}
@Override
public boolean isReadOnly() {
return true;
}
@Override
public String execute(Map