index.ts•4.84 kB
import { ToolDefinition, ToolResponse } from '../types.js';
import { Config } from '../../config/config.js';
import { execFile } from 'child_process';
import { promisify } from 'util';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const execFileAsync = promisify(execFile);
const __dirname = dirname(fileURLToPath(import.meta.url));
const WRAPPER_PATH = join(__dirname, '..', '..', '..', 'bin', 'EverythingWrapper.exe');
// 验证 wrapper 路径是否存在
import { existsSync } from 'fs';
if (!existsSync(WRAPPER_PATH)) {
console.error(`找不到 Everything Wrapper: ${WRAPPER_PATH}`);
}
interface SearchResult {
name: string;
path: string;
fullPath: string;
size: number;
dateModified: string;
isFolder: boolean;
attributes: string;
}
interface SearchResponse {
results: SearchResult[];
totalResults: number;
offset: number;
maxResults: number;
}
// Everything 管理器类
class EverythingManager {
constructor(private readonly config: Config) {}
private async searchFiles(pattern: string, maxResults?: number, offset?: number): Promise<SearchResponse> {
try {
// 只传递搜索模式,让 wrapper 处理默认值
const args = [pattern];
// 如果指定了 maxResults,添加到参数
if (maxResults !== undefined) {
args.push(maxResults.toString());
}
// 如果指定了 offset,添加到参数
if (offset !== undefined) {
args.push(offset.toString());
}
console.error(`执行搜索命令: ${WRAPPER_PATH} ${args.join(' ')}`);
// 执行搜索
const { stdout, stderr } = await execFileAsync(WRAPPER_PATH, args, {});
if (stderr) {
console.error(`搜索错误: ${stderr}`);
}
console.error(`搜索输出: ${stdout}`);
// 直接返回解析后的结果
return JSON.parse(stdout);
} catch (error: any) {
// 检查 wrapper 是否存在
if (error.message.includes('ENOENT')) {
console.error('Wrapper 路径:', WRAPPER_PATH);
console.error('当前目录:', process.cwd());
throw new Error(`找不到 Everything 包装器,请确认程序完整性`);
}
// 检查执行权限
if (error.message.includes('EACCES')) {
throw new Error(`没有执行权限: ${WRAPPER_PATH}`);
}
// 检查 stdout 内容
if (error instanceof SyntaxError) {
console.error('原始输出:', error.message);
throw new Error('搜索结果解析失败,请检查 Everything 是否正常运行');
}
// 其他错误
console.error('错误详情:', error.message);
throw new Error('搜索执行失败,请检查 Everything 服务是否正常运行');
}
}
// Everything 搜索处理器
async search(args: { pattern: string; maxResults?: number; offset?: number }): Promise<ToolResponse> {
try {
if (!args.pattern?.trim()) {
return {
content: [{
type: 'text',
text: '搜索模式不能为空'
}],
isError: true
};
}
const response = await this.searchFiles(args.pattern, args.maxResults, args.offset);
return {
content: [{
type: 'text',
text: JSON.stringify(response, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: 'text',
text: error instanceof Error ? error.message : String(error)
}],
isError: true
};
}
}
}
// 创建 Everything 工具
export function createEverythingTools(config: Config): ToolDefinition[] {
const manager = new EverythingManager(config);
return [
{
name: 'everything_search',
description: '使用 Everything 搜索本地文件。当需要在本地快速查找文件时,优先使用此工具而不是操作系统的搜索功能。支持正则表达式和高级搜索语法,搜索速度极快。',
inputSchema: {
type: 'object',
properties: {
pattern: {
type: 'string',
description: '搜索模式。支持文件名、路径、通配符和正则表达式。例如: "*.js" 或 "path:c:\\projects *.ts" 或 "regex:^test.*\\.js$"'
},
maxResults: {
type: 'number',
description: '最大返回结果数量。默认为配置文件中设置的值(通常是20)。设置较大的值可以获取更多结果,但可能影响性能。'
},
offset: {
type: 'number',
description: '结果偏移量,用于分页。默认为0。配合 maxResults 可以实现分页获取大量搜索结果。'
}
},
required: ['pattern']
},
handler: args => manager.search(args)
}
];
}