@ww_nero/mini-cli 1.0.96 → 1.0.97
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/package.json +1 -1
- package/src/chat.js +40 -18
- package/src/tools/bash.js +5 -5
- package/src/tools/edit.js +9 -9
- package/src/tools/mcp.js +3 -3
- package/src/tools/read.js +3 -3
- package/src/tools/skills.js +1 -1
- package/src/tools/write.js +4 -4
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -111,16 +111,35 @@ const splitDisplayLines = (text = '') => {
|
|
|
111
111
|
return lines;
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
+
const isToolError = (result) => {
|
|
115
|
+
if (typeof result === 'string' && result.startsWith('[Error]')) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (result && typeof result === 'object' && result.success === false) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
};
|
|
123
|
+
|
|
114
124
|
const formatWriteOutput = (result) => {
|
|
125
|
+
if (isToolError(result)) {
|
|
126
|
+
const errorMsg = typeof result === 'string' ? result : (result.message || '写入失败');
|
|
127
|
+
return { isError: true, message: errorMsg.replace('[Error] ', '') };
|
|
128
|
+
}
|
|
115
129
|
if (!result || typeof result !== 'object' || !result.success) {
|
|
116
|
-
return
|
|
130
|
+
return { isError: true, message: '写入失败' };
|
|
117
131
|
}
|
|
118
|
-
|
|
132
|
+
const lineCount = result.lineCount || countLines(result.content);
|
|
133
|
+
return { isError: false, lineCount };
|
|
119
134
|
};
|
|
120
135
|
|
|
121
136
|
const formatEditOutput = (result) => {
|
|
137
|
+
if (isToolError(result)) {
|
|
138
|
+
const errorMsg = typeof result === 'string' ? result : (result.message || '编辑失败');
|
|
139
|
+
return { isError: true, message: errorMsg.replace('[Error] ', '') };
|
|
140
|
+
}
|
|
122
141
|
if (!result || typeof result !== 'object' || !result.success) {
|
|
123
|
-
return
|
|
142
|
+
return { isError: true, message: '编辑失败' };
|
|
124
143
|
}
|
|
125
144
|
const lines = [];
|
|
126
145
|
const parts = diffLines(result.search || '', result.replace || '');
|
|
@@ -133,7 +152,7 @@ const formatEditOutput = (result) => {
|
|
|
133
152
|
}
|
|
134
153
|
}
|
|
135
154
|
|
|
136
|
-
return lines.join('\n');
|
|
155
|
+
return { isError: false, diff: lines.join('\n') };
|
|
137
156
|
};
|
|
138
157
|
|
|
139
158
|
const stringifyToolResult = (value) => {
|
|
@@ -1000,7 +1019,7 @@ const startChatSession = async ({
|
|
|
1000
1019
|
try {
|
|
1001
1020
|
toolResult = await handler(parsedArgs);
|
|
1002
1021
|
} catch (toolError) {
|
|
1003
|
-
toolResult =
|
|
1022
|
+
toolResult = `[Error] 工具执行异常: ${toolError.message}`;
|
|
1004
1023
|
} finally {
|
|
1005
1024
|
clearLoadingPrompt();
|
|
1006
1025
|
}
|
|
@@ -1011,30 +1030,33 @@ const startChatSession = async ({
|
|
|
1011
1030
|
|
|
1012
1031
|
// Display tool output
|
|
1013
1032
|
if (functionName === 'write') {
|
|
1014
|
-
// For write, display full content in white
|
|
1015
1033
|
const writeOutput = formatWriteOutput(toolResult);
|
|
1016
|
-
if (writeOutput) {
|
|
1017
|
-
console.log(writeOutput);
|
|
1034
|
+
if (writeOutput.isError) {
|
|
1035
|
+
console.log(chalk.red(indentToolResult(writeOutput.message)));
|
|
1036
|
+
} else {
|
|
1037
|
+
console.log(chalk.gray(`已写入,共${writeOutput.lineCount}行`));
|
|
1018
1038
|
}
|
|
1019
1039
|
} else if (functionName === 'edit') {
|
|
1020
|
-
// For edit, display diff-style output
|
|
1021
1040
|
const editOutput = formatEditOutput(toolResult);
|
|
1022
|
-
if (editOutput) {
|
|
1023
|
-
console.log(editOutput);
|
|
1041
|
+
if (editOutput.isError) {
|
|
1042
|
+
console.log(chalk.red(indentToolResult(editOutput.message)));
|
|
1043
|
+
} else if (editOutput.diff) {
|
|
1044
|
+
console.log(editOutput.diff);
|
|
1024
1045
|
}
|
|
1025
1046
|
} else if (functionName === 'read') {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
if (normalized && !normalized.startsWith('读取失败') && !normalized.startsWith('无效路径') && !normalized.startsWith('filePath 参数不能为空')) {
|
|
1030
|
-
console.log(chalk.gray(`已读取,共${lineCount}行`));
|
|
1047
|
+
const isError = rawToolContent.startsWith('[Error]');
|
|
1048
|
+
if (isError) {
|
|
1049
|
+
console.log(chalk.red(indentToolResult(rawToolContent.replace('[Error] ', ''))));
|
|
1031
1050
|
} else {
|
|
1032
|
-
|
|
1051
|
+
const normalized = rawToolContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
|
|
1052
|
+
const lineCount = countLines(normalized);
|
|
1053
|
+
console.log(chalk.gray(`已读取,共${lineCount}行`));
|
|
1033
1054
|
}
|
|
1034
1055
|
} else {
|
|
1056
|
+
const isError = rawToolContent.startsWith('[Error]');
|
|
1035
1057
|
const displayResult = formatToolResultForDisplay(rawToolContent, 100);
|
|
1036
1058
|
if (displayResult) {
|
|
1037
|
-
console.log(chalk.gray(indentToolResult(displayResult)));
|
|
1059
|
+
console.log((isError ? chalk.red : chalk.gray)(indentToolResult(displayResult.replace('[Error] ', ''))));
|
|
1038
1060
|
}
|
|
1039
1061
|
}
|
|
1040
1062
|
|
package/src/tools/bash.js
CHANGED
|
@@ -3,7 +3,7 @@ const { resolveWorkspacePath } = require('../utils/helpers');
|
|
|
3
3
|
|
|
4
4
|
const executeCommand = async ({ command, workingDirectory = '.', isService = false } = {}, context = {}) => {
|
|
5
5
|
if (!command || typeof command !== 'string' || !command.trim()) {
|
|
6
|
-
return 'command 参数不能为空';
|
|
6
|
+
return '[Error] command 参数不能为空';
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const normalizedCommand = command.replace(/\bpython3\b/g, 'python');
|
|
@@ -16,7 +16,7 @@ const executeCommand = async ({ command, workingDirectory = '.', isService = fal
|
|
|
16
16
|
try {
|
|
17
17
|
cwd = resolveWorkspacePath(context.workspaceRoot, workingDirectory || '.');
|
|
18
18
|
} catch (error) {
|
|
19
|
-
return
|
|
19
|
+
return `[Error] 工作目录无效: ${error.message}`;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
return new Promise((resolve) => {
|
|
@@ -53,7 +53,7 @@ const executeCommand = async ({ command, workingDirectory = '.', isService = fal
|
|
|
53
53
|
if (settled) return;
|
|
54
54
|
settled = true;
|
|
55
55
|
cleanup();
|
|
56
|
-
resolve(
|
|
56
|
+
resolve(`[Error] 命令执行失败: ${error.message}`);
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
const handleProcessClose = (code) => {
|
|
@@ -66,7 +66,7 @@ const executeCommand = async ({ command, workingDirectory = '.', isService = fal
|
|
|
66
66
|
} else {
|
|
67
67
|
const errOutput = stderr.trim();
|
|
68
68
|
const stdOutput = stdout.trim();
|
|
69
|
-
resolve(
|
|
69
|
+
resolve(`[Error] 命令执行失败,退出码 ${code}${errOutput ? `\n错误: ${errOutput}` : ''}${stdOutput ? `\n输出: ${stdOutput}` : ''}`);
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
|
|
@@ -98,7 +98,7 @@ const executeCommand = async ({ command, workingDirectory = '.', isService = fal
|
|
|
98
98
|
if (settled) return;
|
|
99
99
|
child.kill('SIGTERM');
|
|
100
100
|
cleanup();
|
|
101
|
-
resolve(
|
|
101
|
+
resolve(`[Error] 命令执行超时 (超过 ${executionTimeout / 1000}s)`);
|
|
102
102
|
}, executionTimeout);
|
|
103
103
|
}
|
|
104
104
|
});
|
package/src/tools/edit.js
CHANGED
|
@@ -10,27 +10,27 @@ const {
|
|
|
10
10
|
|
|
11
11
|
const editFile = async ({ filePath, search, replace } = {}, context = {}) => {
|
|
12
12
|
if (!filePath || typeof filePath !== 'string') {
|
|
13
|
-
return 'filePath 参数不能为空';
|
|
13
|
+
return '[Error] filePath 参数不能为空';
|
|
14
14
|
}
|
|
15
15
|
if (typeof search !== 'string' || search.trim() === '') {
|
|
16
|
-
return 'search 参数不能为空';
|
|
16
|
+
return '[Error] search 参数不能为空';
|
|
17
17
|
}
|
|
18
18
|
if (typeof replace !== 'string') {
|
|
19
|
-
return 'replace 参数必须是字符串,可为空字符串表示删除';
|
|
19
|
+
return '[Error] replace 参数必须是字符串,可为空字符串表示删除';
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
let absolutePath;
|
|
23
23
|
try {
|
|
24
24
|
absolutePath = resolveWorkspacePath(context.workspaceRoot, filePath);
|
|
25
25
|
} catch (error) {
|
|
26
|
-
return
|
|
26
|
+
return `[Error] 无效路径: ${error.message}`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
let originCode;
|
|
30
30
|
try {
|
|
31
31
|
originCode = await readTextFile(absolutePath);
|
|
32
32
|
} catch (error) {
|
|
33
|
-
return
|
|
33
|
+
return `[Error] 读取文件失败 ${filePath}: ${error.message}`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const normalizedOrigin = normalizeLineBreaks(originCode);
|
|
@@ -38,7 +38,7 @@ const editFile = async ({ filePath, search, replace } = {}, context = {}) => {
|
|
|
38
38
|
let normalizedReplace = normalizeLineBreaks(replace);
|
|
39
39
|
|
|
40
40
|
if (!normalizedSearch) {
|
|
41
|
-
return 'search 参数规范化后为空';
|
|
41
|
+
return '[Error] search 参数规范化后为空';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
if (normalizedReplace) {
|
|
@@ -58,12 +58,12 @@ const editFile = async ({ filePath, search, replace } = {}, context = {}) => {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
if (!normalizedOrigin.includes(searchPattern)) {
|
|
61
|
-
return
|
|
61
|
+
return `[Error] 在文件 ${filePath} 中未找到要搜索的内容`;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
const updated = normalizedOrigin.replaceAll(searchPattern, normalizedReplace);
|
|
65
65
|
if (isCodeIdentical(originCode, updated)) {
|
|
66
|
-
return
|
|
66
|
+
return `[Error] 修改前后内容相同: ${filePath}`;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
const processed = processContent(updated);
|
|
@@ -78,7 +78,7 @@ const editFile = async ({ filePath, search, replace } = {}, context = {}) => {
|
|
|
78
78
|
replace: normalizedReplace
|
|
79
79
|
};
|
|
80
80
|
} catch (error) {
|
|
81
|
-
return
|
|
81
|
+
return `[Error] 搜索替换失败 ${filePath}: ${error.message}`;
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
84
|
|
package/src/tools/mcp.js
CHANGED
|
@@ -319,7 +319,7 @@ const resolveImageSource = (workspaceRoot, args = {}) => {
|
|
|
319
319
|
const resolved = path.resolve(root, imageSource);
|
|
320
320
|
const relative = path.relative(root, resolved);
|
|
321
321
|
if (relative.startsWith('..') || path.isAbsolute(relative) || !fs.existsSync(resolved)) {
|
|
322
|
-
return { error: '找不到图片' };
|
|
322
|
+
return { error: '[Error] 找不到图片' };
|
|
323
323
|
}
|
|
324
324
|
return { args: { ...args, image_source: resolved } };
|
|
325
325
|
};
|
|
@@ -438,11 +438,11 @@ class McpManager {
|
|
|
438
438
|
const isError = Boolean(result && result.isError);
|
|
439
439
|
const output = formatMcpContent(result && result.content);
|
|
440
440
|
if (isError) {
|
|
441
|
-
return output ? `MCP 工具返回错误:${output}` : 'MCP 工具返回错误';
|
|
441
|
+
return output ? `[Error] MCP 工具返回错误:${output}` : '[Error] MCP 工具返回错误';
|
|
442
442
|
}
|
|
443
443
|
return output || 'MCP 工具无输出';
|
|
444
444
|
} catch (error) {
|
|
445
|
-
return
|
|
445
|
+
return `[Error] 调用 MCP 工具失败:${error.message}`;
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
};
|
package/src/tools/read.js
CHANGED
|
@@ -2,20 +2,20 @@ const { resolveWorkspacePath, readTextFile } = require('../utils/helpers');
|
|
|
2
2
|
|
|
3
3
|
const readFile = async ({ filePath } = {}, context = {}) => {
|
|
4
4
|
if (!filePath || typeof filePath !== 'string') {
|
|
5
|
-
return 'filePath 参数不能为空';
|
|
5
|
+
return '[Error] filePath 参数不能为空';
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
let absolutePath;
|
|
9
9
|
try {
|
|
10
10
|
absolutePath = resolveWorkspacePath(context.workspaceRoot, filePath);
|
|
11
11
|
} catch (error) {
|
|
12
|
-
return
|
|
12
|
+
return `[Error] 无效路径: ${error.message}`;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
16
|
return await readTextFile(absolutePath);
|
|
17
17
|
} catch (error) {
|
|
18
|
-
return
|
|
18
|
+
return `[Error] 读取失败 ${filePath}: ${error.message}`;
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
|
package/src/tools/skills.js
CHANGED
package/src/tools/write.js
CHANGED
|
@@ -11,17 +11,17 @@ const countLines = (text = '') => {
|
|
|
11
11
|
|
|
12
12
|
const writeFile = async ({ filePath, content } = {}, context = {}) => {
|
|
13
13
|
if (!filePath || typeof filePath !== 'string') {
|
|
14
|
-
return 'filePath 参数不能为空';
|
|
14
|
+
return '[Error] filePath 参数不能为空';
|
|
15
15
|
}
|
|
16
16
|
if (typeof content !== 'string') {
|
|
17
|
-
return 'content 必须是字符串';
|
|
17
|
+
return '[Error] content 必须是字符串';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
let absolutePath;
|
|
21
21
|
try {
|
|
22
22
|
absolutePath = resolveWorkspacePath(context.workspaceRoot, filePath);
|
|
23
23
|
} catch (error) {
|
|
24
|
-
return
|
|
24
|
+
return `[Error] 无效路径: ${error.message}`;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
try {
|
|
@@ -33,7 +33,7 @@ const writeFile = async ({ filePath, content } = {}, context = {}) => {
|
|
|
33
33
|
lineCount: countLines(content)
|
|
34
34
|
};
|
|
35
35
|
} catch (error) {
|
|
36
|
-
return
|
|
36
|
+
return `[Error] 写入失败 ${filePath}: ${error.message}`;
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
|