zencode-cli 0.2.2 → 0.4.1
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/bin/zencode.js +791 -664
- package/dist/bin/zencode.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/zencode.js
CHANGED
|
@@ -56,6 +56,7 @@ var init_client = __esm({
|
|
|
56
56
|
params.tools = tools;
|
|
57
57
|
params.tool_choice = "auto";
|
|
58
58
|
}
|
|
59
|
+
params.stream_options = { include_usage: true };
|
|
59
60
|
const abortController = new AbortController();
|
|
60
61
|
this.activeAbortController = abortController;
|
|
61
62
|
try {
|
|
@@ -67,7 +68,16 @@ var init_client = __esm({
|
|
|
67
68
|
let reasoningStarted = false;
|
|
68
69
|
let reasoningEnded = false;
|
|
69
70
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
71
|
+
let usageInfo = null;
|
|
70
72
|
for await (const chunk of stream) {
|
|
73
|
+
const chunkUsage = chunk.usage;
|
|
74
|
+
if (chunkUsage && chunkUsage.total_tokens) {
|
|
75
|
+
usageInfo = {
|
|
76
|
+
prompt_tokens: chunkUsage.prompt_tokens ?? 0,
|
|
77
|
+
completion_tokens: chunkUsage.completion_tokens ?? 0,
|
|
78
|
+
total_tokens: chunkUsage.total_tokens
|
|
79
|
+
};
|
|
80
|
+
}
|
|
71
81
|
const choice = chunk.choices[0];
|
|
72
82
|
if (!choice) continue;
|
|
73
83
|
const delta = choice.delta;
|
|
@@ -140,6 +150,9 @@ var init_client = __esm({
|
|
|
140
150
|
if (toolCalls.length > 0) {
|
|
141
151
|
assistantMessage.tool_calls = toolCalls;
|
|
142
152
|
}
|
|
153
|
+
if (usageInfo) {
|
|
154
|
+
assistantMessage.usage = usageInfo;
|
|
155
|
+
}
|
|
143
156
|
callbacks.onFinish?.(assistantMessage);
|
|
144
157
|
return assistantMessage;
|
|
145
158
|
} catch (error) {
|
|
@@ -193,6 +206,13 @@ var init_client = __esm({
|
|
|
193
206
|
}
|
|
194
207
|
}));
|
|
195
208
|
}
|
|
209
|
+
if (response.usage) {
|
|
210
|
+
result.usage = {
|
|
211
|
+
prompt_tokens: response.usage.prompt_tokens,
|
|
212
|
+
completion_tokens: response.usage.completion_tokens,
|
|
213
|
+
total_tokens: response.usage.total_tokens
|
|
214
|
+
};
|
|
215
|
+
}
|
|
196
216
|
return result;
|
|
197
217
|
}
|
|
198
218
|
get modelName() {
|
|
@@ -797,7 +817,16 @@ function formatToolDetail(toolName, params) {
|
|
|
797
817
|
}
|
|
798
818
|
async function confirmExecution(toolName, params) {
|
|
799
819
|
if (structuredConfirmHandler) {
|
|
800
|
-
|
|
820
|
+
const result = await new Promise((resolve10) => {
|
|
821
|
+
pendingConfirmResolver = resolve10;
|
|
822
|
+
structuredConfirmHandler(toolName, params).then((res) => {
|
|
823
|
+
if (pendingConfirmResolver === resolve10) {
|
|
824
|
+
pendingConfirmResolver = null;
|
|
825
|
+
resolve10(res);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
});
|
|
829
|
+
return result;
|
|
801
830
|
}
|
|
802
831
|
const detail = formatToolDetail(toolName, params);
|
|
803
832
|
const prompt = `
|
|
@@ -808,12 +837,13 @@ ${detail}
|
|
|
808
837
|
const approved = await globalConfirmHandler(`${chalk.yellow("?")} \u662F\u5426\u6267\u884C\uFF1F(${chalk.green("y")}/${chalk.red("N")}) `);
|
|
809
838
|
return { approved };
|
|
810
839
|
}
|
|
811
|
-
var globalConfirmHandler, structuredConfirmHandler;
|
|
840
|
+
var globalConfirmHandler, structuredConfirmHandler, pendingConfirmResolver;
|
|
812
841
|
var init_permission = __esm({
|
|
813
842
|
"src/tools/permission.ts"() {
|
|
814
843
|
"use strict";
|
|
815
844
|
globalConfirmHandler = async () => false;
|
|
816
845
|
structuredConfirmHandler = null;
|
|
846
|
+
pendingConfirmResolver = null;
|
|
817
847
|
}
|
|
818
848
|
});
|
|
819
849
|
|
|
@@ -970,21 +1000,32 @@ var init_agent = __esm({
|
|
|
970
1000
|
tools.length > 0 ? tools : void 0,
|
|
971
1001
|
callbacks
|
|
972
1002
|
);
|
|
1003
|
+
const validToolCalls = [];
|
|
1004
|
+
const invalidToolCalls = [];
|
|
1005
|
+
if (assistantMsg.tool_calls) {
|
|
1006
|
+
for (const toolCall of assistantMsg.tool_calls) {
|
|
1007
|
+
try {
|
|
1008
|
+
JSON.parse(toolCall.function.arguments);
|
|
1009
|
+
validToolCalls.push(toolCall);
|
|
1010
|
+
} catch {
|
|
1011
|
+
invalidToolCalls.push(toolCall);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
assistantMsg.tool_calls = validToolCalls.length > 0 ? validToolCalls : void 0;
|
|
973
1016
|
this.conversation.addAssistantMessage(assistantMsg);
|
|
974
|
-
|
|
1017
|
+
for (const toolCall of invalidToolCalls) {
|
|
1018
|
+
callbacks.onToolResult?.(toolCall.function.name, `\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON \u5B57\u7B26\u4E32
|
|
1019
|
+
${toolCall.function.arguments}`, false);
|
|
1020
|
+
}
|
|
1021
|
+
if (validToolCalls.length === 0) {
|
|
975
1022
|
lastContent = assistantMsg.content || "";
|
|
976
1023
|
break;
|
|
977
1024
|
}
|
|
978
|
-
for (const toolCall of
|
|
1025
|
+
for (const toolCall of validToolCalls) {
|
|
979
1026
|
if (this.interrupted) break;
|
|
980
1027
|
const toolName = toolCall.function.name;
|
|
981
|
-
|
|
982
|
-
try {
|
|
983
|
-
params = JSON.parse(toolCall.function.arguments);
|
|
984
|
-
} catch {
|
|
985
|
-
this.conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
|
|
986
|
-
continue;
|
|
987
|
-
}
|
|
1028
|
+
const params = JSON.parse(toolCall.function.arguments);
|
|
988
1029
|
try {
|
|
989
1030
|
if (toolName === "edit-file") {
|
|
990
1031
|
const editPath = params["path"];
|
|
@@ -1090,6 +1131,8 @@ function buildCorePrompt() {
|
|
|
1090
1131
|
- \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
|
|
1091
1132
|
- \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
|
|
1092
1133
|
- \u5F15\u7528\u4EE3\u7801\u65F6\u4F7F\u7528 \`\u6587\u4EF6\u8DEF\u5F84:\u884C\u53F7\` \u683C\u5F0F\uFF08\u5982 \`src/app.ts:42\`\uFF09\uFF0C\u65B9\u4FBF\u7528\u6237\u8DF3\u8F6C
|
|
1134
|
+
- \u7EDD\u5BF9\u7981\u6B62\u4F7F\u7528 \`// ... existing code\`\u3001\`// TODO\` \u6216\u4EFB\u4F55\u5F62\u5F0F\u7684\u7701\u7565\u5360\u4F4D\u7B26\u3002\u5FC5\u987B\u63D0\u4F9B\u5B8C\u6574\u4E14\u53EF\u8FD0\u884C\u7684\u4EE3\u7801\u3002
|
|
1135
|
+
- \u6C38\u8FDC\u4E0D\u8981\u5047\u8BBE\u67D0\u4E2A\u7B2C\u4E09\u65B9\u5E93\u5DF2\u5B89\u88C5\u3002\u5728\u4F7F\u7528\u4EFB\u4F55\u975E Node.js \u5185\u7F6E\u6A21\u5757\u524D\uFF0C\u5FC5\u987B\u786E\u8BA4\u5176\u5728\u9879\u76EE\u4E2D\u5DF2\u914D\u7F6E\u3002
|
|
1093
1136
|
|
|
1094
1137
|
# \u4EA4\u4E92\u98CE\u683C
|
|
1095
1138
|
|
|
@@ -1122,7 +1165,8 @@ function buildPlanningPrompt() {
|
|
|
1122
1165
|
|
|
1123
1166
|
\u4EE3\u7801\u8D28\u91CF\uFF1A
|
|
1124
1167
|
- \u5982\u679C\u5220\u9664\u4E86\u4EE3\u7801\uFF0C\u5C31\u5F7B\u5E95\u5220\u9664\uFF0C\u4E0D\u8981\u7559\u6CE8\u91CA\u8BF4"\u5DF2\u79FB\u9664"\uFF0C\u4E0D\u8981\u4FDD\u7559\u672A\u4F7F\u7528\u7684\u517C\u5BB9\u6027\u53D8\u91CF
|
|
1125
|
-
- \u4E0D\u8981\u7559\u4E0BTODO\u7136\u540E\u653E\u7740\u4E0D\u7BA1
|
|
1168
|
+
- \u4E0D\u8981\u7559\u4E0BTODO\u7136\u540E\u653E\u7740\u4E0D\u7BA1
|
|
1169
|
+
- \u6267\u884C\u4FEE\u6539\u540E\u5FC5\u987B\u8FDB\u884C\u95ED\u73AF\u9A8C\u8BC1\u3002\u4E3B\u52A8\u68C0\u67E5\u4EE3\u7801\u903B\u8F91\uFF0C\u6216\u4F7F\u7528 bash \u8FD0\u884C\u6784\u5EFA\u3001Lint \u7B49\u547D\u4EE4\u786E\u4FDD\u4FEE\u6539\u6CA1\u6709\u5F15\u5165\u9519\u8BEF`;
|
|
1126
1170
|
}
|
|
1127
1171
|
var init_planning = __esm({
|
|
1128
1172
|
"src/core/prompt/layers/planning.ts"() {
|
|
@@ -1141,6 +1185,7 @@ function buildParallelPrompt() {
|
|
|
1141
1185
|
- \u9700\u8981\u641C\u7D22 2+ \u4E2A\u6A21\u5F0F \u2192 spawn-agents \u5E76\u884C\u641C\u7D22
|
|
1142
1186
|
- \u9700\u8981\u4E86\u89E3 2+ \u4E2A\u6A21\u5757 \u2192 spawn-agents \u5E76\u884C\u5206\u6790
|
|
1143
1187
|
- \u53EA\u6709 1 \u4E2A\u76EE\u6807 \u2192 \u76F4\u63A5\u7528\u5DE5\u5177\uFF0C\u65E0\u9700 spawn-agents
|
|
1188
|
+
- \u5FC5\u987B\u4E3A\u6BCF\u4E2A\u5B50 Agent \u5206\u914D\u660E\u786E\u4E14\u65E0\u91CD\u53E0\u7684\u76EE\u6807\uFF0C\u907F\u514D\u591A\u4E2A Agent \u91CD\u590D\u5904\u7406\u540C\u4E00\u5BF9\u8C61\u9020\u6210\u6D6A\u8D39
|
|
1144
1189
|
|
|
1145
1190
|
\u793A\u4F8B - \u7528\u6237\u8BF4"\u5E2E\u6211\u7406\u89E3\u8BA4\u8BC1\u6A21\u5757"\uFF1A
|
|
1146
1191
|
\u6B63\u786E\uFF1Aspawn-agents \u540C\u65F6\u8BFB auth controller\u3001auth service\u3001auth middleware\u3001auth types
|
|
@@ -1162,7 +1207,8 @@ function buildGitPrompt() {
|
|
|
1162
1207
|
|
|
1163
1208
|
\u63D0\u4EA4\u89C4\u8303\uFF1A
|
|
1164
1209
|
- \u53EA\u5728\u7528\u6237\u660E\u786E\u8981\u6C42\u65F6\u624D\u521B\u5EFA commit
|
|
1165
|
-
- \u7528 git diff \u67E5\
|
|
1210
|
+
- \u63D0\u4EA4\u524D\u5FC5\u987B\u7528 git status \u548C git diff --staged \u68C0\u67E5\u5373\u5C06\u63D0\u4EA4\u7684\u5185\u5BB9\uFF0C\u786E\u4FDD\u6CA1\u6709\u6DF7\u5165\u4E0D\u76F8\u5173\u6587\u4EF6\u6216\u8C03\u8BD5\u4EE3\u7801
|
|
1211
|
+
- \u5EFA\u8BAE\u9075\u5FAA Conventional Commits \u89C4\u8303\uFF08\u5982 feat: ..., fix: ...\uFF09
|
|
1166
1212
|
- commit message \u63CF\u8FF0"\u4E3A\u4EC0\u4E48"\u800C\u975E"\u6539\u4E86\u4EC0\u4E48"
|
|
1167
1213
|
|
|
1168
1214
|
\u5B89\u5168\u89C4\u5219\uFF1A
|
|
@@ -1600,7 +1646,8 @@ var init_sub_agent = __esm({
|
|
|
1600
1646
|
allowedTools;
|
|
1601
1647
|
maxTurns;
|
|
1602
1648
|
timeoutMs;
|
|
1603
|
-
|
|
1649
|
+
tracker;
|
|
1650
|
+
constructor(client, registry, config, task, allowedTools = ["read-file", "glob", "grep"], maxTurns = 10, timeoutMs = DEFAULT_TIMEOUT_MS, tracker) {
|
|
1604
1651
|
this.client = client;
|
|
1605
1652
|
this.registry = registry;
|
|
1606
1653
|
this.config = config;
|
|
@@ -1608,6 +1655,7 @@ var init_sub_agent = __esm({
|
|
|
1608
1655
|
this.allowedTools = allowedTools.filter((t) => t !== "spawn-agents" && t !== "todo");
|
|
1609
1656
|
this.maxTurns = Math.min(maxTurns, 15);
|
|
1610
1657
|
this.timeoutMs = timeoutMs;
|
|
1658
|
+
this.tracker = tracker;
|
|
1611
1659
|
}
|
|
1612
1660
|
async run() {
|
|
1613
1661
|
return Promise.race([
|
|
@@ -1637,6 +1685,9 @@ var init_sub_agent = __esm({
|
|
|
1637
1685
|
conversation.getMessages(),
|
|
1638
1686
|
tools.length > 0 ? tools : void 0
|
|
1639
1687
|
);
|
|
1688
|
+
if (assistantMsg.usage && this.tracker) {
|
|
1689
|
+
this.tracker.addTokens(assistantMsg.usage.total_tokens);
|
|
1690
|
+
}
|
|
1640
1691
|
conversation.addAssistantMessage(assistantMsg);
|
|
1641
1692
|
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1642
1693
|
lastContent = assistantMsg.content || "";
|
|
@@ -1760,7 +1811,7 @@ function createSpawnAgentsTool(client, registry, config, tracker) {
|
|
|
1760
1811
|
if (tools.length === 0) {
|
|
1761
1812
|
tools = DEFAULT_TOOLS.filter((t) => autoTools.includes(t));
|
|
1762
1813
|
}
|
|
1763
|
-
return new SubAgent(client, registry, config, task.description, tools, maxTurns);
|
|
1814
|
+
return new SubAgent(client, registry, config, task.description, tools, maxTurns, void 0, tracker);
|
|
1764
1815
|
});
|
|
1765
1816
|
const wrappedRuns = agents.map(
|
|
1766
1817
|
(agent) => agent.run().then(
|
|
@@ -1929,11 +1980,13 @@ var init_runner = __esm({
|
|
|
1929
1980
|
registry;
|
|
1930
1981
|
config;
|
|
1931
1982
|
agentConfig;
|
|
1932
|
-
|
|
1983
|
+
tracker;
|
|
1984
|
+
constructor(client, registry, config, agentConfig, tracker) {
|
|
1933
1985
|
this.client = client;
|
|
1934
1986
|
this.registry = registry;
|
|
1935
1987
|
this.config = config;
|
|
1936
1988
|
this.agentConfig = agentConfig;
|
|
1989
|
+
this.tracker = tracker;
|
|
1937
1990
|
}
|
|
1938
1991
|
/**
|
|
1939
1992
|
* 执行子 Agent 任务
|
|
@@ -1974,6 +2027,9 @@ ${context}`;
|
|
|
1974
2027
|
tools.length > 0 ? tools : void 0,
|
|
1975
2028
|
callbacks
|
|
1976
2029
|
);
|
|
2030
|
+
if (assistantMsg.usage && this.tracker) {
|
|
2031
|
+
this.tracker.addTokens(assistantMsg.usage.total_tokens);
|
|
2032
|
+
}
|
|
1977
2033
|
conversation.addAssistantMessage(assistantMsg);
|
|
1978
2034
|
if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
|
|
1979
2035
|
lastContent = assistantMsg.content || "";
|
|
@@ -2058,7 +2114,7 @@ ${context}`;
|
|
|
2058
2114
|
});
|
|
2059
2115
|
|
|
2060
2116
|
// src/tools/dispatch.ts
|
|
2061
|
-
function createDispatchTool(defaultClient, toolRegistry, config, agentRegistry, callbacks) {
|
|
2117
|
+
function createDispatchTool(defaultClient, toolRegistry, config, agentRegistry, tracker, callbacks) {
|
|
2062
2118
|
return {
|
|
2063
2119
|
name: "dispatch",
|
|
2064
2120
|
description: "\u8C03\u5EA6\u5B50 Agent \u6267\u884C\u4E13\u95E8\u4EFB\u52A1\u3002\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\u548C\u4E13\u5C5E\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF0C\u9002\u7528\u4E8E\u9700\u8981\u4E13\u4E1A\u89D2\u8272\u7684\u573A\u666F\u3002",
|
|
@@ -2100,11 +2156,14 @@ function createDispatchTool(defaultClient, toolRegistry, config, agentRegistry,
|
|
|
2100
2156
|
maxTokens: config.max_tokens
|
|
2101
2157
|
});
|
|
2102
2158
|
}
|
|
2103
|
-
const runner = new SubAgentRunner(client, toolRegistry, config, agentConfig);
|
|
2159
|
+
const runner = new SubAgentRunner(client, toolRegistry, config, agentConfig, tracker);
|
|
2160
|
+
tracker?.start([`${agentName}: ${task.slice(0, 60)}`]);
|
|
2104
2161
|
try {
|
|
2105
2162
|
const result = await runner.execute(task, context, callbacks?.() ?? {});
|
|
2163
|
+
tracker?.finish();
|
|
2106
2164
|
return { content: result || "\uFF08\u5B50 Agent \u6267\u884C\u5B8C\u6210\uFF0C\u65E0\u8F93\u51FA\uFF09" };
|
|
2107
2165
|
} catch (err) {
|
|
2166
|
+
tracker?.finish();
|
|
2108
2167
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2109
2168
|
return { content: `\u5B50 Agent \u6267\u884C\u9519\u8BEF\uFF1A${msg}` };
|
|
2110
2169
|
}
|
|
@@ -2119,6 +2178,26 @@ var init_dispatch = __esm({
|
|
|
2119
2178
|
}
|
|
2120
2179
|
});
|
|
2121
2180
|
|
|
2181
|
+
// src/cli/tui/stores/progressStore.ts
|
|
2182
|
+
import { EventEmitter } from "events";
|
|
2183
|
+
var ProgressStore, progressStore;
|
|
2184
|
+
var init_progressStore = __esm({
|
|
2185
|
+
"src/cli/tui/stores/progressStore.ts"() {
|
|
2186
|
+
"use strict";
|
|
2187
|
+
ProgressStore = class extends EventEmitter {
|
|
2188
|
+
current;
|
|
2189
|
+
update(progress) {
|
|
2190
|
+
this.current = progress;
|
|
2191
|
+
this.emit("change", progress);
|
|
2192
|
+
}
|
|
2193
|
+
get() {
|
|
2194
|
+
return this.current;
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
progressStore = new ProgressStore();
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
|
|
2122
2201
|
// src/cli/tui/bridge.ts
|
|
2123
2202
|
function gray(text) {
|
|
2124
2203
|
return `${ANSI_GRAY}${text}${ANSI_RESET}`;
|
|
@@ -2212,19 +2291,19 @@ function createThinkFilter() {
|
|
|
2212
2291
|
return result;
|
|
2213
2292
|
};
|
|
2214
2293
|
}
|
|
2215
|
-
function
|
|
2216
|
-
let
|
|
2294
|
+
function createActionBatcher(dispatch) {
|
|
2295
|
+
let pendingActions = /* @__PURE__ */ new Map();
|
|
2217
2296
|
let timer = null;
|
|
2218
2297
|
function flush() {
|
|
2219
|
-
if (
|
|
2220
|
-
const
|
|
2221
|
-
|
|
2222
|
-
dispatch({ type,
|
|
2298
|
+
if (pendingActions.size > 0) {
|
|
2299
|
+
const actions = Array.from(pendingActions.values());
|
|
2300
|
+
pendingActions.clear();
|
|
2301
|
+
dispatch({ type: "BATCH", actions });
|
|
2223
2302
|
}
|
|
2224
2303
|
}
|
|
2225
2304
|
function start() {
|
|
2226
2305
|
if (!timer) {
|
|
2227
|
-
timer = setInterval(flush,
|
|
2306
|
+
timer = setInterval(flush, 200);
|
|
2228
2307
|
}
|
|
2229
2308
|
}
|
|
2230
2309
|
function stop() {
|
|
@@ -2234,17 +2313,17 @@ function createTokenBatcher(dispatch, type) {
|
|
|
2234
2313
|
timer = null;
|
|
2235
2314
|
}
|
|
2236
2315
|
}
|
|
2237
|
-
function
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
clearInterval(timer);
|
|
2244
|
-
timer = null;
|
|
2316
|
+
function queue(key, action) {
|
|
2317
|
+
if (action.type === "APPEND_CONTENT" || action.type === "APPEND_THOUGHT") {
|
|
2318
|
+
const existing = pendingActions.get(key);
|
|
2319
|
+
if (existing) {
|
|
2320
|
+
action = { ...action, text: existing.text + action.text };
|
|
2321
|
+
}
|
|
2245
2322
|
}
|
|
2323
|
+
pendingActions.set(key, action);
|
|
2324
|
+
start();
|
|
2246
2325
|
}
|
|
2247
|
-
return {
|
|
2326
|
+
return { queue, stop, flush };
|
|
2248
2327
|
}
|
|
2249
2328
|
function extractCodeFromArgs(name, args) {
|
|
2250
2329
|
const field = name === "write-file" ? "content" : "new_string";
|
|
@@ -2264,98 +2343,84 @@ function registerConfirmToolId(toolName, id) {
|
|
|
2264
2343
|
activeToolIds.set(toolName, id);
|
|
2265
2344
|
}
|
|
2266
2345
|
function createBridgeCallbacks(dispatch) {
|
|
2267
|
-
const
|
|
2268
|
-
const thoughtBatcher = createTokenBatcher(dispatch, "APPEND_THOUGHT");
|
|
2346
|
+
const batcher = createActionBatcher(dispatch);
|
|
2269
2347
|
let inThink = false;
|
|
2270
2348
|
let tagBuffer = "";
|
|
2271
2349
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2272
2350
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
2273
|
-
|
|
2351
|
+
lastStreamingValues = /* @__PURE__ */ new Map();
|
|
2274
2352
|
toolCallCounter = 0;
|
|
2275
|
-
let streamingThrottleTimer = null;
|
|
2276
|
-
let pendingStreamingUpdate = null;
|
|
2277
|
-
function flushStreamingUpdate() {
|
|
2278
|
-
if (pendingStreamingUpdate) {
|
|
2279
|
-
pendingStreamingUpdate();
|
|
2280
|
-
pendingStreamingUpdate = null;
|
|
2281
|
-
}
|
|
2282
|
-
if (streamingThrottleTimer) {
|
|
2283
|
-
clearTimeout(streamingThrottleTimer);
|
|
2284
|
-
streamingThrottleTimer = null;
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
2353
|
return {
|
|
2288
2354
|
onContent: (text) => {
|
|
2355
|
+
let contentAcc = "";
|
|
2356
|
+
let thoughtAcc = "";
|
|
2289
2357
|
for (let i = 0; i < text.length; i++) {
|
|
2290
2358
|
const ch = text[i];
|
|
2291
2359
|
if (tagBuffer.length > 0 || ch === "<") {
|
|
2292
2360
|
tagBuffer += ch;
|
|
2293
2361
|
if (tagBuffer === "<think>") {
|
|
2294
|
-
|
|
2362
|
+
if (contentAcc) batcher.queue("content", { type: "APPEND_CONTENT", text: contentAcc });
|
|
2363
|
+
contentAcc = "";
|
|
2364
|
+
batcher.flush();
|
|
2295
2365
|
inThink = true;
|
|
2296
2366
|
tagBuffer = "";
|
|
2297
2367
|
continue;
|
|
2298
2368
|
} else if (tagBuffer === "</think>") {
|
|
2299
|
-
|
|
2369
|
+
if (thoughtAcc) batcher.queue("thought", { type: "APPEND_THOUGHT", text: thoughtAcc });
|
|
2370
|
+
thoughtAcc = "";
|
|
2371
|
+
batcher.flush();
|
|
2300
2372
|
inThink = false;
|
|
2301
2373
|
tagBuffer = "";
|
|
2302
2374
|
continue;
|
|
2303
2375
|
} else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
|
|
2304
|
-
if (inThink)
|
|
2305
|
-
|
|
2306
|
-
} else {
|
|
2307
|
-
contentBatcher.append(tagBuffer);
|
|
2308
|
-
}
|
|
2376
|
+
if (inThink) thoughtAcc += tagBuffer;
|
|
2377
|
+
else contentAcc += tagBuffer;
|
|
2309
2378
|
tagBuffer = "";
|
|
2310
2379
|
}
|
|
2311
2380
|
continue;
|
|
2312
2381
|
}
|
|
2313
|
-
if (inThink)
|
|
2314
|
-
|
|
2315
|
-
} else {
|
|
2316
|
-
contentBatcher.append(ch);
|
|
2317
|
-
}
|
|
2382
|
+
if (inThink) thoughtAcc += ch;
|
|
2383
|
+
else contentAcc += ch;
|
|
2318
2384
|
}
|
|
2385
|
+
if (thoughtAcc) batcher.queue("thought", { type: "APPEND_THOUGHT", text: thoughtAcc });
|
|
2386
|
+
if (contentAcc) batcher.queue("content", { type: "APPEND_CONTENT", text: contentAcc });
|
|
2319
2387
|
},
|
|
2320
2388
|
onToolCallStreaming: (index, name, accumulatedArgs) => {
|
|
2321
2389
|
if (!streamingToolIds.has(index)) {
|
|
2322
|
-
|
|
2323
|
-
thoughtBatcher.flush();
|
|
2390
|
+
batcher.flush();
|
|
2324
2391
|
const id2 = `tool-${++toolCallCounter}`;
|
|
2325
2392
|
streamingToolIds.set(index, id2);
|
|
2326
2393
|
activeToolIds.set(name, id2);
|
|
2327
|
-
|
|
2394
|
+
lastStreamingValues.set(id2, "0");
|
|
2395
|
+
batcher.queue(`tool-${id2}`, { type: "TOOL_STREAMING", id: id2, name, streamingContent: "0" });
|
|
2328
2396
|
}
|
|
2329
2397
|
const id = streamingToolIds.get(index);
|
|
2330
|
-
lastStreamingArgs.set(id, accumulatedArgs);
|
|
2331
2398
|
const lineCount = (accumulatedArgs.match(/\\n/g) || []).length;
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
streamingThrottleTimer = setTimeout(() => {
|
|
2337
|
-
flushStreamingUpdate();
|
|
2338
|
-
}, 80);
|
|
2399
|
+
const streamingContent = String(lineCount);
|
|
2400
|
+
if (lastStreamingValues.get(id) !== streamingContent) {
|
|
2401
|
+
lastStreamingValues.set(id, streamingContent);
|
|
2402
|
+
progressStore.update({ name, progress: `${streamingContent} lines` });
|
|
2339
2403
|
}
|
|
2340
2404
|
},
|
|
2341
2405
|
onToolExecuting: (name, params) => {
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
thoughtBatcher.flush();
|
|
2345
|
-
contentBatcher.pause();
|
|
2346
|
-
thoughtBatcher.pause();
|
|
2406
|
+
progressStore.update(void 0);
|
|
2407
|
+
batcher.flush();
|
|
2347
2408
|
const existingId = activeToolIds.get(name);
|
|
2348
2409
|
const id = existingId || `tool-${++toolCallCounter}`;
|
|
2349
2410
|
activeToolIds.set(name, id);
|
|
2411
|
+
lastStreamingValues.set(id, JSON.stringify(params));
|
|
2350
2412
|
dispatch({ type: "TOOL_EXECUTING", id, name, params });
|
|
2351
2413
|
},
|
|
2352
2414
|
onToolResult: (name, result, truncated) => {
|
|
2415
|
+
progressStore.update(void 0);
|
|
2416
|
+
batcher.flush();
|
|
2353
2417
|
const id = activeToolIds.get(name) || `tool-${++toolCallCounter}`;
|
|
2354
2418
|
const lines = result.split("\n");
|
|
2355
2419
|
const isWriteTool = name === "write-file" || name === "edit-file";
|
|
2356
2420
|
let summary;
|
|
2357
2421
|
if (isWriteTool) {
|
|
2358
|
-
const
|
|
2422
|
+
const stored = lastStreamingValues.get(id) || "";
|
|
2423
|
+
const code = extractCodeFromArgs(name, stored);
|
|
2359
2424
|
const codeLines = code ? code.split("\n").length : 0;
|
|
2360
2425
|
summary = codeLines > 0 ? `${codeLines} lines` : truncated ? "truncated" : `${lines.length} lines`;
|
|
2361
2426
|
} else {
|
|
@@ -2366,31 +2431,119 @@ function createBridgeCallbacks(dispatch) {
|
|
|
2366
2431
|
dispatch({ type: "TOOL_RESULT", id, resultSummary: summary, resultContent });
|
|
2367
2432
|
},
|
|
2368
2433
|
onDenied: (toolName, feedback) => {
|
|
2434
|
+
batcher.flush();
|
|
2369
2435
|
const id = activeToolIds.get(toolName) || `tool-${++toolCallCounter}`;
|
|
2370
2436
|
dispatch({ type: "TOOL_DENIED", id, feedback });
|
|
2371
2437
|
},
|
|
2372
2438
|
onError: (err) => {
|
|
2373
|
-
|
|
2374
|
-
thoughtBatcher.stop();
|
|
2439
|
+
batcher.stop();
|
|
2375
2440
|
dispatch({ type: "SET_ERROR", error: err.message });
|
|
2376
2441
|
},
|
|
2377
2442
|
_stopBatcher: () => {
|
|
2378
|
-
|
|
2379
|
-
thoughtBatcher.stop();
|
|
2443
|
+
batcher.stop();
|
|
2380
2444
|
}
|
|
2381
2445
|
};
|
|
2382
2446
|
}
|
|
2383
|
-
var
|
|
2447
|
+
var ANSI_GRAY, ANSI_RESET, toolCallCounter, activeToolIds, streamingToolIds, lastStreamingValues;
|
|
2384
2448
|
var init_bridge = __esm({
|
|
2385
2449
|
"src/cli/tui/bridge.ts"() {
|
|
2386
2450
|
"use strict";
|
|
2387
|
-
|
|
2451
|
+
init_progressStore();
|
|
2388
2452
|
ANSI_GRAY = "\x1B[90m";
|
|
2389
2453
|
ANSI_RESET = "\x1B[0m";
|
|
2390
2454
|
toolCallCounter = 0;
|
|
2391
2455
|
activeToolIds = /* @__PURE__ */ new Map();
|
|
2392
2456
|
streamingToolIds = /* @__PURE__ */ new Map();
|
|
2393
|
-
|
|
2457
|
+
lastStreamingValues = /* @__PURE__ */ new Map();
|
|
2458
|
+
}
|
|
2459
|
+
});
|
|
2460
|
+
|
|
2461
|
+
// src/cli/ui.ts
|
|
2462
|
+
import chalk2 from "chalk";
|
|
2463
|
+
import ora from "ora";
|
|
2464
|
+
import { Marked } from "marked";
|
|
2465
|
+
import * as _markedTerminal from "marked-terminal";
|
|
2466
|
+
import { createTwoFilesPatch } from "diff";
|
|
2467
|
+
function renderMarkdown(text) {
|
|
2468
|
+
const rendered = marked.parse(text);
|
|
2469
|
+
return rendered.replace(/\n+$/, "");
|
|
2470
|
+
}
|
|
2471
|
+
function printStream(text) {
|
|
2472
|
+
process.stdout.write(text);
|
|
2473
|
+
}
|
|
2474
|
+
function printToolCall(toolName, params) {
|
|
2475
|
+
let detail = "";
|
|
2476
|
+
if (toolName === "bash" && params["command"]) {
|
|
2477
|
+
detail = ` ${chalk2.dim(String(params["command"]).slice(0, 80))}`;
|
|
2478
|
+
} else if ((toolName === "read-file" || toolName === "write-file" || toolName === "edit-file") && params["path"]) {
|
|
2479
|
+
detail = ` ${chalk2.dim(String(params["path"]))}`;
|
|
2480
|
+
} else if (toolName === "glob" && params["pattern"]) {
|
|
2481
|
+
detail = ` ${chalk2.dim(String(params["pattern"]))}`;
|
|
2482
|
+
} else if (toolName === "grep" && params["pattern"]) {
|
|
2483
|
+
detail = ` ${chalk2.dim(String(params["pattern"]))}`;
|
|
2484
|
+
} else if (toolName === "spawn-agents" && params["tasks"]) {
|
|
2485
|
+
const tasks = params["tasks"];
|
|
2486
|
+
detail = ` ${chalk2.dim(`${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`)}`;
|
|
2487
|
+
} else if (toolName === "todo" && params["action"]) {
|
|
2488
|
+
const action = String(params["action"]);
|
|
2489
|
+
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
2490
|
+
detail = ` ${chalk2.dim(`${action}${id}`)}`;
|
|
2491
|
+
}
|
|
2492
|
+
const icon = toolName === "spawn-agents" ? ">>" : toolName === "todo" ? "#" : ">";
|
|
2493
|
+
console.log(chalk2.yellow(` ${icon} ${toolName}`) + detail);
|
|
2494
|
+
}
|
|
2495
|
+
function printToolResult(toolName, result, truncated) {
|
|
2496
|
+
if (truncated) {
|
|
2497
|
+
console.log(chalk2.dim(` \u2713 ${toolName} (\u8F93\u51FA\u5DF2\u622A\u65AD)`));
|
|
2498
|
+
} else {
|
|
2499
|
+
const lines = result.split("\n").length;
|
|
2500
|
+
console.log(chalk2.dim(` \u2713 ${toolName} (${lines} \u884C)`));
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function printError(message) {
|
|
2504
|
+
console.error(chalk2.red(`\u2717 ${message}`));
|
|
2505
|
+
}
|
|
2506
|
+
function printInfo(message) {
|
|
2507
|
+
console.log(chalk2.cyan(`\u2139 ${message}`));
|
|
2508
|
+
}
|
|
2509
|
+
function printWarning(message) {
|
|
2510
|
+
console.log(chalk2.yellow(`\u26A0 ${message}`));
|
|
2511
|
+
}
|
|
2512
|
+
function printSuccess(message) {
|
|
2513
|
+
console.log(chalk2.green(`\u2713 ${message}`));
|
|
2514
|
+
}
|
|
2515
|
+
function printWelcome(modelName) {
|
|
2516
|
+
console.log(chalk2.bold.cyan("\n ZenCode") + chalk2.dim(" - \u6781\u7B80 AI \u7F16\u7A0B\u52A9\u624B\n"));
|
|
2517
|
+
console.log(chalk2.dim(` \u6A21\u578B: ${modelName}`));
|
|
2518
|
+
console.log(chalk2.dim(` \u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA
|
|
2519
|
+
`));
|
|
2520
|
+
}
|
|
2521
|
+
var markedTerminal2, marked;
|
|
2522
|
+
var init_ui = __esm({
|
|
2523
|
+
"src/cli/ui.ts"() {
|
|
2524
|
+
"use strict";
|
|
2525
|
+
markedTerminal2 = _markedTerminal.markedTerminal;
|
|
2526
|
+
marked = new Marked(markedTerminal2({
|
|
2527
|
+
reflowText: false,
|
|
2528
|
+
code: chalk2.cyan,
|
|
2529
|
+
codespan: chalk2.yellow,
|
|
2530
|
+
heading: chalk2.bold.magenta,
|
|
2531
|
+
hr: () => chalk2.dim("\u2500".repeat(Math.max(0, (process.stdout.columns || 80) - 12))),
|
|
2532
|
+
strong: chalk2.bold,
|
|
2533
|
+
em: chalk2.italic,
|
|
2534
|
+
link: chalk2.blue,
|
|
2535
|
+
href: chalk2.blue.underline
|
|
2536
|
+
}));
|
|
2537
|
+
marked.use({
|
|
2538
|
+
renderer: {
|
|
2539
|
+
text(token) {
|
|
2540
|
+
if (typeof token === "object" && token.tokens) {
|
|
2541
|
+
return this.parser.parseInline(token.tokens);
|
|
2542
|
+
}
|
|
2543
|
+
return typeof token === "string" ? token : token.text;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
});
|
|
2394
2547
|
}
|
|
2395
2548
|
});
|
|
2396
2549
|
|
|
@@ -2407,7 +2560,8 @@ var init_sub_agent_tracker = __esm({
|
|
|
2407
2560
|
total: descriptions.length,
|
|
2408
2561
|
completed: 0,
|
|
2409
2562
|
failed: 0,
|
|
2410
|
-
descriptions
|
|
2563
|
+
descriptions,
|
|
2564
|
+
tokens: 0
|
|
2411
2565
|
};
|
|
2412
2566
|
this.notify();
|
|
2413
2567
|
}
|
|
@@ -2421,6 +2575,11 @@ var init_sub_agent_tracker = __esm({
|
|
|
2421
2575
|
this.progress = { ...this.progress, failed: this.progress.failed + 1 };
|
|
2422
2576
|
this.notify();
|
|
2423
2577
|
}
|
|
2578
|
+
addTokens(count) {
|
|
2579
|
+
if (!this.progress) return;
|
|
2580
|
+
this.progress = { ...this.progress, tokens: this.progress.tokens + count };
|
|
2581
|
+
this.notify();
|
|
2582
|
+
}
|
|
2424
2583
|
finish() {
|
|
2425
2584
|
this.progress = null;
|
|
2426
2585
|
this.notify();
|
|
@@ -2462,19 +2621,26 @@ function updateLastAssistant(messages, updater) {
|
|
|
2462
2621
|
const result = [...messages];
|
|
2463
2622
|
for (let i = result.length - 1; i >= 0; i--) {
|
|
2464
2623
|
if (result[i].role === "assistant") {
|
|
2465
|
-
|
|
2624
|
+
const updated = updater(result[i]);
|
|
2625
|
+
if (updated === result[i]) return messages;
|
|
2626
|
+
result[i] = updated;
|
|
2466
2627
|
return result;
|
|
2467
2628
|
}
|
|
2468
2629
|
}
|
|
2469
2630
|
return result;
|
|
2470
2631
|
}
|
|
2471
2632
|
function updateToolInBlocks(blocks, toolId, updater) {
|
|
2472
|
-
|
|
2633
|
+
let changed = false;
|
|
2634
|
+
const newBlocks = blocks.map((b) => {
|
|
2473
2635
|
if (b.type === "tool" && b.toolCall.id === toolId) {
|
|
2474
|
-
|
|
2636
|
+
const updatedTc = updater(b.toolCall);
|
|
2637
|
+
if (updatedTc === b.toolCall) return b;
|
|
2638
|
+
changed = true;
|
|
2639
|
+
return { type: "tool", toolCall: updatedTc };
|
|
2475
2640
|
}
|
|
2476
2641
|
return b;
|
|
2477
2642
|
});
|
|
2643
|
+
return changed ? newBlocks : blocks;
|
|
2478
2644
|
}
|
|
2479
2645
|
function tuiReducer(state, action) {
|
|
2480
2646
|
switch (action.type) {
|
|
@@ -2506,186 +2672,190 @@ function tuiReducer(state, action) {
|
|
|
2506
2672
|
]
|
|
2507
2673
|
};
|
|
2508
2674
|
case "APPEND_CONTENT": {
|
|
2509
|
-
return
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
};
|
|
2675
|
+
if (!action.text) return state;
|
|
2676
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2677
|
+
const blocks = [...msg.blocks];
|
|
2678
|
+
const last = blocks[blocks.length - 1];
|
|
2679
|
+
if (last && last.type === "text") {
|
|
2680
|
+
blocks[blocks.length - 1] = { type: "text", text: last.text + action.text };
|
|
2681
|
+
} else {
|
|
2682
|
+
blocks.push({ type: "text", text: action.text });
|
|
2683
|
+
}
|
|
2684
|
+
return { ...msg, blocks };
|
|
2685
|
+
});
|
|
2686
|
+
if (newMessages2 === state.messages) return state;
|
|
2687
|
+
return { ...state, messages: newMessages2 };
|
|
2522
2688
|
}
|
|
2523
2689
|
case "APPEND_THOUGHT": {
|
|
2524
|
-
return
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
};
|
|
2690
|
+
if (!action.text) return state;
|
|
2691
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2692
|
+
const blocks = [...msg.blocks];
|
|
2693
|
+
const last = blocks[blocks.length - 1];
|
|
2694
|
+
if (last && last.type === "thought") {
|
|
2695
|
+
blocks[blocks.length - 1] = { type: "thought", text: last.text + action.text };
|
|
2696
|
+
} else {
|
|
2697
|
+
blocks.push({ type: "thought", text: action.text });
|
|
2698
|
+
}
|
|
2699
|
+
return { ...msg, blocks };
|
|
2700
|
+
});
|
|
2701
|
+
if (newMessages2 === state.messages) return state;
|
|
2702
|
+
return { ...state, messages: newMessages2 };
|
|
2537
2703
|
}
|
|
2538
2704
|
case "TOOL_EXECUTING": {
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
)
|
|
2545
|
-
|
|
2546
|
-
return {
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
id: action.id,
|
|
2563
|
-
name: action.name,
|
|
2564
|
-
params: action.params,
|
|
2565
|
-
status: "running"
|
|
2566
|
-
}
|
|
2705
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2706
|
+
const existingIdx = msg.blocks.findIndex(
|
|
2707
|
+
(b) => b.type === "tool" && b.toolCall.id === action.id
|
|
2708
|
+
);
|
|
2709
|
+
if (existingIdx >= 0) {
|
|
2710
|
+
const newBlocks = updateToolInBlocks(msg.blocks, action.id, (tc) => {
|
|
2711
|
+
if (tc.status === "running" && JSON.stringify(tc.params) === JSON.stringify(action.params)) return tc;
|
|
2712
|
+
return { ...tc, status: "running", params: action.params };
|
|
2713
|
+
});
|
|
2714
|
+
if (newBlocks === msg.blocks) return msg;
|
|
2715
|
+
return { ...msg, blocks: newBlocks };
|
|
2716
|
+
}
|
|
2717
|
+
return {
|
|
2718
|
+
...msg,
|
|
2719
|
+
blocks: [
|
|
2720
|
+
...msg.blocks,
|
|
2721
|
+
{
|
|
2722
|
+
type: "tool",
|
|
2723
|
+
toolCall: {
|
|
2724
|
+
id: action.id,
|
|
2725
|
+
name: action.name,
|
|
2726
|
+
params: action.params,
|
|
2727
|
+
status: "running"
|
|
2567
2728
|
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
}
|
|
2571
|
-
};
|
|
2729
|
+
}
|
|
2730
|
+
]
|
|
2731
|
+
};
|
|
2732
|
+
});
|
|
2733
|
+
if (newMessages2 === state.messages) return state;
|
|
2734
|
+
return { ...state, messages: newMessages2 };
|
|
2572
2735
|
}
|
|
2573
2736
|
case "TOOL_STREAMING": {
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
|
|
2584
|
-
...tc,
|
|
2585
|
-
streamingContent: action.streamingContent
|
|
2586
|
-
}))
|
|
2587
|
-
};
|
|
2588
|
-
}
|
|
2737
|
+
const { id, streamingContent, name } = action;
|
|
2738
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2739
|
+
const existingBlock = msg.blocks.find(
|
|
2740
|
+
(b) => b.type === "tool" && b.toolCall.id === id
|
|
2741
|
+
);
|
|
2742
|
+
if (existingBlock && existingBlock.type === "tool" && existingBlock.toolCall.streamingContent === streamingContent) {
|
|
2743
|
+
return msg;
|
|
2744
|
+
}
|
|
2745
|
+
if (existingBlock) {
|
|
2589
2746
|
return {
|
|
2590
2747
|
...msg,
|
|
2591
|
-
blocks:
|
|
2592
|
-
...
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
toolCall: {
|
|
2596
|
-
id: action.id,
|
|
2597
|
-
name: action.name,
|
|
2598
|
-
params: {},
|
|
2599
|
-
status: "running",
|
|
2600
|
-
streamingContent: action.streamingContent
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
]
|
|
2748
|
+
blocks: updateToolInBlocks(msg.blocks, id, (tc) => ({
|
|
2749
|
+
...tc,
|
|
2750
|
+
streamingContent
|
|
2751
|
+
}))
|
|
2604
2752
|
};
|
|
2605
|
-
}
|
|
2606
|
-
|
|
2607
|
-
}
|
|
2608
|
-
case "TOOL_RESULT": {
|
|
2609
|
-
return {
|
|
2610
|
-
...state,
|
|
2611
|
-
messages: updateLastAssistant(state.messages, (msg) => ({
|
|
2612
|
-
...msg,
|
|
2613
|
-
blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
|
|
2614
|
-
...tc,
|
|
2615
|
-
status: "done",
|
|
2616
|
-
resultSummary: action.resultSummary,
|
|
2617
|
-
resultContent: action.resultContent
|
|
2618
|
-
}))
|
|
2619
|
-
}))
|
|
2620
|
-
};
|
|
2621
|
-
}
|
|
2622
|
-
case "TOOL_DENIED": {
|
|
2623
|
-
return {
|
|
2624
|
-
...state,
|
|
2625
|
-
messages: updateLastAssistant(state.messages, (msg) => ({
|
|
2626
|
-
...msg,
|
|
2627
|
-
blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
|
|
2628
|
-
...tc,
|
|
2629
|
-
status: "denied",
|
|
2630
|
-
denyFeedback: action.feedback
|
|
2631
|
-
}))
|
|
2632
|
-
}))
|
|
2633
|
-
};
|
|
2634
|
-
}
|
|
2635
|
-
case "TOOL_CONFIRMING": {
|
|
2636
|
-
return {
|
|
2637
|
-
...state,
|
|
2638
|
-
messages: updateLastAssistant(state.messages, (msg) => ({
|
|
2753
|
+
}
|
|
2754
|
+
return {
|
|
2639
2755
|
...msg,
|
|
2640
2756
|
blocks: [
|
|
2641
2757
|
...msg.blocks,
|
|
2642
2758
|
{
|
|
2643
2759
|
type: "tool",
|
|
2644
2760
|
toolCall: {
|
|
2645
|
-
id
|
|
2646
|
-
name
|
|
2647
|
-
params:
|
|
2648
|
-
status: "
|
|
2761
|
+
id,
|
|
2762
|
+
name,
|
|
2763
|
+
params: {},
|
|
2764
|
+
status: "running",
|
|
2765
|
+
streamingContent
|
|
2649
2766
|
}
|
|
2650
2767
|
}
|
|
2651
|
-
]
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2768
|
+
]
|
|
2769
|
+
};
|
|
2770
|
+
});
|
|
2771
|
+
if (newMessages2 === state.messages) {
|
|
2772
|
+
return state;
|
|
2773
|
+
}
|
|
2774
|
+
return { ...state, messages: newMessages2 };
|
|
2775
|
+
}
|
|
2776
|
+
case "TOOL_RESULT": {
|
|
2777
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2778
|
+
const newBlocks = updateToolInBlocks(msg.blocks, action.id, (tc) => {
|
|
2779
|
+
if (tc.status === "done" && tc.resultSummary === action.resultSummary) return tc;
|
|
2780
|
+
return { ...tc, status: "done", resultSummary: action.resultSummary, resultContent: action.resultContent };
|
|
2781
|
+
});
|
|
2782
|
+
if (newBlocks === msg.blocks) return msg;
|
|
2783
|
+
return { ...msg, blocks: newBlocks };
|
|
2784
|
+
});
|
|
2785
|
+
if (newMessages2 === state.messages) return state;
|
|
2786
|
+
return { ...state, messages: newMessages2 };
|
|
2787
|
+
}
|
|
2788
|
+
case "TOOL_DENIED": {
|
|
2789
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2790
|
+
const newBlocks = updateToolInBlocks(msg.blocks, action.id, (tc) => ({
|
|
2791
|
+
...tc,
|
|
2792
|
+
status: "denied",
|
|
2793
|
+
denyFeedback: action.feedback
|
|
2794
|
+
}));
|
|
2795
|
+
if (newBlocks === msg.blocks) return msg;
|
|
2796
|
+
return { ...msg, blocks: newBlocks };
|
|
2797
|
+
});
|
|
2798
|
+
if (newMessages2 === state.messages) return state;
|
|
2799
|
+
return { ...state, messages: newMessages2 };
|
|
2800
|
+
}
|
|
2801
|
+
case "TOOL_CONFIRMING": {
|
|
2802
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => ({
|
|
2803
|
+
...msg,
|
|
2804
|
+
blocks: [
|
|
2805
|
+
...msg.blocks,
|
|
2806
|
+
{
|
|
2807
|
+
type: "tool",
|
|
2808
|
+
toolCall: {
|
|
2809
|
+
id: action.id,
|
|
2810
|
+
name: action.name,
|
|
2811
|
+
params: action.params,
|
|
2812
|
+
status: "confirming"
|
|
2813
|
+
}
|
|
2656
2814
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2815
|
+
],
|
|
2816
|
+
confirmPending: {
|
|
2817
|
+
toolName: action.name,
|
|
2818
|
+
params: action.params,
|
|
2819
|
+
resolve: action.resolve
|
|
2820
|
+
}
|
|
2821
|
+
}));
|
|
2822
|
+
return { ...state, messages: newMessages2 };
|
|
2659
2823
|
}
|
|
2660
2824
|
case "CONFIRM_RESPONDED": {
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
};
|
|
2825
|
+
const newMessages2 = updateLastAssistant(state.messages, (msg) => {
|
|
2826
|
+
if (!msg.confirmPending) return msg;
|
|
2827
|
+
return { ...msg, confirmPending: void 0 };
|
|
2828
|
+
});
|
|
2829
|
+
if (newMessages2 === state.messages) return state;
|
|
2830
|
+
return { ...state, messages: newMessages2 };
|
|
2668
2831
|
}
|
|
2669
2832
|
case "FINISH_STREAMING":
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
};
|
|
2833
|
+
const newMessages = updateLastAssistant(state.messages, (msg) => {
|
|
2834
|
+
if (!msg.isStreaming) return msg;
|
|
2835
|
+
return { ...msg, isStreaming: false };
|
|
2836
|
+
});
|
|
2837
|
+
if (newMessages === state.messages) return state;
|
|
2838
|
+
return { ...state, messages: newMessages };
|
|
2677
2839
|
case "SET_RUNNING":
|
|
2840
|
+
if (state.isRunning === action.running) return state;
|
|
2678
2841
|
return { ...state, isRunning: action.running };
|
|
2679
2842
|
case "SET_ERROR":
|
|
2680
2843
|
return { ...state, error: action.error, isRunning: false };
|
|
2681
2844
|
case "CLEAR_ERROR":
|
|
2845
|
+
if (state.error === void 0) return state;
|
|
2682
2846
|
return { ...state, error: void 0 };
|
|
2683
2847
|
case "CLEAR_MESSAGES":
|
|
2848
|
+
if (state.messages.length === 0) return state;
|
|
2684
2849
|
return { ...state, messages: [] };
|
|
2685
2850
|
case "SET_TODO_PLAN":
|
|
2851
|
+
if (state.todoPlan === action.plan) return state;
|
|
2686
2852
|
return { ...state, todoPlan: action.plan };
|
|
2687
2853
|
case "SET_SUB_AGENT_PROGRESS":
|
|
2854
|
+
if (JSON.stringify(state.subAgentProgress) === JSON.stringify(action.progress)) return state;
|
|
2688
2855
|
return { ...state, subAgentProgress: action.progress };
|
|
2856
|
+
case "BATCH":
|
|
2857
|
+
const newState = action.actions.reduce((s, a) => tuiReducer(s, a), state);
|
|
2858
|
+
return newState === state ? state : newState;
|
|
2689
2859
|
default:
|
|
2690
2860
|
return state;
|
|
2691
2861
|
}
|
|
@@ -2699,6 +2869,7 @@ var init_state = __esm({
|
|
|
2699
2869
|
});
|
|
2700
2870
|
|
|
2701
2871
|
// src/cli/tui/components/ToolCallLine.tsx
|
|
2872
|
+
import React from "react";
|
|
2702
2873
|
import { Box, Text } from "ink";
|
|
2703
2874
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2704
2875
|
function getToolParamSummary(name, params) {
|
|
@@ -2712,9 +2883,21 @@ function getToolParamSummary(name, params) {
|
|
|
2712
2883
|
case "glob":
|
|
2713
2884
|
case "grep":
|
|
2714
2885
|
return String(params["pattern"] || "");
|
|
2886
|
+
case "spawn-agents": {
|
|
2887
|
+
const tasks = params["tasks"];
|
|
2888
|
+
if (!tasks || tasks.length === 0) return "";
|
|
2889
|
+
return `${tasks.length} tasks: ${tasks.map((t) => t.description || "?").join(" | ").slice(0, 100)}`;
|
|
2890
|
+
}
|
|
2891
|
+
case "dispatch":
|
|
2892
|
+
return `${params["agent"] || "?"}: ${String(params["task"] || "").slice(0, 80)}`;
|
|
2715
2893
|
default:
|
|
2716
2894
|
const keys = Object.keys(params);
|
|
2717
|
-
|
|
2895
|
+
if (keys.length === 0) return "";
|
|
2896
|
+
const val = params[keys[0]];
|
|
2897
|
+
if (val === null || val === void 0) return "";
|
|
2898
|
+
if (typeof val === "string") return val.slice(0, 60);
|
|
2899
|
+
if (typeof val === "number" || typeof val === "boolean") return String(val);
|
|
2900
|
+
return JSON.stringify(val).slice(0, 60);
|
|
2718
2901
|
}
|
|
2719
2902
|
}
|
|
2720
2903
|
function truncateContent(text, maxLines) {
|
|
@@ -2729,70 +2912,81 @@ function truncateContent(text, maxLines) {
|
|
|
2729
2912
|
}
|
|
2730
2913
|
return result;
|
|
2731
2914
|
}
|
|
2732
|
-
|
|
2733
|
-
const { name, params, status, resultContent, denyFeedback } = toolCall;
|
|
2734
|
-
const summary = getToolParamSummary(name, params);
|
|
2735
|
-
let borderColor = "#504945";
|
|
2736
|
-
let titleColor = "#fabd2f";
|
|
2737
|
-
let statusColor = "#fabd2f";
|
|
2738
|
-
let statusText = "RUNNING";
|
|
2739
|
-
if (status === "done") {
|
|
2740
|
-
borderColor = "#b8bb26";
|
|
2741
|
-
titleColor = "#b8bb26";
|
|
2742
|
-
statusColor = "#b8bb26";
|
|
2743
|
-
statusText = "DONE";
|
|
2744
|
-
} else if (status === "denied") {
|
|
2745
|
-
borderColor = "#fb4934";
|
|
2746
|
-
titleColor = "#fb4934";
|
|
2747
|
-
statusColor = "#fb4934";
|
|
2748
|
-
statusText = "DENIED";
|
|
2749
|
-
} else if (status === "confirming") {
|
|
2750
|
-
borderColor = "#fe8019";
|
|
2751
|
-
titleColor = "#fe8019";
|
|
2752
|
-
statusColor = "#fe8019";
|
|
2753
|
-
statusText = "CONFIRM";
|
|
2754
|
-
}
|
|
2755
|
-
const contentLines = resultContent ? truncateContent(resultContent, 15) : [];
|
|
2756
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, marginBottom: 1, width: "100%", children: /* @__PURE__ */ jsxs(
|
|
2757
|
-
Box,
|
|
2758
|
-
{
|
|
2759
|
-
flexDirection: "column",
|
|
2760
|
-
borderStyle: "round",
|
|
2761
|
-
borderColor,
|
|
2762
|
-
paddingX: 1,
|
|
2763
|
-
width: "100%",
|
|
2764
|
-
children: [
|
|
2765
|
-
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
2766
|
-
/* @__PURE__ */ jsx(Box, { backgroundColor: statusColor, width: 9, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: "#282828", bold: true, children: statusText }) }),
|
|
2767
|
-
/* @__PURE__ */ jsx(Text, { color: titleColor, bold: true, children: name.toUpperCase() }),
|
|
2768
|
-
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", dimColor: true, italic: true, wrap: "truncate-end", children: summary })
|
|
2769
|
-
] }),
|
|
2770
|
-
status === "done" && contentLines.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: contentLines.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "#ebdbb2", wrap: "truncate-end", children: line }, i)) }),
|
|
2771
|
-
status === "denied" && denyFeedback && /* @__PURE__ */ jsxs(Box, { gap: 1, marginTop: 0, children: [
|
|
2772
|
-
/* @__PURE__ */ jsx(Text, { color: "#fb4934", bold: true, children: "REASON:" }),
|
|
2773
|
-
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", children: denyFeedback })
|
|
2774
|
-
] }),
|
|
2775
|
-
status === "confirming" && /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(Text, { color: "#fe8019", italic: true, children: "Waiting for your permission..." }) })
|
|
2776
|
-
]
|
|
2777
|
-
}
|
|
2778
|
-
) });
|
|
2779
|
-
}
|
|
2915
|
+
var ToolCallLine;
|
|
2780
2916
|
var init_ToolCallLine = __esm({
|
|
2781
2917
|
"src/cli/tui/components/ToolCallLine.tsx"() {
|
|
2782
2918
|
"use strict";
|
|
2919
|
+
ToolCallLine = React.memo(function ToolCallLine2({ toolCall }) {
|
|
2920
|
+
const { name, params, status, resultContent, denyFeedback } = toolCall;
|
|
2921
|
+
const summary = getToolParamSummary(name, params);
|
|
2922
|
+
let borderColor = "#504945";
|
|
2923
|
+
let titleColor = "#fabd2f";
|
|
2924
|
+
let statusColor = "#fabd2f";
|
|
2925
|
+
let statusText = "RUNNING";
|
|
2926
|
+
if (status === "done") {
|
|
2927
|
+
borderColor = "#b8bb26";
|
|
2928
|
+
titleColor = "#b8bb26";
|
|
2929
|
+
statusColor = "#b8bb26";
|
|
2930
|
+
statusText = "DONE";
|
|
2931
|
+
} else if (status === "denied") {
|
|
2932
|
+
borderColor = "#fb4934";
|
|
2933
|
+
titleColor = "#fb4934";
|
|
2934
|
+
statusColor = "#fb4934";
|
|
2935
|
+
statusText = "DENIED";
|
|
2936
|
+
} else if (status === "confirming") {
|
|
2937
|
+
borderColor = "#fe8019";
|
|
2938
|
+
titleColor = "#fe8019";
|
|
2939
|
+
statusColor = "#fe8019";
|
|
2940
|
+
statusText = "CONFIRM";
|
|
2941
|
+
}
|
|
2942
|
+
const contentLines = resultContent ? truncateContent(resultContent, 15) : [];
|
|
2943
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, marginBottom: 1, width: "100%", children: /* @__PURE__ */ jsxs(
|
|
2944
|
+
Box,
|
|
2945
|
+
{
|
|
2946
|
+
flexDirection: "column",
|
|
2947
|
+
borderStyle: "round",
|
|
2948
|
+
borderColor,
|
|
2949
|
+
paddingX: 1,
|
|
2950
|
+
width: "100%",
|
|
2951
|
+
children: [
|
|
2952
|
+
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
2953
|
+
/* @__PURE__ */ jsx(Box, { backgroundColor: statusColor, width: 9, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: "#282828", bold: true, children: statusText }) }),
|
|
2954
|
+
/* @__PURE__ */ jsx(Text, { color: titleColor, bold: true, children: name.toUpperCase() }),
|
|
2955
|
+
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", dimColor: true, italic: true, wrap: "truncate-end", children: summary })
|
|
2956
|
+
] }),
|
|
2957
|
+
status === "done" && contentLines.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: contentLines.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "#ebdbb2", wrap: "truncate-end", children: line }, i)) }),
|
|
2958
|
+
status === "denied" && denyFeedback && /* @__PURE__ */ jsxs(Box, { gap: 1, marginTop: 0, children: [
|
|
2959
|
+
/* @__PURE__ */ jsx(Text, { color: "#fb4934", bold: true, children: "REASON:" }),
|
|
2960
|
+
/* @__PURE__ */ jsx(Text, { color: "#ebdbb2", children: denyFeedback })
|
|
2961
|
+
] }),
|
|
2962
|
+
status === "confirming" && /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(Text, { color: "#fe8019", italic: true, children: "Waiting for your permission..." }) })
|
|
2963
|
+
]
|
|
2964
|
+
}
|
|
2965
|
+
) });
|
|
2966
|
+
});
|
|
2783
2967
|
}
|
|
2784
2968
|
});
|
|
2785
2969
|
|
|
2786
2970
|
// src/cli/tui/components/MessageBubble.tsx
|
|
2787
|
-
import
|
|
2788
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
2971
|
+
import React2, { useMemo } from "react";
|
|
2972
|
+
import { Box as Box2, Text as Text2, useStdout } from "ink";
|
|
2789
2973
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2790
|
-
var MessageBubble;
|
|
2974
|
+
var MemoizedMarkdown, MemoizedThought, MessageBubble;
|
|
2791
2975
|
var init_MessageBubble = __esm({
|
|
2792
2976
|
"src/cli/tui/components/MessageBubble.tsx"() {
|
|
2793
2977
|
"use strict";
|
|
2978
|
+
init_ui();
|
|
2794
2979
|
init_ToolCallLine();
|
|
2795
|
-
|
|
2980
|
+
MemoizedMarkdown = React2.memo(({ text }) => {
|
|
2981
|
+
const { stdout } = useStdout();
|
|
2982
|
+
const rendered = useMemo(() => renderMarkdown(text), [text, stdout.columns]);
|
|
2983
|
+
return /* @__PURE__ */ jsx2(Text2, { children: rendered });
|
|
2984
|
+
});
|
|
2985
|
+
MemoizedThought = React2.memo(({ text, isStreaming }) => {
|
|
2986
|
+
const displayText = isStreaming ? text : text.trim();
|
|
2987
|
+
return /* @__PURE__ */ jsx2(Text2, { color: "#928374", italic: true, children: displayText });
|
|
2988
|
+
});
|
|
2989
|
+
MessageBubble = React2.memo(function MessageBubble2({ message }) {
|
|
2796
2990
|
const { role, blocks, isStreaming } = message;
|
|
2797
2991
|
const isUser = role === "user";
|
|
2798
2992
|
const label = isUser ? "USER" : "AI";
|
|
@@ -2809,11 +3003,11 @@ var init_MessageBubble = __esm({
|
|
|
2809
3003
|
if (block.type === "text") {
|
|
2810
3004
|
const text = block.text.trim();
|
|
2811
3005
|
if (!text && !isStreaming) return null;
|
|
2812
|
-
return /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: i < blocks.length - 1 ? 1 : 0, width: "100%", children: /* @__PURE__ */ jsx2(Text2, { color: contentColor, children: text }) }, `text-${i}`);
|
|
3006
|
+
return /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: i < blocks.length - 1 ? 1 : 0, width: "100%", children: isUser ? /* @__PURE__ */ jsx2(Text2, { color: contentColor, children: text }) : /* @__PURE__ */ jsx2(MemoizedMarkdown, { text }) }, `text-${i}`);
|
|
2813
3007
|
} else if (block.type === "thought") {
|
|
2814
3008
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 0, width: "100%", children: [
|
|
2815
3009
|
/* @__PURE__ */ jsx2(Box2, { gap: 1, marginBottom: 0, children: /* @__PURE__ */ jsx2(Box2, { backgroundColor: "#504945", width: 9, justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: "#a89984", bold: true, children: "THINK" }) }) }),
|
|
2816
|
-
/* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: 0, children: /* @__PURE__ */ jsx2(
|
|
3010
|
+
/* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, marginBottom: 0, children: /* @__PURE__ */ jsx2(MemoizedThought, { text: block.text, isStreaming }) })
|
|
2817
3011
|
] }, `thought-${i}`);
|
|
2818
3012
|
} else {
|
|
2819
3013
|
return /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, width: "100%", children: /* @__PURE__ */ jsx2(ToolCallLine, { toolCall: block.toolCall }) }, block.toolCall.id);
|
|
@@ -2826,25 +3020,88 @@ var init_MessageBubble = __esm({
|
|
|
2826
3020
|
}
|
|
2827
3021
|
});
|
|
2828
3022
|
|
|
3023
|
+
// src/cli/tui/components/Header.tsx
|
|
3024
|
+
import { Box as Box3, Text as Text3, useStdout as useStdout2 } from "ink";
|
|
3025
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3026
|
+
function Header({ modelName }) {
|
|
3027
|
+
const { stdout } = useStdout2();
|
|
3028
|
+
const logo = [
|
|
3029
|
+
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
3030
|
+
" \u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D",
|
|
3031
|
+
" \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
3032
|
+
" \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D ",
|
|
3033
|
+
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
3034
|
+
" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
|
|
3035
|
+
];
|
|
3036
|
+
const width = (stdout?.columns ?? 80) - 2;
|
|
3037
|
+
return /* @__PURE__ */ jsxs3(
|
|
3038
|
+
Box3,
|
|
3039
|
+
{
|
|
3040
|
+
flexDirection: "column",
|
|
3041
|
+
width,
|
|
3042
|
+
borderStyle: "round",
|
|
3043
|
+
borderColor: "#504945",
|
|
3044
|
+
paddingX: 1,
|
|
3045
|
+
marginTop: 0,
|
|
3046
|
+
marginBottom: 1,
|
|
3047
|
+
children: [
|
|
3048
|
+
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginBottom: 1, alignItems: "center", children: logo.map((line, i) => /* @__PURE__ */ jsx3(Text3, { color: "#fe8019", bold: true, children: line }, i)) }),
|
|
3049
|
+
/* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "#3c3836", paddingTop: 0, children: [
|
|
3050
|
+
/* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
3051
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "#fabd2f", children: "ZEN CODE" }),
|
|
3052
|
+
/* @__PURE__ */ jsx3(Text3, { color: "#a89984", dimColor: true, children: "v0.4.1" })
|
|
3053
|
+
] }),
|
|
3054
|
+
/* @__PURE__ */ jsx3(Text3, { color: "#83a598", bold: true, children: modelName })
|
|
3055
|
+
] })
|
|
3056
|
+
]
|
|
3057
|
+
}
|
|
3058
|
+
);
|
|
3059
|
+
}
|
|
3060
|
+
var init_Header = __esm({
|
|
3061
|
+
"src/cli/tui/components/Header.tsx"() {
|
|
3062
|
+
"use strict";
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
|
|
2829
3066
|
// src/cli/tui/components/ChatArea.tsx
|
|
2830
|
-
import
|
|
2831
|
-
import { Box as
|
|
2832
|
-
import { jsx as
|
|
2833
|
-
var ChatArea;
|
|
3067
|
+
import React3, { useMemo as useMemo2 } from "react";
|
|
3068
|
+
import { Box as Box4, Static } from "ink";
|
|
3069
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3070
|
+
var HEADER_ITEM, ChatArea;
|
|
2834
3071
|
var init_ChatArea = __esm({
|
|
2835
3072
|
"src/cli/tui/components/ChatArea.tsx"() {
|
|
2836
3073
|
"use strict";
|
|
2837
3074
|
init_MessageBubble();
|
|
2838
|
-
|
|
2839
|
-
|
|
3075
|
+
init_Header();
|
|
3076
|
+
HEADER_ITEM = { id: "static-header", isHeader: true };
|
|
3077
|
+
ChatArea = React3.memo(function ChatArea2({ messages, modelName }) {
|
|
3078
|
+
const completedMessages = messages.filter((m) => !m.isStreaming);
|
|
3079
|
+
const activeMessages = messages.filter((m) => m.isStreaming);
|
|
3080
|
+
const completedCount = completedMessages.length;
|
|
3081
|
+
const lastCompletedId = completedMessages[completedMessages.length - 1]?.id;
|
|
3082
|
+
const staticItems = useMemo2(() => [
|
|
3083
|
+
HEADER_ITEM,
|
|
3084
|
+
...completedMessages
|
|
3085
|
+
], [completedCount, lastCompletedId]);
|
|
3086
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", width: "100%", children: [
|
|
3087
|
+
/* @__PURE__ */ jsx4(Static, { items: staticItems, children: (item) => {
|
|
3088
|
+
if (item.isHeader) {
|
|
3089
|
+
return /* @__PURE__ */ jsx4(Header, { modelName }, "header");
|
|
3090
|
+
}
|
|
3091
|
+
return /* @__PURE__ */ jsx4(MessageBubble, { message: item }, item.id);
|
|
3092
|
+
} }),
|
|
3093
|
+
activeMessages.map((msg) => /* @__PURE__ */ jsx4(MessageBubble, { message: msg }, msg.id))
|
|
3094
|
+
] });
|
|
3095
|
+
}, (prev, next) => {
|
|
3096
|
+
return prev.messages === next.messages && prev.modelName === next.modelName;
|
|
2840
3097
|
});
|
|
2841
3098
|
}
|
|
2842
3099
|
});
|
|
2843
3100
|
|
|
2844
3101
|
// src/cli/tui/components/InputArea.tsx
|
|
2845
|
-
import { useRef, useState } from "react";
|
|
2846
|
-
import { Box as
|
|
2847
|
-
import {
|
|
3102
|
+
import React4, { useRef, useState } from "react";
|
|
3103
|
+
import { Box as Box5, Text as Text4, useInput, useStdout as useStdout4 } from "ink";
|
|
3104
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2848
3105
|
function CleanTextInput({
|
|
2849
3106
|
onSubmit,
|
|
2850
3107
|
placeholder,
|
|
@@ -2854,6 +3111,7 @@ function CleanTextInput({
|
|
|
2854
3111
|
const [value, setValue] = useState("");
|
|
2855
3112
|
const [cursor, setCursor] = useState(0);
|
|
2856
3113
|
const lastCtrlCAtRef = useRef(0);
|
|
3114
|
+
const pastedTextsRef = useRef([]);
|
|
2857
3115
|
useInput((input, key) => {
|
|
2858
3116
|
if (onScroll) {
|
|
2859
3117
|
if (key.pageUp || key.upArrow && key.shift) {
|
|
@@ -2870,6 +3128,7 @@ function CleanTextInput({
|
|
|
2870
3128
|
setValue("");
|
|
2871
3129
|
setCursor(0);
|
|
2872
3130
|
lastCtrlCAtRef.current = 0;
|
|
3131
|
+
pastedTextsRef.current = [];
|
|
2873
3132
|
return;
|
|
2874
3133
|
}
|
|
2875
3134
|
const now = Date.now();
|
|
@@ -2883,12 +3142,18 @@ function CleanTextInput({
|
|
|
2883
3142
|
return;
|
|
2884
3143
|
}
|
|
2885
3144
|
if (key.return) {
|
|
2886
|
-
|
|
3145
|
+
let trimmed = value.trim();
|
|
2887
3146
|
if (trimmed) {
|
|
3147
|
+
const stored = pastedTextsRef.current;
|
|
3148
|
+
trimmed = trimmed.replace(/\[pasted text#(\d+)\]/g, (_match, numStr) => {
|
|
3149
|
+
const idx = parseInt(numStr, 10) - 1;
|
|
3150
|
+
return idx >= 0 && idx < stored.length ? stored[idx] : _match;
|
|
3151
|
+
});
|
|
2888
3152
|
onSubmit(trimmed);
|
|
2889
3153
|
}
|
|
2890
3154
|
setValue("");
|
|
2891
3155
|
setCursor(0);
|
|
3156
|
+
pastedTextsRef.current = [];
|
|
2892
3157
|
return;
|
|
2893
3158
|
}
|
|
2894
3159
|
if (key.backspace || key.delete) {
|
|
@@ -2919,6 +3184,16 @@ function CleanTextInput({
|
|
|
2919
3184
|
setCursor(0);
|
|
2920
3185
|
return;
|
|
2921
3186
|
}
|
|
3187
|
+
if (input.includes("\n") || input.includes("\r")) {
|
|
3188
|
+
const cleaned = input.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
3189
|
+
if (cleaned.length > 0) {
|
|
3190
|
+
pastedTextsRef.current.push(cleaned);
|
|
3191
|
+
const placeholder2 = `[pasted text#${pastedTextsRef.current.length}]`;
|
|
3192
|
+
setValue((prev) => prev.slice(0, cursor) + placeholder2 + prev.slice(cursor));
|
|
3193
|
+
setCursor((prev) => prev + placeholder2.length);
|
|
3194
|
+
}
|
|
3195
|
+
return;
|
|
3196
|
+
}
|
|
2922
3197
|
if (key.ctrl || key.meta || key.escape) return;
|
|
2923
3198
|
if (!input || input.length === 0) return;
|
|
2924
3199
|
if (input.includes("\x1B") || input.includes("\0")) return;
|
|
@@ -2934,84 +3209,119 @@ function CleanTextInput({
|
|
|
2934
3209
|
setCursor((prev) => prev + input.length);
|
|
2935
3210
|
});
|
|
2936
3211
|
if (value.length === 0) {
|
|
2937
|
-
return /* @__PURE__ */
|
|
2938
|
-
/* @__PURE__ */
|
|
2939
|
-
/* @__PURE__ */
|
|
3212
|
+
return /* @__PURE__ */ jsxs5(Text4, { wrap: "wrap", children: [
|
|
3213
|
+
/* @__PURE__ */ jsx5(Text4, { inverse: true, children: " " }),
|
|
3214
|
+
/* @__PURE__ */ jsx5(Text4, { dimColor: true, children: placeholder || "" })
|
|
2940
3215
|
] });
|
|
2941
3216
|
}
|
|
2942
3217
|
const before = value.slice(0, cursor);
|
|
2943
3218
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
2944
3219
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
2945
|
-
return /* @__PURE__ */
|
|
2946
|
-
|
|
2947
|
-
/* @__PURE__ */
|
|
2948
|
-
|
|
2949
|
-
] });
|
|
2950
|
-
}
|
|
2951
|
-
function InputArea({ onSubmit, isRunning, onExitRequest, onScroll }) {
|
|
2952
|
-
return /* @__PURE__ */ jsxs3(Box4, { paddingX: 0, children: [
|
|
2953
|
-
/* @__PURE__ */ jsx4(Box4, { backgroundColor: isRunning ? "#504945" : "#b8bb26", paddingX: 1, marginRight: 1, children: /* @__PURE__ */ jsx4(Text3, { color: "#282828", bold: true, children: isRunning ? " WAIT " : " INPUT " }) }),
|
|
2954
|
-
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1, children: isRunning ? /* @__PURE__ */ jsx4(Box4, { flexGrow: 1, onWheel: (event) => {
|
|
2955
|
-
}, children: /* @__PURE__ */ jsx4(Text3, { color: "#a89984", italic: true, children: "Thinking..." }) }) : /* @__PURE__ */ jsx4(
|
|
2956
|
-
CleanTextInput,
|
|
2957
|
-
{
|
|
2958
|
-
onSubmit,
|
|
2959
|
-
placeholder: "Type a message or /command...",
|
|
2960
|
-
onExitRequest,
|
|
2961
|
-
onScroll
|
|
2962
|
-
}
|
|
2963
|
-
) })
|
|
3220
|
+
return /* @__PURE__ */ jsxs5(Text4, { wrap: "wrap", children: [
|
|
3221
|
+
before,
|
|
3222
|
+
/* @__PURE__ */ jsx5(Text4, { inverse: true, children: at }),
|
|
3223
|
+
after
|
|
2964
3224
|
] });
|
|
2965
3225
|
}
|
|
3226
|
+
var InputArea;
|
|
2966
3227
|
var init_InputArea = __esm({
|
|
2967
3228
|
"src/cli/tui/components/InputArea.tsx"() {
|
|
2968
3229
|
"use strict";
|
|
3230
|
+
InputArea = React4.memo(function InputArea2({ onSubmit, isRunning, onExitRequest, onScroll }) {
|
|
3231
|
+
const { stdout } = useStdout4();
|
|
3232
|
+
const labelCols = 10;
|
|
3233
|
+
const textWidth = (stdout?.columns ?? 80) - 2 - labelCols;
|
|
3234
|
+
return /* @__PURE__ */ jsxs5(Box5, { paddingX: 0, children: [
|
|
3235
|
+
/* @__PURE__ */ jsx5(Box5, { backgroundColor: isRunning ? "#504945" : "#b8bb26", paddingX: 1, marginRight: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#282828", bold: true, children: isRunning ? " WAIT " : " INPUT " }) }),
|
|
3236
|
+
/* @__PURE__ */ jsx5(Box5, { width: textWidth, children: isRunning ? /* @__PURE__ */ jsx5(Text4, { color: "#a89984", italic: true, children: "Thinking..." }) : /* @__PURE__ */ jsx5(
|
|
3237
|
+
CleanTextInput,
|
|
3238
|
+
{
|
|
3239
|
+
onSubmit,
|
|
3240
|
+
placeholder: "Type a message or /command...",
|
|
3241
|
+
onExitRequest,
|
|
3242
|
+
onScroll
|
|
3243
|
+
}
|
|
3244
|
+
) })
|
|
3245
|
+
] });
|
|
3246
|
+
});
|
|
2969
3247
|
}
|
|
2970
3248
|
});
|
|
2971
3249
|
|
|
2972
3250
|
// src/cli/tui/components/StatusBar.tsx
|
|
2973
|
-
import {
|
|
2974
|
-
import {
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: modelName }),
|
|
2981
|
-
isRunning && !subAgentProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2982
|
-
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
2983
|
-
/* @__PURE__ */ jsx5(Text4, { color: "#8ec07c", children: "\u25CF thinking..." })
|
|
2984
|
-
] }),
|
|
2985
|
-
subAgentProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2986
|
-
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
2987
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "#b8bb26", children: [
|
|
2988
|
-
"\u{1F916} Agents: ",
|
|
2989
|
-
subAgentProgress.completed + subAgentProgress.failed,
|
|
2990
|
-
"/",
|
|
2991
|
-
subAgentProgress.total
|
|
2992
|
-
] })
|
|
2993
|
-
] }),
|
|
2994
|
-
todoProgress && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2995
|
-
/* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: " \u2502 " }),
|
|
2996
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "#fabd2f", children: [
|
|
2997
|
-
"\u{1F4CB} Plan: ",
|
|
2998
|
-
todoProgress
|
|
2999
|
-
] })
|
|
3000
|
-
] })
|
|
3001
|
-
] }),
|
|
3002
|
-
/* @__PURE__ */ jsx5(Box5, { backgroundColor: "#3c3836", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "#ebdbb2", children: "/help" }) })
|
|
3003
|
-
] });
|
|
3251
|
+
import React5, { useEffect, useState as useState2 } from "react";
|
|
3252
|
+
import { Box as Box6, Text as Text5 } from "ink";
|
|
3253
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3254
|
+
function formatTokens(n) {
|
|
3255
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
3256
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
3257
|
+
return String(n);
|
|
3004
3258
|
}
|
|
3259
|
+
var StatusBar;
|
|
3005
3260
|
var init_StatusBar = __esm({
|
|
3006
3261
|
"src/cli/tui/components/StatusBar.tsx"() {
|
|
3007
3262
|
"use strict";
|
|
3263
|
+
init_progressStore();
|
|
3264
|
+
StatusBar = React5.memo(function StatusBar2({ isRunning, modelName, todoPlan, subAgentProgress }) {
|
|
3265
|
+
const [activeStreamingTool, setActiveStreamingTool] = useState2(progressStore.get());
|
|
3266
|
+
useEffect(() => {
|
|
3267
|
+
const handleUpdate = (progress) => {
|
|
3268
|
+
setActiveStreamingTool(progress);
|
|
3269
|
+
};
|
|
3270
|
+
progressStore.on("change", handleUpdate);
|
|
3271
|
+
return () => {
|
|
3272
|
+
progressStore.off("change", handleUpdate);
|
|
3273
|
+
};
|
|
3274
|
+
}, []);
|
|
3275
|
+
const todoProgress = todoPlan ? `${todoPlan.items.filter((i) => i.status === "completed").length}/${todoPlan.items.length}` : null;
|
|
3276
|
+
return /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, height: 1, width: "100%", children: [
|
|
3277
|
+
/* @__PURE__ */ jsx6(Box6, { backgroundColor: "#3c3836", paddingX: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", bold: true, children: "ZENCODE" }) }),
|
|
3278
|
+
/* @__PURE__ */ jsxs6(Box6, { backgroundColor: "#504945", paddingX: 1, flexGrow: 1, flexBasis: 0, children: [
|
|
3279
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", wrap: "truncate-end", children: modelName }),
|
|
3280
|
+
isRunning && !subAgentProgress && !activeStreamingTool && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
3281
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3282
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#8ec07c", children: "\u25CF thinking..." })
|
|
3283
|
+
] }),
|
|
3284
|
+
activeStreamingTool && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
3285
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3286
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "#fabd2f", wrap: "truncate-end", children: [
|
|
3287
|
+
"\u25CF ",
|
|
3288
|
+
activeStreamingTool.name,
|
|
3289
|
+
": ",
|
|
3290
|
+
activeStreamingTool.progress
|
|
3291
|
+
] })
|
|
3292
|
+
] }),
|
|
3293
|
+
subAgentProgress && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
3294
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3295
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "#b8bb26", children: [
|
|
3296
|
+
"Agents: ",
|
|
3297
|
+
subAgentProgress.completed + subAgentProgress.failed,
|
|
3298
|
+
"/",
|
|
3299
|
+
subAgentProgress.total
|
|
3300
|
+
] }),
|
|
3301
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3302
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "#83a598", children: [
|
|
3303
|
+
"tokens: ",
|
|
3304
|
+
formatTokens(subAgentProgress.tokens)
|
|
3305
|
+
] })
|
|
3306
|
+
] }),
|
|
3307
|
+
todoProgress && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
3308
|
+
/* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", children: " \u2502 " }),
|
|
3309
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "#fabd2f", children: [
|
|
3310
|
+
"Plan: ",
|
|
3311
|
+
todoProgress
|
|
3312
|
+
] })
|
|
3313
|
+
] })
|
|
3314
|
+
] }),
|
|
3315
|
+
/* @__PURE__ */ jsx6(Box6, { backgroundColor: "#3c3836", paddingX: 1, flexShrink: 0, children: /* @__PURE__ */ jsx6(Text5, { color: "#ebdbb2", children: "/help" }) })
|
|
3316
|
+
] });
|
|
3317
|
+
});
|
|
3008
3318
|
}
|
|
3009
3319
|
});
|
|
3010
3320
|
|
|
3011
3321
|
// src/cli/tui/components/ConfirmPrompt.tsx
|
|
3012
|
-
import { useState as
|
|
3013
|
-
import { Box as
|
|
3014
|
-
import { Fragment as
|
|
3322
|
+
import { useState as useState3 } from "react";
|
|
3323
|
+
import { Box as Box7, Text as Text6, useInput as useInput2 } from "ink";
|
|
3324
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3015
3325
|
function getToolDetails(toolName, params) {
|
|
3016
3326
|
const lines = [];
|
|
3017
3327
|
let label = toolName;
|
|
@@ -3057,8 +3367,8 @@ function getToolDetails(toolName, params) {
|
|
|
3057
3367
|
return { lines, label };
|
|
3058
3368
|
}
|
|
3059
3369
|
function FeedbackInput({ onSubmit }) {
|
|
3060
|
-
const [value, setValue] =
|
|
3061
|
-
const [cursor, setCursor] =
|
|
3370
|
+
const [value, setValue] = useState3("");
|
|
3371
|
+
const [cursor, setCursor] = useState3(0);
|
|
3062
3372
|
useInput2((input, key) => {
|
|
3063
3373
|
if (key.return) {
|
|
3064
3374
|
onSubmit(value);
|
|
@@ -3097,23 +3407,23 @@ function FeedbackInput({ onSubmit }) {
|
|
|
3097
3407
|
setCursor((prev) => prev + input.length);
|
|
3098
3408
|
});
|
|
3099
3409
|
if (value.length === 0) {
|
|
3100
|
-
return /* @__PURE__ */
|
|
3101
|
-
/* @__PURE__ */
|
|
3102
|
-
/* @__PURE__ */
|
|
3410
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
3411
|
+
/* @__PURE__ */ jsx7(Text6, { inverse: true, children: " " }),
|
|
3412
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u8F93\u5165\u53CD\u9988\u6307\u4EE4\u7ED9 AI..." })
|
|
3103
3413
|
] });
|
|
3104
3414
|
}
|
|
3105
3415
|
const before = value.slice(0, cursor);
|
|
3106
3416
|
const at = cursor < value.length ? value[cursor] : " ";
|
|
3107
3417
|
const after = cursor < value.length ? value.slice(cursor + 1) : "";
|
|
3108
|
-
return /* @__PURE__ */
|
|
3109
|
-
/* @__PURE__ */
|
|
3110
|
-
/* @__PURE__ */
|
|
3111
|
-
/* @__PURE__ */
|
|
3418
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
3419
|
+
/* @__PURE__ */ jsx7(Text6, { children: before }),
|
|
3420
|
+
/* @__PURE__ */ jsx7(Text6, { inverse: true, children: at }),
|
|
3421
|
+
/* @__PURE__ */ jsx7(Text6, { children: after })
|
|
3112
3422
|
] });
|
|
3113
3423
|
}
|
|
3114
3424
|
function ConfirmPrompt({ confirm, onRespond }) {
|
|
3115
|
-
const [selected, setSelected] =
|
|
3116
|
-
const [feedbackMode, setFeedbackMode] =
|
|
3425
|
+
const [selected, setSelected] = useState3(0);
|
|
3426
|
+
const [feedbackMode, setFeedbackMode] = useState3(false);
|
|
3117
3427
|
useInput2((input, key) => {
|
|
3118
3428
|
if (feedbackMode) return;
|
|
3119
3429
|
if (key.leftArrow) {
|
|
@@ -3136,21 +3446,21 @@ function ConfirmPrompt({ confirm, onRespond }) {
|
|
|
3136
3446
|
}, { isActive: !feedbackMode });
|
|
3137
3447
|
const { lines, label } = getToolDetails(confirm.toolName, confirm.params);
|
|
3138
3448
|
if (feedbackMode) {
|
|
3139
|
-
return /* @__PURE__ */
|
|
3140
|
-
/* @__PURE__ */
|
|
3141
|
-
/* @__PURE__ */
|
|
3142
|
-
|
|
3449
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 0, marginY: 0, children: [
|
|
3450
|
+
/* @__PURE__ */ jsx7(Box7, { backgroundColor: "#fe8019", paddingX: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "#282828", bold: true, children: " FEEDBACK " }) }),
|
|
3451
|
+
/* @__PURE__ */ jsxs7(
|
|
3452
|
+
Box7,
|
|
3143
3453
|
{
|
|
3144
3454
|
flexDirection: "column",
|
|
3145
3455
|
borderStyle: "round",
|
|
3146
3456
|
borderColor: "#fe8019",
|
|
3147
3457
|
paddingX: 1,
|
|
3148
3458
|
children: [
|
|
3149
|
-
/* @__PURE__ */
|
|
3150
|
-
lines.map((line, i) => /* @__PURE__ */
|
|
3151
|
-
/* @__PURE__ */
|
|
3152
|
-
/* @__PURE__ */
|
|
3153
|
-
/* @__PURE__ */
|
|
3459
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "#fabd2f", children: label }),
|
|
3460
|
+
lines.map((line, i) => /* @__PURE__ */ jsx7(Text6, { color: "#a89984", children: line }, i)),
|
|
3461
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 0, gap: 1, children: [
|
|
3462
|
+
/* @__PURE__ */ jsx7(Text6, { color: "#83a598", bold: true, children: "Reason: " }),
|
|
3463
|
+
/* @__PURE__ */ jsx7(
|
|
3154
3464
|
FeedbackInput,
|
|
3155
3465
|
{
|
|
3156
3466
|
onSubmit: (text) => {
|
|
@@ -3167,29 +3477,29 @@ function ConfirmPrompt({ confirm, onRespond }) {
|
|
|
3167
3477
|
]
|
|
3168
3478
|
}
|
|
3169
3479
|
),
|
|
3170
|
-
/* @__PURE__ */
|
|
3480
|
+
/* @__PURE__ */ jsx7(Box7, { paddingX: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Enter to send \xB7 Esc to cancel" }) })
|
|
3171
3481
|
] });
|
|
3172
3482
|
}
|
|
3173
|
-
return /* @__PURE__ */
|
|
3174
|
-
/* @__PURE__ */
|
|
3175
|
-
/* @__PURE__ */
|
|
3176
|
-
|
|
3483
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 0, marginY: 0, children: [
|
|
3484
|
+
/* @__PURE__ */ jsx7(Box7, { backgroundColor: "#fe8019", paddingX: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "#282828", bold: true, children: " CONFIRMATION REQUIRED " }) }),
|
|
3485
|
+
/* @__PURE__ */ jsxs7(
|
|
3486
|
+
Box7,
|
|
3177
3487
|
{
|
|
3178
3488
|
flexDirection: "column",
|
|
3179
3489
|
borderStyle: "round",
|
|
3180
3490
|
borderColor: "#fe8019",
|
|
3181
3491
|
paddingX: 1,
|
|
3182
3492
|
children: [
|
|
3183
|
-
/* @__PURE__ */
|
|
3184
|
-
lines.map((line, i) => /* @__PURE__ */
|
|
3185
|
-
/* @__PURE__ */
|
|
3493
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "#fabd2f", children: label }),
|
|
3494
|
+
lines.map((line, i) => /* @__PURE__ */ jsx7(Text6, { color: "#a89984", children: line }, i)),
|
|
3495
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 0, gap: 2, justifyContent: "center", children: OPTIONS.map((opt, i) => {
|
|
3186
3496
|
const isSelected = i === selected;
|
|
3187
3497
|
const optColor = opt.key === "deny" ? "#fb4934" : opt.key === "allow" ? "#b8bb26" : "#8ec07c";
|
|
3188
|
-
return /* @__PURE__ */
|
|
3498
|
+
return /* @__PURE__ */ jsx7(Box7, { children: isSelected ? /* @__PURE__ */ jsxs7(Text6, { bold: true, backgroundColor: optColor, color: "#282828", children: [
|
|
3189
3499
|
" ",
|
|
3190
3500
|
opt.label,
|
|
3191
3501
|
" "
|
|
3192
|
-
] }) : /* @__PURE__ */
|
|
3502
|
+
] }) : /* @__PURE__ */ jsxs7(Text6, { color: optColor, children: [
|
|
3193
3503
|
" ",
|
|
3194
3504
|
opt.label,
|
|
3195
3505
|
" "
|
|
@@ -3198,7 +3508,7 @@ function ConfirmPrompt({ confirm, onRespond }) {
|
|
|
3198
3508
|
]
|
|
3199
3509
|
}
|
|
3200
3510
|
),
|
|
3201
|
-
/* @__PURE__ */
|
|
3511
|
+
/* @__PURE__ */ jsx7(Box7, { paddingX: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2190 \u2192 select \xB7 Enter confirm \xB7 y/n shortcuts \xB7 Tab feedback" }) })
|
|
3202
3512
|
] });
|
|
3203
3513
|
}
|
|
3204
3514
|
var OPTIONS;
|
|
@@ -3214,16 +3524,17 @@ var init_ConfirmPrompt = __esm({
|
|
|
3214
3524
|
});
|
|
3215
3525
|
|
|
3216
3526
|
// src/cli/tui/components/TodoPanel.tsx
|
|
3217
|
-
import
|
|
3218
|
-
import {
|
|
3527
|
+
import React7 from "react";
|
|
3528
|
+
import { Box as Box8, Text as Text7 } from "ink";
|
|
3529
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3219
3530
|
function getStatusIcon(status) {
|
|
3220
3531
|
switch (status) {
|
|
3221
3532
|
case "completed":
|
|
3222
|
-
return "
|
|
3533
|
+
return "*";
|
|
3223
3534
|
case "in-progress":
|
|
3224
|
-
return "
|
|
3535
|
+
return ">";
|
|
3225
3536
|
default:
|
|
3226
|
-
return "
|
|
3537
|
+
return " ";
|
|
3227
3538
|
}
|
|
3228
3539
|
}
|
|
3229
3540
|
function getStatusColor(status) {
|
|
@@ -3236,100 +3547,89 @@ function getStatusColor(status) {
|
|
|
3236
3547
|
return "#928374";
|
|
3237
3548
|
}
|
|
3238
3549
|
}
|
|
3239
|
-
|
|
3240
|
-
const completed = plan.items.filter((i) => i.status === "completed").length;
|
|
3241
|
-
const total = plan.items.length;
|
|
3242
|
-
return /* @__PURE__ */ jsxs6(
|
|
3243
|
-
Box7,
|
|
3244
|
-
{
|
|
3245
|
-
flexDirection: "column",
|
|
3246
|
-
borderStyle: "round",
|
|
3247
|
-
borderColor: "#83a598",
|
|
3248
|
-
paddingX: 1,
|
|
3249
|
-
marginY: 1,
|
|
3250
|
-
children: [
|
|
3251
|
-
/* @__PURE__ */ jsxs6(Box7, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
3252
|
-
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "#83a598", children: "\u{1F4CB} PROJECT PLAN" }),
|
|
3253
|
-
/* @__PURE__ */ jsxs6(Text6, { color: "#ebdbb2", children: [
|
|
3254
|
-
completed,
|
|
3255
|
-
"/",
|
|
3256
|
-
total,
|
|
3257
|
-
" tasks"
|
|
3258
|
-
] })
|
|
3259
|
-
] }),
|
|
3260
|
-
plan.items.map((item) => /* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
|
|
3261
|
-
/* @__PURE__ */ jsx7(Text6, { color: getStatusColor(item.status), children: getStatusIcon(item.status) }),
|
|
3262
|
-
/* @__PURE__ */ jsx7(
|
|
3263
|
-
Text6,
|
|
3264
|
-
{
|
|
3265
|
-
color: item.status === "completed" ? "#b8bb26" : item.status === "in-progress" ? "#fabd2f" : "#ebdbb2",
|
|
3266
|
-
dimColor: item.status === "pending",
|
|
3267
|
-
strikethrough: item.status === "completed",
|
|
3268
|
-
children: item.title
|
|
3269
|
-
}
|
|
3270
|
-
)
|
|
3271
|
-
] }, item.id))
|
|
3272
|
-
]
|
|
3273
|
-
}
|
|
3274
|
-
);
|
|
3275
|
-
}
|
|
3550
|
+
var TodoPanel;
|
|
3276
3551
|
var init_TodoPanel = __esm({
|
|
3277
3552
|
"src/cli/tui/components/TodoPanel.tsx"() {
|
|
3278
3553
|
"use strict";
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3554
|
+
TodoPanel = React7.memo(function TodoPanel2({ plan }) {
|
|
3555
|
+
const completed = plan.items.filter((i) => i.status === "completed").length;
|
|
3556
|
+
const total = plan.items.length;
|
|
3557
|
+
return /* @__PURE__ */ jsxs8(
|
|
3558
|
+
Box8,
|
|
3559
|
+
{
|
|
3560
|
+
flexDirection: "column",
|
|
3561
|
+
borderStyle: "round",
|
|
3562
|
+
borderColor: "#83a598",
|
|
3563
|
+
paddingX: 1,
|
|
3564
|
+
marginY: 0,
|
|
3565
|
+
width: "100%",
|
|
3566
|
+
children: [
|
|
3567
|
+
/* @__PURE__ */ jsxs8(Box8, { justifyContent: "space-between", marginBottom: 0, children: [
|
|
3568
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "#83a598", children: "PROJECT PLAN" }),
|
|
3569
|
+
/* @__PURE__ */ jsxs8(Text7, { color: "#ebdbb2", children: [
|
|
3570
|
+
completed,
|
|
3571
|
+
"/",
|
|
3572
|
+
total,
|
|
3573
|
+
" tasks"
|
|
3574
|
+
] })
|
|
3575
|
+
] }),
|
|
3576
|
+
plan.items.map((item) => /* @__PURE__ */ jsxs8(Box8, { gap: 0, children: [
|
|
3577
|
+
/* @__PURE__ */ jsxs8(Text7, { color: getStatusColor(item.status), children: [
|
|
3578
|
+
"[",
|
|
3579
|
+
getStatusIcon(item.status),
|
|
3580
|
+
"]"
|
|
3581
|
+
] }),
|
|
3582
|
+
/* @__PURE__ */ jsx8(Text7, { children: " " }),
|
|
3583
|
+
/* @__PURE__ */ jsx8(
|
|
3584
|
+
Text7,
|
|
3585
|
+
{
|
|
3586
|
+
color: item.status === "completed" ? "#b8bb26" : item.status === "in-progress" ? "#fabd2f" : "#ebdbb2",
|
|
3587
|
+
dimColor: item.status === "pending",
|
|
3588
|
+
strikethrough: item.status === "completed",
|
|
3589
|
+
children: item.title
|
|
3590
|
+
}
|
|
3591
|
+
)
|
|
3592
|
+
] }, item.id))
|
|
3593
|
+
]
|
|
3594
|
+
}
|
|
3595
|
+
);
|
|
3596
|
+
}, (prev, next) => {
|
|
3597
|
+
return prev.plan === next.plan;
|
|
3598
|
+
});
|
|
3309
3599
|
}
|
|
3310
3600
|
});
|
|
3311
3601
|
|
|
3312
3602
|
// src/cli/tui/App.tsx
|
|
3313
|
-
import { useReducer, useCallback, useEffect, useRef as useRef2, useState as
|
|
3314
|
-
import { Box as Box9, Text as Text8, useInput as useInput3, useStdout } from "ink";
|
|
3315
|
-
import { jsx as jsx9, jsxs as
|
|
3603
|
+
import { useReducer, useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState4 } from "react";
|
|
3604
|
+
import { Box as Box9, Text as Text8, useInput as useInput3, useStdout as useStdout5 } from "ink";
|
|
3605
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3316
3606
|
function App({ config, client, agent, registry, todoStore, subAgentTracker, agentRegistry, skillRegistry }) {
|
|
3317
|
-
const { stdout } =
|
|
3318
|
-
const [,
|
|
3319
|
-
|
|
3607
|
+
const { stdout } = useStdout5();
|
|
3608
|
+
const [resetKey, setResetKey] = useState4(0);
|
|
3609
|
+
useEffect2(() => {
|
|
3610
|
+
let timer;
|
|
3611
|
+
let lastCols = stdout.columns;
|
|
3612
|
+
let lastRows = stdout.rows;
|
|
3320
3613
|
const onResize = () => {
|
|
3321
|
-
|
|
3614
|
+
if (stdout.columns === lastCols && stdout.rows === lastRows) return;
|
|
3615
|
+
lastCols = stdout.columns;
|
|
3616
|
+
lastRows = stdout.rows;
|
|
3617
|
+
clearTimeout(timer);
|
|
3618
|
+
timer = setTimeout(() => {
|
|
3619
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
3620
|
+
setResetKey((prev) => prev + 1);
|
|
3621
|
+
}, 100);
|
|
3322
3622
|
};
|
|
3323
3623
|
stdout.on("resize", onResize);
|
|
3324
3624
|
return () => {
|
|
3325
3625
|
stdout.off("resize", onResize);
|
|
3626
|
+
clearTimeout(timer);
|
|
3326
3627
|
};
|
|
3327
3628
|
}, [stdout]);
|
|
3328
3629
|
const [state, dispatch] = useReducer(
|
|
3329
3630
|
tuiReducer,
|
|
3330
3631
|
createInitialState(config.model)
|
|
3331
3632
|
);
|
|
3332
|
-
const [resetKey, setResetKey] = useState3(0);
|
|
3333
3633
|
const currentCallbacksRef = useRef2(null);
|
|
3334
3634
|
const agentRef = useRef2(agent);
|
|
3335
3635
|
const todoStoreRef = useRef2(todoStore);
|
|
@@ -3337,12 +3637,12 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3337
3637
|
agentRef.current = agent;
|
|
3338
3638
|
todoStoreRef.current = todoStore;
|
|
3339
3639
|
subAgentTrackerRef.current = subAgentTracker;
|
|
3340
|
-
|
|
3640
|
+
useEffect2(() => {
|
|
3341
3641
|
return todoStoreRef.current.subscribe((plan) => {
|
|
3342
3642
|
dispatch({ type: "SET_TODO_PLAN", plan });
|
|
3343
3643
|
});
|
|
3344
3644
|
}, []);
|
|
3345
|
-
|
|
3645
|
+
useEffect2(() => {
|
|
3346
3646
|
let timer = null;
|
|
3347
3647
|
let latest = null;
|
|
3348
3648
|
const unsub = subAgentTrackerRef.current.subscribe((progress) => {
|
|
@@ -3372,7 +3672,7 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3372
3672
|
(found, msg) => found || msg.confirmPending,
|
|
3373
3673
|
void 0
|
|
3374
3674
|
);
|
|
3375
|
-
|
|
3675
|
+
useEffect2(() => {
|
|
3376
3676
|
setStructuredConfirmHandler((toolName, params) => {
|
|
3377
3677
|
return new Promise((resolve10) => {
|
|
3378
3678
|
const id = `confirm-${Date.now()}`;
|
|
@@ -3432,7 +3732,7 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3432
3732
|
await runAgent(expandedPrompt);
|
|
3433
3733
|
return;
|
|
3434
3734
|
}
|
|
3435
|
-
|
|
3735
|
+
handleSlashCommand(text, {
|
|
3436
3736
|
config,
|
|
3437
3737
|
agent: agentRef.current,
|
|
3438
3738
|
registry,
|
|
@@ -3475,9 +3775,10 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3475
3775
|
return;
|
|
3476
3776
|
}
|
|
3477
3777
|
if (input === "c" && key.ctrl) {
|
|
3478
|
-
if (state.isRunning) {
|
|
3778
|
+
if (state.isRunning || confirmPending) {
|
|
3479
3779
|
agentRef.current.interrupt();
|
|
3480
3780
|
currentCallbacksRef.current?._stopBatcher?.();
|
|
3781
|
+
dispatch({ type: "CONFIRM_RESPONDED", id: "" });
|
|
3481
3782
|
dispatch({ type: "SET_RUNNING", running: false });
|
|
3482
3783
|
dispatch({ type: "FINISH_STREAMING" });
|
|
3483
3784
|
return;
|
|
@@ -3487,18 +3788,21 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3487
3788
|
process.exit(0);
|
|
3488
3789
|
}
|
|
3489
3790
|
});
|
|
3490
|
-
return /* @__PURE__ */
|
|
3491
|
-
/* @__PURE__ */
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3791
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, width: "100%", children: [
|
|
3792
|
+
/* @__PURE__ */ jsx9(
|
|
3793
|
+
ChatArea,
|
|
3794
|
+
{
|
|
3795
|
+
messages: state.messages,
|
|
3796
|
+
modelName: state.modelName
|
|
3797
|
+
}
|
|
3798
|
+
),
|
|
3799
|
+
state.error && /* @__PURE__ */ jsxs9(Box9, { borderStyle: "round", borderColor: "#fb4934", paddingX: 1, marginBottom: 0, children: [
|
|
3496
3800
|
/* @__PURE__ */ jsx9(Text8, { color: "#fb4934", bold: true, children: "ERROR: " }),
|
|
3497
3801
|
/* @__PURE__ */ jsx9(Text8, { color: "#fb4934", children: state.error })
|
|
3498
3802
|
] }),
|
|
3499
3803
|
confirmPending && /* @__PURE__ */ jsx9(ConfirmPrompt, { confirm: confirmPending, onRespond: handleConfirmResponse }),
|
|
3500
3804
|
state.todoPlan && /* @__PURE__ */ jsx9(TodoPanel, { plan: state.todoPlan }),
|
|
3501
|
-
/* @__PURE__ */ jsx9(Box9, { marginTop:
|
|
3805
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 0, children: /* @__PURE__ */ jsx9(
|
|
3502
3806
|
InputArea,
|
|
3503
3807
|
{
|
|
3504
3808
|
onSubmit: handleSubmit,
|
|
@@ -3506,7 +3810,7 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3506
3810
|
onExitRequest: () => process.exit(0)
|
|
3507
3811
|
}
|
|
3508
3812
|
) }),
|
|
3509
|
-
/* @__PURE__ */ jsx9(
|
|
3813
|
+
/* @__PURE__ */ jsx9(
|
|
3510
3814
|
StatusBar,
|
|
3511
3815
|
{
|
|
3512
3816
|
isRunning: state.isRunning,
|
|
@@ -3514,125 +3818,9 @@ function App({ config, client, agent, registry, todoStore, subAgentTracker, agen
|
|
|
3514
3818
|
todoPlan: state.todoPlan,
|
|
3515
3819
|
subAgentProgress: state.subAgentProgress
|
|
3516
3820
|
}
|
|
3517
|
-
)
|
|
3821
|
+
)
|
|
3518
3822
|
] }, resetKey);
|
|
3519
3823
|
}
|
|
3520
|
-
function handleSlashCommand2(input, ctx) {
|
|
3521
|
-
const { config, agent, registry, dispatch, setResetKey, client, todoStore, subAgentTracker, agentRegistry, skillRegistry } = ctx;
|
|
3522
|
-
const parts = input.trim().split(/\s+/);
|
|
3523
|
-
const command = parts[0];
|
|
3524
|
-
switch (command) {
|
|
3525
|
-
case "/help":
|
|
3526
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3527
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3528
|
-
{
|
|
3529
|
-
let helpText = `\u53EF\u7528\u547D\u4EE4:
|
|
3530
|
-
/help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
|
|
3531
|
-
/skills \u5217\u51FA\u6240\u6709\u53EF\u7528\u6280\u80FD
|
|
3532
|
-
/agents \u5217\u51FA\u6240\u6709\u53EF\u7528\u5B50 Agent
|
|
3533
|
-
/parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
|
|
3534
|
-
/todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
|
|
3535
|
-
/clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
|
|
3536
|
-
/info \u663E\u793A\u5F53\u524D\u914D\u7F6E
|
|
3537
|
-
Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42 / \u9000\u51FA
|
|
3538
|
-
Ctrl+D \u9000\u51FA`;
|
|
3539
|
-
const skills = skillRegistry.list();
|
|
3540
|
-
if (skills.length > 0) {
|
|
3541
|
-
helpText += `
|
|
3542
|
-
|
|
3543
|
-
\u53EF\u7528\u6280\u80FD:
|
|
3544
|
-
${skills.map((s) => ` /${s.name} ${s.description}`).join("\n")}`;
|
|
3545
|
-
}
|
|
3546
|
-
dispatch({ type: "APPEND_CONTENT", text: helpText });
|
|
3547
|
-
}
|
|
3548
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3549
|
-
break;
|
|
3550
|
-
case "/skills": {
|
|
3551
|
-
const skills = skillRegistry.list();
|
|
3552
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3553
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3554
|
-
if (skills.length === 0) {
|
|
3555
|
-
dispatch({ type: "APPEND_CONTENT", text: "\u6682\u65E0\u53EF\u7528\u6280\u80FD\u3002\u5728 ~/.zencode/skills/ \u6216 .zencode/skills/ \u653E\u7F6E YAML \u6587\u4EF6\u6DFB\u52A0\u6280\u80FD\u3002" });
|
|
3556
|
-
} else {
|
|
3557
|
-
const lines = skills.map((s) => ` /${s.name}: ${s.description}`);
|
|
3558
|
-
dispatch({ type: "APPEND_CONTENT", text: `\u53EF\u7528\u6280\u80FD (${skills.length}):
|
|
3559
|
-
${lines.join("\n")}` });
|
|
3560
|
-
}
|
|
3561
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3562
|
-
break;
|
|
3563
|
-
}
|
|
3564
|
-
case "/agents": {
|
|
3565
|
-
const agents = agentRegistry.list();
|
|
3566
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3567
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3568
|
-
if (agents.length === 0) {
|
|
3569
|
-
dispatch({ type: "APPEND_CONTENT", text: "\u6682\u65E0\u53EF\u7528\u5B50 Agent\u3002" });
|
|
3570
|
-
} else {
|
|
3571
|
-
const lines = agents.map((a) => ` ${a.name}: ${a.description} [tools: ${a.tools.join(", ")}]`);
|
|
3572
|
-
dispatch({ type: "APPEND_CONTENT", text: `\u53EF\u7528\u5B50 Agent (${agents.length}):
|
|
3573
|
-
${lines.join("\n")}` });
|
|
3574
|
-
}
|
|
3575
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3576
|
-
break;
|
|
3577
|
-
}
|
|
3578
|
-
case "/clear":
|
|
3579
|
-
agent.getConversation().clear();
|
|
3580
|
-
dispatch({ type: "CLEAR_MESSAGES" });
|
|
3581
|
-
setResetKey((prev) => prev + 1);
|
|
3582
|
-
break;
|
|
3583
|
-
case "/parallel": {
|
|
3584
|
-
const current = config.features.parallel_agents;
|
|
3585
|
-
const next = current === "on" ? "off" : "on";
|
|
3586
|
-
config.features.parallel_agents = next;
|
|
3587
|
-
if (next === "off") {
|
|
3588
|
-
registry.unregister("spawn-agents");
|
|
3589
|
-
} else if (!registry.has("spawn-agents")) {
|
|
3590
|
-
registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker));
|
|
3591
|
-
}
|
|
3592
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3593
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3594
|
-
dispatch({ type: "APPEND_CONTENT", text: `\u5E76\u884C\u5B50 Agent \u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}` });
|
|
3595
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3596
|
-
break;
|
|
3597
|
-
}
|
|
3598
|
-
case "/todo": {
|
|
3599
|
-
const current = config.features.todo;
|
|
3600
|
-
const next = current === "on" ? "off" : "on";
|
|
3601
|
-
config.features.todo = next;
|
|
3602
|
-
if (next === "off") {
|
|
3603
|
-
registry.unregister("todo");
|
|
3604
|
-
} else if (!registry.has("todo")) {
|
|
3605
|
-
registry.register(createTodoTool(todoStore));
|
|
3606
|
-
}
|
|
3607
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3608
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3609
|
-
dispatch({ type: "APPEND_CONTENT", text: `Todo \u8BA1\u5212\u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}` });
|
|
3610
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3611
|
-
break;
|
|
3612
|
-
}
|
|
3613
|
-
case "/info":
|
|
3614
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3615
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3616
|
-
dispatch({
|
|
3617
|
-
type: "APPEND_CONTENT",
|
|
3618
|
-
text: `\u6A21\u578B: ${config.model}
|
|
3619
|
-
\u57FA\u7840 URL: ${config.base_url}
|
|
3620
|
-
\u5B50 Agent: ${agentRegistry.listNames().join(", ") || "\u65E0"}
|
|
3621
|
-
\u6280\u80FD: ${skillRegistry.listNames().map((n) => "/" + n).join(", ") || "\u65E0"}`
|
|
3622
|
-
});
|
|
3623
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3624
|
-
break;
|
|
3625
|
-
default:
|
|
3626
|
-
dispatch({ type: "ADD_USER_MESSAGE", text: input });
|
|
3627
|
-
dispatch({ type: "START_ASSISTANT" });
|
|
3628
|
-
dispatch({
|
|
3629
|
-
type: "APPEND_CONTENT",
|
|
3630
|
-
text: `\u672A\u77E5\u547D\u4EE4: ${command}\u3002\u8F93\u5165 /help \u67E5\u770B\u5E2E\u52A9\u3002`
|
|
3631
|
-
});
|
|
3632
|
-
dispatch({ type: "FINISH_STREAMING" });
|
|
3633
|
-
break;
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
3636
3824
|
var init_App = __esm({
|
|
3637
3825
|
"src/cli/tui/App.tsx"() {
|
|
3638
3826
|
"use strict";
|
|
@@ -3640,14 +3828,11 @@ var init_App = __esm({
|
|
|
3640
3828
|
init_permission();
|
|
3641
3829
|
init_state();
|
|
3642
3830
|
init_bridge();
|
|
3643
|
-
init_spawn_agents();
|
|
3644
|
-
init_todo();
|
|
3645
3831
|
init_ChatArea();
|
|
3646
3832
|
init_InputArea();
|
|
3647
3833
|
init_StatusBar();
|
|
3648
3834
|
init_ConfirmPrompt();
|
|
3649
3835
|
init_TodoPanel();
|
|
3650
|
-
init_Header();
|
|
3651
3836
|
}
|
|
3652
3837
|
});
|
|
3653
3838
|
|
|
@@ -3687,7 +3872,7 @@ async function startTui(options) {
|
|
|
3687
3872
|
if (config.features.todo === "on") {
|
|
3688
3873
|
registry.register(createTodoTool(todoStore));
|
|
3689
3874
|
}
|
|
3690
|
-
registry.register(createDispatchTool(client, registry, config, agentRegistry));
|
|
3875
|
+
registry.register(createDispatchTool(client, registry, config, agentRegistry, subAgentTracker));
|
|
3691
3876
|
const agent = new Agent(client, registry, config, systemPrompt);
|
|
3692
3877
|
const { waitUntilExit } = render(
|
|
3693
3878
|
/* @__PURE__ */ jsx10(
|
|
@@ -3841,69 +4026,9 @@ init_spawn_agents();
|
|
|
3841
4026
|
init_todo();
|
|
3842
4027
|
init_dispatch();
|
|
3843
4028
|
init_bridge();
|
|
4029
|
+
init_ui();
|
|
3844
4030
|
import * as readline from "readline";
|
|
3845
|
-
|
|
3846
|
-
// src/cli/ui.ts
|
|
3847
|
-
import chalk2 from "chalk";
|
|
3848
|
-
import ora from "ora";
|
|
3849
|
-
import { marked } from "marked";
|
|
3850
|
-
import * as _markedTerminal from "marked-terminal";
|
|
3851
|
-
import { createTwoFilesPatch } from "diff";
|
|
3852
|
-
var markedTerminal2 = _markedTerminal.markedTerminal;
|
|
3853
|
-
marked.use(markedTerminal2());
|
|
3854
|
-
function printStream(text) {
|
|
3855
|
-
process.stdout.write(text);
|
|
3856
|
-
}
|
|
3857
|
-
function printToolCall(toolName, params) {
|
|
3858
|
-
let detail = "";
|
|
3859
|
-
if (toolName === "bash" && params["command"]) {
|
|
3860
|
-
detail = ` ${chalk2.dim(String(params["command"]).slice(0, 80))}`;
|
|
3861
|
-
} else if ((toolName === "read-file" || toolName === "write-file" || toolName === "edit-file") && params["path"]) {
|
|
3862
|
-
detail = ` ${chalk2.dim(String(params["path"]))}`;
|
|
3863
|
-
} else if (toolName === "glob" && params["pattern"]) {
|
|
3864
|
-
detail = ` ${chalk2.dim(String(params["pattern"]))}`;
|
|
3865
|
-
} else if (toolName === "grep" && params["pattern"]) {
|
|
3866
|
-
detail = ` ${chalk2.dim(String(params["pattern"]))}`;
|
|
3867
|
-
} else if (toolName === "spawn-agents" && params["tasks"]) {
|
|
3868
|
-
const tasks = params["tasks"];
|
|
3869
|
-
detail = ` ${chalk2.dim(`${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`)}`;
|
|
3870
|
-
} else if (toolName === "todo" && params["action"]) {
|
|
3871
|
-
const action = String(params["action"]);
|
|
3872
|
-
const id = params["id"] ? ` [${params["id"]}]` : "";
|
|
3873
|
-
detail = ` ${chalk2.dim(`${action}${id}`)}`;
|
|
3874
|
-
}
|
|
3875
|
-
const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" : "\u2699";
|
|
3876
|
-
console.log(chalk2.yellow(` ${icon} ${toolName}`) + detail);
|
|
3877
|
-
}
|
|
3878
|
-
function printToolResult(toolName, result, truncated) {
|
|
3879
|
-
if (truncated) {
|
|
3880
|
-
console.log(chalk2.dim(` \u2713 ${toolName} (\u8F93\u51FA\u5DF2\u622A\u65AD)`));
|
|
3881
|
-
} else {
|
|
3882
|
-
const lines = result.split("\n").length;
|
|
3883
|
-
console.log(chalk2.dim(` \u2713 ${toolName} (${lines} \u884C)`));
|
|
3884
|
-
}
|
|
3885
|
-
}
|
|
3886
|
-
function printError(message) {
|
|
3887
|
-
console.error(chalk2.red(`\u2717 ${message}`));
|
|
3888
|
-
}
|
|
3889
|
-
function printInfo(message) {
|
|
3890
|
-
console.log(chalk2.cyan(`\u2139 ${message}`));
|
|
3891
|
-
}
|
|
3892
|
-
function printWarning(message) {
|
|
3893
|
-
console.log(chalk2.yellow(`\u26A0 ${message}`));
|
|
3894
|
-
}
|
|
3895
|
-
function printSuccess(message) {
|
|
3896
|
-
console.log(chalk2.green(`\u2713 ${message}`));
|
|
3897
|
-
}
|
|
3898
|
-
function printWelcome(modelName) {
|
|
3899
|
-
console.log(chalk2.bold.cyan("\n ZenCode") + chalk2.dim(" - \u6781\u7B80 AI \u7F16\u7A0B\u52A9\u624B\n"));
|
|
3900
|
-
console.log(chalk2.dim(` \u6A21\u578B: ${modelName}`));
|
|
3901
|
-
console.log(chalk2.dim(` \u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA
|
|
3902
|
-
`));
|
|
3903
|
-
}
|
|
3904
|
-
|
|
3905
|
-
// src/cli/repl.ts
|
|
3906
|
-
function handleSlashCommand(input, context) {
|
|
4031
|
+
function handleSlashCommand2(input, context) {
|
|
3907
4032
|
const parts = input.trim().split(/\s+/);
|
|
3908
4033
|
const command = parts[0];
|
|
3909
4034
|
switch (command) {
|
|
@@ -4095,7 +4220,7 @@ async function startRepl(options) {
|
|
|
4095
4220
|
rl.prompt();
|
|
4096
4221
|
return;
|
|
4097
4222
|
}
|
|
4098
|
-
const handled =
|
|
4223
|
+
const handled = handleSlashCommand2(input, {
|
|
4099
4224
|
config,
|
|
4100
4225
|
registry,
|
|
4101
4226
|
client,
|
|
@@ -4171,6 +4296,7 @@ init_spawn_agents();
|
|
|
4171
4296
|
init_todo();
|
|
4172
4297
|
init_dispatch();
|
|
4173
4298
|
init_bridge();
|
|
4299
|
+
init_ui();
|
|
4174
4300
|
async function runOnce(prompt, config) {
|
|
4175
4301
|
const agentRegistry = new SubAgentConfigRegistry();
|
|
4176
4302
|
for (const agentConfig of loadAllAgentConfigs()) {
|
|
@@ -4234,7 +4360,7 @@ async function runOnce(prompt, config) {
|
|
|
4234
4360
|
}
|
|
4235
4361
|
function createCli() {
|
|
4236
4362
|
const program2 = new Command();
|
|
4237
|
-
program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.
|
|
4363
|
+
program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.4.1").option("-m, --model <model>", "\u6307\u5B9A\u6A21\u578B\u540D\u79F0").option("-k, --api-key <key>", "API \u5BC6\u94A5").option("-u, --base-url <url>", "API \u57FA\u7840 URL").option("--simple", "\u4F7F\u7528\u7B80\u5355 REPL \u6A21\u5F0F\uFF08\u975E\u5168\u5C4F TUI\uFF09").argument("[prompt...]", "\u76F4\u63A5\u6267\u884C\u7684\u63D0\u793A\u8BCD\uFF08\u975E\u4EA4\u4E92\u5F0F\uFF09").action(async (promptParts, opts) => {
|
|
4238
4364
|
const config = loadConfig(opts);
|
|
4239
4365
|
if (!config.api_key) {
|
|
4240
4366
|
printError("\u672A\u8BBE\u7F6E API \u5BC6\u94A5\u3002\u8BF7\u901A\u8FC7\u4EE5\u4E0B\u65B9\u5F0F\u4E4B\u4E00\u8BBE\u7F6E\uFF1A");
|
|
@@ -4250,6 +4376,7 @@ function createCli() {
|
|
|
4250
4376
|
await startRepl({ config });
|
|
4251
4377
|
} else {
|
|
4252
4378
|
const { startTui: startTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
|
|
4379
|
+
process.stdout.write("\x1Bc");
|
|
4253
4380
|
await startTui2({ config });
|
|
4254
4381
|
}
|
|
4255
4382
|
});
|