MCPcopy Create free account
hub / github.com/promptfoo/promptfoo / grepFiles

Function grepFiles

src/util/agent/fsOperations.ts:116–166  ·  view source on GitHub ↗
(
  pattern: string,
  rootDir: string,
  options?: { path?: string; include?: string },
)

Source from the content-addressed store, hash-verified

114 * Returns up to MAX_GREP_MATCHES results.
115 */
116export async function grepFiles(
117 pattern: string,
118 rootDir: string,
119 options?: { path?: string; include?: string },
120): Promise<{
121 matches: Array<{ file: string; line: number; content: string }>;
122 truncated: boolean;
123}> {
124 const searchDir = options?.path ? await resolveAndValidate(options.path, rootDir) : rootDir;
125
126 // Build grep command
127 const args = ['-rn', '--binary-files=without-match'];
128 if (options?.include) {
129 args.push(`--include=${options.include}`);
130 }
131 // Cap output per-file
132 args.push('-m', String(MAX_GREP_MATCHES));
133 args.push('--', pattern, searchDir);
134
135 try {
136 const output = execSync(`grep ${args.map(shellEscape).join(' ')}`, {
137 encoding: 'utf-8',
138 maxBuffer: 1024 * 1024, // 1MB
139 timeout: 30_000,
140 });
141
142 const lines = output.trim().split('\n').filter(Boolean);
143
144 const allMatches = lines.map((line) => {
145 // grep -n format: file:line:content
146 const firstColon = line.indexOf(':');
147 const secondColon = line.indexOf(':', firstColon + 1);
148 const file = path.relative(rootDir, line.slice(0, firstColon));
149 const lineNum = parseInt(line.slice(firstColon + 1, secondColon), 10);
150 const content = line.slice(secondColon + 1).slice(0, 200); // cap line length
151 return { file, line: lineNum, content };
152 });
153
154 // Enforce global cap — grep -m only limits per-file
155 const truncated = allMatches.length >= MAX_GREP_MATCHES;
156 const matches = allMatches.slice(0, MAX_GREP_MATCHES);
157
158 return { matches, truncated };
159 } catch (error) {
160 // grep exits 1 when no matches found — that's not an error
161 if (error && typeof error === 'object' && 'status' in error && error.status === 1) {
162 return { matches: [], truncated: false };
163 }
164 throw error;
165 }
166}
167
168function shellEscape(arg: string): string {
169 return `'${arg.replace(/'/g, "'\\''")}'`;

Callers 1

attachTargetLinkFsFunction · 0.90

Calls 3

resolveAndValidateFunction · 0.85
pushMethod · 0.80
splitMethod · 0.80

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…