@saber2pr/ai-agent 0.0.46 → 0.0.47

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.
@@ -10,6 +10,16 @@ const path_1 = __importDefault(require("path"));
10
10
  const zod_1 = require("zod");
11
11
  const createTool_1 = require("../../utils/createTool");
12
12
  const lib_1 = require("./lib");
13
+ const DEFAULT_IGNORE = [
14
+ 'node_modules/**',
15
+ '.git/**',
16
+ 'dist/**',
17
+ 'build/**',
18
+ '.next/**',
19
+ 'out/**',
20
+ '*.log',
21
+ '.DS_Store'
22
+ ];
13
23
  // Schema definitions
14
24
  const ReadTextFileArgsSchema = zod_1.z.object({
15
25
  path: zod_1.z.string(),
@@ -215,6 +225,8 @@ const getFilesystemTools = (targetDir) => {
215
225
  '默认仅展示 2 层深度以节省 Token。如果需要看更深层级,请调大 depth 参数。',
216
226
  parameters: DirectoryTreeArgsSchema,
217
227
  handler: async (args) => {
228
+ // 在 directory_tree 的 handler 内部
229
+ const combinedExcludes = [...DEFAULT_IGNORE, ...(args.excludePatterns || [])];
218
230
  const rootPath = args.path;
219
231
  async function buildTree(currentPath, currentDepth, maxDepth, excludePatterns = []) {
220
232
  if (currentDepth > maxDepth)
@@ -239,7 +251,7 @@ const getFilesystemTools = (targetDir) => {
239
251
  }
240
252
  return result;
241
253
  }
242
- const treeData = await buildTree(rootPath, 1, args.depth, args.excludePatterns);
254
+ const treeData = await buildTree(rootPath, 1, args.depth, combinedExcludes);
243
255
  return JSON.stringify(treeData, null, 2);
244
256
  },
245
257
  });
@@ -265,9 +277,10 @@ const getFilesystemTools = (targetDir) => {
265
277
  'Used only for filename search',
266
278
  parameters: SearchFilesArgsSchema,
267
279
  handler: async (args) => {
280
+ const combinedExcludes = [...DEFAULT_IGNORE, ...(args.excludePatterns || [])];
268
281
  const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
269
282
  const results = await (0, lib_1.searchFilesWithValidation)(targetDir, validPath, args.pattern, [targetDir], {
270
- excludePatterns: ['node_modules', ...((args === null || args === void 0 ? void 0 : args.excludePatterns) || [])],
283
+ excludePatterns: combinedExcludes,
271
284
  });
272
285
  const text = results.length > 0 ? results.join('\n') : 'No matches found';
273
286
  return text;
@@ -315,6 +328,9 @@ const getFilesystemTools = (targetDir) => {
315
328
  const stats = await promises_1.default.stat(filePath);
316
329
  if (!stats.isFile())
317
330
  return;
331
+ // 在 grep_search 扫描文件时增加判断
332
+ if ((0, lib_1.isBinaryOrIrrelevant)(path_1.default.extname(filePath)))
333
+ return;
318
334
  const content = await (0, lib_1.readFileContent)(filePath);
319
335
  if (content.includes(args.query)) {
320
336
  const relativePath = path_1.default.relative(targetDir, filePath);
@@ -31,4 +31,5 @@ export declare function applyFileEdits(filePath: string, edits: FileEdit[], dryR
31
31
  export declare function tailFile(filePath: string, numLines: number): Promise<string>;
32
32
  export declare function headFile(filePath: string, numLines: number): Promise<string>;
33
33
  export declare function searchFilesWithValidation(cwd: string, rootPath: string, pattern: string, allowedDirectories: string[], options?: SearchOptions): Promise<string[]>;
34
+ export declare const isBinaryOrIrrelevant: (filePath: string) => boolean;
34
35
  export {};
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isBinaryOrIrrelevant = void 0;
6
7
  exports.setAllowedDirectories = setAllowedDirectories;
7
8
  exports.getAllowedDirectories = getAllowedDirectories;
8
9
  exports.formatSize = formatSize;
@@ -115,7 +116,7 @@ async function writeFileContent(filePath, content) {
115
116
  try {
116
117
  // Security: 'wx' flag ensures exclusive creation - fails if file/symlink exists,
117
118
  // preventing writes through pre-existing symlinks
118
- await promises_1.default.writeFile(filePath, content, { encoding: "utf-8", flag: 'wx' });
119
+ await promises_1.default.writeFile(filePath, content, { encoding: 'utf-8', flag: 'wx' });
119
120
  }
120
121
  catch (error) {
121
122
  if (error.code === 'EEXIST') {
@@ -230,7 +231,7 @@ async function tailFile(filePath, numLines) {
230
231
  try {
231
232
  const lines = [];
232
233
  let position = fileSize;
233
- let chunk = Buffer.alloc(CHUNK_SIZE);
234
+ const chunk = Buffer.alloc(CHUNK_SIZE);
234
235
  let linesFound = 0;
235
236
  let remainingText = '';
236
237
  // Read chunks from the end of the file until we have enough lines
@@ -328,3 +329,34 @@ async function searchFilesWithValidation(cwd, rootPath, pattern, allowedDirector
328
329
  await search(rootPath);
329
330
  return results;
330
331
  }
332
+ const isBinaryOrIrrelevant = (filePath) => {
333
+ const ext = path_1.default.extname(filePath).toLowerCase();
334
+ // 1. 常见的二进制媒体/资源格式
335
+ const binaryExtensions = new Set([
336
+ // 图片
337
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.ico', '.tiff',
338
+ // 字体
339
+ '.ttf', '.otf', '.woff', '.woff2', '.eot',
340
+ // 压缩包
341
+ '.zip', '.tar', '.gz', '.7z', '.rar',
342
+ // 编译产物/执行文件
343
+ '.exe', '.dll', '.so', '.dylib', '.bin', '.obj', '.pyc', '.pyo', '.class',
344
+ // 音视频
345
+ '.mp3', '.mp4', '.mov', '.wav', '.flv', '.wmv',
346
+ // 文档 (虽然是文档,但通常是二进制格式)
347
+ '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx'
348
+ ]);
349
+ if (binaryExtensions.has(ext))
350
+ return true;
351
+ // 2. 干扰性极强的非二进制文件 (Source Maps 等)
352
+ const noiseExtensions = new Set([
353
+ '.map', // 源代码映射文件,极长且无意义
354
+ '.lock', // package-lock.json, yarn.lock 等锁定文件 (可选,如果 AI 不需要分析版本)
355
+ '.pyc',
356
+ '.DS_Store'
357
+ ]);
358
+ if (noiseExtensions.has(ext))
359
+ return true;
360
+ return false;
361
+ };
362
+ exports.isBinaryOrIrrelevant = isBinaryOrIrrelevant;
@@ -45,13 +45,13 @@ function normalizePath(p) {
45
45
  // Check if this is a Unix path that should not be converted
46
46
  // WSL paths (/mnt/) should ALWAYS be preserved as they work correctly in WSL with Node.js fs
47
47
  // Regular Unix paths should also be preserved
48
- const isUnixPath = p.startsWith('/') && (
49
- // Always preserve WSL paths (/mnt/c/, /mnt/d/, etc.)
50
- p.match(/^\/mnt\/[a-z]\//i) ||
51
- // On non-Windows platforms, treat all absolute paths as Unix paths
52
- (process.platform !== 'win32') ||
53
- // On Windows, preserve Unix paths that aren't Unix-style Windows paths (/c/, /d/, etc.)
54
- (process.platform === 'win32' && !p.match(/^\/[a-zA-Z]\//)));
48
+ const isUnixPath = p.startsWith('/') &&
49
+ // Always preserve WSL paths (/mnt/c/, /mnt/d/, etc.)
50
+ (p.match(/^\/mnt\/[a-z]\//i) ||
51
+ // On non-Windows platforms, treat all absolute paths as Unix paths
52
+ process.platform !== 'win32' ||
53
+ // On Windows, preserve Unix paths that aren't Unix-style Windows paths (/c/, /d/, etc.)
54
+ (process.platform === 'win32' && !p.match(/^\/[a-zA-Z]\//)));
55
55
  if (isUnixPath) {
56
56
  // For Unix paths, just normalize without converting to Windows format
57
57
  // Replace double slashes with single slashes and remove trailing slashes
@@ -74,7 +74,7 @@ function isPathWithinAllowedDirectories(absolutePath, allowedDirectories) {
74
74
  // Ensure both paths are on the same drive
75
75
  const dirDrive = normalizedDir.charAt(0).toLowerCase();
76
76
  const pathDrive = normalizedPath.charAt(0).toLowerCase();
77
- return pathDrive === dirDrive && normalizedPath.startsWith(normalizedDir.replace(/\\?$/, '\\'));
77
+ return (pathDrive === dirDrive && normalizedPath.startsWith(normalizedDir.replace(/\\?$/, '\\')));
78
78
  }
79
79
  return normalizedPath.startsWith(normalizedDir + path_1.default.sep);
80
80
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saber2pr/ai-agent",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "description": "AI Assistant CLI.",
5
5
  "author": "saber2pr",
6
6
  "license": "ISC",