@ww_nero/mini-cli 1.0.102 → 1.0.103
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 +143 -69
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -760,6 +760,145 @@ const startChatSession = async ({
|
|
|
760
760
|
loadingPromptVisible = false;
|
|
761
761
|
};
|
|
762
762
|
|
|
763
|
+
const displayToolExecutionResult = (functionName, toolResult, rawToolContent) => {
|
|
764
|
+
if (functionName === 'write') {
|
|
765
|
+
const writeOutput = formatWriteOutput(toolResult);
|
|
766
|
+
if (writeOutput.isError) {
|
|
767
|
+
console.log(chalk.red(formatTreeResult(writeOutput.message)));
|
|
768
|
+
} else {
|
|
769
|
+
console.log(chalk.gray(formatTreeResult(`已写入,共${writeOutput.lineCount}行`)));
|
|
770
|
+
}
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (functionName === 'edit') {
|
|
775
|
+
const editOutput = formatEditOutput(toolResult);
|
|
776
|
+
if (editOutput.isError) {
|
|
777
|
+
console.log(chalk.red(indentToolResult(editOutput.message)));
|
|
778
|
+
} else if (editOutput.diff) {
|
|
779
|
+
console.log(editOutput.diff);
|
|
780
|
+
}
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (functionName === 'read') {
|
|
785
|
+
const isError = rawToolContent.startsWith('[Error]');
|
|
786
|
+
if (isError) {
|
|
787
|
+
console.log(chalk.red(formatTreeResult(rawToolContent.replace('[Error] ', ''))));
|
|
788
|
+
} else {
|
|
789
|
+
const normalized = rawToolContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
|
|
790
|
+
const lineCount = countLines(normalized);
|
|
791
|
+
console.log(chalk.gray(formatTreeResult(`已读取,共${lineCount}行`)));
|
|
792
|
+
}
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const isError = rawToolContent.startsWith('[Error]');
|
|
797
|
+
const displayResult = formatToolResultForDisplay(rawToolContent, 100);
|
|
798
|
+
if (displayResult) {
|
|
799
|
+
console.log((isError ? chalk.red : chalk.gray)(formatTreeResult(displayResult.replace('[Error] ', ''))));
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
const executeToolCallAndAppendResult = async (toolCall, { ensureNotAborted } = {}) => {
|
|
804
|
+
if (typeof ensureNotAborted === 'function') {
|
|
805
|
+
ensureNotAborted();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const functionName = toolCall?.function?.name || 'unknown_tool';
|
|
809
|
+
const rawArgs = toolCall?.function?.arguments || '';
|
|
810
|
+
const parsedArgs = safeJsonParse(rawArgs);
|
|
811
|
+
const handler = toolHandlers[functionName];
|
|
812
|
+
const displayArgs = parsedArgs && typeof parsedArgs === 'object'
|
|
813
|
+
? parsedArgs
|
|
814
|
+
: { raw: typeof rawArgs === 'string' ? rawArgs : '' };
|
|
815
|
+
|
|
816
|
+
renderToolCall({ functionName, args: displayArgs }, mcpToolNames);
|
|
817
|
+
|
|
818
|
+
let toolResult;
|
|
819
|
+
if (!parsedArgs || typeof parsedArgs !== 'object') {
|
|
820
|
+
toolResult = '工具调用失败:参数解析错误';
|
|
821
|
+
} else if (!handler) {
|
|
822
|
+
toolResult = `工具 ${functionName} 未实现`;
|
|
823
|
+
} else {
|
|
824
|
+
showLoadingPrompt();
|
|
825
|
+
try {
|
|
826
|
+
toolResult = await handler(parsedArgs);
|
|
827
|
+
} catch (toolError) {
|
|
828
|
+
toolResult = `[Error] 工具执行异常: ${toolError.message}`;
|
|
829
|
+
} finally {
|
|
830
|
+
clearLoadingPrompt();
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const rawToolContent = stringifyToolResult(toolResult);
|
|
835
|
+
const toolContent = enforceToolOutputLimit(rawToolContent, toolOutputTokenLimit);
|
|
836
|
+
|
|
837
|
+
displayToolExecutionResult(functionName, toolResult, rawToolContent);
|
|
838
|
+
process.stdout.write('\n');
|
|
839
|
+
|
|
840
|
+
messages.push({
|
|
841
|
+
role: 'tool',
|
|
842
|
+
tool_call_id: toolCall.id,
|
|
843
|
+
content: toolContent
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
const getPendingToolCallsAtTail = () => {
|
|
848
|
+
let assistantIndex = -1;
|
|
849
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
850
|
+
const message = messages[index];
|
|
851
|
+
if (!message || typeof message !== 'object') {
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
if (message.role === 'tool') {
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
assistantIndex = index;
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
if (assistantIndex < 0) {
|
|
862
|
+
return [];
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const assistantMessage = messages[assistantIndex];
|
|
866
|
+
const toolCalls = Array.isArray(assistantMessage?.tool_calls) ? assistantMessage.tool_calls.filter(Boolean) : [];
|
|
867
|
+
if (assistantMessage?.role !== 'assistant' || toolCalls.length === 0) {
|
|
868
|
+
return [];
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const completedToolCallIds = new Set();
|
|
872
|
+
for (let index = assistantIndex + 1; index < messages.length; index += 1) {
|
|
873
|
+
const message = messages[index];
|
|
874
|
+
if (!message || typeof message !== 'object') {
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (message.role !== 'tool' || !message.tool_call_id) {
|
|
878
|
+
return [];
|
|
879
|
+
}
|
|
880
|
+
completedToolCallIds.add(message.tool_call_id);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return toolCalls.filter((toolCall) => !completedToolCallIds.has(toolCall.id));
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
const resolvePendingToolCallsIfNeeded = async () => {
|
|
887
|
+
const pendingToolCalls = getPendingToolCallsAtTail();
|
|
888
|
+
if (pendingToolCalls.length === 0) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
console.log(chalk.yellow(`检测到上次中断后遗留的 ${pendingToolCalls.length} 个工具调用,正在继续执行...`));
|
|
893
|
+
process.stdout.write('\n');
|
|
894
|
+
|
|
895
|
+
for (const toolCall of pendingToolCalls) {
|
|
896
|
+
await executeToolCallAndAppendResult(toolCall);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
persistHistorySafely();
|
|
900
|
+
};
|
|
901
|
+
|
|
763
902
|
let inFlightController = null;
|
|
764
903
|
let closing = false;
|
|
765
904
|
let cancelNoticeShown = false;
|
|
@@ -880,6 +1019,8 @@ const startChatSession = async ({
|
|
|
880
1019
|
return !slashCommandResult.shouldExit;
|
|
881
1020
|
}
|
|
882
1021
|
|
|
1022
|
+
await resolvePendingToolCallsIfNeeded();
|
|
1023
|
+
|
|
883
1024
|
const targetEndpoint = modelManager.getActiveEndpoint();
|
|
884
1025
|
|
|
885
1026
|
const userMessage = { role: 'user', content: text };
|
|
@@ -1009,76 +1150,9 @@ const startChatSession = async ({
|
|
|
1009
1150
|
messages.push(assistantMessage);
|
|
1010
1151
|
|
|
1011
1152
|
for (const toolCall of toolCalls) {
|
|
1012
|
-
ensureNotAborted();
|
|
1013
|
-
const functionName = toolCall?.function?.name || 'unknown_tool';
|
|
1014
|
-
const rawArgs = toolCall?.function?.arguments || '';
|
|
1015
|
-
const parsedArgs = safeJsonParse(rawArgs);
|
|
1016
|
-
const handler = toolHandlers[functionName];
|
|
1017
|
-
const displayArgs = parsedArgs && typeof parsedArgs === 'object'
|
|
1018
|
-
? parsedArgs
|
|
1019
|
-
: { raw: typeof rawArgs === 'string' ? rawArgs : '' };
|
|
1020
|
-
|
|
1021
1153
|
ensureNewline();
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
let toolResult;
|
|
1025
|
-
|
|
1026
|
-
if (!parsedArgs || typeof parsedArgs !== 'object') {
|
|
1027
|
-
toolResult = '工具调用失败:参数解析错误';
|
|
1028
|
-
} else if (!handler) {
|
|
1029
|
-
toolResult = `工具 ${functionName} 未实现`;
|
|
1030
|
-
} else {
|
|
1031
|
-
showLoadingPrompt();
|
|
1032
|
-
try {
|
|
1033
|
-
toolResult = await handler(parsedArgs);
|
|
1034
|
-
} catch (toolError) {
|
|
1035
|
-
toolResult = `[Error] 工具执行异常: ${toolError.message}`;
|
|
1036
|
-
} finally {
|
|
1037
|
-
clearLoadingPrompt();
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
const rawToolContent = stringifyToolResult(toolResult);
|
|
1042
|
-
const toolContent = enforceToolOutputLimit(rawToolContent, toolOutputTokenLimit);
|
|
1043
|
-
|
|
1044
|
-
// Display tool output
|
|
1045
|
-
if (functionName === 'write') {
|
|
1046
|
-
const writeOutput = formatWriteOutput(toolResult);
|
|
1047
|
-
if (writeOutput.isError) {
|
|
1048
|
-
console.log(chalk.red(formatTreeResult(writeOutput.message)));
|
|
1049
|
-
} else {
|
|
1050
|
-
console.log(chalk.gray(formatTreeResult(`已写入,共${writeOutput.lineCount}行`)));
|
|
1051
|
-
}
|
|
1052
|
-
} else if (functionName === 'edit') {
|
|
1053
|
-
const editOutput = formatEditOutput(toolResult);
|
|
1054
|
-
if (editOutput.isError) {
|
|
1055
|
-
console.log(chalk.red(indentToolResult(editOutput.message)));
|
|
1056
|
-
} else if (editOutput.diff) {
|
|
1057
|
-
console.log(editOutput.diff);
|
|
1058
|
-
}
|
|
1059
|
-
} else if (functionName === 'read') {
|
|
1060
|
-
const isError = rawToolContent.startsWith('[Error]');
|
|
1061
|
-
if (isError) {
|
|
1062
|
-
console.log(chalk.red(formatTreeResult(rawToolContent.replace('[Error] ', ''))));
|
|
1063
|
-
} else {
|
|
1064
|
-
const normalized = rawToolContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
|
|
1065
|
-
const lineCount = countLines(normalized);
|
|
1066
|
-
console.log(chalk.gray(formatTreeResult(`已读取,共${lineCount}行`)));
|
|
1067
|
-
}
|
|
1068
|
-
} else {
|
|
1069
|
-
const isError = rawToolContent.startsWith('[Error]');
|
|
1070
|
-
const displayResult = formatToolResultForDisplay(rawToolContent, 100);
|
|
1071
|
-
if (displayResult) {
|
|
1072
|
-
console.log((isError ? chalk.red : chalk.gray)(formatTreeResult(displayResult.replace('[Error] ', ''))));
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
process.stdout.write('\n');
|
|
1077
|
-
|
|
1078
|
-
messages.push({
|
|
1079
|
-
role: 'tool',
|
|
1080
|
-
tool_call_id: toolCall.id,
|
|
1081
|
-
content: toolContent
|
|
1154
|
+
await executeToolCallAndAppendResult(toolCall, {
|
|
1155
|
+
ensureNotAborted
|
|
1082
1156
|
});
|
|
1083
1157
|
}
|
|
1084
1158
|
persistHistorySafely();
|