refactor(core): 移除不再需要的文件系统工具
删除以下工具及相关文件: - copy_file: 复制文件 - create_directory: 创建目录 - delete_file: 删除文件 - move_file: 移动文件 - search_files: 搜索文件 清理范围: - 工具实现文件 (5个) - 工具描述文件 (5个) - 单元测试文件 (6个) - Agent presets 中的引用 - Checkpoint 系统中的触发类型 - Hook 系统中的相关处理
This commit is contained in:
@@ -1 +0,0 @@
|
||||
复制文件或目录。支持递归复制整个目录结构。
|
||||
@@ -1 +0,0 @@
|
||||
创建新目录。支持递归创建父目录。如果目录已存在则不会报错。
|
||||
@@ -1 +0,0 @@
|
||||
删除文件或目录。删除目录时可以选择是否递归删除。需要谨慎使用。
|
||||
@@ -1 +0,0 @@
|
||||
移动或重命名文件/目录。可以将文件移动到新位置或更改文件名。
|
||||
@@ -1 +0,0 @@
|
||||
在目录中搜索匹配模式的文件
|
||||
@@ -1,140 +0,0 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import type { ToolResult } from '../../types/index.js';
|
||||
import type { ToolWithMetadata } from '../types.js';
|
||||
import { loadDescription } from '../load_description.js';
|
||||
import { getPermissionManager } from '../../permission/index.js';
|
||||
|
||||
async function copyRecursive(source: string, dest: string): Promise<void> {
|
||||
const stats = await fs.stat(source);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
await fs.mkdir(dest, { recursive: true });
|
||||
const entries = await fs.readdir(source);
|
||||
for (const entry of entries) {
|
||||
await copyRecursive(path.join(source, entry), path.join(dest, entry));
|
||||
}
|
||||
} else {
|
||||
await fs.copyFile(source, dest);
|
||||
}
|
||||
}
|
||||
|
||||
export const copyFileTool: ToolWithMetadata = {
|
||||
name: 'copy_file',
|
||||
description: loadDescription('copy_file'),
|
||||
metadata: {
|
||||
name: 'copy_file',
|
||||
category: 'filesystem',
|
||||
description: '复制文件或目录',
|
||||
keywords: ['copy', 'file', 'cp', 'duplicate', '复制', '文件', '拷贝'],
|
||||
deferLoading: true,
|
||||
},
|
||||
parameters: {
|
||||
source: {
|
||||
type: 'string',
|
||||
description: '源文件或目录的路径',
|
||||
required: true,
|
||||
},
|
||||
destination: {
|
||||
type: 'string',
|
||||
description: '目标路径',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
execute: async (params: Record<string, unknown>): Promise<ToolResult> => {
|
||||
const source = params.source as string;
|
||||
const destination = params.destination as string;
|
||||
const cwd = process.cwd();
|
||||
|
||||
const absoluteSource = path.isAbsolute(source)
|
||||
? source
|
||||
: path.join(cwd, source);
|
||||
|
||||
const absoluteDest = path.isAbsolute(destination)
|
||||
? destination
|
||||
: path.join(cwd, destination);
|
||||
|
||||
// 权限检查 - 源文件需要 read 权限
|
||||
const permissionManager = getPermissionManager();
|
||||
const sourcePermResult = await permissionManager.checkFilePermission({
|
||||
operation: 'read',
|
||||
path: absoluteSource,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!sourcePermResult.allowed) {
|
||||
if (sourcePermResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 读取 ${absoluteSource}\n原因: ${sourcePermResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${sourcePermResult.reason || '不允许读取此文件'}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 权限检查 - 目标位置需要 copy 权限
|
||||
const destPermResult = await permissionManager.checkFilePermission({
|
||||
operation: 'copy',
|
||||
path: absoluteDest,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!destPermResult.allowed) {
|
||||
if (destPermResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 复制到 ${absoluteDest}\n原因: ${destPermResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${destPermResult.reason || '不允许复制到此位置'}`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查源文件是否存在
|
||||
const sourceStats = await fs.stat(absoluteSource);
|
||||
|
||||
// 检查目标是否是目录
|
||||
let finalDest = absoluteDest;
|
||||
try {
|
||||
const destStats = await fs.stat(absoluteDest);
|
||||
if (destStats.isDirectory()) {
|
||||
// 如果目标是目录,将源文件复制到该目录下
|
||||
finalDest = path.join(absoluteDest, path.basename(absoluteSource));
|
||||
}
|
||||
} catch {
|
||||
// 目标不存在,直接使用目标路径
|
||||
}
|
||||
|
||||
// 确保目标目录存在
|
||||
await fs.mkdir(path.dirname(finalDest), { recursive: true });
|
||||
|
||||
// 执行复制
|
||||
if (sourceStats.isDirectory()) {
|
||||
await copyRecursive(absoluteSource, finalDest);
|
||||
} else {
|
||||
await fs.copyFile(absoluteSource, finalDest);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: `已复制: ${absoluteSource} -> ${finalDest}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,91 +0,0 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import type { ToolResult } from '../../types/index.js';
|
||||
import type { ToolWithMetadata } from '../types.js';
|
||||
import { loadDescription } from '../load_description.js';
|
||||
import { getPermissionManager } from '../../permission/index.js';
|
||||
|
||||
export const createDirectoryTool: ToolWithMetadata = {
|
||||
name: 'create_directory',
|
||||
description: loadDescription('create_directory'),
|
||||
metadata: {
|
||||
name: 'create_directory',
|
||||
category: 'filesystem',
|
||||
description: '创建目录',
|
||||
keywords: ['create', 'directory', 'mkdir', 'folder', 'new', '创建', '目录', '文件夹', '新建'],
|
||||
deferLoading: true,
|
||||
},
|
||||
parameters: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: '要创建的目录路径',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
execute: async (params: Record<string, unknown>): Promise<ToolResult> => {
|
||||
const dirPath = params.path as string;
|
||||
const cwd = process.cwd();
|
||||
|
||||
const absolutePath = path.isAbsolute(dirPath)
|
||||
? dirPath
|
||||
: path.join(cwd, dirPath);
|
||||
|
||||
// 权限检查
|
||||
const permissionManager = getPermissionManager();
|
||||
const permResult = await permissionManager.checkFilePermission({
|
||||
operation: 'mkdir',
|
||||
path: absolutePath,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!permResult.allowed) {
|
||||
if (permResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 创建目录 ${absolutePath}\n原因: ${permResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${permResult.reason || '不允许创建此目录'}`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查目录是否已存在
|
||||
try {
|
||||
const stats = await fs.stat(absolutePath);
|
||||
if (stats.isDirectory()) {
|
||||
return {
|
||||
success: true,
|
||||
output: `目录已存在: ${absolutePath}`,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `路径已存在且不是目录: ${absolutePath}`,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// 目录不存在,继续创建
|
||||
}
|
||||
|
||||
// 创建目录(递归创建父目录)
|
||||
await fs.mkdir(absolutePath, { recursive: true });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: `已创建目录: ${absolutePath}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,99 +0,0 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import type { ToolResult } from '../../types/index.js';
|
||||
import type { ToolWithMetadata } from '../types.js';
|
||||
import { loadDescription } from '../load_description.js';
|
||||
import { getPermissionManager } from '../../permission/index.js';
|
||||
|
||||
export const deleteFileTool: ToolWithMetadata = {
|
||||
name: 'delete_file',
|
||||
description: loadDescription('delete_file'),
|
||||
metadata: {
|
||||
name: 'delete_file',
|
||||
category: 'filesystem',
|
||||
description: '删除文件或目录',
|
||||
keywords: ['delete', 'remove', 'file', 'rm', '删除', '移除', '文件'],
|
||||
deferLoading: true,
|
||||
},
|
||||
parameters: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: '要删除的文件或目录的路径',
|
||||
required: true,
|
||||
},
|
||||
recursive: {
|
||||
type: 'boolean',
|
||||
description: '是否递归删除目录(默认 false)',
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
execute: async (params: Record<string, unknown>): Promise<ToolResult> => {
|
||||
const filePath = params.path as string;
|
||||
const recursive = (params.recursive as boolean) || false;
|
||||
const cwd = process.cwd();
|
||||
|
||||
const absolutePath = path.isAbsolute(filePath)
|
||||
? filePath
|
||||
: path.join(cwd, filePath);
|
||||
|
||||
// 权限检查
|
||||
const permissionManager = getPermissionManager();
|
||||
const permResult = await permissionManager.checkFilePermission({
|
||||
operation: 'delete',
|
||||
path: absolutePath,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!permResult.allowed) {
|
||||
if (permResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 删除 ${absolutePath}\n原因: ${permResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${permResult.reason || '不允许删除此文件'}`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = await fs.stat(absolutePath);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
if (!recursive) {
|
||||
// 检查目录是否为空
|
||||
const entries = await fs.readdir(absolutePath);
|
||||
if (entries.length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `目录不为空。如需删除非空目录,请设置 recursive: true`,
|
||||
};
|
||||
}
|
||||
await fs.rmdir(absolutePath);
|
||||
} else {
|
||||
await fs.rm(absolutePath, { recursive: true });
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: `已删除目录: ${absolutePath}`,
|
||||
};
|
||||
} else {
|
||||
await fs.unlink(absolutePath);
|
||||
return {
|
||||
success: true,
|
||||
output: `已删除文件: ${absolutePath}`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -6,17 +6,10 @@ export { multiEditTool } from './multi_edit.js';
|
||||
|
||||
// 目录操作
|
||||
export { listDirTool } from './list_directory.js';
|
||||
export { createDirectoryTool } from './create_directory.js';
|
||||
|
||||
// 搜索
|
||||
export { searchFilesTool } from './search_files.js';
|
||||
export { globTool } from './glob.js';
|
||||
export { grepTool } from './grep.js';
|
||||
|
||||
// 文件信息
|
||||
export { getFileInfoTool } from './get_file_info.js';
|
||||
|
||||
// 文件管理
|
||||
export { moveFileTool } from './move_file.js';
|
||||
export { copyFileTool } from './copy_file.js';
|
||||
export { deleteFileTool } from './delete_file.js';
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import type { ToolResult } from '../../types/index.js';
|
||||
import type { ToolWithMetadata } from '../types.js';
|
||||
import { loadDescription } from '../load_description.js';
|
||||
import { getPermissionManager } from '../../permission/index.js';
|
||||
|
||||
export const moveFileTool: ToolWithMetadata = {
|
||||
name: 'move_file',
|
||||
description: loadDescription('move_file'),
|
||||
metadata: {
|
||||
name: 'move_file',
|
||||
category: 'filesystem',
|
||||
description: '移动或重命名文件/目录',
|
||||
keywords: ['move', 'rename', 'file', 'mv', '移动', '重命名', '文件'],
|
||||
deferLoading: true,
|
||||
},
|
||||
parameters: {
|
||||
source: {
|
||||
type: 'string',
|
||||
description: '源文件或目录的路径',
|
||||
required: true,
|
||||
},
|
||||
destination: {
|
||||
type: 'string',
|
||||
description: '目标路径',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
execute: async (params: Record<string, unknown>): Promise<ToolResult> => {
|
||||
const source = params.source as string;
|
||||
const destination = params.destination as string;
|
||||
const cwd = process.cwd();
|
||||
|
||||
const absoluteSource = path.isAbsolute(source)
|
||||
? source
|
||||
: path.join(cwd, source);
|
||||
|
||||
const absoluteDest = path.isAbsolute(destination)
|
||||
? destination
|
||||
: path.join(cwd, destination);
|
||||
|
||||
// 权限检查 - 源文件需要 move 权限
|
||||
const permissionManager = getPermissionManager();
|
||||
const sourcePermResult = await permissionManager.checkFilePermission({
|
||||
operation: 'move',
|
||||
path: absoluteSource,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!sourcePermResult.allowed) {
|
||||
if (sourcePermResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 移动 ${absoluteSource}\n原因: ${sourcePermResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${sourcePermResult.reason || '不允许移动此文件'}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 权限检查 - 目标位置需要 write 权限
|
||||
const destPermResult = await permissionManager.checkFilePermission({
|
||||
operation: 'write',
|
||||
path: absoluteDest,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!destPermResult.allowed) {
|
||||
if (destPermResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 写入到 ${absoluteDest}\n原因: ${destPermResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${destPermResult.reason || '不允许写入到此位置'}`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查源文件是否存在
|
||||
await fs.access(absoluteSource);
|
||||
|
||||
// 检查目标是否是目录
|
||||
let finalDest = absoluteDest;
|
||||
try {
|
||||
const destStats = await fs.stat(absoluteDest);
|
||||
if (destStats.isDirectory()) {
|
||||
// 如果目标是目录,将源文件移动到该目录下
|
||||
finalDest = path.join(absoluteDest, path.basename(absoluteSource));
|
||||
}
|
||||
} catch {
|
||||
// 目标不存在,直接使用目标路径
|
||||
}
|
||||
|
||||
// 确保目标目录存在
|
||||
await fs.mkdir(path.dirname(finalDest), { recursive: true });
|
||||
|
||||
// 执行移动
|
||||
await fs.rename(absoluteSource, finalDest);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: `已移动: ${absoluteSource} -> ${finalDest}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,107 +0,0 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import type { ToolResult } from '../../types/index.js';
|
||||
import type { ToolWithMetadata } from '../types.js';
|
||||
import { loadDescription } from '../load_description.js';
|
||||
import { getPermissionManager } from '../../permission/index.js';
|
||||
|
||||
export const searchFilesTool: ToolWithMetadata = {
|
||||
name: 'search_files',
|
||||
description: loadDescription('search_files'),
|
||||
metadata: {
|
||||
name: 'search_files',
|
||||
category: 'filesystem',
|
||||
description: '按文件名搜索文件',
|
||||
keywords: ['search', 'file', 'find', 'glob', 'pattern', '搜索', '文件', '查找', '匹配'],
|
||||
deferLoading: true,
|
||||
},
|
||||
parameters: {
|
||||
directory: {
|
||||
type: 'string',
|
||||
description: '搜索的起始目录',
|
||||
required: true,
|
||||
},
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: '文件名匹配模式(支持 glob 模式,如 *.ts)',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
execute: async (params: Record<string, unknown>): Promise<ToolResult> => {
|
||||
const directory = params.directory as string;
|
||||
const pattern = params.pattern as string;
|
||||
const cwd = process.cwd();
|
||||
const absolutePath = path.isAbsolute(directory)
|
||||
? directory
|
||||
: path.join(cwd, directory);
|
||||
|
||||
// 权限检查
|
||||
const permissionManager = getPermissionManager();
|
||||
const permResult = await permissionManager.checkFilePermission({
|
||||
operation: 'search',
|
||||
path: absolutePath,
|
||||
workdir: cwd,
|
||||
});
|
||||
|
||||
if (!permResult.allowed) {
|
||||
if (permResult.needsConfirmation) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `需要用户确认: 搜索目录 ${absolutePath}\n原因: ${permResult.reason || '需要权限确认'}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: `权限被拒绝: ${permResult.reason || '不允许搜索此目录'}`,
|
||||
};
|
||||
}
|
||||
|
||||
const matches: string[] = [];
|
||||
const regex = new RegExp(
|
||||
pattern.replace(/\*/g, '.*').replace(/\?/g, '.'),
|
||||
'i'
|
||||
);
|
||||
|
||||
async function searchRecursive(dir: string, depth = 0): Promise<void> {
|
||||
if (depth > 10) return;
|
||||
|
||||
try {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
await searchRecursive(fullPath, depth + 1);
|
||||
} else if (regex.test(entry.name)) {
|
||||
matches.push(fullPath);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// 忽略权限错误
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await searchRecursive(absolutePath);
|
||||
return {
|
||||
success: true,
|
||||
output:
|
||||
matches.length > 0
|
||||
? matches.join('\n')
|
||||
: '没有找到匹配的文件',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: '',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -21,14 +21,9 @@ import {
|
||||
editFileTool,
|
||||
multiEditTool,
|
||||
listDirTool,
|
||||
createDirectoryTool,
|
||||
searchFilesTool,
|
||||
globTool,
|
||||
grepTool,
|
||||
getFileInfoTool,
|
||||
moveFileTool,
|
||||
copyFileTool,
|
||||
deleteFileTool,
|
||||
} from './filesystem/index.js';
|
||||
|
||||
// Web 工具
|
||||
@@ -87,14 +82,9 @@ const allToolsWithMetadata: ToolWithMetadata[] = [
|
||||
editFileTool,
|
||||
multiEditTool,
|
||||
listDirTool,
|
||||
createDirectoryTool,
|
||||
searchFilesTool,
|
||||
globTool,
|
||||
grepTool,
|
||||
getFileInfoTool,
|
||||
moveFileTool,
|
||||
copyFileTool,
|
||||
deleteFileTool,
|
||||
|
||||
// Web 工具 (deferLoading: false)
|
||||
webSearchTool,
|
||||
|
||||
@@ -15,14 +15,9 @@ const TOOL_CATEGORY_MAP: Record<string, string> = {
|
||||
edit_file: 'filesystem',
|
||||
multi_edit: 'filesystem',
|
||||
list_directory: 'filesystem',
|
||||
create_directory: 'filesystem',
|
||||
search_files: 'filesystem',
|
||||
glob: 'filesystem',
|
||||
grep: 'filesystem',
|
||||
get_file_info: 'filesystem',
|
||||
move_file: 'filesystem',
|
||||
copy_file: 'filesystem',
|
||||
delete_file: 'filesystem',
|
||||
// web
|
||||
web_search: 'web',
|
||||
web_extract: 'web',
|
||||
|
||||
Reference in New Issue
Block a user