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,128 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
export const web_search: Tool = {
|
|
4
|
+
name: 'web_search',
|
|
5
|
+
description: '使用 Bing 搜索引擎查询,返回真实搜索结果摘要',
|
|
6
|
+
category: 'network',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
query: { type: 'string', description: '搜索查询关键词' },
|
|
11
|
+
count: { type: 'number', description: '返回结果数 (默认 5, 最多 10)' },
|
|
12
|
+
},
|
|
13
|
+
required: ['query'],
|
|
14
|
+
},
|
|
15
|
+
dangerous: false,
|
|
16
|
+
requiresApproval: false,
|
|
17
|
+
async execute(params) {
|
|
18
|
+
try {
|
|
19
|
+
const query = params.query as string;
|
|
20
|
+
const count = Math.min((params.count as number) ?? 5, 10);
|
|
21
|
+
const encoded = encodeURIComponent(query);
|
|
22
|
+
const url = `https://www.bing.com/search?q=${encoded}&count=${count}`;
|
|
23
|
+
|
|
24
|
+
const resp = await fetch(url, {
|
|
25
|
+
headers: {
|
|
26
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
|
|
27
|
+
'Accept': 'text/html,application/xhtml+xml',
|
|
28
|
+
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
|
29
|
+
},
|
|
30
|
+
signal: AbortSignal.timeout(12000),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (!resp.ok) {
|
|
34
|
+
return { success: false, error: `Bing 搜索失败: HTTP ${resp.status}`, output: '' };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const html = await resp.text();
|
|
38
|
+
const results = extractBingResults(html, count);
|
|
39
|
+
|
|
40
|
+
if (results.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
success: true,
|
|
43
|
+
output: `Bing 搜索 "${query}" — 未提取到结果\n直接访问: ${url}`,
|
|
44
|
+
metadata: { query, engine: 'bing', url },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const lines = [`Bing 搜索: "${query}"`, ''];
|
|
49
|
+
for (let i = 0; i < results.length; i++) {
|
|
50
|
+
const r = results[i];
|
|
51
|
+
lines.push(`${i + 1}. ${r.title}`);
|
|
52
|
+
lines.push(` ${r.snippet}`);
|
|
53
|
+
lines.push(` ${r.url}`);
|
|
54
|
+
lines.push('');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
output: lines.join('\n'),
|
|
60
|
+
metadata: { query, engine: 'bing', count: results.length },
|
|
61
|
+
};
|
|
62
|
+
} catch (err: unknown) {
|
|
63
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
interface SearchResult {
|
|
69
|
+
title: string;
|
|
70
|
+
url: string;
|
|
71
|
+
snippet: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extractBingResults(html: string, maxCount: number): SearchResult[] {
|
|
75
|
+
const results: SearchResult[] = [];
|
|
76
|
+
|
|
77
|
+
// Bing 搜索结果在 <li class="b_algo"> 或 <div class="b_caption"> 中
|
|
78
|
+
const algoRegex = /<li class="b_algo"[^>]*>([\s\S]*?)<\/li>/gi;
|
|
79
|
+
const matches = html.match(algoRegex);
|
|
80
|
+
|
|
81
|
+
if (matches) {
|
|
82
|
+
for (const block of matches.slice(0, maxCount)) {
|
|
83
|
+
const r = parseAlgoBlock(block);
|
|
84
|
+
if (r) results.push(r);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 回退: 直接抓标题链接
|
|
89
|
+
if (results.length === 0) {
|
|
90
|
+
const linkRegex = /<h2[^>]*><a[^>]*href="(https?:\/\/(?!www\.bing\.com)[^"]+)"[^>]*>([\s\S]*?)<\/a><\/h2>/gi;
|
|
91
|
+
let m;
|
|
92
|
+
while ((m = linkRegex.exec(html)) !== null && results.length < maxCount) {
|
|
93
|
+
const url = m[1]!.replace(/&/g, '&');
|
|
94
|
+
const title = m[2]!.replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').trim();
|
|
95
|
+
const snippet = extractSnippet(html, url);
|
|
96
|
+
results.push({ title: title.slice(0, 120), url: url.slice(0, 200), snippet: snippet.slice(0, 200) });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function parseAlgoBlock(block: string): SearchResult | null {
|
|
104
|
+
const titleMatch = block.match(/<h2[^>]*><a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a><\/h2>/i);
|
|
105
|
+
if (!titleMatch) return null;
|
|
106
|
+
|
|
107
|
+
const url = titleMatch[1]!.replace(/&/g, '&').slice(0, 200);
|
|
108
|
+
const title = titleMatch[2]!.replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').trim();
|
|
109
|
+
|
|
110
|
+
// 尝试多种摘要容器
|
|
111
|
+
const snippetMatch = block.match(/<p[^>]*class="[^"]*b_lineclamp[^"]*"[^>]*>([\s\S]*?)<\/p>/i)
|
|
112
|
+
|| block.match(/<div class="b_caption"[^>]*>\s*<p[^>]*>([\s\S]*?)<\/p>/i)
|
|
113
|
+
|| block.match(/<p[^>]*>([\s\S]*?)<\/p>/i);
|
|
114
|
+
|
|
115
|
+
const snippet = snippetMatch
|
|
116
|
+
? snippetMatch[1]!.replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').trim()
|
|
117
|
+
: '';
|
|
118
|
+
|
|
119
|
+
return { title: title.slice(0, 120), url, snippet: snippet.slice(0, 200) };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function extractSnippet(html: string, _url: string): string {
|
|
123
|
+
// 简单回退: 找标题附近的文本
|
|
124
|
+
const idx = html.indexOf(_url) + _url.length;
|
|
125
|
+
const nearby = html.slice(idx, idx + 500);
|
|
126
|
+
const text = nearby.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
127
|
+
return text.slice(0, 200);
|
|
128
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Tool } from '../../tool-types.js';
|
|
2
|
+
|
|
3
|
+
let activeWs: import('ws').WebSocket | null = null;
|
|
4
|
+
|
|
5
|
+
export const websocket_connect: Tool = {
|
|
6
|
+
name: 'websocket_connect',
|
|
7
|
+
description: '建立 WebSocket 连接',
|
|
8
|
+
category: 'network',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
url: { type: 'string', description: 'WebSocket URL (ws:// 或 wss://)' },
|
|
13
|
+
message: { type: 'string', description: '要发送的消息' },
|
|
14
|
+
timeout_ms: { type: 'number', description: '连接超时毫秒数' },
|
|
15
|
+
},
|
|
16
|
+
required: ['url'],
|
|
17
|
+
},
|
|
18
|
+
dangerous: false,
|
|
19
|
+
requiresApproval: true,
|
|
20
|
+
async execute(params) {
|
|
21
|
+
try {
|
|
22
|
+
const url = params.url as string;
|
|
23
|
+
const message = params.message as string | undefined;
|
|
24
|
+
const timeout = (params.timeout_ms as number) ?? 10000;
|
|
25
|
+
|
|
26
|
+
const WS = await import('ws');
|
|
27
|
+
const ws = new WS.WebSocket(url);
|
|
28
|
+
|
|
29
|
+
const result = await new Promise<{ success: boolean; output: string }>((resolve, reject) => {
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
ws.close();
|
|
32
|
+
reject(new Error('WebSocket 连接超时'));
|
|
33
|
+
}, timeout);
|
|
34
|
+
|
|
35
|
+
let response = '';
|
|
36
|
+
|
|
37
|
+
ws.on('open', () => {
|
|
38
|
+
response += `已连接: ${url}\n`;
|
|
39
|
+
if (message) {
|
|
40
|
+
ws.send(message);
|
|
41
|
+
response += `已发送: ${message}\n`;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
ws.on('message', (data: Buffer) => {
|
|
46
|
+
response += `收到: ${data.toString('utf-8')}\n`;
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
ws.close();
|
|
49
|
+
resolve({ success: true, output: response });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
ws.on('error', (err: Error) => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
reject(err);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
ws.on('close', () => {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
if (response) {
|
|
60
|
+
resolve({ success: true, output: response + '\n连接已关闭' });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return result;
|
|
66
|
+
} catch (err: unknown) {
|
|
67
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
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 build_project: Tool = {
|
|
7
|
+
name: 'build_project',
|
|
8
|
+
description: '构建项目',
|
|
9
|
+
category: 'project',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
cwd: { type: 'string', description: '项目目录' },
|
|
14
|
+
target: { type: 'string', description: '构建目标: dev, prod, dist', enum: ['dev', 'prod', 'dist'] },
|
|
15
|
+
tool: { type: 'string', description: '构建工具: auto, tsup, vite, webpack', enum: ['auto', 'tsup', 'vite', 'webpack'] },
|
|
16
|
+
},
|
|
17
|
+
required: [],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: true,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const cwd = (params.cwd as string) ?? process.cwd();
|
|
24
|
+
const target = (params.target as string) ?? 'prod';
|
|
25
|
+
const tool = (params.tool as string) ?? 'auto';
|
|
26
|
+
|
|
27
|
+
let cmd = '';
|
|
28
|
+
const pkgPath = resolve(cwd, 'package.json');
|
|
29
|
+
|
|
30
|
+
if (tool === 'auto' && existsSync(pkgPath)) {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
32
|
+
const scripts = pkg.scripts || {};
|
|
33
|
+
if (scripts.build) cmd = `npm run build`;
|
|
34
|
+
else if (scripts.compile) cmd = `npm run compile`;
|
|
35
|
+
else if (scripts.bundle) cmd = `npm run bundle`;
|
|
36
|
+
else cmd = `npx tsup src/index.ts --format esm`;
|
|
37
|
+
} else {
|
|
38
|
+
switch (tool) {
|
|
39
|
+
case 'tsup': cmd = 'npx tsup src/index.ts --format esm'; break;
|
|
40
|
+
case 'vite': cmd = 'npx vite build'; break;
|
|
41
|
+
case 'webpack': cmd = 'npx webpack --mode production'; break;
|
|
42
|
+
default: cmd = 'npm run build';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const output = execSync(cmd, {
|
|
47
|
+
cwd,
|
|
48
|
+
encoding: 'utf-8',
|
|
49
|
+
timeout: 180000,
|
|
50
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
51
|
+
stdio: 'pipe',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
output: `构建完成\n${output.slice(0, 10000)}`,
|
|
57
|
+
metadata: { cwd, tool, command: cmd },
|
|
58
|
+
};
|
|
59
|
+
} catch (err: unknown) {
|
|
60
|
+
const e = err as { message?: string; stdout?: string; stderr?: string };
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
error: '构建失败: ' + (e.message || String(err)),
|
|
64
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
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 config_manage: Tool = {
|
|
6
|
+
name: 'config_manage',
|
|
7
|
+
description: '管理项目配置文件',
|
|
8
|
+
category: 'project',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
action: { type: 'string', description: '操作: read, set, list, init', enum: ['read', 'set', 'list', 'init'] },
|
|
13
|
+
file_path: { type: 'string', description: '配置文件路径 (.json, .yaml, .env, .toml)' },
|
|
14
|
+
key: { type: 'string', description: '配置键名' },
|
|
15
|
+
value: { type: 'string', description: '配置值' },
|
|
16
|
+
},
|
|
17
|
+
required: ['action'],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: true,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const action = params.action as string;
|
|
24
|
+
const filePath = params.file_path as string | undefined;
|
|
25
|
+
const key = params.key as string | undefined;
|
|
26
|
+
const value = params.value as string | undefined;
|
|
27
|
+
|
|
28
|
+
if (action === 'list') {
|
|
29
|
+
const configs = [
|
|
30
|
+
'.env', '.env.local', '.env.development', '.env.production',
|
|
31
|
+
'package.json', 'tsconfig.json', '.eslintrc.json', '.prettierrc',
|
|
32
|
+
];
|
|
33
|
+
const cwd = process.cwd();
|
|
34
|
+
const found = configs.filter(f => existsSync(resolve(cwd, f)));
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
output: `项目配置文件:\n${found.map(f => ` ${f}`).join('\n') || ' (未找到)'}`,
|
|
38
|
+
metadata: { files: found },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (action === 'init') {
|
|
43
|
+
const defaultEnv = `# DeeperCode 配置文件\nNODE_ENV=development\n`;
|
|
44
|
+
writeFileSync(resolve(process.cwd(), '.env'), defaultEnv, 'utf-8');
|
|
45
|
+
return { success: true, output: '已创建 .env 配置文件' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!filePath) {
|
|
49
|
+
return { success: false, error: '缺少 file_path 参数', output: '' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const abs = resolve(filePath);
|
|
53
|
+
if (!existsSync(abs)) {
|
|
54
|
+
return { success: false, error: `配置文件不存在: ${abs}`, output: '' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (action === 'read') {
|
|
58
|
+
const content = readFileSync(abs, 'utf-8');
|
|
59
|
+
if (filePath.endsWith('.json')) {
|
|
60
|
+
const parsed = JSON.parse(content);
|
|
61
|
+
if (key) {
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
output: `${key}: ${JSON.stringify((parsed as Record<string, unknown>)[key])}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { success: true, output: JSON.stringify(parsed, null, 2) };
|
|
68
|
+
}
|
|
69
|
+
return { success: true, output: content.slice(0, 10000) };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (action === 'set' && key && value !== undefined) {
|
|
73
|
+
if (filePath.endsWith('.json')) {
|
|
74
|
+
const parsed = JSON.parse(readFileSync(abs, 'utf-8'));
|
|
75
|
+
try { parsed[key] = JSON.parse(value); } catch { parsed[key] = value; }
|
|
76
|
+
writeFileSync(abs, JSON.stringify(parsed, null, 2), 'utf-8');
|
|
77
|
+
} else {
|
|
78
|
+
const content = readFileSync(abs, 'utf-8');
|
|
79
|
+
const lines = content.split('\n');
|
|
80
|
+
let found = false;
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
if (lines[i].startsWith(`${key}=`)) {
|
|
83
|
+
lines[i] = `${key}=${value}`;
|
|
84
|
+
found = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!found) lines.push(`${key}=${value}`);
|
|
89
|
+
writeFileSync(abs, lines.join('\n'), 'utf-8');
|
|
90
|
+
}
|
|
91
|
+
return { success: true, output: `已设置: ${key} = ${value}` };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { success: false, error: `不支持的操作组合: ${action}`, output: '' };
|
|
95
|
+
} catch (err: unknown) {
|
|
96
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
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 coverage_report: Tool = {
|
|
7
|
+
name: 'coverage_report',
|
|
8
|
+
description: '生成代码覆盖报告',
|
|
9
|
+
category: 'project',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
cwd: { type: 'string', description: '项目目录' },
|
|
14
|
+
runner: { type: 'string', description: '测试运行器', enum: ['vitest', 'jest', 'nyc'] },
|
|
15
|
+
format: { type: 'string', description: '报告格式: text, html, json, lcov', enum: ['text', 'html', 'json', 'lcov'] },
|
|
16
|
+
},
|
|
17
|
+
required: [],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: false,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const cwd = (params.cwd as string) ?? process.cwd();
|
|
24
|
+
const runner = (params.runner as string) ?? 'vitest';
|
|
25
|
+
const format = (params.format as string) ?? 'text';
|
|
26
|
+
|
|
27
|
+
let cmd = '';
|
|
28
|
+
switch (runner) {
|
|
29
|
+
case 'vitest': cmd = `npx vitest run --coverage --reporter=${format}`; break;
|
|
30
|
+
case 'jest': cmd = `npx jest --coverage`; break;
|
|
31
|
+
case 'nyc': cmd = `npx nyc --reporter=${format} npm test`; break;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const output = execSync(cmd, {
|
|
36
|
+
cwd,
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
timeout: 180000,
|
|
39
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
40
|
+
stdio: 'pipe',
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
success: true,
|
|
44
|
+
output: `覆盖报告:\n${output.slice(0, 10000)}`,
|
|
45
|
+
metadata: { runner, format, cwd },
|
|
46
|
+
};
|
|
47
|
+
} catch (err: unknown) {
|
|
48
|
+
const e = err as { stdout?: string; stderr?: string };
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: '覆盖报告生成失败',
|
|
52
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
} catch (err: unknown) {
|
|
56
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import type { Tool } from '../../tool-types.js';
|
|
5
|
+
|
|
6
|
+
export const docker_manage: Tool = {
|
|
7
|
+
name: 'docker_manage',
|
|
8
|
+
description: 'Docker 容器管理',
|
|
9
|
+
category: 'project',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
action: { type: 'string', description: '操作: build, run, stop, ps, logs, compose_up, compose_down, pull', enum: ['build', 'run', 'stop', 'ps', 'logs', 'compose_up', 'compose_down', 'pull'] },
|
|
14
|
+
image: { type: 'string', description: '镜像名' },
|
|
15
|
+
container: { type: 'string', description: '容器名' },
|
|
16
|
+
cwd: { type: 'string', description: '工作目录' },
|
|
17
|
+
ports: { type: 'string', description: '端口映射' },
|
|
18
|
+
options: { type: 'string', description: '附加选项' },
|
|
19
|
+
},
|
|
20
|
+
required: ['action'],
|
|
21
|
+
},
|
|
22
|
+
dangerous: false,
|
|
23
|
+
requiresApproval: true,
|
|
24
|
+
async execute(params) {
|
|
25
|
+
try {
|
|
26
|
+
const action = params.action as string;
|
|
27
|
+
const image = params.image as string | undefined;
|
|
28
|
+
const container = params.container as string | undefined;
|
|
29
|
+
const cwd = (params.cwd as string) ?? process.cwd();
|
|
30
|
+
const ports = params.ports as string | undefined;
|
|
31
|
+
const options = params.options as string ?? '';
|
|
32
|
+
|
|
33
|
+
let cmd = '';
|
|
34
|
+
|
|
35
|
+
switch (action) {
|
|
36
|
+
case 'build': {
|
|
37
|
+
const dockerfile = resolve(cwd, 'Dockerfile');
|
|
38
|
+
if (!existsSync(dockerfile)) {
|
|
39
|
+
return { success: false, error: `Dockerfile 不存在: ${dockerfile}`, output: '' };
|
|
40
|
+
}
|
|
41
|
+
cmd = `docker build ${options} -t ${image || 'app'} .`;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case 'run':
|
|
45
|
+
cmd = `docker run ${ports ? `-p ${ports}` : ''} ${options} ${image || 'app'}`;
|
|
46
|
+
break;
|
|
47
|
+
case 'stop':
|
|
48
|
+
cmd = `docker stop ${container || ''}`;
|
|
49
|
+
break;
|
|
50
|
+
case 'ps':
|
|
51
|
+
cmd = 'docker ps -a';
|
|
52
|
+
break;
|
|
53
|
+
case 'logs':
|
|
54
|
+
cmd = `docker logs ${container || ''}`;
|
|
55
|
+
break;
|
|
56
|
+
case 'compose_up':
|
|
57
|
+
cmd = 'docker-compose up -d';
|
|
58
|
+
break;
|
|
59
|
+
case 'compose_down':
|
|
60
|
+
cmd = 'docker-compose down';
|
|
61
|
+
break;
|
|
62
|
+
case 'pull':
|
|
63
|
+
cmd = `docker pull ${image || ''}`;
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
return { success: false, error: `不支持的操作: ${action}`, output: '' };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const output = execSync(cmd, {
|
|
71
|
+
cwd,
|
|
72
|
+
encoding: 'utf-8',
|
|
73
|
+
timeout: 120000,
|
|
74
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
75
|
+
stdio: 'pipe',
|
|
76
|
+
});
|
|
77
|
+
return { success: true, output: output || `${action} 完成`, metadata: { action } };
|
|
78
|
+
} catch (err: unknown) {
|
|
79
|
+
const e = err as { message?: string; stdout?: string; stderr?: string };
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: `Docker 操作失败: ${e.message || String(err)}`,
|
|
83
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
} catch (err: unknown) {
|
|
87
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
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 env_manage: Tool = {
|
|
6
|
+
name: 'env_manage',
|
|
7
|
+
description: '管理环境变量文件',
|
|
8
|
+
category: 'project',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
action: { type: 'string', description: '操作: read, set, remove, list_envs', enum: ['read', 'set', 'remove', 'list_envs'] },
|
|
13
|
+
file_path: { type: 'string', description: '.env 文件路径' },
|
|
14
|
+
key: { type: 'string', description: '环境变量名' },
|
|
15
|
+
value: { type: 'string', description: '环境变量值' },
|
|
16
|
+
},
|
|
17
|
+
required: ['action'],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: true,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const action = params.action as string;
|
|
24
|
+
const filePath = (params.file_path as string) ?? '.env';
|
|
25
|
+
const key = params.key as string | undefined;
|
|
26
|
+
const value = params.value as string | undefined;
|
|
27
|
+
|
|
28
|
+
const abs = resolve(filePath);
|
|
29
|
+
|
|
30
|
+
if (action === 'list_envs') {
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
const envs = ['.env', '.env.local', '.env.development', '.env.production', '.env.test'];
|
|
33
|
+
const found = envs.filter(f => existsSync(resolve(cwd, f)));
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
output: found.join('\n') || '未找到 .env 文件',
|
|
37
|
+
metadata: { files: found },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!existsSync(abs)) {
|
|
42
|
+
if (action === 'set' && key && value !== undefined) {
|
|
43
|
+
writeFileSync(abs, `${key}=${value}`, 'utf-8');
|
|
44
|
+
return { success: true, output: `已创建 ${filePath} 并设置 ${key}=${value}` };
|
|
45
|
+
}
|
|
46
|
+
return { success: false, error: `文件不存在: ${abs}`, output: '' };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const content = readFileSync(abs, 'utf-8');
|
|
50
|
+
const lines = content.split('\n');
|
|
51
|
+
|
|
52
|
+
if (action === 'read') {
|
|
53
|
+
if (key) {
|
|
54
|
+
const found = lines.find(l => l.startsWith(`${key}=`));
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
output: found ? found.slice(key.length + 1) : `未找到 ${key}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return { success: true, output: content };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (action === 'set' && key && value !== undefined) {
|
|
64
|
+
let found = false;
|
|
65
|
+
for (let i = 0; i < lines.length; i++) {
|
|
66
|
+
if (lines[i].startsWith(`${key}=`) || lines[i].startsWith(`# ${key}=`)) {
|
|
67
|
+
lines[i] = `${key}=${value}`;
|
|
68
|
+
found = true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!found) lines.push(`${key}=${value}`);
|
|
73
|
+
writeFileSync(abs, lines.join('\n'), 'utf-8');
|
|
74
|
+
return { success: true, output: `已设置 ${key}=${value}` };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (action === 'remove' && key) {
|
|
78
|
+
const filtered = lines.filter(l => !l.startsWith(`${key}=`));
|
|
79
|
+
writeFileSync(abs, filtered.join('\n'), 'utf-8');
|
|
80
|
+
return { success: true, output: `已移除 ${key}` };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { success: false, error: `不支持的操作: ${action}`, output: '' };
|
|
84
|
+
} catch (err: unknown) {
|
|
85
|
+
return { success: false, error: (err as Error).message, output: '' };
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import type { Tool } from '../../tool-types.js';
|
|
3
|
+
|
|
4
|
+
export const npm_manage: Tool = {
|
|
5
|
+
name: 'npm_manage',
|
|
6
|
+
description: '管理 npm 包(安装、更新、删除、查看)',
|
|
7
|
+
category: 'project',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
action: { type: 'string', description: '操作: install, update, remove, list, audit, init', enum: ['install', 'update', 'remove', 'list', 'audit', 'init'] },
|
|
12
|
+
package: { type: 'string', description: '包名(install/remove/update 时使用)' },
|
|
13
|
+
cwd: { type: 'string', description: '工作目录' },
|
|
14
|
+
dev: { type: 'boolean', description: '是否安装为 devDependencies' },
|
|
15
|
+
global: { type: 'boolean', description: '是否全局安装' },
|
|
16
|
+
},
|
|
17
|
+
required: ['action'],
|
|
18
|
+
},
|
|
19
|
+
dangerous: false,
|
|
20
|
+
requiresApproval: true,
|
|
21
|
+
async execute(params) {
|
|
22
|
+
try {
|
|
23
|
+
const action = params.action as string;
|
|
24
|
+
const pkg = params.package as string | undefined;
|
|
25
|
+
const cwd = (params.cwd as string) ?? process.cwd();
|
|
26
|
+
const dev = (params.dev as boolean) ?? false;
|
|
27
|
+
const global = (params.global as boolean) ?? false;
|
|
28
|
+
|
|
29
|
+
const registry = 'https://registry.npmmirror.com';
|
|
30
|
+
let cmd = '';
|
|
31
|
+
|
|
32
|
+
switch (action) {
|
|
33
|
+
case 'install':
|
|
34
|
+
cmd = `npm install ${pkg || ''} ${dev ? '-D' : ''} ${global ? '-g' : ''} --registry ${registry}`;
|
|
35
|
+
break;
|
|
36
|
+
case 'update':
|
|
37
|
+
cmd = `npm update ${pkg || ''} --registry ${registry}`;
|
|
38
|
+
break;
|
|
39
|
+
case 'remove':
|
|
40
|
+
cmd = `npm uninstall ${pkg || ''}`;
|
|
41
|
+
break;
|
|
42
|
+
case 'list':
|
|
43
|
+
cmd = `npm list --depth=0 ${pkg || ''}`;
|
|
44
|
+
break;
|
|
45
|
+
case 'audit':
|
|
46
|
+
cmd = 'npm audit';
|
|
47
|
+
break;
|
|
48
|
+
case 'init':
|
|
49
|
+
cmd = 'npm init -y';
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const output = execSync(cmd, {
|
|
54
|
+
cwd,
|
|
55
|
+
encoding: 'utf-8',
|
|
56
|
+
timeout: 120000,
|
|
57
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
58
|
+
stdio: 'pipe',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return { success: true, output: output || `${action} 完成`, metadata: { action, cwd } };
|
|
62
|
+
} catch (err: unknown) {
|
|
63
|
+
const e = err as { message?: string; stdout?: string; stderr?: string };
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: e.message || String(err),
|
|
67
|
+
output: (e.stdout || '') + (e.stderr || ''),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
};
|