@amical/aiagent-sdk 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -21,7 +21,8 @@ interface IAgentRuntime {
21
21
  * @param filePattern (可选) 文件名匹配模式,比如 '*.ts'
22
22
  * @returns 返回格式化后的包含上下文的搜索结果字符串
23
23
  */
24
- searchFiles?(path: string, regex: string, filePattern?: string): Promise<string>;
24
+ searchFiles?(path: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
25
+ searchFileContent?(path: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
25
26
  };
26
27
  path: {
27
28
  join(...paths: string[]): string;
@@ -59,6 +60,7 @@ declare enum AgentDefaultTool {
59
60
  FILE_DELETE = "agent_delete_file",
60
61
  DIR_DELETE = "agent_delete_directory",
61
62
  SEARCH = "agent_search_files",
63
+ SEARCH_CONTENT = "agent_search_file_content",
62
64
  LIST_FILES = "agent_list_files",
63
65
  LIST_CODE_DEF = "agent_list_code_definition_names",
64
66
  BROWSER = "agent_browser_action",
@@ -137,11 +139,21 @@ declare const defaultTools: readonly [{
137
139
  };
138
140
  }, {
139
141
  readonly name: AgentDefaultTool.SEARCH;
140
- readonly description: "Search for files in a directory that match a regex pattern or glob pattern.";
142
+ readonly description: "Search for files or directories by name in a directory tree, matching the name against a regex (and optionally a glob). Directories are traversed level by level: all matches in the current level are returned before descending into deeper levels. Use this tool only to locate files/directories by name.";
141
143
  readonly params: {
142
144
  readonly path: "(required) The absolute or relative directory path to search.";
143
- readonly regex: "(required) The regex pattern to match files against.";
145
+ readonly regex: "(required) The regex pattern to match file or directory names against.";
144
146
  readonly file_pattern: "(optional) glob pattern like *.ts.";
147
+ readonly include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped.";
148
+ };
149
+ }, {
150
+ readonly name: AgentDefaultTool.SEARCH_CONTENT;
151
+ readonly description: "Search the contents of files in a directory tree for a regex pattern. Use this tool when you need to find text or code inside files rather than matching file or directory names.";
152
+ readonly params: {
153
+ readonly path: "(required) The absolute or relative directory path to search.";
154
+ readonly regex: "(required) The regex pattern to match file content against.";
155
+ readonly file_pattern: "(optional) glob pattern like *.ts to limit which files are searched.";
156
+ readonly include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped.";
145
157
  };
146
158
  }, {
147
159
  readonly name: AgentDefaultTool.LIST_FILES;
@@ -166,7 +178,7 @@ declare const defaultTools: readonly [{
166
178
  declare const toolUseNames: AgentDefaultTool[];
167
179
  declare function setDynamicToolUseNames(namespace: string, names: string[]): void;
168
180
  declare function getToolUseNames(): string[];
169
- declare const READ_ONLY_TOOLS: readonly [AgentDefaultTool.LIST_FILES, AgentDefaultTool.FILE_READ, AgentDefaultTool.SEARCH, AgentDefaultTool.LIST_CODE_DEF, AgentDefaultTool.BROWSER, AgentDefaultTool.ASK, AgentDefaultTool.WEB_SEARCH, AgentDefaultTool.WEB_FETCH, AgentDefaultTool.LOAD_SKILL, AgentDefaultTool.USE_SUBAGENTS];
181
+ declare const READ_ONLY_TOOLS: readonly [AgentDefaultTool.LIST_FILES, AgentDefaultTool.FILE_READ, AgentDefaultTool.SEARCH, AgentDefaultTool.SEARCH_CONTENT, AgentDefaultTool.LIST_CODE_DEF, AgentDefaultTool.BROWSER, AgentDefaultTool.ASK, AgentDefaultTool.WEB_SEARCH, AgentDefaultTool.WEB_FETCH, AgentDefaultTool.LOAD_SKILL, AgentDefaultTool.USE_SUBAGENTS];
170
182
 
171
183
  /**
172
184
  * @description **Version 2**
@@ -198,7 +210,7 @@ interface TextStreamContent {
198
210
  content: string;
199
211
  partial: boolean;
200
212
  }
201
- declare const toolParamNames: readonly ["command", "requires_approval", "path", "absolutePath", "content", "diff", "regex", "file_pattern", "recursive", "action", "url", "coordinate", "text", "query", "allowed_domains", "blocked_domains", "prompt", "server_name", "tool_name", "arguments", "uri", "question", "options", "response", "result", "context", "title", "what_happened", "steps_to_reproduce", "api_request_output", "additional_context", "needs_more_exploration", "task_progress", "timeout", "input", "from_ref", "to_ref", "skill_name", "prompt_1", "prompt_2", "prompt_3", "prompt_4", "prompt_5", "start_line", "end_line"];
213
+ declare const toolParamNames: readonly ["command", "requires_approval", "path", "absolutePath", "content", "diff", "regex", "file_pattern", "include_node_modules", "recursive", "action", "url", "coordinate", "text", "query", "allowed_domains", "blocked_domains", "prompt", "server_name", "tool_name", "arguments", "uri", "question", "options", "response", "result", "context", "title", "what_happened", "steps_to_reproduce", "api_request_output", "additional_context", "needs_more_exploration", "task_progress", "timeout", "input", "from_ref", "to_ref", "skill_name", "prompt_1", "prompt_2", "prompt_3", "prompt_4", "prompt_5", "start_line", "end_line"];
202
214
  type ToolParamName = (typeof toolParamNames)[number];
203
215
  interface ToolUse {
204
216
  type: "tool_use";
@@ -484,7 +496,19 @@ declare class ClineAgent {
484
496
  resume(userInput?: string): Promise<void>;
485
497
  appendHistory(history: AgentMessage[]): Promise<void>;
486
498
  getHistory(): Promise<AgentMessage[]>;
487
- searchFiles(dirpath: string, regex: string, filePattern?: string): Promise<string>;
499
+ /**
500
+ * 判断文件是否为可进行内容搜索的文本类文件
501
+ */
502
+ private isTextFile;
503
+ /**
504
+ * 按文件名/目录名搜索,层级优先(BFS):
505
+ * 每一层先匹配完当前层级的所有条目,再进入更深的子目录。
506
+ */
507
+ searchFiles(dirpath: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
508
+ /**
509
+ * 按文件内容搜索:正则匹配文件内容,返回匹配文件路径。
510
+ */
511
+ searchFileContent(dirpath: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
488
512
  }
489
513
 
490
514
  declare const VERSION = "0.1.0";
package/dist/index.d.ts CHANGED
@@ -21,7 +21,8 @@ interface IAgentRuntime {
21
21
  * @param filePattern (可选) 文件名匹配模式,比如 '*.ts'
22
22
  * @returns 返回格式化后的包含上下文的搜索结果字符串
23
23
  */
24
- searchFiles?(path: string, regex: string, filePattern?: string): Promise<string>;
24
+ searchFiles?(path: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
25
+ searchFileContent?(path: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
25
26
  };
26
27
  path: {
27
28
  join(...paths: string[]): string;
@@ -59,6 +60,7 @@ declare enum AgentDefaultTool {
59
60
  FILE_DELETE = "agent_delete_file",
60
61
  DIR_DELETE = "agent_delete_directory",
61
62
  SEARCH = "agent_search_files",
63
+ SEARCH_CONTENT = "agent_search_file_content",
62
64
  LIST_FILES = "agent_list_files",
63
65
  LIST_CODE_DEF = "agent_list_code_definition_names",
64
66
  BROWSER = "agent_browser_action",
@@ -137,11 +139,21 @@ declare const defaultTools: readonly [{
137
139
  };
138
140
  }, {
139
141
  readonly name: AgentDefaultTool.SEARCH;
140
- readonly description: "Search for files in a directory that match a regex pattern or glob pattern.";
142
+ readonly description: "Search for files or directories by name in a directory tree, matching the name against a regex (and optionally a glob). Directories are traversed level by level: all matches in the current level are returned before descending into deeper levels. Use this tool only to locate files/directories by name.";
141
143
  readonly params: {
142
144
  readonly path: "(required) The absolute or relative directory path to search.";
143
- readonly regex: "(required) The regex pattern to match files against.";
145
+ readonly regex: "(required) The regex pattern to match file or directory names against.";
144
146
  readonly file_pattern: "(optional) glob pattern like *.ts.";
147
+ readonly include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped.";
148
+ };
149
+ }, {
150
+ readonly name: AgentDefaultTool.SEARCH_CONTENT;
151
+ readonly description: "Search the contents of files in a directory tree for a regex pattern. Use this tool when you need to find text or code inside files rather than matching file or directory names.";
152
+ readonly params: {
153
+ readonly path: "(required) The absolute or relative directory path to search.";
154
+ readonly regex: "(required) The regex pattern to match file content against.";
155
+ readonly file_pattern: "(optional) glob pattern like *.ts to limit which files are searched.";
156
+ readonly include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped.";
145
157
  };
146
158
  }, {
147
159
  readonly name: AgentDefaultTool.LIST_FILES;
@@ -166,7 +178,7 @@ declare const defaultTools: readonly [{
166
178
  declare const toolUseNames: AgentDefaultTool[];
167
179
  declare function setDynamicToolUseNames(namespace: string, names: string[]): void;
168
180
  declare function getToolUseNames(): string[];
169
- declare const READ_ONLY_TOOLS: readonly [AgentDefaultTool.LIST_FILES, AgentDefaultTool.FILE_READ, AgentDefaultTool.SEARCH, AgentDefaultTool.LIST_CODE_DEF, AgentDefaultTool.BROWSER, AgentDefaultTool.ASK, AgentDefaultTool.WEB_SEARCH, AgentDefaultTool.WEB_FETCH, AgentDefaultTool.LOAD_SKILL, AgentDefaultTool.USE_SUBAGENTS];
181
+ declare const READ_ONLY_TOOLS: readonly [AgentDefaultTool.LIST_FILES, AgentDefaultTool.FILE_READ, AgentDefaultTool.SEARCH, AgentDefaultTool.SEARCH_CONTENT, AgentDefaultTool.LIST_CODE_DEF, AgentDefaultTool.BROWSER, AgentDefaultTool.ASK, AgentDefaultTool.WEB_SEARCH, AgentDefaultTool.WEB_FETCH, AgentDefaultTool.LOAD_SKILL, AgentDefaultTool.USE_SUBAGENTS];
170
182
 
171
183
  /**
172
184
  * @description **Version 2**
@@ -198,7 +210,7 @@ interface TextStreamContent {
198
210
  content: string;
199
211
  partial: boolean;
200
212
  }
201
- declare const toolParamNames: readonly ["command", "requires_approval", "path", "absolutePath", "content", "diff", "regex", "file_pattern", "recursive", "action", "url", "coordinate", "text", "query", "allowed_domains", "blocked_domains", "prompt", "server_name", "tool_name", "arguments", "uri", "question", "options", "response", "result", "context", "title", "what_happened", "steps_to_reproduce", "api_request_output", "additional_context", "needs_more_exploration", "task_progress", "timeout", "input", "from_ref", "to_ref", "skill_name", "prompt_1", "prompt_2", "prompt_3", "prompt_4", "prompt_5", "start_line", "end_line"];
213
+ declare const toolParamNames: readonly ["command", "requires_approval", "path", "absolutePath", "content", "diff", "regex", "file_pattern", "include_node_modules", "recursive", "action", "url", "coordinate", "text", "query", "allowed_domains", "blocked_domains", "prompt", "server_name", "tool_name", "arguments", "uri", "question", "options", "response", "result", "context", "title", "what_happened", "steps_to_reproduce", "api_request_output", "additional_context", "needs_more_exploration", "task_progress", "timeout", "input", "from_ref", "to_ref", "skill_name", "prompt_1", "prompt_2", "prompt_3", "prompt_4", "prompt_5", "start_line", "end_line"];
202
214
  type ToolParamName = (typeof toolParamNames)[number];
203
215
  interface ToolUse {
204
216
  type: "tool_use";
@@ -484,7 +496,19 @@ declare class ClineAgent {
484
496
  resume(userInput?: string): Promise<void>;
485
497
  appendHistory(history: AgentMessage[]): Promise<void>;
486
498
  getHistory(): Promise<AgentMessage[]>;
487
- searchFiles(dirpath: string, regex: string, filePattern?: string): Promise<string>;
499
+ /**
500
+ * 判断文件是否为可进行内容搜索的文本类文件
501
+ */
502
+ private isTextFile;
503
+ /**
504
+ * 按文件名/目录名搜索,层级优先(BFS):
505
+ * 每一层先匹配完当前层级的所有条目,再进入更深的子目录。
506
+ */
507
+ searchFiles(dirpath: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
508
+ /**
509
+ * 按文件内容搜索:正则匹配文件内容,返回匹配文件路径。
510
+ */
511
+ searchFileContent(dirpath: string, regex: string, filePattern?: string, includeNodeModules?: boolean): Promise<string>;
488
512
  }
489
513
 
490
514
  declare const VERSION = "0.1.0";
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ var AgentDefaultTool = /* @__PURE__ */ ((AgentDefaultTool2) => {
18
18
  AgentDefaultTool2["FILE_DELETE"] = "agent_delete_file";
19
19
  AgentDefaultTool2["DIR_DELETE"] = "agent_delete_directory";
20
20
  AgentDefaultTool2["SEARCH"] = "agent_search_files";
21
+ AgentDefaultTool2["SEARCH_CONTENT"] = "agent_search_file_content";
21
22
  AgentDefaultTool2["LIST_FILES"] = "agent_list_files";
22
23
  AgentDefaultTool2["LIST_CODE_DEF"] = "agent_list_code_definition_names";
23
24
  AgentDefaultTool2["BROWSER"] = "agent_browser_action";
@@ -191,11 +192,22 @@ return (
191
192
  },
192
193
  {
193
194
  name: "agent_search_files" /* SEARCH */,
194
- description: "Search for files in a directory that match a regex pattern or glob pattern.",
195
+ description: "Search for files or directories by name in a directory tree, matching the name against a regex (and optionally a glob). Directories are traversed level by level: all matches in the current level are returned before descending into deeper levels. Use this tool only to locate files/directories by name.",
195
196
  params: {
196
197
  path: "(required) The absolute or relative directory path to search.",
197
- regex: "(required) The regex pattern to match files against.",
198
- file_pattern: "(optional) glob pattern like *.ts."
198
+ regex: "(required) The regex pattern to match file or directory names against.",
199
+ file_pattern: "(optional) glob pattern like *.ts.",
200
+ include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped."
201
+ }
202
+ },
203
+ {
204
+ name: "agent_search_file_content" /* SEARCH_CONTENT */,
205
+ description: "Search the contents of files in a directory tree for a regex pattern. Use this tool when you need to find text or code inside files rather than matching file or directory names.",
206
+ params: {
207
+ path: "(required) The absolute or relative directory path to search.",
208
+ regex: "(required) The regex pattern to match file content against.",
209
+ file_pattern: "(optional) glob pattern like *.ts to limit which files are searched.",
210
+ include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped."
199
211
  }
200
212
  },
201
213
  {
@@ -264,6 +276,7 @@ var READ_ONLY_TOOLS = [
264
276
  "agent_list_files" /* LIST_FILES */,
265
277
  "agent_read_file" /* FILE_READ */,
266
278
  "agent_search_files" /* SEARCH */,
279
+ "agent_search_file_content" /* SEARCH_CONTENT */,
267
280
  "agent_list_code_definition_names" /* LIST_CODE_DEF */,
268
281
  "agent_browser_action" /* BROWSER */,
269
282
  "agent_ask_followup_question" /* ASK */,
@@ -433,6 +446,7 @@ var toolParamNames = [
433
446
  "diff",
434
447
  "regex",
435
448
  "file_pattern",
449
+ "include_node_modules",
436
450
  "recursive",
437
451
  "action",
438
452
  "url",
@@ -471,6 +485,66 @@ var toolParamNames = [
471
485
  "start_line",
472
486
  "end_line"
473
487
  ];
488
+ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
489
+ "txt",
490
+ "md",
491
+ "markdown",
492
+ "json",
493
+ "js",
494
+ "mjs",
495
+ "cjs",
496
+ "ts",
497
+ "tsx",
498
+ "jsx",
499
+ "html",
500
+ "htm",
501
+ "css",
502
+ "scss",
503
+ "sass",
504
+ "less",
505
+ "py",
506
+ "java",
507
+ "c",
508
+ "cpp",
509
+ "cc",
510
+ "cxx",
511
+ "h",
512
+ "hpp",
513
+ "cs",
514
+ "go",
515
+ "rs",
516
+ "rb",
517
+ "php",
518
+ "sh",
519
+ "bash",
520
+ "zsh",
521
+ "bat",
522
+ "cmd",
523
+ "ps1",
524
+ "yaml",
525
+ "yml",
526
+ "toml",
527
+ "ini",
528
+ "cfg",
529
+ "conf",
530
+ "xml",
531
+ "svg",
532
+ "csv",
533
+ "log",
534
+ "sql",
535
+ "vue",
536
+ "svelte",
537
+ "astro",
538
+ "graphql",
539
+ "gql",
540
+ "prisma",
541
+ "env",
542
+ "gitignore",
543
+ "dockerfile",
544
+ "makefile",
545
+ "properties",
546
+ "lock"
547
+ ]);
474
548
  var AgentState = /* @__PURE__ */ ((AgentState2) => {
475
549
  AgentState2["IDLE"] = "idle";
476
550
  AgentState2["THINKING"] = "thinking";
@@ -826,15 +900,36 @@ STDERR: ${result.stderr}` });
826
900
  const path = tool.params.path || this.runtime.env.cwd();
827
901
  const regexStr = tool.params.regex;
828
902
  const filePattern = tool.params.file_pattern;
903
+ const includeNodeModules = tool.params.include_node_modules === "true";
829
904
  if (regexStr && path) {
830
905
  try {
831
906
  let result;
832
907
  if (typeof this.runtime.fs.searchFiles === "function") {
833
908
  result = await this.runtime.fs.searchFiles(path, regexStr, filePattern);
834
909
  } else {
835
- result = await this.searchFiles(path, regexStr, filePattern);
910
+ result = await this.searchFiles(path, regexStr, filePattern, includeNodeModules);
911
+ }
912
+ toolResults.push({ tool, result });
913
+ } catch (e) {
914
+ toolResults.push({ tool, result: `Error: ${e.message}` });
915
+ }
916
+ } else {
917
+ toolResults.push({ tool, result: `Error: miss path or regex params` });
918
+ }
919
+ } else if (tool.name === "agent_search_file_content" /* SEARCH_CONTENT */) {
920
+ const path = tool.params.path || this.runtime.env.cwd();
921
+ const regexStr = tool.params.regex;
922
+ const filePattern = tool.params.file_pattern;
923
+ const includeNodeModules = tool.params.include_node_modules === "true";
924
+ if (regexStr && path) {
925
+ try {
926
+ let result;
927
+ if (typeof this.runtime.fs.searchFileContent === "function") {
928
+ result = await this.runtime.fs.searchFileContent(path, regexStr, filePattern);
929
+ } else {
930
+ result = await this.searchFileContent(path, regexStr, filePattern, includeNodeModules);
836
931
  }
837
- toolResults.push({ tool, result: `${result}` });
932
+ toolResults.push({ tool, result });
838
933
  } catch (e) {
839
934
  toolResults.push({ tool, result: `Error: ${e.message}` });
840
935
  }
@@ -1009,13 +1104,80 @@ ${searchBlock.substring(0, 100)}...`);
1009
1104
  async getHistory() {
1010
1105
  return this.memory.getHistory();
1011
1106
  }
1012
- async searchFiles(dirpath, regex, filePattern) {
1107
+ /**
1108
+ * 判断文件是否为可进行内容搜索的文本类文件
1109
+ */
1110
+ isTextFile(name) {
1111
+ const ext = name.includes(".") ? String(name.split(".").pop()).toLowerCase() : "";
1112
+ if (!ext) {
1113
+ return true;
1114
+ }
1115
+ return TEXT_FILE_EXTENSIONS.has(ext);
1116
+ }
1117
+ /**
1118
+ * 按文件名/目录名搜索,层级优先(BFS):
1119
+ * 每一层先匹配完当前层级的所有条目,再进入更深的子目录。
1120
+ */
1121
+ async searchFiles(dirpath, regex, filePattern, includeNodeModules = false) {
1013
1122
  const matches = [];
1014
- const contentRe = new RegExp(regex);
1015
- const nameRe = filePattern ? new RegExp(filePattern.replace(/\./g, "\\.").replace(/\*/g, ".*")) : null;
1123
+ const MAX_RESULTS = 100;
1124
+ const MAX_DEPTH = 8;
1016
1125
  const SKIP_DIRS = /* @__PURE__ */ new Set([".git", "dist", ".agent", ".turbopack"]);
1126
+ const shouldSkipDir = (name) => SKIP_DIRS.has(name) || name === "node_modules" && !includeNodeModules;
1127
+ const nameRe = new RegExp(regex);
1128
+ const globRe = filePattern ? new RegExp(filePattern.replace(/\./g, "\\.").replace(/\*/g, ".*")) : null;
1129
+ const queue = [{ dir: dirpath, depth: 0 }];
1130
+ while (queue.length > 0 && matches.length < MAX_RESULTS) {
1131
+ const { dir, depth } = queue.shift();
1132
+ if (depth > MAX_DEPTH) {
1133
+ continue;
1134
+ }
1135
+ let entries;
1136
+ try {
1137
+ entries = await this.runtime.fs.readDir(dir, { recursive: false });
1138
+ } catch {
1139
+ continue;
1140
+ }
1141
+ for (const entry of entries) {
1142
+ if (matches.length >= MAX_RESULTS) {
1143
+ break;
1144
+ }
1145
+ const child = dir === "." ? entry.name : `${dir}/${entry.name}`;
1146
+ if (entry.isDirectory()) {
1147
+ if (!shouldSkipDir(entry.name)) {
1148
+ if (nameRe.test(entry.name)) {
1149
+ matches.push(`${child}/`);
1150
+ }
1151
+ queue.push({ dir: child, depth: depth + 1 });
1152
+ }
1153
+ } else if (entry.isFile()) {
1154
+ if (globRe && !globRe.test(entry.name)) {
1155
+ continue;
1156
+ }
1157
+ if (nameRe.test(entry.name)) {
1158
+ matches.push(child);
1159
+ }
1160
+ }
1161
+ }
1162
+ }
1163
+ if (matches.length === 0) {
1164
+ return "No files found.";
1165
+ }
1166
+ return matches.join("\n");
1167
+ }
1168
+ /**
1169
+ * 按文件内容搜索:正则匹配文件内容,返回匹配文件路径。
1170
+ */
1171
+ async searchFileContent(dirpath, regex, filePattern, includeNodeModules = false) {
1172
+ const matches = [];
1173
+ const MAX_RESULTS = 100;
1174
+ const MAX_DEPTH = 8;
1175
+ const SKIP_DIRS = /* @__PURE__ */ new Set([".git", "dist", ".agent", ".turbopack"]);
1176
+ const shouldSkipDir = (name) => SKIP_DIRS.has(name) || name === "node_modules" && !includeNodeModules;
1177
+ const contentRe = new RegExp(regex);
1178
+ const globRe = filePattern ? new RegExp(filePattern.replace(/\./g, "\\.").replace(/\*/g, ".*")) : null;
1017
1179
  const walk = async (dir, depth) => {
1018
- if (depth > 8 || matches.length >= 100) {
1180
+ if (depth > MAX_DEPTH || matches.length >= MAX_RESULTS) {
1019
1181
  return;
1020
1182
  }
1021
1183
  let entries;
@@ -1025,21 +1187,24 @@ ${searchBlock.substring(0, 100)}...`);
1025
1187
  return;
1026
1188
  }
1027
1189
  for (const entry of entries) {
1028
- if (matches.length >= 100) {
1190
+ if (matches.length >= MAX_RESULTS) {
1029
1191
  break;
1030
1192
  }
1031
1193
  const child = dir === "." ? entry.name : `${dir}/${entry.name}`;
1032
1194
  if (entry.isDirectory()) {
1033
- if (!SKIP_DIRS.has(entry.name)) {
1195
+ if (!shouldSkipDir(entry.name)) {
1034
1196
  await walk(child, depth + 1);
1035
1197
  }
1036
1198
  } else if (entry.isFile()) {
1037
- if (nameRe && !nameRe.test(entry.name)) {
1199
+ if (!this.isTextFile(entry.name)) {
1200
+ continue;
1201
+ }
1202
+ if (globRe && !globRe.test(entry.name)) {
1038
1203
  continue;
1039
1204
  }
1040
1205
  try {
1041
- const content = (await this.runtime.fs.readFile(child, "utf8")).toString();
1042
- if (contentRe.test(content)) {
1206
+ const content = await this.runtime.fs.readFile(child);
1207
+ if (typeof content === "string" && contentRe.test(content)) {
1043
1208
  matches.push(child);
1044
1209
  }
1045
1210
  } catch {
@@ -1048,6 +1213,9 @@ ${searchBlock.substring(0, 100)}...`);
1048
1213
  }
1049
1214
  };
1050
1215
  await walk(dirpath, 0);
1216
+ if (matches.length === 0) {
1217
+ return "No files found with matching content.";
1218
+ }
1051
1219
  return matches.join("\n");
1052
1220
  }
1053
1221
  };