deeper-cli 1.0.0
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/README.md +254 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +12067 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +415 -0
- package/dist/index.js +1599 -0
- package/dist/index.js.map +1 -0
- package/docs/superpowers/plans/2026-05-14-deepercode-implementation.md +24 -0
- package/docs/superpowers/plans/2026-05-14-deepercode-plan.md +1248 -0
- package/docs/superpowers/specs/2026-05-14-deepercode-design.md +560 -0
- package/package.json +60 -0
- package/src/cli/bootstrap.ts +69 -0
- package/src/cli/chat-repl.ts +932 -0
- package/src/cli/commands/chat.ts +39 -0
- package/src/cli/commands/chat.tsx +39 -0
- package/src/cli/commands/config.ts +133 -0
- package/src/cli/commands/mcp.ts +172 -0
- package/src/cli/commands/run.ts +147 -0
- package/src/cli/commands/skill.ts +152 -0
- package/src/cli/index.ts +184 -0
- package/src/core/bugscan.ts +145 -0
- package/src/core/config.ts +285 -0
- package/src/core/constants.ts +49 -0
- package/src/core/eventbus.ts +202 -0
- package/src/core/logger.ts +109 -0
- package/src/core/storage.ts +96 -0
- package/src/index.ts +26 -0
- package/src/mcp/ConfigLoader.ts +74 -0
- package/src/mcp/MCPClient.ts +326 -0
- package/src/mcp/ResourceAdapter.ts +58 -0
- package/src/mcp/SSETransport.ts +133 -0
- package/src/mcp/StdioTransport.ts +116 -0
- package/src/mcp/ToolAdapter.ts +71 -0
- package/src/mcp/types.ts +58 -0
- package/src/memory/xmemory.ts +275 -0
- package/src/model/DeepSeekClient.ts +292 -0
- package/src/model/MessageBuilder.ts +155 -0
- package/src/model/RetryManager.ts +82 -0
- package/src/model/StreamHandler.ts +158 -0
- package/src/model/types.ts +86 -0
- package/src/skills/SkillCreator.ts +153 -0
- package/src/skills/SkillEngine.ts +158 -0
- package/src/skills/SkillExecutor.ts +107 -0
- package/src/skills/SkillLoader.ts +182 -0
- package/src/skills/SkillRegistry.ts +73 -0
- package/src/skills/SkillTrigger.ts +82 -0
- package/src/skills/types.ts +28 -0
- package/src/tools/ToolExecutor.ts +103 -0
- package/src/tools/ToolRegistry.ts +71 -0
- package/src/tools/ToolValidator.ts +103 -0
- package/src/tools/builtin/ai/context_summarize.ts +76 -0
- package/src/tools/builtin/ai/memory_store.ts +86 -0
- package/src/tools/builtin/ai/prompt_template.ts +71 -0
- package/src/tools/builtin/ai/skill_create.ts +53 -0
- package/src/tools/builtin/ai/subagent.ts +39 -0
- package/src/tools/builtin/ai/todo_manager.ts +157 -0
- package/src/tools/builtin/ai/token_count.ts +196 -0
- package/src/tools/builtin/ai/tool_create.ts +52 -0
- package/src/tools/builtin/code/analyze_deps.ts +72 -0
- package/src/tools/builtin/code/bug_scan.ts +80 -0
- package/src/tools/builtin/code/code_metrics.ts +111 -0
- package/src/tools/builtin/code/extract_function.ts +86 -0
- package/src/tools/builtin/code/format_code.ts +57 -0
- package/src/tools/builtin/code/generate_code.ts +75 -0
- package/src/tools/builtin/code/import_organizer.ts +82 -0
- package/src/tools/builtin/code/lint_code.ts +48 -0
- package/src/tools/builtin/code/parse_ast.ts +86 -0
- package/src/tools/builtin/code/refactor_code.ts +63 -0
- package/src/tools/builtin/code/type_check.ts +48 -0
- package/src/tools/builtin/data/chart_generate.ts +62 -0
- package/src/tools/builtin/data/csv_parse.ts +56 -0
- package/src/tools/builtin/data/data_diff.ts +79 -0
- package/src/tools/builtin/data/data_transform.ts +74 -0
- package/src/tools/builtin/data/data_validate.ts +75 -0
- package/src/tools/builtin/data/json_parse.ts +71 -0
- package/src/tools/builtin/data/template_render.ts +58 -0
- package/src/tools/builtin/data/toml_parse.ts +42 -0
- package/src/tools/builtin/data/xml_parse.ts +79 -0
- package/src/tools/builtin/data/yaml_parse.ts +42 -0
- package/src/tools/builtin/database/db_backup.ts +53 -0
- package/src/tools/builtin/database/db_restore.ts +51 -0
- package/src/tools/builtin/database/db_schema.ts +66 -0
- package/src/tools/builtin/database/nosql_query.ts +50 -0
- package/src/tools/builtin/database/orm_generate.ts +66 -0
- package/src/tools/builtin/database/redis_command.ts +46 -0
- package/src/tools/builtin/database/sql_migrate.ts +55 -0
- package/src/tools/builtin/database/sql_query.ts +60 -0
- package/src/tools/builtin/filesystem/batch_read.ts +56 -0
- package/src/tools/builtin/filesystem/batch_write.ts +67 -0
- package/src/tools/builtin/filesystem/copy_file.ts +36 -0
- package/src/tools/builtin/filesystem/create_dir.ts +30 -0
- package/src/tools/builtin/filesystem/delete_file.ts +30 -0
- package/src/tools/builtin/filesystem/diff_files.ts +47 -0
- package/src/tools/builtin/filesystem/edit_file.ts +47 -0
- package/src/tools/builtin/filesystem/file_info.ts +52 -0
- package/src/tools/builtin/filesystem/glob_find.ts +44 -0
- package/src/tools/builtin/filesystem/list_dir.ts +51 -0
- package/src/tools/builtin/filesystem/merge_files.ts +44 -0
- package/src/tools/builtin/filesystem/move_file.ts +37 -0
- package/src/tools/builtin/filesystem/read_file.ts +55 -0
- package/src/tools/builtin/filesystem/watch_file.ts +33 -0
- package/src/tools/builtin/filesystem/write_file.ts +45 -0
- package/src/tools/builtin/index.ts +244 -0
- package/src/tools/builtin/network/api_call.ts +79 -0
- package/src/tools/builtin/network/browser_action.ts +54 -0
- package/src/tools/builtin/network/check_url.ts +59 -0
- package/src/tools/builtin/network/download_file.ts +64 -0
- package/src/tools/builtin/network/graphql_query.ts +46 -0
- package/src/tools/builtin/network/http_request.ts +61 -0
- package/src/tools/builtin/network/parse_html.ts +101 -0
- package/src/tools/builtin/network/proxy_request.ts +53 -0
- package/src/tools/builtin/network/screenshot_page.ts +58 -0
- package/src/tools/builtin/network/web_fetch.ts +70 -0
- package/src/tools/builtin/network/web_search.ts +128 -0
- package/src/tools/builtin/network/websocket_connect.ts +70 -0
- package/src/tools/builtin/project/build_project.ts +68 -0
- package/src/tools/builtin/project/config_manage.ts +99 -0
- package/src/tools/builtin/project/coverage_report.ts +59 -0
- package/src/tools/builtin/project/docker_manage.ts +90 -0
- package/src/tools/builtin/project/env_manage.ts +88 -0
- package/src/tools/builtin/project/npm_manage.ts +71 -0
- package/src/tools/builtin/project/project_init.ts +59 -0
- package/src/tools/builtin/project/run_test.ts +74 -0
- package/src/tools/builtin/search/codebase_search.ts +76 -0
- package/src/tools/builtin/search/find_definition.ts +84 -0
- package/src/tools/builtin/search/find_references.ts +75 -0
- package/src/tools/builtin/search/fuzzy_find.ts +75 -0
- package/src/tools/builtin/search/grep_search.ts +90 -0
- package/src/tools/builtin/search/regex_find.ts +91 -0
- package/src/tools/builtin/search/search_docs.ts +51 -0
- package/src/tools/builtin/search/search_package.ts +50 -0
- package/src/tools/builtin/search/symbol_search.ts +82 -0
- package/src/tools/builtin/search/text_search.ts +63 -0
- package/src/tools/builtin/security/decrypt_file.ts +54 -0
- package/src/tools/builtin/security/encrypt_file.ts +52 -0
- package/src/tools/builtin/security/hash_generate.ts +48 -0
- package/src/tools/builtin/security/jwt_decode.ts +53 -0
- package/src/tools/builtin/security/secret_scan.ts +82 -0
- package/src/tools/builtin/security/vulnerability_check.ts +71 -0
- package/src/tools/builtin/shell/background_terminal.ts +38 -0
- package/src/tools/builtin/shell/check_status.ts +48 -0
- package/src/tools/builtin/shell/interactive_terminal.ts +31 -0
- package/src/tools/builtin/shell/kill_terminal.ts +29 -0
- package/src/tools/builtin/shell/list_terminals.ts +61 -0
- package/src/tools/builtin/shell/pipe_commands.ts +55 -0
- package/src/tools/builtin/shell/process-pool.ts +150 -0
- package/src/tools/builtin/shell/run_async.ts +73 -0
- package/src/tools/builtin/shell/run_command.ts +60 -0
- package/src/tools/builtin/shell/send_ctrl_keys.ts +43 -0
- package/src/tools/builtin/shell/send_keys.ts +36 -0
- package/src/tools/builtin/shell/send_text.ts +35 -0
- package/src/tools/builtin/shell/shell_script.ts +65 -0
- package/src/tools/builtin/shell/stop_command.ts +40 -0
- package/src/tools/builtin/shell/terminal_resize.ts +31 -0
- package/src/tools/builtin/shell/terminal_screenshot.ts +28 -0
- package/src/tools/builtin/system/log_viewer.ts +89 -0
- package/src/tools/builtin/system/notify_user.ts +55 -0
- package/src/tools/builtin/system/process_list.ts +66 -0
- package/src/tools/builtin/system/resource_monitor.ts +66 -0
- package/src/tools/builtin/system/system_info.ts +41 -0
- package/src/tools/tool-types.ts +97 -0
- package/src/ui/AgentTree.tsx +98 -0
- package/src/ui/App.tsx +46 -0
- package/src/ui/ChatView.tsx +278 -0
- package/src/ui/ConfirmDialog.tsx +68 -0
- package/src/ui/DiffView.tsx +64 -0
- package/src/ui/FilePreview.tsx +59 -0
- package/src/ui/InputBox.tsx +267 -0
- package/src/ui/MessageBubble.tsx +30 -0
- package/src/ui/Spinner.tsx +35 -0
- package/src/ui/StatusBar.tsx +41 -0
- package/src/ui/ToolCallCard.tsx +73 -0
- package/src/ui/ansi.ts +50 -0
- package/src/ui/markdown.ts +238 -0
- package/src/ui/themes/dark.ts +4 -0
- package/src/ui/themes/default.ts +25 -0
- package/src/ui/themes/light.ts +14 -0
- package/tests/unit/BuiltinTools.test.ts +129 -0
- package/tests/unit/BuiltinToolsIntegration.test.ts +111 -0
- package/tests/unit/FilesystemTools.test.ts +211 -0
- package/tests/unit/SkillLoader.test.ts +141 -0
- package/tests/unit/SkillRegistry.test.ts +113 -0
- package/tests/unit/ToolExecutor.test.ts +160 -0
- package/tests/unit/ToolRegistry.test.ts +103 -0
- package/tests/unit/ToolValidator.test.ts +137 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +20 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import type { Tool } from '../../tool-types.js';
|
|
3
|
+
|
|
4
|
+
export const db_schema: Tool = {
|
|
5
|
+
name: 'db_schema',
|
|
6
|
+
description: '查看数据库表结构',
|
|
7
|
+
category: 'database',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
connection: { type: 'string', description: '数据库连接字符串' },
|
|
12
|
+
engine: { type: 'string', description: '数据库引擎: mysql, postgresql, sqlite, mssql', enum: ['mysql', 'postgresql', 'sqlite', 'mssql'] },
|
|
13
|
+
table: { type: 'string', description: '表名(可选)' },
|
|
14
|
+
},
|
|
15
|
+
required: ['engine'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: false,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const engine = params.engine as string;
|
|
22
|
+
const table = params.table as string | undefined;
|
|
23
|
+
const connection = params.connection as string | undefined;
|
|
24
|
+
|
|
25
|
+
let output = `数据库 Schema 查询\n引擎: ${engine}\n\n`;
|
|
26
|
+
|
|
27
|
+
switch (engine) {
|
|
28
|
+
case 'sqlite': {
|
|
29
|
+
if (!connection) {
|
|
30
|
+
return { success: false, error: 'sqlite 需要 connection 参数(文件路径)', output: '' };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const result = execSync(`sqlite3 "${connection}" ".schema ${table || ''}"`, {
|
|
34
|
+
encoding: 'utf-8', timeout: 10000, stdio: 'pipe',
|
|
35
|
+
});
|
|
36
|
+
output += result;
|
|
37
|
+
} catch {
|
|
38
|
+
output += 'sqlite3 命令行不可用,请安装 sqlite3 或使用文件路径直接查看数据库。\n';
|
|
39
|
+
output += `文件: ${connection}`;
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case 'mysql': {
|
|
44
|
+
output += table
|
|
45
|
+
? `使用: mysql -e "DESCRIBE ${table};" ${connection ? connection : ''}`
|
|
46
|
+
: `使用: mysql -e "SHOW TABLES;" ${connection ? connection : ''}`;
|
|
47
|
+
output += '\n(请确保 mysql 客户端已安装)';
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'postgresql': {
|
|
51
|
+
output += table
|
|
52
|
+
? `使用: psql ${connection || ''} -c "\\d ${table}"`
|
|
53
|
+
: `使用: psql ${connection || ''} -c "\\dt"`;
|
|
54
|
+
output += '\n(请确保 psql 客户端已安装)';
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
default:
|
|
58
|
+
output += `请使用 ${engine} CLI 或 GUI 工具查看数据库 schema。`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { success: true, output, metadata: { engine, table } };
|
|
62
|
+
} catch (err: unknown) {
|
|
63
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import type { Tool } from '../../tool-types.js';
|
|
5
|
+
|
|
6
|
+
export const nosql_query: Tool = {
|
|
7
|
+
name: 'nosql_query',
|
|
8
|
+
description: '执行 NoSQL 数据库操作(MongoDB shell)',
|
|
9
|
+
category: 'database',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
query: { type: 'string', description: '查询语句(MongoDB shell 语法)' },
|
|
14
|
+
connection: { type: 'string', description: '连接字符串 mongodb://...' },
|
|
15
|
+
collection: { type: 'string', description: '集合名称' },
|
|
16
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
17
|
+
},
|
|
18
|
+
required: ['query'],
|
|
19
|
+
},
|
|
20
|
+
dangerous: false,
|
|
21
|
+
requiresApproval: true,
|
|
22
|
+
async execute(params) {
|
|
23
|
+
try {
|
|
24
|
+
const query = params.query as string;
|
|
25
|
+
const connection = (params.connection as string) ?? 'mongodb://localhost:27017';
|
|
26
|
+
const collection = params.collection as string | undefined;
|
|
27
|
+
const timeout = (params.timeout_ms as number) ?? 30000;
|
|
28
|
+
|
|
29
|
+
const queryScript = collection
|
|
30
|
+
? `db.${collection}.${query}`
|
|
31
|
+
: query;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const output = execSync(
|
|
35
|
+
`mongosh "${connection}" --eval "${queryScript.replace(/"/g, '\\"')}"`,
|
|
36
|
+
{ encoding: 'utf-8', timeout, maxBuffer: 50 * 1024 * 1024, stdio: 'pipe' }
|
|
37
|
+
);
|
|
38
|
+
return { success: true, output, metadata: { connection, collection } };
|
|
39
|
+
} catch {
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
output: `MongoDB 查询请求:\n连接: ${connection}\n集合: ${collection || '(未指定)'}\n查询: ${queryScript}\n\n注意: mongosh 命令行不可用。请确保已安装 MongoDB Shell,或将查询语句直接在 mongosh 中执行。`,
|
|
43
|
+
metadata: { connection, fallback: true },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
} catch (err: unknown) {
|
|
47
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const orm_generate: Tool = {
|
|
6
|
+
name: 'orm_generate',
|
|
7
|
+
description: '生成 ORM 模型代码(基于数据库 schema)',
|
|
8
|
+
category: 'database',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
engine: { type: 'string', description: '数据库引擎', enum: ['mysql', 'postgresql', 'sqlite', 'mssql'] },
|
|
13
|
+
connection: { type: 'string', description: '连接字符串' },
|
|
14
|
+
output_dir: { type: 'string', description: '输出目录' },
|
|
15
|
+
orm: { type: 'string', description: 'ORM 框架: prisma, typeorm, drizzle, sequelize', enum: ['prisma', 'typeorm', 'drizzle', 'sequelize'] },
|
|
16
|
+
},
|
|
17
|
+
required: ['engine', 'orm'],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: true,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const engine = params.engine as string;
|
|
24
|
+
const connection = params.connection as string | undefined;
|
|
25
|
+
const outputDir = (params.output_dir as string) ?? './models';
|
|
26
|
+
const orm = params.orm as string;
|
|
27
|
+
|
|
28
|
+
const tips: Record<string, string[]> = {
|
|
29
|
+
prisma: [
|
|
30
|
+
`npx prisma init --datasource-provider ${engine}`,
|
|
31
|
+
'将上述 schema 写入 prisma/schema.prisma',
|
|
32
|
+
'npx prisma db pull # 从数据库拉取 schema',
|
|
33
|
+
'npx prisma generate # 生成 Prisma Client',
|
|
34
|
+
],
|
|
35
|
+
typeorm: [
|
|
36
|
+
'npx typeorm-model-generator -h host -d database -p port -u user -x password -e engine -o outputDir',
|
|
37
|
+
'或使用: npx typeorm entity:create',
|
|
38
|
+
],
|
|
39
|
+
drizzle: [
|
|
40
|
+
'npx drizzle-kit introspect:pg # PostgreSQL',
|
|
41
|
+
'npx drizzle-kit introspect:mysql # MySQL',
|
|
42
|
+
'npx drizzle-kit generate',
|
|
43
|
+
],
|
|
44
|
+
sequelize: [
|
|
45
|
+
'npx sequelize-auto -h host -d database -u user -x password -p port --dialect engine -o outputDir',
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const output = [
|
|
50
|
+
`ORM 模型生成方案`,
|
|
51
|
+
`数据库引擎: ${engine}`,
|
|
52
|
+
`ORM 框架: ${orm}`,
|
|
53
|
+
`输出目录: ${outputDir}`,
|
|
54
|
+
'',
|
|
55
|
+
'操作步骤:',
|
|
56
|
+
...(tips[orm] || []).map(t => ` ${t}`),
|
|
57
|
+
'',
|
|
58
|
+
'提示: 生成模型代码后,请根据项目需要手动调整字段类型和关联关系。',
|
|
59
|
+
].join('\n');
|
|
60
|
+
|
|
61
|
+
return { success: true, output, metadata: { engine, orm, outputDir } };
|
|
62
|
+
} catch (err: unknown) {
|
|
63
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import type { Tool } from '../../tool-types.js';
|
|
3
|
+
|
|
4
|
+
export const redis_command: Tool = {
|
|
5
|
+
name: 'redis_command',
|
|
6
|
+
description: '执行 Redis 命令',
|
|
7
|
+
category: 'database',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
host: { type: 'string', description: 'Redis 主机' },
|
|
12
|
+
port: { type: 'number', description: 'Redis 端口' },
|
|
13
|
+
password: { type: 'string', description: '认证密码' },
|
|
14
|
+
command: { type: 'string', description: 'Redis 命令' },
|
|
15
|
+
},
|
|
16
|
+
required: ['command'],
|
|
17
|
+
},
|
|
18
|
+
dangerous: false,
|
|
19
|
+
requiresApproval: true,
|
|
20
|
+
async execute(params) {
|
|
21
|
+
try {
|
|
22
|
+
const host = (params.host as string) ?? 'localhost';
|
|
23
|
+
const port = (params.port as number) ?? 6379;
|
|
24
|
+
const password = params.password as string | undefined;
|
|
25
|
+
const command = params.command as string;
|
|
26
|
+
|
|
27
|
+
let cmd = `redis-cli -h ${host} -p ${port}`;
|
|
28
|
+
if (password) cmd += ` -a "${password}"`;
|
|
29
|
+
cmd += ` ${command}`;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const output = execSync(cmd, { encoding: 'utf-8', timeout: 15000, stdio: 'pipe' });
|
|
33
|
+
return { success: true, output, metadata: { host, port } };
|
|
34
|
+
} catch (err: unknown) {
|
|
35
|
+
const e = err as { message?: string; stdout?: string; stderr?: string };
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: `Redis 命令失败: ${e.message || String(err)}`,
|
|
39
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
} catch (err: unknown) {
|
|
43
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import type { Tool } from '../../tool-types.js';
|
|
3
|
+
|
|
4
|
+
export const sql_migrate: Tool = {
|
|
5
|
+
name: 'sql_migrate',
|
|
6
|
+
description: '执行数据库迁移',
|
|
7
|
+
category: 'database',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
command: { type: 'string', description: '迁移命令或 SQL 文件路径' },
|
|
12
|
+
connection: { type: 'string', description: '数据库连接字符串' },
|
|
13
|
+
engine: { type: 'string', description: '数据库引擎', enum: ['mysql', 'postgresql', 'sqlite', 'mssql'] },
|
|
14
|
+
},
|
|
15
|
+
required: ['command'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: true,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const command = params.command as string;
|
|
22
|
+
const connection = params.connection as string | undefined;
|
|
23
|
+
const engine = (params.engine as string) ?? 'sqlite';
|
|
24
|
+
|
|
25
|
+
let cmd = '';
|
|
26
|
+
switch (engine) {
|
|
27
|
+
case 'sqlite':
|
|
28
|
+
cmd = `sqlite3 "${connection || 'database.db'}" < "${command}"`;
|
|
29
|
+
break;
|
|
30
|
+
case 'mysql':
|
|
31
|
+
cmd = `mysql ${connection || ''} < "${command}"`;
|
|
32
|
+
break;
|
|
33
|
+
case 'postgresql':
|
|
34
|
+
cmd = `psql ${connection || ''} -f "${command}"`;
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
return { success: false, error: `不支持的数据库引擎: ${engine}`, output: '' };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const output = execSync(cmd, { encoding: 'utf-8', timeout: 60000, stdio: 'pipe' });
|
|
42
|
+
return { success: true, output: output || '迁移执行成功', metadata: { engine, command } };
|
|
43
|
+
} catch (err: unknown) {
|
|
44
|
+
const e = err as { message?: string; stdout?: string; stderr?: string };
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: `迁移失败: ${e.message || String(err)}`,
|
|
48
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
} catch (err: unknown) {
|
|
52
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import type { Tool } from '../../tool-types.js';
|
|
3
|
+
|
|
4
|
+
export const sql_query: Tool = {
|
|
5
|
+
name: 'sql_query',
|
|
6
|
+
description: '执行 SQL 查询',
|
|
7
|
+
category: 'database',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
query: { type: 'string', description: 'SQL 查询语句' },
|
|
12
|
+
connection: { type: 'string', description: '数据库连接字符串' },
|
|
13
|
+
engine: { type: 'string', description: '数据库引擎: mysql, postgresql, sqlite, mssql', enum: ['mysql', 'postgresql', 'sqlite', 'mssql'] },
|
|
14
|
+
timeout_ms: { type: 'number', description: '超时毫秒数' },
|
|
15
|
+
},
|
|
16
|
+
required: ['query', 'engine'],
|
|
17
|
+
},
|
|
18
|
+
dangerous: false,
|
|
19
|
+
requiresApproval: true,
|
|
20
|
+
async execute(params) {
|
|
21
|
+
try {
|
|
22
|
+
const query = params.query as string;
|
|
23
|
+
const engine = params.engine as string;
|
|
24
|
+
const connection = params.connection as string | undefined;
|
|
25
|
+
const timeout = (params.timeout_ms as number) ?? 30000;
|
|
26
|
+
|
|
27
|
+
let cmd = '';
|
|
28
|
+
switch (engine) {
|
|
29
|
+
case 'sqlite':
|
|
30
|
+
cmd = `sqlite3 "${connection || 'database.db'}" "${query.replace(/"/g, '\\"')}"`;
|
|
31
|
+
break;
|
|
32
|
+
case 'mysql':
|
|
33
|
+
cmd = `mysql ${connection || ''} -e "${query.replace(/"/g, '\\"')}"`;
|
|
34
|
+
break;
|
|
35
|
+
case 'postgresql':
|
|
36
|
+
cmd = `psql ${connection || ''} -c "${query.replace(/"/g, '\\"')}"`;
|
|
37
|
+
break;
|
|
38
|
+
case 'mssql':
|
|
39
|
+
cmd = `sqlcmd ${connection ? connection : ''} -Q "${query.replace(/"/g, '\\"')}"`;
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
return { success: false, error: `不支持的数据库引擎: ${engine}`, output: '' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const output = execSync(cmd, { encoding: 'utf-8', timeout, maxBuffer: 50 * 1024 * 1024, stdio: 'pipe' });
|
|
47
|
+
return { success: true, output, metadata: { engine } };
|
|
48
|
+
} catch (err: unknown) {
|
|
49
|
+
const e = err as { message?: string; stdout?: string; stderr?: string };
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: `查询失败: ${e.message || String(err)}`,
|
|
53
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
} catch (err: unknown) {
|
|
57
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const batch_read: Tool = {
|
|
6
|
+
name: 'batch_read',
|
|
7
|
+
description: '批量读取多个文件',
|
|
8
|
+
category: 'filesystem',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
file_paths: { type: 'array', items: { type: 'string' }, description: '文件路径列表' },
|
|
13
|
+
max_size_per_file: { type: 'number', description: '单文件最大字节数' },
|
|
14
|
+
},
|
|
15
|
+
required: ['file_paths'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: false,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const paths = params.file_paths as string[];
|
|
22
|
+
const maxSize = (params.max_size_per_file as number) ?? 1024 * 1024;
|
|
23
|
+
const results: Record<string, { content: string; error?: string }> = {};
|
|
24
|
+
for (const p of paths) {
|
|
25
|
+
const abs = resolve(p);
|
|
26
|
+
try {
|
|
27
|
+
if (!existsSync(abs)) {
|
|
28
|
+
results[p] = { content: '', error: '文件不存在' };
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const stat = await import('node:fs').then(m => m.statSync(abs));
|
|
32
|
+
if (stat.size > maxSize) {
|
|
33
|
+
results[p] = { content: '', error: `文件过大 (${stat.size} > ${maxSize})` };
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
results[p] = { content: readFileSync(abs, 'utf-8') };
|
|
37
|
+
} catch (e) {
|
|
38
|
+
results[p] = { content: '', error: (e as Error).message };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const output = Object.entries(results)
|
|
42
|
+
.map(([path, r]) => {
|
|
43
|
+
if (r.error) return `--- ${path} (ERROR: ${r.error}) ---`;
|
|
44
|
+
return `--- ${path} ---\n${r.content}`;
|
|
45
|
+
})
|
|
46
|
+
.join('\n\n');
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
output,
|
|
50
|
+
metadata: { files: paths.length, read: Object.values(results).filter(r => !r.error).length },
|
|
51
|
+
};
|
|
52
|
+
} catch (err: unknown) {
|
|
53
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
|
+
import { resolve, dirname } from 'node:path';
|
|
4
|
+
import type { Tool } from '../../tool-types.js';
|
|
5
|
+
|
|
6
|
+
export const batch_write: Tool = {
|
|
7
|
+
name: 'batch_write',
|
|
8
|
+
description: '批量写入多个文件,自动创建父目录,跳过空内容文件',
|
|
9
|
+
category: 'filesystem',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
files: {
|
|
14
|
+
type: 'array',
|
|
15
|
+
items: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
file_path: { type: 'string' },
|
|
19
|
+
content: { type: 'string' },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
description: '文件列表 [{file_path, content}]',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['files'],
|
|
26
|
+
},
|
|
27
|
+
dangerous: false,
|
|
28
|
+
requiresApproval: false,
|
|
29
|
+
async execute(params) {
|
|
30
|
+
try {
|
|
31
|
+
const files = params.files as { file_path: string; content: string }[];
|
|
32
|
+
if (!files || !files.length) {
|
|
33
|
+
return { success: false, error: '文件列表为空', output: '' };
|
|
34
|
+
}
|
|
35
|
+
const results: string[] = [];
|
|
36
|
+
const skipped: string[] = [];
|
|
37
|
+
for (const f of files) {
|
|
38
|
+
const abs = resolve(f.file_path);
|
|
39
|
+
const content = f.content || '';
|
|
40
|
+
if (!content.trim()) {
|
|
41
|
+
skipped.push(abs);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (content.length > 524_288) {
|
|
45
|
+
skipped.push(`${abs} (内容过大)`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const dir = dirname(abs);
|
|
49
|
+
if (!existsSync(dir)) {
|
|
50
|
+
mkdirSync(dir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
await writeFile(abs, content, 'utf-8');
|
|
53
|
+
results.push(abs);
|
|
54
|
+
}
|
|
55
|
+
let output = `已写入 ${results.length} 个文件`;
|
|
56
|
+
if (skipped.length > 0) output += `,跳过 ${skipped.length} 个(空内容)`;
|
|
57
|
+
if (results.length > 0) output += ':\n' + results.join('\n');
|
|
58
|
+
return {
|
|
59
|
+
success: results.length > 0,
|
|
60
|
+
output,
|
|
61
|
+
metadata: { count: results.length, skipped: skipped.length },
|
|
62
|
+
};
|
|
63
|
+
} catch (err: unknown) {
|
|
64
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const copy_file: Tool = {
|
|
6
|
+
name: 'copy_file',
|
|
7
|
+
description: '复制文件',
|
|
8
|
+
category: 'filesystem',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
source: { type: 'string', description: '源文件路径' },
|
|
13
|
+
destination: { type: 'string', description: '目标文件路径' },
|
|
14
|
+
},
|
|
15
|
+
required: ['source', 'destination'],
|
|
16
|
+
},
|
|
17
|
+
dangerous: false,
|
|
18
|
+
requiresApproval: true,
|
|
19
|
+
async execute(params) {
|
|
20
|
+
try {
|
|
21
|
+
const source = resolve(params.source as string);
|
|
22
|
+
const dest = resolve(params.destination as string);
|
|
23
|
+
if (!existsSync(source)) {
|
|
24
|
+
return { success: false, error: `源文件不存在: ${source}`, output: '' };
|
|
25
|
+
}
|
|
26
|
+
const destDir = dirname(dest);
|
|
27
|
+
if (!existsSync(destDir)) {
|
|
28
|
+
mkdirSync(destDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
copyFileSync(source, dest);
|
|
31
|
+
return { success: true, output: `已复制: ${source} -> ${dest}` };
|
|
32
|
+
} catch (err: unknown) {
|
|
33
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mkdirSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const create_dir: Tool = {
|
|
6
|
+
name: 'create_dir',
|
|
7
|
+
description: '创建目录(递归创建父目录)',
|
|
8
|
+
category: 'filesystem',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
dir_path: { type: 'string', description: '目录绝对路径' },
|
|
13
|
+
},
|
|
14
|
+
required: ['dir_path'],
|
|
15
|
+
},
|
|
16
|
+
dangerous: false,
|
|
17
|
+
requiresApproval: false,
|
|
18
|
+
async execute(params) {
|
|
19
|
+
try {
|
|
20
|
+
const dirPath = resolve(params.dir_path as string);
|
|
21
|
+
if (existsSync(dirPath)) {
|
|
22
|
+
return { success: true, output: `目录已存在: ${dirPath}` };
|
|
23
|
+
}
|
|
24
|
+
mkdirSync(dirPath, { recursive: true });
|
|
25
|
+
return { success: true, output: `目录已创建: ${dirPath}` };
|
|
26
|
+
} catch (err: unknown) {
|
|
27
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { unlinkSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const delete_file: Tool = {
|
|
6
|
+
name: 'delete_file',
|
|
7
|
+
description: '删除指定文件',
|
|
8
|
+
category: 'filesystem',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
file_path: { type: 'string', description: '文件绝对路径' },
|
|
13
|
+
},
|
|
14
|
+
required: ['file_path'],
|
|
15
|
+
},
|
|
16
|
+
dangerous: false,
|
|
17
|
+
requiresApproval: true,
|
|
18
|
+
async execute(params) {
|
|
19
|
+
try {
|
|
20
|
+
const filePath = resolve(params.file_path as string);
|
|
21
|
+
if (!existsSync(filePath)) {
|
|
22
|
+
return { success: false, error: `文件不存在: ${filePath}`, output: '' };
|
|
23
|
+
}
|
|
24
|
+
unlinkSync(filePath);
|
|
25
|
+
return { success: true, output: `已删除: ${filePath}` };
|
|
26
|
+
} catch (err: unknown) {
|
|
27
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const diff_files: Tool = {
|
|
6
|
+
name: 'diff_files',
|
|
7
|
+
description: '比较两个文件的差异',
|
|
8
|
+
category: 'filesystem',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
file_a: { type: 'string', description: '文件A路径' },
|
|
13
|
+
file_b: { type: 'string', description: '文件B路径' },
|
|
14
|
+
context_lines: { type: 'number', description: '上下文行数' },
|
|
15
|
+
},
|
|
16
|
+
required: ['file_a', 'file_b'],
|
|
17
|
+
},
|
|
18
|
+
dangerous: false,
|
|
19
|
+
requiresApproval: true,
|
|
20
|
+
async execute(params) {
|
|
21
|
+
try {
|
|
22
|
+
const fileA = resolve(params.file_a as string);
|
|
23
|
+
const fileB = resolve(params.file_b as string);
|
|
24
|
+
const contextLines = (params.context_lines as number) ?? 3;
|
|
25
|
+
if (!existsSync(fileA)) return { success: false, error: `文件A不存在: ${fileA}`, output: '' };
|
|
26
|
+
if (!existsSync(fileB)) return { success: false, error: `文件B不存在: ${fileB}`, output: '' };
|
|
27
|
+
const contentA = readFileSync(fileA, 'utf-8');
|
|
28
|
+
const contentB = readFileSync(fileB, 'utf-8');
|
|
29
|
+
const Diff = await import('diff');
|
|
30
|
+
const patch = Diff.structuredPatch(
|
|
31
|
+
fileA, fileB, contentA, contentB,
|
|
32
|
+
'a', 'b',
|
|
33
|
+
{ context: contextLines }
|
|
34
|
+
);
|
|
35
|
+
let output = '';
|
|
36
|
+
for (const hunk of patch.hunks) {
|
|
37
|
+
output += `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@\n`;
|
|
38
|
+
for (const line of hunk.lines) {
|
|
39
|
+
output += line + '\n';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { success: true, output: output || '文件内容相同' };
|
|
43
|
+
} catch (err: unknown) {
|
|
44
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import type { Tool } from '../../tool-types.js';
|
|
4
|
+
|
|
5
|
+
export const edit_file: Tool = {
|
|
6
|
+
name: 'edit_file',
|
|
7
|
+
description: '通过搜索替换编辑文件内容',
|
|
8
|
+
category: 'filesystem',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
file_path: { type: 'string', description: '文件绝对路径' },
|
|
13
|
+
old_str: { type: 'string', description: '要替换的源字符串' },
|
|
14
|
+
new_str: { type: 'string', description: '替换后的字符串' },
|
|
15
|
+
replace_all: { type: 'boolean', description: '是否替换所有匹配项' },
|
|
16
|
+
},
|
|
17
|
+
required: ['file_path', 'old_str', 'new_str'],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: false,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const filePath = resolve(params.file_path as string);
|
|
24
|
+
const oldStr = params.old_str as string;
|
|
25
|
+
const newStr = params.new_str as string;
|
|
26
|
+
const replaceAll = (params.replace_all as boolean) ?? false;
|
|
27
|
+
if (!existsSync(filePath)) return { success: false, error: `文件不存在: ${filePath}`, output: '' };
|
|
28
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
29
|
+
const index = content.indexOf(oldStr);
|
|
30
|
+
if (index === -1) return { success: false, error: '未找到匹配的字符串', output: '' };
|
|
31
|
+
let replaced: string; let count: number;
|
|
32
|
+
if (replaceAll) {
|
|
33
|
+
const regex = new RegExp(oldStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
34
|
+
const before = content;
|
|
35
|
+
replaced = content.replace(regex, newStr.replace(/\$/g, '$$$$'));
|
|
36
|
+
count = (before.match(regex) || []).length;
|
|
37
|
+
} else {
|
|
38
|
+
replaced = content.slice(0, index) + newStr + content.slice(index + oldStr.length);
|
|
39
|
+
count = 1;
|
|
40
|
+
}
|
|
41
|
+
writeFileSync(filePath, replaced, 'utf-8');
|
|
42
|
+
return { success: true, output: `已替换 ${count} 处匹配: ${filePath}` };
|
|
43
|
+
} catch (err: unknown) {
|
|
44
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|