yuangs 6.2.3 → 6.4.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/dist/agent/AgentRuntime.d.ts +7 -23
- package/dist/agent/AgentRuntime.js +35 -542
- package/dist/agent/AgentRuntime.js.map +1 -1
- package/dist/agent/DualAgentRuntime.js +16 -16
- package/dist/agent/DualAgentRuntime.js.map +1 -1
- package/dist/agent/ExecutionCompleter.d.ts +31 -0
- package/dist/agent/ExecutionCompleter.js +83 -0
- package/dist/agent/ExecutionCompleter.js.map +1 -0
- package/dist/agent/ExecutionHandler.d.ts +34 -0
- package/dist/agent/ExecutionHandler.js +227 -0
- package/dist/agent/ExecutionHandler.js.map +1 -0
- package/dist/agent/ExecutionLearning.d.ts +8 -0
- package/dist/agent/ExecutionLearning.js +31 -0
- package/dist/agent/ExecutionLearning.js.map +1 -0
- package/dist/agent/ExecutionRecovery.d.ts +26 -0
- package/dist/agent/ExecutionRecovery.js +99 -0
- package/dist/agent/ExecutionRecovery.js.map +1 -0
- package/dist/agent/ExecutionStabilizer.d.ts +20 -0
- package/dist/agent/ExecutionStabilizer.js +101 -0
- package/dist/agent/ExecutionStabilizer.js.map +1 -0
- package/dist/agent/ExecutionTypes.d.ts +13 -0
- package/dist/agent/ExecutionTypes.js +9 -0
- package/dist/agent/ExecutionTypes.js.map +1 -0
- package/dist/agent/LLMCaller.d.ts +19 -0
- package/dist/agent/LLMCaller.js +103 -0
- package/dist/agent/LLMCaller.js.map +1 -0
- package/dist/agent/PreFlightChecker.d.ts +51 -0
- package/dist/agent/PreFlightChecker.js +148 -0
- package/dist/agent/PreFlightChecker.js.map +1 -0
- package/dist/agent/commandSemantics.js +12 -0
- package/dist/agent/commandSemantics.js.map +1 -1
- package/dist/agent/errorTracker.d.ts +10 -0
- package/dist/agent/errorTracker.js +36 -0
- package/dist/agent/errorTracker.js.map +1 -1
- package/dist/agent/executor.d.ts +12 -90
- package/dist/agent/executor.js +59 -881
- package/dist/agent/executor.js.map +1 -1
- package/dist/agent/governance/core.js.map +1 -1
- package/dist/agent/governance/riskScoring.js +6 -3
- package/dist/agent/governance/riskScoring.js.map +1 -1
- package/dist/agent/governance.js +4 -36
- package/dist/agent/governance.js.map +1 -1
- package/dist/agent/llm.js +11 -17
- package/dist/agent/llm.js.map +1 -1
- package/dist/agent/policy/engine.d.ts +2 -5
- package/dist/agent/policy/engine.js.map +1 -1
- package/dist/agent/policy/index.d.ts +1 -0
- package/dist/agent/policy/index.js +1 -0
- package/dist/agent/policy/index.js.map +1 -1
- package/dist/agent/policy/policies/WorkdirWrite.d.ts +11 -0
- package/dist/agent/policy/policies/WorkdirWrite.js +65 -0
- package/dist/agent/policy/policies/WorkdirWrite.js.map +1 -0
- package/dist/agent/policy/policies/noDangerousShell.js +1 -1
- package/dist/agent/policy/policies/noDangerousShell.js.map +1 -1
- package/dist/agent/policy/types.d.ts +6 -4
- package/dist/agent/skills.d.ts +12 -17
- package/dist/agent/skills.js +45 -52
- package/dist/agent/skills.js.map +1 -1
- package/dist/agent/state.d.ts +33 -3
- package/dist/agent/state.js +14 -0
- package/dist/agent/state.js.map +1 -1
- package/dist/agent/tools/AnalyzeDependencies.d.ts +9 -0
- package/dist/agent/tools/AnalyzeDependencies.js +91 -0
- package/dist/agent/tools/AnalyzeDependencies.js.map +1 -0
- package/dist/agent/tools/AppendFile.d.ts +8 -0
- package/dist/agent/tools/AppendFile.js +36 -0
- package/dist/agent/tools/AppendFile.js.map +1 -0
- package/dist/agent/tools/CodeDiff.d.ts +8 -0
- package/dist/agent/tools/CodeDiff.js +36 -0
- package/dist/agent/tools/CodeDiff.js.map +1 -0
- package/dist/agent/tools/ContinueReading.d.ts +8 -0
- package/dist/agent/tools/ContinueReading.js +59 -0
- package/dist/agent/tools/ContinueReading.js.map +1 -0
- package/dist/agent/tools/FileInfo.d.ts +8 -0
- package/dist/agent/tools/FileInfo.js +43 -0
- package/dist/agent/tools/FileInfo.js.map +1 -0
- package/dist/agent/tools/GitDiff.d.ts +8 -0
- package/dist/agent/tools/GitDiff.js +40 -0
- package/dist/agent/tools/GitDiff.js.map +1 -0
- package/dist/agent/tools/GitLog.d.ts +8 -0
- package/dist/agent/tools/GitLog.js +36 -0
- package/dist/agent/tools/GitLog.js.map +1 -0
- package/dist/agent/tools/GitStatus.d.ts +8 -0
- package/dist/agent/tools/GitStatus.js +38 -0
- package/dist/agent/tools/GitStatus.js.map +1 -0
- package/dist/agent/tools/ListDirectoryTree.d.ts +9 -0
- package/dist/agent/tools/ListDirectoryTree.js +63 -0
- package/dist/agent/tools/ListDirectoryTree.js.map +1 -0
- package/dist/agent/tools/ListFiles.d.ts +9 -0
- package/dist/agent/tools/ListFiles.js +53 -0
- package/dist/agent/tools/ListFiles.js.map +1 -0
- package/dist/agent/tools/ReadFile.d.ts +8 -0
- package/dist/agent/tools/ReadFile.js +33 -0
- package/dist/agent/tools/ReadFile.js.map +1 -0
- package/dist/agent/tools/ReadFileLines.d.ts +8 -0
- package/dist/agent/tools/ReadFileLines.js +45 -0
- package/dist/agent/tools/ReadFileLines.js.map +1 -0
- package/dist/agent/tools/ReadFileLinesFromEnd.d.ts +8 -0
- package/dist/agent/tools/ReadFileLinesFromEnd.js +48 -0
- package/dist/agent/tools/ReadFileLinesFromEnd.js.map +1 -0
- package/dist/agent/tools/SearchInFiles.d.ts +8 -0
- package/dist/agent/tools/SearchInFiles.js +70 -0
- package/dist/agent/tools/SearchInFiles.js.map +1 -0
- package/dist/agent/tools/SearchSymbol.d.ts +8 -0
- package/dist/agent/tools/SearchSymbol.js +62 -0
- package/dist/agent/tools/SearchSymbol.js.map +1 -0
- package/dist/agent/tools/ShellCmd.d.ts +8 -0
- package/dist/agent/tools/ShellCmd.js +40 -0
- package/dist/agent/tools/ShellCmd.js.map +1 -0
- package/dist/agent/tools/WriteFile.d.ts +8 -0
- package/dist/agent/tools/WriteFile.js +35 -0
- package/dist/agent/tools/WriteFile.js.map +1 -0
- package/dist/agent/tools/index.d.ts +20 -0
- package/dist/agent/tools/index.js +50 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/tools/pathSafety.d.ts +9 -0
- package/dist/agent/tools/pathSafety.js +85 -0
- package/dist/agent/tools/pathSafety.js.map +1 -0
- package/dist/agent/tools/registry.d.ts +40 -0
- package/dist/agent/tools/registry.js +77 -0
- package/dist/agent/tools/registry.js.map +1 -0
- package/dist/agent/tools/types.d.ts +21 -0
- package/dist/agent/tools/types.js +3 -0
- package/dist/agent/tools/types.js.map +1 -0
- package/dist/agent/tools/utils.d.ts +43 -0
- package/dist/agent/tools/utils.js +158 -0
- package/dist/agent/tools/utils.js.map +1 -0
- package/dist/agent/types.d.ts +5 -3
- package/dist/ai/client.d.ts +2 -2
- package/dist/ai/client.js +16 -23
- package/dist/ai/client.js.map +1 -1
- package/dist/audit/timeline.js +7 -5
- package/dist/audit/timeline.js.map +1 -1
- package/dist/cli.js +12 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/handleAIChat.js +1 -1
- package/dist/commands/handleAIChat.js.map +1 -1
- package/dist/commands/handleAICommand.js +3 -3
- package/dist/commands/handleAICommand.js.map +1 -1
- package/dist/commands/skillsCommands.js +4 -42
- package/dist/commands/skillsCommands.js.map +1 -1
- package/dist/core/ConfigService.d.ts +87 -0
- package/dist/core/ConfigService.js +259 -0
- package/dist/core/ConfigService.js.map +1 -0
- package/dist/core/executionRecord.d.ts +1 -1
- package/dist/core/explain.js +2 -2
- package/dist/core/explain.js.map +1 -1
- package/dist/core/git/BackupManager.d.ts +30 -0
- package/dist/core/git/BackupManager.js +153 -0
- package/dist/core/git/BackupManager.js.map +1 -0
- package/dist/core/git/ConflictResolver.js.map +1 -1
- package/dist/core/replayDiff.js +5 -5
- package/dist/core/replayDiff.js.map +1 -1
- package/dist/core/skillTypes.d.ts +22 -0
- package/dist/core/skillTypes.js +17 -0
- package/dist/core/skillTypes.js.map +1 -0
- package/dist/utils/Logger.d.ts +24 -5
- package/dist/utils/Logger.js +89 -63
- package/dist/utils/Logger.js.map +1 -1
- package/dist/utils/storage.d.ts +22 -0
- package/dist/utils/storage.js +62 -0
- package/dist/utils/storage.js.map +1 -0
- package/package.json +3 -1
- package/dist/core/ConfigManager.d.ts +0 -39
- package/dist/core/ConfigManager.js +0 -127
- package/dist/core/ConfigManager.js.map +0 -1
package/dist/agent/executor.js
CHANGED
|
@@ -1,67 +1,68 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.ToolExecutor = void 0;
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
|
-
const util_1 = require("util");
|
|
9
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
4
|
const toolCapability_1 = require("./toolCapability");
|
|
12
5
|
const CapabilityLevel_1 = require("../core/capability/CapabilityLevel");
|
|
13
6
|
const commandSemantics_1 = require("./commandSemantics");
|
|
14
|
-
const
|
|
15
|
-
|
|
7
|
+
const tools_1 = require("./tools");
|
|
8
|
+
// Initialize registry with all built-in tools
|
|
9
|
+
const registry = new tools_1.ToolRegistry();
|
|
10
|
+
registry.registerAll([
|
|
11
|
+
new tools_1.ReadFile(),
|
|
12
|
+
new tools_1.ReadFileLines(),
|
|
13
|
+
new tools_1.ReadFileLinesFromEnd(),
|
|
14
|
+
new tools_1.WriteFile(),
|
|
15
|
+
new tools_1.AppendFile(),
|
|
16
|
+
new tools_1.FileInfo(),
|
|
17
|
+
new tools_1.ContinueReading(),
|
|
18
|
+
new tools_1.ListFiles(),
|
|
19
|
+
new tools_1.ListDirectoryTree(),
|
|
20
|
+
new tools_1.SearchInFiles(),
|
|
21
|
+
new tools_1.SearchSymbol(),
|
|
22
|
+
new tools_1.AnalyzeDependencies(),
|
|
23
|
+
new tools_1.GitStatus(),
|
|
24
|
+
new tools_1.GitDiff(),
|
|
25
|
+
new tools_1.GitLog(),
|
|
26
|
+
new tools_1.ShellCmd(),
|
|
27
|
+
new tools_1.CodeDiff(),
|
|
28
|
+
]);
|
|
16
29
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* 集成能力感知的工具调用
|
|
20
|
-
* 集成命令语义分析和路径安全验证
|
|
30
|
+
* ToolExecutor — thin facade over ToolRegistry.
|
|
31
|
+
* Maintains backward compatibility for existing callers (AgentRuntime, DualAgentRuntime).
|
|
21
32
|
*/
|
|
22
33
|
class ToolExecutor {
|
|
23
|
-
static MAX_OUTPUT_LENGTH = 2000;
|
|
24
|
-
static
|
|
25
|
-
static
|
|
26
|
-
static allowedCwd = process.cwd(); // 允许的工作目录
|
|
34
|
+
static MAX_OUTPUT_LENGTH = 2000;
|
|
35
|
+
static currentCapabilityLevel = CapabilityLevel_1.CapabilityLevel.STRUCTURAL;
|
|
36
|
+
static allowedCwd = process.cwd();
|
|
27
37
|
/**
|
|
28
|
-
*
|
|
38
|
+
* Expose the registry for external registration of custom tools.
|
|
29
39
|
*/
|
|
40
|
+
static getRegistry() {
|
|
41
|
+
return registry;
|
|
42
|
+
}
|
|
30
43
|
static setAllowedCwd(cwd) {
|
|
31
44
|
this.allowedCwd = cwd;
|
|
32
45
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
static getAllowedCwd() {
|
|
47
|
+
return this.allowedCwd;
|
|
48
|
+
}
|
|
36
49
|
static analyzeCommandSafety(command) {
|
|
37
50
|
return (0, commandSemantics_1.analyzeCommand)(command, this.allowedCwd);
|
|
38
51
|
}
|
|
39
|
-
/**
|
|
40
|
-
* 设置当前能力等级
|
|
41
|
-
*/
|
|
42
52
|
static setCapabilityLevel(level) {
|
|
43
53
|
this.currentCapabilityLevel = level;
|
|
44
54
|
}
|
|
45
|
-
/**
|
|
46
|
-
* 获取当前能力等级
|
|
47
|
-
*/
|
|
48
55
|
static getCapabilityLevel() {
|
|
49
56
|
return this.currentCapabilityLevel;
|
|
50
57
|
}
|
|
51
|
-
/**
|
|
52
|
-
* 检查工具是否可以被当前能力等级执行
|
|
53
|
-
*/
|
|
54
58
|
static checkToolCapability(toolName) {
|
|
55
59
|
const required = (0, toolCapability_1.getToolCapabilityRequirement)(toolName);
|
|
56
60
|
if (required === null) {
|
|
57
|
-
return { allowed: false };
|
|
61
|
+
return { allowed: false };
|
|
58
62
|
}
|
|
59
63
|
const allowed = (0, toolCapability_1.canExecuteTool)(toolName, this.currentCapabilityLevel);
|
|
60
64
|
return { allowed, required, current: this.currentCapabilityLevel };
|
|
61
65
|
}
|
|
62
|
-
/**
|
|
63
|
-
* 获取指定能力等级下可用的工具列表
|
|
64
|
-
*/
|
|
65
66
|
static getAvailableTools() {
|
|
66
67
|
const tools = [];
|
|
67
68
|
for (const [name] of Object.entries(toolCapability_1.TOOL_CAPABILITY_MAP)) {
|
|
@@ -71,9 +72,6 @@ class ToolExecutor {
|
|
|
71
72
|
}
|
|
72
73
|
return tools;
|
|
73
74
|
}
|
|
74
|
-
/**
|
|
75
|
-
* 获取能力等级的可读名称
|
|
76
|
-
*/
|
|
77
75
|
static getCapabilityName(level) {
|
|
78
76
|
switch (level) {
|
|
79
77
|
case CapabilityLevel_1.CapabilityLevel.SEMANTIC: return 'SEMANTIC (极致语义)';
|
|
@@ -84,74 +82,11 @@ class ToolExecutor {
|
|
|
84
82
|
default: return 'UNKNOWN';
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
|
-
/**
|
|
88
|
-
* 智能截断输出
|
|
89
|
-
* 当输出过长时,返回特殊标记和继续读取的提示
|
|
90
|
-
*/
|
|
91
|
-
static maybeTruncate(output, toolName, filePath) {
|
|
92
|
-
if (output.length <= this.MAX_OUTPUT_LENGTH) {
|
|
93
|
-
return output;
|
|
94
|
-
}
|
|
95
|
-
const truncated = output.slice(0, this.MAX_OUTPUT_LENGTH);
|
|
96
|
-
const remaining = output.length - this.MAX_OUTPUT_LENGTH;
|
|
97
|
-
// 根据工具类型提供不同的建议
|
|
98
|
-
let suggestion = `
|
|
99
|
-
|
|
100
|
-
[⚠️ OUTPUT TRUNCATED]
|
|
101
|
-
输出被截断,还有 ${remaining} 个字符未显示。
|
|
102
|
-
|
|
103
|
-
`;
|
|
104
|
-
if (toolName === 'read_file' && filePath) {
|
|
105
|
-
// 记录读取位置
|
|
106
|
-
this.READ_POSITIONS.set(filePath, this.MAX_OUTPUT_LENGTH);
|
|
107
|
-
suggestion += `
|
|
108
|
-
**建议操作**:
|
|
109
|
-
1. 使用 \`read_file_lines\` 工具读取特定行范围:
|
|
110
|
-
{ "tool_name": "read_file_lines", "parameters": { "path": "${filePath}", "start_line": 1, "end_line": 100 } }
|
|
111
|
-
|
|
112
|
-
2. 使用 \`continue_reading\` 工具继续读取:
|
|
113
|
-
{ "tool_name": "continue_reading", "parameters": { "path": "${filePath}" } }
|
|
114
|
-
|
|
115
|
-
3. 使用 \`search_in_files\` 工具搜索关键词:
|
|
116
|
-
{ "tool_name": "search_in_files", "parameters": { "pattern": "关键词", "path": "${filePath}" } }
|
|
117
|
-
`;
|
|
118
|
-
}
|
|
119
|
-
else if (toolName === 'shell_cmd') {
|
|
120
|
-
suggestion += `
|
|
121
|
-
**建议操作**:
|
|
122
|
-
1. 使用 \`head\` 查看前几行:
|
|
123
|
-
head -n 50 filename
|
|
124
|
-
|
|
125
|
-
2. 使用 \`tail\` 查看后几行:
|
|
126
|
-
tail -n 50 filename
|
|
127
|
-
|
|
128
|
-
3. 使用 \`grep\` 过滤内容:
|
|
129
|
-
grep "keyword" filename
|
|
130
|
-
|
|
131
|
-
4. 将输出重定向到文件再读取:
|
|
132
|
-
command > output.txt && read_file output.txt
|
|
133
|
-
`;
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
suggestion += `
|
|
137
|
-
**建议操作**:
|
|
138
|
-
1. 检查输出是否已经包含所需信息
|
|
139
|
-
2. 使用更精确的搜索参数
|
|
140
|
-
3. 将结果分批处理
|
|
141
|
-
`;
|
|
142
|
-
}
|
|
143
|
-
return truncated + suggestion;
|
|
144
|
-
}
|
|
145
85
|
static async execute(action) {
|
|
146
86
|
const { type, payload } = action;
|
|
147
87
|
try {
|
|
148
88
|
const result = await this.executeAction(type, payload);
|
|
149
|
-
|
|
150
|
-
let filePath;
|
|
151
|
-
if (type === 'tool_call' && payload?.tool_name === 'read_file') {
|
|
152
|
-
filePath = payload?.parameters?.path;
|
|
153
|
-
}
|
|
154
|
-
const truncated = this.maybeTruncate(result.output, type, filePath);
|
|
89
|
+
const truncated = this.maybeTruncate(result);
|
|
155
90
|
return {
|
|
156
91
|
...result,
|
|
157
92
|
output: truncated,
|
|
@@ -190,17 +125,12 @@ class ToolExecutor {
|
|
|
190
125
|
}
|
|
191
126
|
static async executeTool(payload) {
|
|
192
127
|
const toolName = payload.tool_name;
|
|
193
|
-
//
|
|
128
|
+
// Capability check
|
|
194
129
|
const capabilityCheck = this.checkToolCapability(toolName);
|
|
195
130
|
if (!capabilityCheck.allowed) {
|
|
196
131
|
if (capabilityCheck.required === undefined) {
|
|
197
|
-
return {
|
|
198
|
-
success: false,
|
|
199
|
-
error: `Unknown tool: ${toolName}`,
|
|
200
|
-
output: ''
|
|
201
|
-
};
|
|
132
|
+
return { success: false, error: `Unknown tool: ${toolName}`, output: '' };
|
|
202
133
|
}
|
|
203
|
-
// 工具存在但当前能力等级不足
|
|
204
134
|
const currentLevel = capabilityCheck.current ?? this.currentCapabilityLevel;
|
|
205
135
|
return {
|
|
206
136
|
success: false,
|
|
@@ -208,791 +138,39 @@ class ToolExecutor {
|
|
|
208
138
|
output: ''
|
|
209
139
|
};
|
|
210
140
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return await this.toolReadFile(payload.parameters);
|
|
215
|
-
case 'read_file_lines':
|
|
216
|
-
return await this.toolReadFileLines(payload.parameters);
|
|
217
|
-
case 'read_file_lines_from_end':
|
|
218
|
-
return await this.toolReadFileLinesFromEnd(payload.parameters);
|
|
219
|
-
case 'write_file':
|
|
220
|
-
return await this.toolWriteFile(payload.parameters);
|
|
221
|
-
case 'append_file':
|
|
222
|
-
return await this.toolAppendFile(payload.parameters);
|
|
223
|
-
// ===== 文件搜索和列表 =====
|
|
224
|
-
case 'list_files':
|
|
225
|
-
return await this.toolListFiles(payload.parameters);
|
|
226
|
-
case 'list_directory_tree':
|
|
227
|
-
return await this.toolListDirectoryTree(payload.parameters);
|
|
228
|
-
case 'search_in_files':
|
|
229
|
-
return await this.toolSearchInFiles(payload.parameters);
|
|
230
|
-
// ===== 代码分析工具 =====
|
|
231
|
-
case 'search_symbol':
|
|
232
|
-
return await this.toolSearchSymbol(payload.parameters);
|
|
233
|
-
case 'analyze_dependencies':
|
|
234
|
-
return await this.toolAnalyzeDependencies(payload.parameters);
|
|
235
|
-
// ===== Git 操作 =====
|
|
236
|
-
case 'git_status':
|
|
237
|
-
return await this.toolGitStatus(payload.parameters);
|
|
238
|
-
case 'git_diff':
|
|
239
|
-
return await this.toolGitDiff(payload.parameters);
|
|
240
|
-
case 'git_log':
|
|
241
|
-
return await this.toolGitLog(payload.parameters);
|
|
242
|
-
// ===== 上下文管理工具 =====
|
|
243
|
-
case 'continue_reading':
|
|
244
|
-
return await this.toolContinueReading(payload.parameters);
|
|
245
|
-
case 'file_info':
|
|
246
|
-
return await this.toolFileInfo(payload.parameters);
|
|
247
|
-
// ===== 已弃用/未实现 =====
|
|
248
|
-
case 'shell_cmd':
|
|
249
|
-
// 兼容 AI 将 shell_cmd 作为 tool_call 发送的情况
|
|
250
|
-
return await this.executeShell(payload.parameters?.command || payload.command || '');
|
|
251
|
-
case 'web_search':
|
|
252
|
-
return {
|
|
253
|
-
success: false,
|
|
254
|
-
error: 'web_search not implemented yet',
|
|
255
|
-
output: ''
|
|
256
|
-
};
|
|
257
|
-
default:
|
|
258
|
-
return {
|
|
259
|
-
success: false,
|
|
260
|
-
error: `Unknown tool: ${toolName}`,
|
|
261
|
-
output: ''
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
static async toolReadFile(params) {
|
|
266
|
-
const filePath = params.path;
|
|
267
|
-
try {
|
|
268
|
-
const content = await promises_1.default.readFile(filePath, 'utf-8');
|
|
269
|
-
return {
|
|
270
|
-
success: true,
|
|
271
|
-
output: content,
|
|
272
|
-
artifacts: [filePath]
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
const friendly = this.getFriendlyError('read_file', error);
|
|
277
|
-
return {
|
|
278
|
-
success: false,
|
|
279
|
-
error: friendly.message,
|
|
280
|
-
output: friendly.suggestion
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
static async toolWriteFile(params) {
|
|
285
|
-
const filePath = params.path;
|
|
286
|
-
const content = params.content;
|
|
287
|
-
try {
|
|
288
|
-
await promises_1.default.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
289
|
-
await promises_1.default.writeFile(filePath, content, 'utf-8');
|
|
290
|
-
return {
|
|
291
|
-
success: true,
|
|
292
|
-
output: `Successfully wrote ${filePath}`,
|
|
293
|
-
artifacts: [filePath]
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
catch (error) {
|
|
297
|
-
return {
|
|
298
|
-
success: false,
|
|
299
|
-
error: error.message,
|
|
300
|
-
output: ''
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
static async toolListFiles(params) {
|
|
305
|
-
const dirPath = params.path || '.';
|
|
306
|
-
const recursive = params.recursive || false;
|
|
307
|
-
try {
|
|
308
|
-
const files = await this.getFiles(dirPath, recursive);
|
|
309
|
-
return {
|
|
310
|
-
success: true,
|
|
311
|
-
output: JSON.stringify(files, null, 2),
|
|
312
|
-
artifacts: files.map(f => f.path)
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
catch (error) {
|
|
316
|
-
return {
|
|
317
|
-
success: false,
|
|
318
|
-
error: error.message,
|
|
319
|
-
output: ''
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
static async getFiles(dir, recursive) {
|
|
324
|
-
const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
|
|
325
|
-
const files = [];
|
|
326
|
-
for (const entry of entries) {
|
|
327
|
-
const fullPath = path_1.default.join(dir, entry.name);
|
|
328
|
-
if (entry.isDirectory()) {
|
|
329
|
-
files.push({ path: fullPath, type: 'directory' });
|
|
330
|
-
if (recursive) {
|
|
331
|
-
const subFiles = await this.getFiles(fullPath, recursive);
|
|
332
|
-
files.push(...subFiles);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
files.push({ path: fullPath, type: 'file' });
|
|
337
|
-
}
|
|
141
|
+
const tool = registry.get(toolName);
|
|
142
|
+
if (!tool) {
|
|
143
|
+
return { success: false, error: `Unknown tool: ${toolName}`, output: '' };
|
|
338
144
|
}
|
|
339
|
-
return
|
|
145
|
+
return await tool.execute(payload.parameters);
|
|
340
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Execute shell command — delegates to ShellCmd tool but retains
|
|
149
|
+
* the command safety analysis that was part of the original executor.
|
|
150
|
+
*/
|
|
341
151
|
static async executeShell(command) {
|
|
342
|
-
// 语义分析:拦截危险命令
|
|
343
152
|
const analysis = this.analyzeCommandSafety(command);
|
|
344
153
|
if (analysis.category === 'DANGEROUS') {
|
|
345
|
-
return {
|
|
346
|
-
success: false,
|
|
347
|
-
error: analysis.description,
|
|
348
|
-
output: ''
|
|
349
|
-
};
|
|
154
|
+
return { success: false, error: analysis.description, output: '' };
|
|
350
155
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
cwd: process.cwd()
|
|
355
|
-
});
|
|
356
|
-
let output = stdout || stderr || '';
|
|
357
|
-
// 大结果持久化到磁盘
|
|
358
|
-
const baseCmd = command.split(/\s+/)[0];
|
|
359
|
-
const persisted = await (0, toolResultStorage_1.persistToolResult)(output, baseCmd);
|
|
360
|
-
if (persisted) {
|
|
361
|
-
output = `${persisted.preview}\n\n[⚠️ 输出已截断,完整结果已保存到 ${persisted.filepath}(${(0, toolResultStorage_1.formatFileSize)(persisted.originalSize)})]`;
|
|
362
|
-
}
|
|
363
|
-
return {
|
|
364
|
-
success: true,
|
|
365
|
-
output,
|
|
366
|
-
artifacts: persisted ? [persisted.filepath] : []
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
catch (error) {
|
|
370
|
-
return {
|
|
371
|
-
success: false,
|
|
372
|
-
error: error.message,
|
|
373
|
-
output: error.stdout || error.stderr || ''
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
static async executeDiff(diff) {
|
|
378
|
-
try {
|
|
379
|
-
const tempFile = path_1.default.join(process.cwd(), '.yuangs_temp.patch');
|
|
380
|
-
await promises_1.default.writeFile(tempFile, diff, 'utf-8');
|
|
381
|
-
await execAsync(`git apply --check ${tempFile}`, {
|
|
382
|
-
cwd: process.cwd()
|
|
383
|
-
});
|
|
384
|
-
const { stdout: applyOutput } = await execAsync(`git apply ${tempFile}`, {
|
|
385
|
-
cwd: process.cwd()
|
|
386
|
-
});
|
|
387
|
-
await promises_1.default.unlink(tempFile);
|
|
388
|
-
return {
|
|
389
|
-
success: true,
|
|
390
|
-
output: applyOutput || 'Diff applied successfully',
|
|
391
|
-
artifacts: ['.yuangs_temp.patch']
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
return {
|
|
396
|
-
success: false,
|
|
397
|
-
error: error.message,
|
|
398
|
-
output: error.stdout || error.stderr || 'Failed to apply diff'
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
// ===== 新增工具方法实现 =====
|
|
403
|
-
/**
|
|
404
|
-
* 读取文件的指定行范围
|
|
405
|
-
*/
|
|
406
|
-
static async toolReadFileLines(params) {
|
|
407
|
-
const filePath = params.path;
|
|
408
|
-
const startLine = params.start_line || 1;
|
|
409
|
-
const endLine = params.end_line;
|
|
410
|
-
const encoding = params.encoding || 'utf-8';
|
|
411
|
-
try {
|
|
412
|
-
const content = await promises_1.default.readFile(filePath, encoding);
|
|
413
|
-
const lines = String(content).split('\n');
|
|
414
|
-
const startIndex = Math.max(0, startLine - 1);
|
|
415
|
-
const endIndex = endLine ? Math.min(lines.length, endLine) : lines.length;
|
|
416
|
-
if (startIndex >= lines.length) {
|
|
417
|
-
return {
|
|
418
|
-
success: false,
|
|
419
|
-
error: `起始行号 ${startLine} 超出文件范围(文件共 ${lines.length} 行)`,
|
|
420
|
-
output: ''
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
const selectedLines = lines.slice(startIndex, endIndex);
|
|
424
|
-
const result = selectedLines
|
|
425
|
-
.map((line, idx) => `${startIndex + idx + 1}: ${line}`)
|
|
426
|
-
.join('\n');
|
|
427
|
-
return {
|
|
428
|
-
success: true,
|
|
429
|
-
output: result,
|
|
430
|
-
artifacts: [filePath]
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
catch (error) {
|
|
434
|
-
return {
|
|
435
|
-
success: false,
|
|
436
|
-
error: error.message,
|
|
437
|
-
output: ''
|
|
438
|
-
};
|
|
156
|
+
const shellCmd = registry.get('shell_cmd');
|
|
157
|
+
if (!shellCmd) {
|
|
158
|
+
return { success: false, error: 'shell_cmd tool not found', output: '' };
|
|
439
159
|
}
|
|
160
|
+
return await shellCmd.execute({ command });
|
|
440
161
|
}
|
|
441
162
|
/**
|
|
442
|
-
*
|
|
443
|
-
* 例如:count=5 表示读取最后5行,count=5, start_offset=2 表示读取倒数第2到5行
|
|
163
|
+
* Execute diff — delegates to CodeDiff tool.
|
|
444
164
|
*/
|
|
445
|
-
static async
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const encoding = params.encoding || 'utf-8';
|
|
450
|
-
try {
|
|
451
|
-
const content = await promises_1.default.readFile(filePath, encoding);
|
|
452
|
-
const lines = String(content).split('\n');
|
|
453
|
-
const totalLines = lines.length;
|
|
454
|
-
// 计算实际行号
|
|
455
|
-
// startOffset=0 表示从最后一行开始,所以是倒数第 count 行
|
|
456
|
-
// startOffset=2 表示从倒数第 count+2 行开始
|
|
457
|
-
const startIndex = Math.max(0, totalLines - count - startOffset);
|
|
458
|
-
const endIndex = totalLines;
|
|
459
|
-
if (totalLines === 0) {
|
|
460
|
-
return {
|
|
461
|
-
success: true,
|
|
462
|
-
output: '(空文件)',
|
|
463
|
-
artifacts: [filePath]
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
const selectedLines = lines.slice(startIndex, endIndex);
|
|
467
|
-
const result = selectedLines
|
|
468
|
-
.map((line, idx) => `${startIndex + idx + 1}: ${line}`)
|
|
469
|
-
.join('\n');
|
|
470
|
-
return {
|
|
471
|
-
success: true,
|
|
472
|
-
output: result,
|
|
473
|
-
artifacts: [filePath]
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
catch (error) {
|
|
477
|
-
return {
|
|
478
|
-
success: false,
|
|
479
|
-
error: error.message,
|
|
480
|
-
output: ''
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* 向文件末尾追加内容
|
|
486
|
-
*/
|
|
487
|
-
static async toolAppendFile(params) {
|
|
488
|
-
const filePath = params.path;
|
|
489
|
-
const content = params.content;
|
|
490
|
-
const encoding = params.encoding || 'utf-8';
|
|
491
|
-
try {
|
|
492
|
-
await promises_1.default.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
493
|
-
await promises_1.default.appendFile(filePath, content, encoding);
|
|
494
|
-
return {
|
|
495
|
-
success: true,
|
|
496
|
-
output: `Successfully appended to ${filePath}`,
|
|
497
|
-
artifacts: [filePath]
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
catch (error) {
|
|
501
|
-
return {
|
|
502
|
-
success: false,
|
|
503
|
-
error: error.message,
|
|
504
|
-
output: ''
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* 生成目录结构的树形展示
|
|
510
|
-
*/
|
|
511
|
-
static async toolListDirectoryTree(params) {
|
|
512
|
-
const dirPath = params.path || '.';
|
|
513
|
-
const maxDepth = params.max_depth || 3;
|
|
514
|
-
const includeFiles = params.include_files !== false;
|
|
515
|
-
const excludePatterns = params.exclude_patterns || ['node_modules', '.git', 'dist', 'build'];
|
|
516
|
-
try {
|
|
517
|
-
const tree = await this.buildDirectoryTree(dirPath, maxDepth, includeFiles, excludePatterns, 0);
|
|
518
|
-
return {
|
|
519
|
-
success: true,
|
|
520
|
-
output: tree,
|
|
521
|
-
artifacts: []
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
catch (error) {
|
|
525
|
-
return {
|
|
526
|
-
success: false,
|
|
527
|
-
error: error.message,
|
|
528
|
-
output: ''
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
static async buildDirectoryTree(dirPath, maxDepth, includeFiles, excludePatterns, currentDepth) {
|
|
533
|
-
if (currentDepth >= maxDepth)
|
|
534
|
-
return '';
|
|
535
|
-
const entries = await promises_1.default.readdir(dirPath, { withFileTypes: true });
|
|
536
|
-
const lines = [];
|
|
537
|
-
const isLast = (index) => index === entries.length - 1;
|
|
538
|
-
for (let i = 0; i < entries.length; i++) {
|
|
539
|
-
const entry = entries[i];
|
|
540
|
-
const fullName = entry.name;
|
|
541
|
-
// 跳过排除的目录/文件
|
|
542
|
-
if (excludePatterns.some(pattern => fullName.includes(pattern))) {
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
const prefix = currentDepth === 0 ? '' : '│ '.repeat(currentDepth);
|
|
546
|
-
const connector = isLast(i) ? '└── ' : '├── ';
|
|
547
|
-
if (entry.isDirectory()) {
|
|
548
|
-
lines.push(`${prefix}${connector}${fullName}/`);
|
|
549
|
-
const subTree = await this.buildDirectoryTree(path_1.default.join(dirPath, fullName), maxDepth, includeFiles, excludePatterns, currentDepth + 1);
|
|
550
|
-
if (subTree) {
|
|
551
|
-
lines.push(subTree);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
else if (includeFiles) {
|
|
555
|
-
lines.push(`${prefix}${connector}${fullName}`);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
return lines.join('\n');
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* 在文件中搜索指定内容(类似 grep)
|
|
562
|
-
*/
|
|
563
|
-
static async toolSearchInFiles(params) {
|
|
564
|
-
const pattern = params.pattern;
|
|
565
|
-
const searchPath = params.path || '.';
|
|
566
|
-
const filePattern = params.file_pattern;
|
|
567
|
-
const ignoreCase = params.ignore_case || false;
|
|
568
|
-
const contextLines = params.context_lines || 0;
|
|
569
|
-
const maxResults = params.max_results || 100;
|
|
570
|
-
try {
|
|
571
|
-
// 如果指定了文件模式,使用 find + grep 组合,否则直接用 grep
|
|
572
|
-
let baseCmd = 'grep';
|
|
573
|
-
// 构建 grep 命令
|
|
574
|
-
if (ignoreCase)
|
|
575
|
-
baseCmd += ' -i';
|
|
576
|
-
baseCmd += ' -r';
|
|
577
|
-
if (contextLines > 0)
|
|
578
|
-
baseCmd += ` -C ${contextLines}`;
|
|
579
|
-
baseCmd += ` -n`; // 显示行号
|
|
580
|
-
// 转义 pattern
|
|
581
|
-
const escapedPattern = pattern.replace(/'/g, "'\\''");
|
|
582
|
-
let grepCmd;
|
|
583
|
-
if (filePattern) {
|
|
584
|
-
// 使用 find 过滤文件,然后 grep,最后用 head 限制结果
|
|
585
|
-
grepCmd = `find ${searchPath} -type f -name '${filePattern}' -exec grep ${ignoreCase ? '-i' : ''} -n -- '${escapedPattern}' {} + 2>/dev/null | head -n ${maxResults}`;
|
|
586
|
-
}
|
|
587
|
-
else {
|
|
588
|
-
// 直接 grep,用 head 限制结果
|
|
589
|
-
grepCmd = `${baseCmd} -- '${escapedPattern}' ${searchPath} 2>/dev/null | head -n ${maxResults}`;
|
|
590
|
-
}
|
|
591
|
-
// 执行搜索,使用更大的 buffer
|
|
592
|
-
const { stdout } = await execAsync(grepCmd, {
|
|
593
|
-
maxBuffer: 50 * 1024 * 1024, // 50MB
|
|
594
|
-
cwd: process.cwd(),
|
|
595
|
-
shell: '/bin/bash'
|
|
596
|
-
});
|
|
597
|
-
const output = String(stdout).trim();
|
|
598
|
-
const lines = output.split('\n').filter(line => line.trim());
|
|
599
|
-
// 检查是否达到限制
|
|
600
|
-
const hasMore = lines.length >= maxResults;
|
|
601
|
-
const resultOutput = lines.length > 0
|
|
602
|
-
? lines.join('\n') + (hasMore ? `\n\n[⚠️] 结果已限制为前 ${maxResults} 条匹配,使用更大的 max_results 参数获取更多结果` : '')
|
|
603
|
-
: '未找到匹配结果';
|
|
604
|
-
return {
|
|
605
|
-
success: true,
|
|
606
|
-
output: resultOutput,
|
|
607
|
-
artifacts: []
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
catch (error) {
|
|
611
|
-
// grep 没有找到结果时会返回错误码
|
|
612
|
-
if (error.code === 1) {
|
|
613
|
-
return {
|
|
614
|
-
success: true,
|
|
615
|
-
output: '未找到匹配结果',
|
|
616
|
-
artifacts: []
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
return {
|
|
620
|
-
success: false,
|
|
621
|
-
error: error.message,
|
|
622
|
-
output: ''
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* 搜索代码符号(函数、类、变量等)
|
|
628
|
-
* 使用 grep 进行简化搜索
|
|
629
|
-
*/
|
|
630
|
-
static async toolSearchSymbol(params) {
|
|
631
|
-
const symbol = params.symbol;
|
|
632
|
-
const symbolType = params.symbol_type;
|
|
633
|
-
const searchPath = params.path || '.';
|
|
634
|
-
const filePattern = params.file_pattern;
|
|
635
|
-
try {
|
|
636
|
-
let pattern = symbol;
|
|
637
|
-
// 根据符号类型构建搜索模式
|
|
638
|
-
switch (symbolType) {
|
|
639
|
-
case 'function':
|
|
640
|
-
pattern = `function\\s+${symbol}|${symbol}\\s*[:=]\\s*function|const\\s+${symbol}\\s*=`;
|
|
641
|
-
break;
|
|
642
|
-
case 'class':
|
|
643
|
-
pattern = `class\\s+${symbol}`;
|
|
644
|
-
break;
|
|
645
|
-
case 'interface':
|
|
646
|
-
pattern = `interface\\s+${symbol}`;
|
|
647
|
-
break;
|
|
648
|
-
default:
|
|
649
|
-
pattern = symbol;
|
|
650
|
-
}
|
|
651
|
-
let grepCmd = `grep -rn --color=never -E "${pattern}" ${searchPath}`;
|
|
652
|
-
if (filePattern) {
|
|
653
|
-
grepCmd += ` --include="${filePattern}"`;
|
|
654
|
-
}
|
|
655
|
-
const { stdout } = await execAsync(grepCmd, {
|
|
656
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
657
|
-
cwd: process.cwd()
|
|
658
|
-
});
|
|
659
|
-
if (!stdout.trim()) {
|
|
660
|
-
return {
|
|
661
|
-
success: true,
|
|
662
|
-
output: `未找到符号 "${symbol}"`,
|
|
663
|
-
artifacts: []
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
return {
|
|
667
|
-
success: true,
|
|
668
|
-
output: stdout,
|
|
669
|
-
artifacts: []
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
catch (error) {
|
|
673
|
-
if (error.code === 1) {
|
|
674
|
-
return {
|
|
675
|
-
success: true,
|
|
676
|
-
output: `未找到符号 "${symbol}"`,
|
|
677
|
-
artifacts: []
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
return {
|
|
681
|
-
success: false,
|
|
682
|
-
error: error.message,
|
|
683
|
-
output: ''
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* 分析文件的依赖关系(import/require)
|
|
689
|
-
*/
|
|
690
|
-
static async toolAnalyzeDependencies(params) {
|
|
691
|
-
const targetPath = params.path;
|
|
692
|
-
// recursive 参数保留用于未来扩展
|
|
693
|
-
params.recursive;
|
|
694
|
-
try {
|
|
695
|
-
const stat = await promises_1.default.stat(targetPath);
|
|
696
|
-
let files = [];
|
|
697
|
-
if (stat.isFile()) {
|
|
698
|
-
files = [targetPath];
|
|
699
|
-
}
|
|
700
|
-
else if (stat.isDirectory()) {
|
|
701
|
-
// 获取目录下所有文件
|
|
702
|
-
const allFiles = await this.getFiles(targetPath, true);
|
|
703
|
-
files = allFiles
|
|
704
|
-
.filter(f => f.type === 'file')
|
|
705
|
-
.filter(f => /\.(ts|js|tsx|jsx|vue|svelte)$/.test(f.path))
|
|
706
|
-
.map(f => f.path);
|
|
707
|
-
}
|
|
708
|
-
const dependencies = {};
|
|
709
|
-
for (const file of files) {
|
|
710
|
-
const content = await promises_1.default.readFile(file, 'utf-8');
|
|
711
|
-
const deps = [];
|
|
712
|
-
// 匹配 ES6 imports
|
|
713
|
-
const importRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
714
|
-
let match;
|
|
715
|
-
while ((match = importRegex.exec(content)) !== null) {
|
|
716
|
-
deps.push(match[1]);
|
|
717
|
-
}
|
|
718
|
-
// 匹配 CommonJS requires
|
|
719
|
-
const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
|
|
720
|
-
while ((match = requireRegex.exec(content)) !== null) {
|
|
721
|
-
deps.push(match[1]);
|
|
722
|
-
}
|
|
723
|
-
// 匹配动态 imports
|
|
724
|
-
const dynamicImportRegex = /import\(['"]([^'"]+)['"]\)/g;
|
|
725
|
-
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
726
|
-
deps.push(match[1]);
|
|
727
|
-
}
|
|
728
|
-
if (deps.length > 0) {
|
|
729
|
-
dependencies[file] = [...new Set(deps)]; // 去重
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
// 格式化输出
|
|
733
|
-
let output = '依赖关系分析结果:\n\n';
|
|
734
|
-
for (const [file, deps] of Object.entries(dependencies)) {
|
|
735
|
-
output += `${file}:\n`;
|
|
736
|
-
for (const dep of deps) {
|
|
737
|
-
output += ` - ${dep}\n`;
|
|
738
|
-
}
|
|
739
|
-
output += '\n';
|
|
740
|
-
}
|
|
741
|
-
return {
|
|
742
|
-
success: true,
|
|
743
|
-
output: output || '未找到依赖关系',
|
|
744
|
-
artifacts: []
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
catch (error) {
|
|
748
|
-
return {
|
|
749
|
-
success: false,
|
|
750
|
-
error: error.message,
|
|
751
|
-
output: ''
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* Git 状态
|
|
757
|
-
*/
|
|
758
|
-
static async toolGitStatus(params) {
|
|
759
|
-
const repoPath = params.path || '.';
|
|
760
|
-
try {
|
|
761
|
-
const { stdout } = await execAsync(`git -C ${repoPath} status --porcelain -b`, {
|
|
762
|
-
cwd: process.cwd()
|
|
763
|
-
});
|
|
764
|
-
const { stdout: branchInfo } = await execAsync(`git -C ${repoPath} branch --show-current`, {
|
|
765
|
-
cwd: process.cwd()
|
|
766
|
-
});
|
|
767
|
-
let output = `当前分支: ${branchInfo.trim()}\n\n`;
|
|
768
|
-
if (!stdout.trim()) {
|
|
769
|
-
output += '工作区干净,没有未提交的更改';
|
|
770
|
-
}
|
|
771
|
-
else {
|
|
772
|
-
output += '未提交的更改:\n';
|
|
773
|
-
output += stdout;
|
|
774
|
-
}
|
|
775
|
-
return {
|
|
776
|
-
success: true,
|
|
777
|
-
output,
|
|
778
|
-
artifacts: []
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
catch (error) {
|
|
782
|
-
return {
|
|
783
|
-
success: false,
|
|
784
|
-
error: error.message,
|
|
785
|
-
output: '不是 Git 仓库或 Git 命令执行失败'
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
/**
|
|
790
|
-
* Git 差异
|
|
791
|
-
*/
|
|
792
|
-
static async toolGitDiff(params) {
|
|
793
|
-
const filePath = params.file;
|
|
794
|
-
const cached = params.cached || false;
|
|
795
|
-
const lines = params.lines;
|
|
796
|
-
try {
|
|
797
|
-
let cmd = 'git diff';
|
|
798
|
-
if (cached)
|
|
799
|
-
cmd += ' --cached';
|
|
800
|
-
if (filePath)
|
|
801
|
-
cmd += ` -- ${filePath}`;
|
|
802
|
-
if (lines)
|
|
803
|
-
cmd += ` | head -n ${lines}`;
|
|
804
|
-
const { stdout } = await execAsync(cmd, {
|
|
805
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
806
|
-
cwd: process.cwd()
|
|
807
|
-
});
|
|
808
|
-
if (!stdout.trim()) {
|
|
809
|
-
return {
|
|
810
|
-
success: true,
|
|
811
|
-
output: filePath ? `文件 ${filePath} 没有更改` : '没有更改',
|
|
812
|
-
artifacts: []
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
return {
|
|
816
|
-
success: true,
|
|
817
|
-
output: stdout,
|
|
818
|
-
artifacts: []
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
catch (error) {
|
|
822
|
-
return {
|
|
823
|
-
success: false,
|
|
824
|
-
error: error.message,
|
|
825
|
-
output: ''
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* Git 日志
|
|
831
|
-
*/
|
|
832
|
-
static async toolGitLog(params) {
|
|
833
|
-
const maxCount = params.max_count || 10;
|
|
834
|
-
const filePath = params.file;
|
|
835
|
-
const oneline = params.oneline !== false;
|
|
836
|
-
try {
|
|
837
|
-
let cmd = `git log -n ${maxCount}`;
|
|
838
|
-
if (oneline)
|
|
839
|
-
cmd += ' --oneline';
|
|
840
|
-
if (filePath)
|
|
841
|
-
cmd += ` -- ${filePath}`;
|
|
842
|
-
const { stdout } = await execAsync(cmd, {
|
|
843
|
-
cwd: process.cwd()
|
|
844
|
-
});
|
|
845
|
-
return {
|
|
846
|
-
success: true,
|
|
847
|
-
output: stdout || '没有提交历史',
|
|
848
|
-
artifacts: []
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
catch (error) {
|
|
852
|
-
return {
|
|
853
|
-
success: false,
|
|
854
|
-
error: error.message,
|
|
855
|
-
output: ''
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* 继续读取之前被截断的文件内容
|
|
861
|
-
*/
|
|
862
|
-
static async toolContinueReading(params) {
|
|
863
|
-
const filePath = params.path;
|
|
864
|
-
const fromPosition = params.from_position;
|
|
865
|
-
const length = params.length || this.MAX_OUTPUT_LENGTH;
|
|
866
|
-
try {
|
|
867
|
-
let startPos = fromPosition;
|
|
868
|
-
// 如果没有指定位置,尝试从记录中获取
|
|
869
|
-
if (startPos === undefined) {
|
|
870
|
-
startPos = this.READ_POSITIONS.get(filePath) || this.MAX_OUTPUT_LENGTH;
|
|
871
|
-
}
|
|
872
|
-
const content = await promises_1.default.readFile(filePath, 'utf-8');
|
|
873
|
-
if (startPos >= content.length) {
|
|
874
|
-
return {
|
|
875
|
-
success: true,
|
|
876
|
-
output: '[EOF] 已到达文件末尾',
|
|
877
|
-
artifacts: []
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
const endPos = Math.min(startPos + length, content.length);
|
|
881
|
-
const result = content.slice(startPos, endPos);
|
|
882
|
-
// 更新读取位置
|
|
883
|
-
this.READ_POSITIONS.set(filePath, endPos);
|
|
884
|
-
const prefix = `[从位置 ${startPos} 读取,共 ${result.length} 字符]\n\n`;
|
|
885
|
-
const remaining = content.length - endPos;
|
|
886
|
-
let suffix = '';
|
|
887
|
-
if (remaining > 0) {
|
|
888
|
-
suffix = `\n\n[还有 ${remaining} 字符未读取,使用 continue_reading 继续]`;
|
|
889
|
-
this.READ_POSITIONS.set(filePath, endPos);
|
|
890
|
-
}
|
|
891
|
-
else {
|
|
892
|
-
this.READ_POSITIONS.delete(filePath); // 到达末尾,清除记录
|
|
893
|
-
}
|
|
894
|
-
return {
|
|
895
|
-
success: true,
|
|
896
|
-
output: prefix + result + suffix,
|
|
897
|
-
readPosition: endPos,
|
|
898
|
-
artifacts: [filePath]
|
|
899
|
-
};
|
|
900
|
-
}
|
|
901
|
-
catch (error) {
|
|
902
|
-
return {
|
|
903
|
-
success: false,
|
|
904
|
-
error: error.message,
|
|
905
|
-
output: ''
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* 获取文件元信息
|
|
911
|
-
*/
|
|
912
|
-
static async toolFileInfo(params) {
|
|
913
|
-
const filePath = params.path;
|
|
914
|
-
try {
|
|
915
|
-
const stat = await promises_1.default.stat(filePath);
|
|
916
|
-
const info = {
|
|
917
|
-
path: filePath,
|
|
918
|
-
type: stat.isDirectory() ? 'directory' : 'file',
|
|
919
|
-
size: stat.size,
|
|
920
|
-
sizeHuman: this.formatBytes(stat.size),
|
|
921
|
-
modified: stat.mtime.toISOString(),
|
|
922
|
-
created: stat.birthtime.toISOString(),
|
|
923
|
-
permissions: stat.mode.toString(8),
|
|
924
|
-
isReadable: !!(stat.mode & parseInt('0400', 8)), // 检查读权限
|
|
925
|
-
isWritable: !!(stat.mode & parseInt('0200', 8)) // 检查写权限
|
|
926
|
-
};
|
|
927
|
-
return {
|
|
928
|
-
success: true,
|
|
929
|
-
output: JSON.stringify(info, null, 2),
|
|
930
|
-
artifacts: []
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
catch (error) {
|
|
934
|
-
return {
|
|
935
|
-
success: false,
|
|
936
|
-
error: error.message,
|
|
937
|
-
output: ''
|
|
938
|
-
};
|
|
165
|
+
static async executeDiff(diff) {
|
|
166
|
+
const codeDiff = registry.get('code_diff');
|
|
167
|
+
if (!codeDiff) {
|
|
168
|
+
return { success: false, error: 'code_diff tool not found', output: '' };
|
|
939
169
|
}
|
|
170
|
+
return await codeDiff.execute({ diff });
|
|
940
171
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
*/
|
|
944
|
-
static formatBytes(bytes) {
|
|
945
|
-
if (bytes === 0)
|
|
946
|
-
return '0 B';
|
|
947
|
-
const k = 1024;
|
|
948
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
949
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
950
|
-
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
951
|
-
}
|
|
952
|
-
/**
|
|
953
|
-
* 获取用户友好的错误消息和建议
|
|
954
|
-
*/
|
|
955
|
-
static getFriendlyError(toolName, error) {
|
|
956
|
-
const errorMsg = error?.message || String(error);
|
|
957
|
-
const lowerError = errorMsg.toLowerCase();
|
|
958
|
-
// 文件不存在
|
|
959
|
-
if (lowerError.includes('enoent') || lowerError.includes('no such file') || lowerError.includes('not found')) {
|
|
960
|
-
const match = errorMsg.match(/['"](.*?)['"]|['`](.*?)['`]/);
|
|
961
|
-
const fileName = match ? match[1] : '指定文件';
|
|
962
|
-
return {
|
|
963
|
-
message: `文件未找到: ${fileName}`,
|
|
964
|
-
suggestion: `💡 建议:使用 list_files 查看可用文件,或检查文件路径是否正确`
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
// 权限错误
|
|
968
|
-
if (lowerError.includes('eacces') || lowerError.includes('permission denied')) {
|
|
969
|
-
return {
|
|
970
|
-
message: `权限不足:无法访问该文件`,
|
|
971
|
-
suggestion: `💡 建议:检查文件权限,或使用 sudo(如果适用)`
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
// 语法错误
|
|
975
|
-
if (lowerError.includes('syntax') || lowerError.includes('parse')) {
|
|
976
|
-
return {
|
|
977
|
-
message: `命令语法错误`,
|
|
978
|
-
suggestion: `💡 建议:检查命令格式,参考工具文档`
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
// 根据工具名称提供特定建议
|
|
982
|
-
const suggestions = {
|
|
983
|
-
read_file_lines: '💡 建议:检查 start_line 是否在文件范围内,使用 file_info 查看文件总行数',
|
|
984
|
-
read_file_lines_from_end: '💡 建议:count 参数不要超过文件总行数',
|
|
985
|
-
search_in_files: '💡 建议:使用更具体的搜索词,或使用 file_pattern 限制搜索范围',
|
|
986
|
-
search_symbol: '💡 建议:检查符号名称是否正确,或尝试使用 search_in_files 搜索',
|
|
987
|
-
write_file: '💡 建议:确保目录存在,检查文件路径是否正确',
|
|
988
|
-
git_status: '💡 建议:确认当前在 Git 仓库中',
|
|
989
|
-
git_diff: '💡 建议:检查是否有未提交的更改',
|
|
990
|
-
git_log: '💡 建议:检查是否有提交历史'
|
|
991
|
-
};
|
|
992
|
-
return {
|
|
993
|
-
message: errorMsg,
|
|
994
|
-
suggestion: suggestions[toolName] || '💡 建议:检查参数是否正确,或尝试不同的工具'
|
|
995
|
-
};
|
|
172
|
+
static maybeTruncate(result) {
|
|
173
|
+
return (0, tools_1.maybeTruncateOutput)(result.output, undefined, undefined);
|
|
996
174
|
}
|
|
997
175
|
}
|
|
998
176
|
exports.ToolExecutor = ToolExecutor;
|