zencode-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4176 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/config/defaults.ts
13
+ var DEFAULT_CONFIG;
14
+ var init_defaults = __esm({
15
+ "src/config/defaults.ts"() {
16
+ "use strict";
17
+ DEFAULT_CONFIG = {
18
+ model: "deepseek-chat",
19
+ api_key: "",
20
+ base_url: "https://api.deepseek.com/v1",
21
+ temperature: 0.7,
22
+ max_tokens: 8192,
23
+ agent_mode: "dual",
24
+ collaboration: "delegated",
25
+ dual_agent: {},
26
+ features: {
27
+ git: "auto",
28
+ mcp: "off",
29
+ planning_layer: "on",
30
+ parallel_agents: "on",
31
+ todo: "on"
32
+ },
33
+ permissions: {
34
+ auto_approve: ["read-file", "glob", "grep", "spawn-agents", "todo", "memo"],
35
+ require_approval: ["write-file", "edit-file", "bash", "git"]
36
+ },
37
+ mcp_servers: [],
38
+ prompts: [],
39
+ max_tool_output: 3e4
40
+ };
41
+ }
42
+ });
43
+
44
+ // src/config/loader.ts
45
+ import * as fs from "fs";
46
+ import * as path from "path";
47
+ import * as os from "os";
48
+ import { parse as parseYaml } from "yaml";
49
+ function deepMerge(target, source) {
50
+ const result = { ...target };
51
+ for (const key of Object.keys(source)) {
52
+ const sourceVal = source[key];
53
+ const targetVal = target[key];
54
+ if (sourceVal !== void 0 && sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && typeof targetVal === "object" && !Array.isArray(targetVal) && targetVal !== null) {
55
+ result[key] = deepMerge(targetVal, sourceVal);
56
+ } else if (sourceVal !== void 0) {
57
+ result[key] = sourceVal;
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ function loadYamlFile(filePath) {
63
+ try {
64
+ const content = fs.readFileSync(filePath, "utf-8");
65
+ return parseYaml(content) || {};
66
+ } catch {
67
+ return {};
68
+ }
69
+ }
70
+ function loadEnvConfig() {
71
+ const config = {};
72
+ if (process.env["ZENCODE_API_KEY"]) {
73
+ config.api_key = process.env["ZENCODE_API_KEY"];
74
+ }
75
+ if (process.env["ZENCODE_MODEL"]) {
76
+ config.model = process.env["ZENCODE_MODEL"];
77
+ }
78
+ if (process.env["ZENCODE_BASE_URL"]) {
79
+ config.base_url = process.env["ZENCODE_BASE_URL"];
80
+ }
81
+ if (process.env["ZENCODE_MODE"]) {
82
+ const mode = process.env["ZENCODE_MODE"];
83
+ if (mode === "dual" || mode === "single") {
84
+ config.agent_mode = mode;
85
+ }
86
+ }
87
+ return config;
88
+ }
89
+ function loadCliConfig(opts) {
90
+ const config = {};
91
+ if (opts.model) config.model = opts.model;
92
+ if (opts.apiKey) config.api_key = opts.apiKey;
93
+ if (opts.baseUrl) config.base_url = opts.baseUrl;
94
+ if (opts.single) config.agent_mode = "single";
95
+ if (opts.dual) config.agent_mode = "dual";
96
+ if (opts.mode) {
97
+ const m = opts.mode;
98
+ if (m === "delegated" || m === "autonomous" || m === "controlled") {
99
+ config.collaboration = m;
100
+ }
101
+ }
102
+ return config;
103
+ }
104
+ function loadConfig(cliOpts = {}) {
105
+ const globalConfigPath = path.join(os.homedir(), ".zencode", "config.yaml");
106
+ const projectDirConfigPath = path.resolve(".zencode", "config.yaml");
107
+ const projectFileConfigPath = path.resolve(".zencode.yaml");
108
+ let config = { ...DEFAULT_CONFIG };
109
+ config = deepMerge(config, loadYamlFile(globalConfigPath));
110
+ config = deepMerge(config, loadYamlFile(projectDirConfigPath));
111
+ config = deepMerge(config, loadYamlFile(projectFileConfigPath));
112
+ config = deepMerge(config, loadEnvConfig());
113
+ config = deepMerge(config, loadCliConfig(cliOpts));
114
+ return config;
115
+ }
116
+ function resolveModelConfig(config, role) {
117
+ const roleConfig = config.dual_agent[role] || {};
118
+ return {
119
+ model: roleConfig.model || config.model,
120
+ api_key: roleConfig.api_key || config.api_key,
121
+ base_url: roleConfig.base_url || config.base_url,
122
+ temperature: roleConfig.temperature ?? config.temperature,
123
+ max_tokens: roleConfig.max_tokens ?? config.max_tokens
124
+ };
125
+ }
126
+ var init_loader = __esm({
127
+ "src/config/loader.ts"() {
128
+ "use strict";
129
+ init_defaults();
130
+ }
131
+ });
132
+
133
+ // src/llm/client.ts
134
+ import OpenAI from "openai";
135
+ function createLLMClient(options) {
136
+ return new LLMClient(options);
137
+ }
138
+ var LLMClient;
139
+ var init_client = __esm({
140
+ "src/llm/client.ts"() {
141
+ "use strict";
142
+ LLMClient = class {
143
+ client;
144
+ model;
145
+ temperature;
146
+ maxTokens;
147
+ constructor(options) {
148
+ this.client = new OpenAI({
149
+ apiKey: options.apiKey,
150
+ baseURL: options.baseURL
151
+ });
152
+ this.model = options.model;
153
+ this.temperature = options.temperature ?? 0.7;
154
+ this.maxTokens = options.maxTokens ?? 8192;
155
+ }
156
+ /**
157
+ * 流式调用 LLM,实时回调文本内容和工具调用
158
+ */
159
+ async chatStream(messages, tools, callbacks) {
160
+ const params = {
161
+ model: this.model,
162
+ messages,
163
+ temperature: this.temperature,
164
+ max_tokens: this.maxTokens,
165
+ stream: true
166
+ };
167
+ if (tools && tools.length > 0) {
168
+ params.tools = tools;
169
+ params.tool_choice = "auto";
170
+ }
171
+ try {
172
+ const stream = await this.client.chat.completions.create(params);
173
+ let contentParts = [];
174
+ const toolCallMap = /* @__PURE__ */ new Map();
175
+ for await (const chunk of stream) {
176
+ const choice = chunk.choices[0];
177
+ if (!choice) continue;
178
+ const delta = choice.delta;
179
+ if (delta.content) {
180
+ contentParts.push(delta.content);
181
+ callbacks.onContent?.(delta.content);
182
+ }
183
+ if (delta.tool_calls) {
184
+ for (const tc of delta.tool_calls) {
185
+ const existing = toolCallMap.get(tc.index);
186
+ if (existing) {
187
+ if (tc.function?.arguments) {
188
+ existing.args += tc.function.arguments;
189
+ }
190
+ } else {
191
+ toolCallMap.set(tc.index, {
192
+ id: tc.id || "",
193
+ name: tc.function?.name || "",
194
+ args: tc.function?.arguments || ""
195
+ });
196
+ }
197
+ const entry = toolCallMap.get(tc.index);
198
+ if (entry && callbacks.onToolCallStreaming) {
199
+ const n = entry.name;
200
+ if (n === "write-file" || n === "edit-file") {
201
+ callbacks.onToolCallStreaming(tc.index, n, entry.args);
202
+ }
203
+ }
204
+ }
205
+ }
206
+ }
207
+ const toolCalls = [];
208
+ for (const [, tc] of [...toolCallMap.entries()].sort(([a], [b]) => a - b)) {
209
+ const toolCall = {
210
+ id: tc.id,
211
+ type: "function",
212
+ function: {
213
+ name: tc.name,
214
+ arguments: tc.args
215
+ }
216
+ };
217
+ toolCalls.push(toolCall);
218
+ callbacks.onToolCall?.(toolCall);
219
+ }
220
+ const fullContent = contentParts.join("");
221
+ const assistantMessage = {
222
+ role: "assistant",
223
+ content: fullContent || null
224
+ };
225
+ if (toolCalls.length > 0) {
226
+ assistantMessage.tool_calls = toolCalls;
227
+ }
228
+ callbacks.onFinish?.(assistantMessage);
229
+ return assistantMessage;
230
+ } catch (error) {
231
+ const err = error instanceof Error ? error : new Error(String(error));
232
+ callbacks.onError?.(err);
233
+ throw err;
234
+ }
235
+ }
236
+ /**
237
+ * 非流式调用 LLM
238
+ */
239
+ async chat(messages, tools) {
240
+ const params = {
241
+ model: this.model,
242
+ messages,
243
+ temperature: this.temperature,
244
+ max_tokens: this.maxTokens,
245
+ stream: false
246
+ };
247
+ if (tools && tools.length > 0) {
248
+ params.tools = tools;
249
+ params.tool_choice = "auto";
250
+ }
251
+ const response = await this.client.chat.completions.create(params);
252
+ const choice = response.choices[0];
253
+ if (!choice) {
254
+ throw new Error("No response from LLM");
255
+ }
256
+ const msg = choice.message;
257
+ const result = {
258
+ role: "assistant",
259
+ content: msg.content
260
+ };
261
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
262
+ result.tool_calls = msg.tool_calls.map((tc) => ({
263
+ id: tc.id,
264
+ type: "function",
265
+ function: {
266
+ name: tc.function.name,
267
+ arguments: tc.function.arguments
268
+ }
269
+ }));
270
+ }
271
+ return result;
272
+ }
273
+ get modelName() {
274
+ return this.model;
275
+ }
276
+ };
277
+ }
278
+ });
279
+
280
+ // src/tools/registry.ts
281
+ var ToolRegistry;
282
+ var init_registry = __esm({
283
+ "src/tools/registry.ts"() {
284
+ "use strict";
285
+ ToolRegistry = class {
286
+ tools = /* @__PURE__ */ new Map();
287
+ permissionOverrides = null;
288
+ register(tool) {
289
+ this.tools.set(tool.name, tool);
290
+ }
291
+ get(name) {
292
+ return this.tools.get(name);
293
+ }
294
+ has(name) {
295
+ return this.tools.has(name);
296
+ }
297
+ unregister(name) {
298
+ return this.tools.delete(name);
299
+ }
300
+ /**
301
+ * 设置权限覆盖配置
302
+ */
303
+ setPermissions(permissions) {
304
+ this.permissionOverrides = permissions;
305
+ }
306
+ /**
307
+ * 运行时将某工具升级为自动批准(始终允许)
308
+ */
309
+ addAutoApprove(toolName) {
310
+ if (!this.permissionOverrides) {
311
+ this.permissionOverrides = { auto_approve: [], require_approval: [] };
312
+ }
313
+ if (!this.permissionOverrides.auto_approve.includes(toolName)) {
314
+ this.permissionOverrides.auto_approve.push(toolName);
315
+ }
316
+ this.permissionOverrides.require_approval = this.permissionOverrides.require_approval.filter((n) => n !== toolName);
317
+ }
318
+ /**
319
+ * 获取工具的有效权限级别
320
+ */
321
+ getPermissionLevel(toolName) {
322
+ if (this.permissionOverrides) {
323
+ if (this.permissionOverrides.auto_approve.includes(toolName)) return "auto";
324
+ if (this.permissionOverrides.require_approval.includes(toolName)) return "confirm";
325
+ }
326
+ const tool = this.tools.get(toolName);
327
+ return tool?.permissionLevel ?? "confirm";
328
+ }
329
+ /**
330
+ * 执行工具调用
331
+ */
332
+ async execute(name, params, maxOutput) {
333
+ const tool = this.tools.get(name);
334
+ if (!tool) {
335
+ return { content: `\u9519\u8BEF\uFF1A\u672A\u627E\u5230\u5DE5\u5177 "${name}"` };
336
+ }
337
+ try {
338
+ const result = await tool.execute(params);
339
+ if (result.content.length > maxOutput) {
340
+ return {
341
+ content: result.content.slice(0, maxOutput) + "\n\n[\u8F93\u51FA\u5DF2\u622A\u65AD]",
342
+ truncated: true
343
+ };
344
+ }
345
+ return result;
346
+ } catch (error) {
347
+ const msg = error instanceof Error ? error.message : String(error);
348
+ return { content: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF\uFF1A${msg}` };
349
+ }
350
+ }
351
+ /**
352
+ * 导出为 OpenAI function calling 格式
353
+ */
354
+ toToolDefinitions(filter) {
355
+ const tools = [];
356
+ for (const [name, tool] of this.tools) {
357
+ if (filter && !filter.includes(name)) continue;
358
+ tools.push({
359
+ type: "function",
360
+ function: {
361
+ name: tool.name,
362
+ description: tool.description,
363
+ parameters: tool.parameters
364
+ }
365
+ });
366
+ }
367
+ return tools;
368
+ }
369
+ /**
370
+ * 获取所有已注册的工具名
371
+ */
372
+ listTools() {
373
+ return [...this.tools.keys()];
374
+ }
375
+ };
376
+ }
377
+ });
378
+
379
+ // src/tools/read-file.ts
380
+ import * as fs2 from "fs/promises";
381
+ import * as path2 from "path";
382
+ var readFileTool;
383
+ var init_read_file = __esm({
384
+ "src/tools/read-file.ts"() {
385
+ "use strict";
386
+ readFileTool = {
387
+ name: "read-file",
388
+ description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002\u4FEE\u6539\u6587\u4EF6\u524D\u5FC5\u987B\u5148\u8BFB\u53D6\u3002\u652F\u6301 offset/limit \u8BFB\u53D6\u5927\u6587\u4EF6\u7684\u6307\u5B9A\u90E8\u5206\u3002\u8FD4\u56DE\u5E26\u884C\u53F7\u7684\u5185\u5BB9\u3002",
389
+ parameters: {
390
+ type: "object",
391
+ properties: {
392
+ path: {
393
+ type: "string",
394
+ description: "\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u5DE5\u4F5C\u76EE\u5F55\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09"
395
+ },
396
+ offset: {
397
+ type: "number",
398
+ description: "\u8D77\u59CB\u884C\u53F7\uFF08\u4ECE1\u5F00\u59CB\uFF09\uFF0C\u9ED8\u8BA4\u4ECE\u5934\u8BFB\u53D6"
399
+ },
400
+ limit: {
401
+ type: "number",
402
+ description: "\u8BFB\u53D6\u7684\u884C\u6570\uFF0C\u9ED8\u8BA4\u8BFB\u53D6\u5168\u90E8"
403
+ }
404
+ },
405
+ required: ["path"]
406
+ },
407
+ permissionLevel: "auto",
408
+ async execute(params) {
409
+ const filePath = path2.resolve(params["path"]);
410
+ const offset = params["offset"] || 1;
411
+ const limit = params["limit"];
412
+ try {
413
+ const content = await fs2.readFile(filePath, "utf-8");
414
+ const lines = content.split("\n");
415
+ const startIdx = Math.max(0, offset - 1);
416
+ const endIdx = limit ? startIdx + limit : lines.length;
417
+ const selectedLines = lines.slice(startIdx, endIdx);
418
+ const numbered = selectedLines.map((line, i) => `${String(startIdx + i + 1).padStart(5)} ${line}`).join("\n");
419
+ return { content: numbered || "\uFF08\u7A7A\u6587\u4EF6\uFF09" };
420
+ } catch (error) {
421
+ const msg = error instanceof Error ? error.message : String(error);
422
+ return { content: `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A${msg}` };
423
+ }
424
+ }
425
+ };
426
+ }
427
+ });
428
+
429
+ // src/tools/write-file.ts
430
+ import * as fs3 from "fs/promises";
431
+ import * as path3 from "path";
432
+ var writeFileTool;
433
+ var init_write_file = __esm({
434
+ "src/tools/write-file.ts"() {
435
+ "use strict";
436
+ writeFileTool = {
437
+ name: "write-file",
438
+ description: "\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u5B8C\u6574\u91CD\u5199\u6587\u4EF6\u3002\u5982\u679C\u662F\u4FEE\u6539\u5DF2\u6709\u6587\u4EF6\uFF0C\u4F18\u5148\u4F7F\u7528 edit-file\u3002\u4F1A\u81EA\u52A8\u521B\u5EFA\u7236\u76EE\u5F55\u3002",
439
+ parameters: {
440
+ type: "object",
441
+ properties: {
442
+ path: {
443
+ type: "string",
444
+ description: "\u6587\u4EF6\u8DEF\u5F84"
445
+ },
446
+ content: {
447
+ type: "string",
448
+ description: "\u8981\u5199\u5165\u7684\u6587\u4EF6\u5185\u5BB9"
449
+ }
450
+ },
451
+ required: ["path", "content"]
452
+ },
453
+ permissionLevel: "confirm",
454
+ async execute(params) {
455
+ const filePath = path3.resolve(params["path"]);
456
+ const content = params["content"];
457
+ try {
458
+ await fs3.mkdir(path3.dirname(filePath), { recursive: true });
459
+ await fs3.writeFile(filePath, content, "utf-8");
460
+ return { content: `\u6587\u4EF6\u5DF2\u5199\u5165\uFF1A${filePath}` };
461
+ } catch (error) {
462
+ const msg = error instanceof Error ? error.message : String(error);
463
+ return { content: `\u5199\u5165\u6587\u4EF6\u5931\u8D25\uFF1A${msg}` };
464
+ }
465
+ }
466
+ };
467
+ }
468
+ });
469
+
470
+ // src/tools/edit-file.ts
471
+ import * as fs4 from "fs/promises";
472
+ import * as path4 from "path";
473
+ var editFileTool;
474
+ var init_edit_file = __esm({
475
+ "src/tools/edit-file.ts"() {
476
+ "use strict";
477
+ editFileTool = {
478
+ name: "edit-file",
479
+ description: "\u901A\u8FC7\u5B57\u7B26\u4E32\u66FF\u6362\u7F16\u8F91\u6587\u4EF6\uFF08\u63A8\u8350\u7684\u6587\u4EF6\u4FEE\u6539\u65B9\u5F0F\uFF09\u3002old_string \u5FC5\u987B\u5728\u6587\u4EF6\u4E2D\u552F\u4E00\u5339\u914D\uFF0C\u5C06\u88AB\u66FF\u6362\u4E3A new_string\u3002\u5339\u914D\u5931\u8D25\u65F6\u8BF7\u63D0\u4F9B\u66F4\u591A\u4E0A\u4E0B\u6587\u4F7F\u5176\u552F\u4E00\uFF0C\u6216\u4F7F\u7528 replace_all\u3002",
480
+ parameters: {
481
+ type: "object",
482
+ properties: {
483
+ path: {
484
+ type: "string",
485
+ description: "\u6587\u4EF6\u8DEF\u5F84"
486
+ },
487
+ old_string: {
488
+ type: "string",
489
+ description: "\u8981\u88AB\u66FF\u6362\u7684\u539F\u59CB\u5B57\u7B26\u4E32\uFF08\u5FC5\u987B\u552F\u4E00\u5339\u914D\uFF09"
490
+ },
491
+ new_string: {
492
+ type: "string",
493
+ description: "\u66FF\u6362\u540E\u7684\u5B57\u7B26\u4E32"
494
+ },
495
+ replace_all: {
496
+ type: "boolean",
497
+ description: "\u662F\u5426\u66FF\u6362\u6240\u6709\u5339\u914D\u9879\uFF0C\u9ED8\u8BA4 false"
498
+ }
499
+ },
500
+ required: ["path", "old_string", "new_string"]
501
+ },
502
+ permissionLevel: "confirm",
503
+ async execute(params) {
504
+ const filePath = path4.resolve(params["path"]);
505
+ const oldString = params["old_string"];
506
+ const newString = params["new_string"];
507
+ const replaceAll = params["replace_all"] ?? false;
508
+ try {
509
+ const content = await fs4.readFile(filePath, "utf-8");
510
+ if (replaceAll) {
511
+ const newContent2 = content.split(oldString).join(newString);
512
+ if (newContent2 === content) {
513
+ return { content: `\u672A\u627E\u5230\u5339\u914D\u5185\u5BB9\uFF1A${oldString.slice(0, 50)}...` };
514
+ }
515
+ await fs4.writeFile(filePath, newContent2, "utf-8");
516
+ const count = content.split(oldString).length - 1;
517
+ return { content: `\u5DF2\u66FF\u6362 ${count} \u5904\u5339\u914D` };
518
+ }
519
+ const firstIdx = content.indexOf(oldString);
520
+ if (firstIdx === -1) {
521
+ return { content: `\u672A\u627E\u5230\u5339\u914D\u5185\u5BB9\uFF1A${oldString.slice(0, 100)}` };
522
+ }
523
+ const secondIdx = content.indexOf(oldString, firstIdx + 1);
524
+ if (secondIdx !== -1) {
525
+ return { content: `old_string \u4E0D\u552F\u4E00\uFF0C\u627E\u5230\u591A\u5904\u5339\u914D\u3002\u8BF7\u63D0\u4F9B\u66F4\u591A\u4E0A\u4E0B\u6587\u4F7F\u5176\u552F\u4E00\u3002` };
526
+ }
527
+ const newContent = content.slice(0, firstIdx) + newString + content.slice(firstIdx + oldString.length);
528
+ await fs4.writeFile(filePath, newContent, "utf-8");
529
+ return { content: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}` };
530
+ } catch (error) {
531
+ const msg = error instanceof Error ? error.message : String(error);
532
+ return { content: `\u7F16\u8F91\u6587\u4EF6\u5931\u8D25\uFF1A${msg}` };
533
+ }
534
+ }
535
+ };
536
+ }
537
+ });
538
+
539
+ // src/tools/bash.ts
540
+ import { exec } from "child_process";
541
+ var DEFAULT_TIMEOUT, bashTool;
542
+ var init_bash = __esm({
543
+ "src/tools/bash.ts"() {
544
+ "use strict";
545
+ DEFAULT_TIMEOUT = 12e4;
546
+ bashTool = {
547
+ name: "bash",
548
+ description: "\u6267\u884C shell \u547D\u4EE4\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u7CFB\u7EDF\u547D\u4EE4\u3002\u4E0D\u8981\u7528 bash \u505A\u6587\u4EF6\u8BFB\u5199\uFF08\u7528 read-file/edit-file/write-file\uFF09\u6216\u641C\u7D22\uFF08\u7528 glob/grep\uFF09\u3002",
549
+ parameters: {
550
+ type: "object",
551
+ properties: {
552
+ command: {
553
+ type: "string",
554
+ description: "\u8981\u6267\u884C\u7684 shell \u547D\u4EE4"
555
+ },
556
+ timeout: {
557
+ type: "number",
558
+ description: "\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 120000"
559
+ }
560
+ },
561
+ required: ["command"]
562
+ },
563
+ permissionLevel: "confirm",
564
+ async execute(params) {
565
+ const command = params["command"];
566
+ const timeout = params["timeout"] || DEFAULT_TIMEOUT;
567
+ return new Promise((resolve7) => {
568
+ exec(
569
+ command,
570
+ {
571
+ cwd: process.cwd(),
572
+ timeout,
573
+ maxBuffer: 1024 * 1024 * 10,
574
+ // 10MB
575
+ shell: process.platform === "win32" ? "cmd.exe" : "/bin/bash"
576
+ },
577
+ (error, stdout, stderr) => {
578
+ let output = "";
579
+ if (stdout) output += stdout;
580
+ if (stderr) output += (output ? "\n" : "") + `[stderr]
581
+ ${stderr}`;
582
+ if (error && error.killed) {
583
+ output += `
584
+ [\u547D\u4EE4\u8D85\u65F6\uFF0C\u5DF2\u7EC8\u6B62]`;
585
+ } else if (error && !stdout && !stderr) {
586
+ output = `\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${error.message}`;
587
+ }
588
+ resolve7({ content: output || "\uFF08\u65E0\u8F93\u51FA\uFF09" });
589
+ }
590
+ );
591
+ });
592
+ }
593
+ };
594
+ }
595
+ });
596
+
597
+ // src/tools/glob.ts
598
+ import { glob as globFn } from "glob";
599
+ var globTool;
600
+ var init_glob = __esm({
601
+ "src/tools/glob.ts"() {
602
+ "use strict";
603
+ globTool = {
604
+ name: "glob",
605
+ description: '\u6309 glob \u6A21\u5F0F\u641C\u7D22\u6587\u4EF6\u8DEF\u5F84\u3002\u7528\u4E8E\u67E5\u627E\u6587\u4EF6\u4F4D\u7F6E\uFF0C\u5982 "**/*.ts"\u3001"src/**/config.*"\u3002\u81EA\u52A8\u5FFD\u7565 node_modules \u548C .git\u3002',
606
+ parameters: {
607
+ type: "object",
608
+ properties: {
609
+ pattern: {
610
+ type: "string",
611
+ description: 'Glob \u6A21\u5F0F\uFF0C\u5982 "**/*.ts"\u3001"src/**/*.js"'
612
+ },
613
+ cwd: {
614
+ type: "string",
615
+ description: "\u641C\u7D22\u7684\u6839\u76EE\u5F55\uFF0C\u9ED8\u8BA4\u4E3A\u5DE5\u4F5C\u76EE\u5F55"
616
+ }
617
+ },
618
+ required: ["pattern"]
619
+ },
620
+ permissionLevel: "auto",
621
+ async execute(params) {
622
+ const pattern = params["pattern"];
623
+ const cwd = params["cwd"] || process.cwd();
624
+ try {
625
+ const files = await globFn(pattern, {
626
+ cwd,
627
+ nodir: true,
628
+ dot: false,
629
+ ignore: ["**/node_modules/**", "**/.git/**"]
630
+ });
631
+ if (files.length === 0) {
632
+ return { content: "\u672A\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6" };
633
+ }
634
+ return { content: files.join("\n") };
635
+ } catch (error) {
636
+ const msg = error instanceof Error ? error.message : String(error);
637
+ return { content: `\u641C\u7D22\u5931\u8D25\uFF1A${msg}` };
638
+ }
639
+ }
640
+ };
641
+ }
642
+ });
643
+
644
+ // src/tools/grep.ts
645
+ import * as fs5 from "fs/promises";
646
+ import * as path5 from "path";
647
+ async function jsGrep(pattern, searchPath, options) {
648
+ const regex = new RegExp(pattern, options.ignoreCase ? "i" : "");
649
+ const results = [];
650
+ const maxResults = options.maxResults ?? 200;
651
+ async function searchFile(filePath) {
652
+ if (results.length >= maxResults) return;
653
+ try {
654
+ const content = await fs5.readFile(filePath, "utf-8");
655
+ const lines = content.split("\n");
656
+ for (let i = 0; i < lines.length; i++) {
657
+ if (results.length >= maxResults) break;
658
+ if (regex.test(lines[i])) {
659
+ results.push(`${filePath}:${i + 1}: ${lines[i]}`);
660
+ }
661
+ }
662
+ } catch {
663
+ }
664
+ }
665
+ async function searchDir(dirPath) {
666
+ if (results.length >= maxResults) return;
667
+ try {
668
+ const entries = await fs5.readdir(dirPath, { withFileTypes: true });
669
+ for (const entry of entries) {
670
+ if (results.length >= maxResults) break;
671
+ const fullPath = path5.join(dirPath, entry.name);
672
+ if (entry.isDirectory()) {
673
+ if (["node_modules", ".git", "dist", ".next", "__pycache__"].includes(entry.name)) {
674
+ continue;
675
+ }
676
+ await searchDir(fullPath);
677
+ } else if (entry.isFile()) {
678
+ const ext = path5.extname(entry.name).toLowerCase();
679
+ const textExts = [
680
+ ".ts",
681
+ ".tsx",
682
+ ".js",
683
+ ".jsx",
684
+ ".json",
685
+ ".md",
686
+ ".txt",
687
+ ".yaml",
688
+ ".yml",
689
+ ".toml",
690
+ ".css",
691
+ ".scss",
692
+ ".html",
693
+ ".vue",
694
+ ".svelte",
695
+ ".py",
696
+ ".go",
697
+ ".rs",
698
+ ".java",
699
+ ".c",
700
+ ".cpp",
701
+ ".h",
702
+ ".hpp",
703
+ ".rb",
704
+ ".php",
705
+ ".sh",
706
+ ".bash",
707
+ ".zsh",
708
+ ".fish",
709
+ ".sql",
710
+ ".xml",
711
+ ".svg",
712
+ ".env",
713
+ ".gitignore",
714
+ ".editorconfig",
715
+ ".prettierrc",
716
+ ".eslintrc"
717
+ ];
718
+ if (ext && !textExts.includes(ext)) continue;
719
+ await searchFile(fullPath);
720
+ }
721
+ }
722
+ } catch {
723
+ }
724
+ }
725
+ const stat2 = await fs5.stat(searchPath);
726
+ if (stat2.isFile()) {
727
+ await searchFile(searchPath);
728
+ } else {
729
+ await searchDir(searchPath);
730
+ }
731
+ return results;
732
+ }
733
+ var grepTool;
734
+ var init_grep = __esm({
735
+ "src/tools/grep.ts"() {
736
+ "use strict";
737
+ grepTool = {
738
+ name: "grep",
739
+ description: "\u5728\u6587\u4EF6\u5185\u5BB9\u4E2D\u641C\u7D22\u6B63\u5219\u8868\u8FBE\u5F0F\u3002\u7528\u4E8E\u67E5\u627E\u51FD\u6570\u5B9A\u4E49\u3001\u7C7B\u5F15\u7528\u3001\u7279\u5B9A\u4EE3\u7801\u6A21\u5F0F\u3002\u8FD4\u56DE\u5339\u914D\u7684\u6587\u4EF6\u8DEF\u5F84\u3001\u884C\u53F7\u548C\u5185\u5BB9\u3002",
740
+ parameters: {
741
+ type: "object",
742
+ properties: {
743
+ pattern: {
744
+ type: "string",
745
+ description: "\u6B63\u5219\u8868\u8FBE\u5F0F\u641C\u7D22\u6A21\u5F0F"
746
+ },
747
+ path: {
748
+ type: "string",
749
+ description: "\u641C\u7D22\u7684\u6587\u4EF6\u6216\u76EE\u5F55\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5DE5\u4F5C\u76EE\u5F55"
750
+ },
751
+ ignore_case: {
752
+ type: "boolean",
753
+ description: "\u662F\u5426\u5FFD\u7565\u5927\u5C0F\u5199"
754
+ }
755
+ },
756
+ required: ["pattern"]
757
+ },
758
+ permissionLevel: "auto",
759
+ async execute(params) {
760
+ const pattern = params["pattern"];
761
+ const searchPath = params["path"] || process.cwd();
762
+ const ignoreCase = params["ignore_case"] ?? false;
763
+ try {
764
+ const resolvedPath = path5.resolve(searchPath);
765
+ const results = await jsGrep(pattern, resolvedPath, {
766
+ ignoreCase,
767
+ maxResults: 200
768
+ });
769
+ if (results.length === 0) {
770
+ return { content: "\u672A\u627E\u5230\u5339\u914D\u5185\u5BB9" };
771
+ }
772
+ return { content: results.join("\n") };
773
+ } catch (error) {
774
+ const msg = error instanceof Error ? error.message : String(error);
775
+ return { content: `\u641C\u7D22\u5931\u8D25\uFF1A${msg}` };
776
+ }
777
+ }
778
+ };
779
+ }
780
+ });
781
+
782
+ // src/tools/register.ts
783
+ function registerCoreTools(registry) {
784
+ registry.register(readFileTool);
785
+ registry.register(writeFileTool);
786
+ registry.register(editFileTool);
787
+ registry.register(bashTool);
788
+ registry.register(globTool);
789
+ registry.register(grepTool);
790
+ }
791
+ var init_register = __esm({
792
+ "src/tools/register.ts"() {
793
+ "use strict";
794
+ init_read_file();
795
+ init_write_file();
796
+ init_edit_file();
797
+ init_bash();
798
+ init_glob();
799
+ init_grep();
800
+ }
801
+ });
802
+
803
+ // src/tools/permission.ts
804
+ import chalk from "chalk";
805
+ function setConfirmHandler(handler) {
806
+ globalConfirmHandler = handler;
807
+ }
808
+ function setStructuredConfirmHandler(handler) {
809
+ structuredConfirmHandler = handler;
810
+ }
811
+ function formatToolDetail(toolName, params) {
812
+ const lines = [];
813
+ switch (toolName) {
814
+ case "bash":
815
+ lines.push(` ${chalk.dim("\u547D\u4EE4:")} ${chalk.white(String(params["command"] || ""))}`);
816
+ break;
817
+ case "write-file":
818
+ lines.push(` ${chalk.dim("\u6587\u4EF6:")} ${chalk.white(String(params["path"] || ""))}`);
819
+ if (params["content"]) {
820
+ const content = String(params["content"]);
821
+ const preview = content.length > 200 ? content.slice(0, 200) + "..." : content;
822
+ lines.push(` ${chalk.dim("\u5185\u5BB9:")} ${chalk.gray(preview.split("\n").join("\n "))}`);
823
+ }
824
+ break;
825
+ case "edit-file":
826
+ lines.push(` ${chalk.dim("\u6587\u4EF6:")} ${chalk.white(String(params["path"] || ""))}`);
827
+ if (params["old_string"]) {
828
+ const old = String(params["old_string"]);
829
+ const preview = old.length > 100 ? old.slice(0, 100) + "..." : old;
830
+ lines.push(` ${chalk.dim("\u66FF\u6362:")} ${chalk.red(preview)}`);
831
+ }
832
+ if (params["new_string"]) {
833
+ const neu = String(params["new_string"]);
834
+ const preview = neu.length > 100 ? neu.slice(0, 100) + "..." : neu;
835
+ lines.push(` ${chalk.dim("\u4E3A :")} ${chalk.green(preview)}`);
836
+ }
837
+ break;
838
+ case "git":
839
+ lines.push(` ${chalk.dim("\u547D\u4EE4:")} git ${chalk.white(String(params["command"] || ""))}`);
840
+ break;
841
+ default:
842
+ for (const [key, value] of Object.entries(params)) {
843
+ const str = typeof value === "string" ? value : JSON.stringify(value);
844
+ lines.push(` ${chalk.dim(key + ":")} ${str.slice(0, 120)}`);
845
+ }
846
+ break;
847
+ }
848
+ return lines.join("\n");
849
+ }
850
+ async function confirmExecution(toolName, params) {
851
+ if (structuredConfirmHandler) {
852
+ return structuredConfirmHandler(toolName, params);
853
+ }
854
+ const detail = formatToolDetail(toolName, params);
855
+ const prompt = `
856
+ ${chalk.yellow("\u26A0")} ${chalk.bold("\u9700\u8981\u786E\u8BA4")} ${chalk.cyan(`[${toolName}]`)}
857
+ ${detail}
858
+ `;
859
+ process.stderr.write(prompt);
860
+ const approved = await globalConfirmHandler(`${chalk.yellow("?")} \u662F\u5426\u6267\u884C\uFF1F(${chalk.green("y")}/${chalk.red("N")}) `);
861
+ return { approved };
862
+ }
863
+ var globalConfirmHandler, structuredConfirmHandler;
864
+ var init_permission = __esm({
865
+ "src/tools/permission.ts"() {
866
+ "use strict";
867
+ globalConfirmHandler = async () => false;
868
+ structuredConfirmHandler = null;
869
+ }
870
+ });
871
+
872
+ // src/core/conversation.ts
873
+ var Conversation;
874
+ var init_conversation = __esm({
875
+ "src/core/conversation.ts"() {
876
+ "use strict";
877
+ Conversation = class {
878
+ messages = [];
879
+ systemPrompt = "";
880
+ setSystemPrompt(prompt) {
881
+ this.systemPrompt = prompt;
882
+ }
883
+ addMessage(message) {
884
+ this.messages.push(message);
885
+ }
886
+ addUserMessage(content) {
887
+ this.messages.push({ role: "user", content });
888
+ }
889
+ addAssistantMessage(message) {
890
+ this.messages.push(message);
891
+ }
892
+ addToolResult(toolCallId, content) {
893
+ this.messages.push({
894
+ role: "tool",
895
+ tool_call_id: toolCallId,
896
+ content
897
+ });
898
+ }
899
+ /**
900
+ * 获取完整的消息列表(包含系统提示词)
901
+ */
902
+ getMessages() {
903
+ const result = [];
904
+ if (this.systemPrompt) {
905
+ result.push({ role: "system", content: this.systemPrompt });
906
+ }
907
+ result.push(...this.messages);
908
+ return result;
909
+ }
910
+ /**
911
+ * 获取不含系统提示词的历史消息
912
+ */
913
+ getHistory() {
914
+ return [...this.messages];
915
+ }
916
+ /**
917
+ * 清空对话历史(保留系统提示词)
918
+ */
919
+ clear() {
920
+ this.messages = [];
921
+ }
922
+ /**
923
+ * 获取消息数量
924
+ */
925
+ get length() {
926
+ return this.messages.length;
927
+ }
928
+ };
929
+ }
930
+ });
931
+
932
+ // src/core/agent.ts
933
+ var Agent;
934
+ var init_agent = __esm({
935
+ "src/core/agent.ts"() {
936
+ "use strict";
937
+ init_permission();
938
+ init_conversation();
939
+ Agent = class {
940
+ conversation;
941
+ client;
942
+ registry;
943
+ config;
944
+ fixedTools;
945
+ constructor(client, registry, config, systemPrompt, tools) {
946
+ this.client = client;
947
+ this.registry = registry;
948
+ this.config = config;
949
+ this.conversation = new Conversation();
950
+ this.conversation.setSystemPrompt(systemPrompt);
951
+ this.fixedTools = tools;
952
+ }
953
+ /**
954
+ * 执行一轮完整的 agent 循环
955
+ */
956
+ async run(userMessage, callbacks = {}) {
957
+ this.conversation.addUserMessage(userMessage);
958
+ let lastContent = "";
959
+ while (true) {
960
+ const tools = this.fixedTools || this.registry.toToolDefinitions();
961
+ const assistantMsg = await this.client.chatStream(
962
+ this.conversation.getMessages(),
963
+ tools.length > 0 ? tools : void 0,
964
+ callbacks
965
+ );
966
+ this.conversation.addAssistantMessage(assistantMsg);
967
+ if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
968
+ lastContent = assistantMsg.content || "";
969
+ break;
970
+ }
971
+ for (const toolCall of assistantMsg.tool_calls) {
972
+ const toolName = toolCall.function.name;
973
+ let params;
974
+ try {
975
+ params = JSON.parse(toolCall.function.arguments);
976
+ } catch {
977
+ this.conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
978
+ continue;
979
+ }
980
+ try {
981
+ const permLevel = this.registry.getPermissionLevel(toolName);
982
+ if (permLevel === "deny") {
983
+ callbacks.onDenied?.(toolName);
984
+ this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
985
+ continue;
986
+ }
987
+ if (permLevel === "auto") {
988
+ callbacks.onToolExecuting?.(toolName, params);
989
+ }
990
+ if (permLevel === "confirm") {
991
+ const confirmResult = await confirmExecution(toolName, params);
992
+ if (!confirmResult.approved) {
993
+ callbacks.onDenied?.(toolName, confirmResult.feedback);
994
+ const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
995
+ this.conversation.addToolResult(toolCall.id, denyMsg);
996
+ continue;
997
+ }
998
+ }
999
+ const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
1000
+ callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
1001
+ this.conversation.addToolResult(toolCall.id, result.content);
1002
+ } catch (err) {
1003
+ const msg = err instanceof Error ? err.message : String(err);
1004
+ this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
1005
+ }
1006
+ }
1007
+ if (assistantMsg.content) {
1008
+ lastContent = assistantMsg.content;
1009
+ }
1010
+ }
1011
+ return lastContent;
1012
+ }
1013
+ /**
1014
+ * 获取对话历史
1015
+ */
1016
+ getConversation() {
1017
+ return this.conversation;
1018
+ }
1019
+ };
1020
+ }
1021
+ });
1022
+
1023
+ // src/core/dual-agent/coder.ts
1024
+ var Coder;
1025
+ var init_coder = __esm({
1026
+ "src/core/dual-agent/coder.ts"() {
1027
+ "use strict";
1028
+ init_conversation();
1029
+ init_permission();
1030
+ Coder = class {
1031
+ client;
1032
+ registry;
1033
+ config;
1034
+ systemPrompt;
1035
+ tools;
1036
+ constructor(client, registry, config, systemPrompt, tools) {
1037
+ this.client = client;
1038
+ this.registry = registry;
1039
+ this.config = config;
1040
+ this.systemPrompt = systemPrompt;
1041
+ this.tools = tools;
1042
+ }
1043
+ /**
1044
+ * 执行编码任务(短生命周期,每次新建会话)
1045
+ *
1046
+ * @param taskMessage 调度者发来的任务描述(作为 user message)
1047
+ * @param callbacks 回调
1048
+ * @returns 编码者的最终响应
1049
+ */
1050
+ async execute(taskMessage, callbacks = {}) {
1051
+ const conversation = new Conversation();
1052
+ conversation.setSystemPrompt(this.systemPrompt);
1053
+ conversation.addUserMessage(taskMessage);
1054
+ let lastContent = "";
1055
+ while (true) {
1056
+ const assistantMsg = await this.client.chatStream(
1057
+ conversation.getMessages(),
1058
+ this.tools.length > 0 ? this.tools : void 0,
1059
+ callbacks
1060
+ );
1061
+ conversation.addAssistantMessage(assistantMsg);
1062
+ if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
1063
+ lastContent = assistantMsg.content || "";
1064
+ break;
1065
+ }
1066
+ for (const toolCall of assistantMsg.tool_calls) {
1067
+ const toolName = toolCall.function.name;
1068
+ let params;
1069
+ try {
1070
+ params = JSON.parse(toolCall.function.arguments);
1071
+ } catch {
1072
+ conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
1073
+ continue;
1074
+ }
1075
+ try {
1076
+ const permLevel = this.registry.getPermissionLevel(toolName);
1077
+ if (permLevel === "deny") {
1078
+ callbacks.onDenied?.(toolName);
1079
+ conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
1080
+ continue;
1081
+ }
1082
+ if (permLevel === "auto") {
1083
+ callbacks.onToolExecuting?.(toolName, params);
1084
+ }
1085
+ if (permLevel === "confirm") {
1086
+ const confirmResult = await confirmExecution(toolName, params);
1087
+ if (!confirmResult.approved) {
1088
+ callbacks.onDenied?.(toolName, confirmResult.feedback);
1089
+ const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
1090
+ conversation.addToolResult(toolCall.id, denyMsg);
1091
+ continue;
1092
+ }
1093
+ }
1094
+ const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
1095
+ callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
1096
+ conversation.addToolResult(toolCall.id, result.content);
1097
+ } catch (err) {
1098
+ const msg = err instanceof Error ? err.message : String(err);
1099
+ conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
1100
+ }
1101
+ }
1102
+ if (assistantMsg.content) {
1103
+ lastContent = assistantMsg.content;
1104
+ }
1105
+ }
1106
+ return lastContent;
1107
+ }
1108
+ };
1109
+ }
1110
+ });
1111
+
1112
+ // src/core/dual-agent/modes.ts
1113
+ function getMode(name) {
1114
+ switch (name) {
1115
+ case "delegated":
1116
+ return DELEGATED_MODE;
1117
+ case "autonomous":
1118
+ return AUTONOMOUS_MODE;
1119
+ case "controlled":
1120
+ return CONTROLLED_MODE;
1121
+ default:
1122
+ return DELEGATED_MODE;
1123
+ }
1124
+ }
1125
+ var CODER_IDENTITY, DELEGATED_MODE, AUTONOMOUS_MODE, CONTROLLED_MODE;
1126
+ var init_modes = __esm({
1127
+ "src/core/dual-agent/modes.ts"() {
1128
+ "use strict";
1129
+ CODER_IDENTITY = `\u4F60\u662F\u7F16\u7801\u5B50 Agent\u3002\u6309\u4EE5\u4E0B\u987A\u5E8F\u5DE5\u4F5C\uFF1A
1130
+
1131
+ 1. **\u8BFB memo**\uFF1A\u5982\u679C\u4EFB\u52A1\u63D0\u5230\u4E86 memo key\uFF0C\u5148 memo read \u83B7\u53D6\u8C03\u5EA6\u8005\u8BB0\u5F55\u7684\u4E0A\u4E0B\u6587
1132
+ 2. **\u7F16\u7801**\uFF1A
1133
+ - \u65B0\u5EFA\u6587\u4EF6 \u2192 \u76F4\u63A5 write-file
1134
+ - \u4FEE\u6539\u6587\u4EF6 \u2192 read-file \u9605\u8BFB\u8981\u6539\u7684\u6587\u4EF6 \u2192 edit-file \u4FEE\u6539
1135
+ 3. **\u5199 memo**\uFF1A\u5B8C\u6210\u540E memo write \u8BB0\u5F55\u6539\u52A8\u6458\u8981\uFF08key \u5982 "done-xxx"\uFF09\uFF0C\u4FBF\u4E8E\u8C03\u5EA6\u8005\u4E86\u89E3\u8FDB\u5C55
1136
+ 4. **\u56DE\u590D**\uFF1A\u4E00\u53E5\u8BDD\u8BF4\u660E\u7ED3\u679C
1137
+
1138
+ ## \u7981\u6B62
1139
+ - \u274C \u7F16\u7801\u524D\u505A\u63A2\u7D22\uFF08bash ls/dir/pwd\u3001glob\u3001grep\uFF09\u2014 \u8C03\u5EA6\u8005\u5DF2\u7ECF\u505A\u8FC7\u4E86\uFF0C\u7ED3\u679C\u5728 memo \u4E2D
1140
+ - \u274C \u8F93\u51FA\u8BA1\u5212\u3001\u5206\u6790\u3001\u601D\u8DEF\u8BF4\u660E \u2014 \u76F4\u63A5\u52A8\u624B
1141
+ - \u274C \u505A\u4EFB\u52A1\u8303\u56F4\u5916\u7684\u6539\u52A8
1142
+ - \u274C \u6DFB\u52A0\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u9519\u8BEF\u5904\u7406
1143
+ - \u274C \u8FC7\u5EA6\u5DE5\u7A0B\u5316`;
1144
+ DELEGATED_MODE = {
1145
+ name: "delegated",
1146
+ description: "A\u6536\u96C6\u4E0A\u4E0B\u6587\u5E76\u59D4\u6D3E\uFF0CB\u62E5\u6709\u5B8C\u6574\u5DE5\u5177\u72EC\u7ACB\u6267\u884C",
1147
+ coderHasTools: true,
1148
+ coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
1149
+ coderSystemPrompt: `${CODER_IDENTITY}`
1150
+ };
1151
+ AUTONOMOUS_MODE = {
1152
+ name: "autonomous",
1153
+ description: "A\u89C4\u5212\uFF0CB\u81EA\u4E3B\u6267\u884C\uFF08\u9002\u5408\u80FD\u529B\u5F3A\u7684\u6A21\u578B\uFF09",
1154
+ coderHasTools: true,
1155
+ coderToolNames: ["read-file", "write-file", "edit-file", "bash", "glob", "grep", "memo"],
1156
+ coderSystemPrompt: `${CODER_IDENTITY}`
1157
+ };
1158
+ CONTROLLED_MODE = {
1159
+ name: "controlled",
1160
+ description: "A\u5168\u6743\u7BA1\u7406\uFF0CB\u53EA\u8FD4\u56DE\u4EE3\u7801",
1161
+ coderHasTools: false,
1162
+ coderSystemPrompt: "\u4F60\u662F\u88AB\u8C03\u5EA6\u8005\u6D3E\u51FA\u7684\u5B50 Agent\u3002\u6839\u636E\u63D0\u4F9B\u7684\u4EE3\u7801\u548C\u9700\u6C42\uFF0C\u53EA\u8FD4\u56DE\u4FEE\u6539\u540E\u7684\u4EE3\u7801\u3002\u4E0D\u8981\u81EA\u884C\u64CD\u4F5C\u6587\u4EF6\uFF0C\u4E0D\u8981\u505A\u989D\u5916\u6539\u52A8\u3002"
1163
+ };
1164
+ }
1165
+ });
1166
+
1167
+ // src/core/dual-agent/orchestrator.ts
1168
+ var SEND_TO_CODER_TOOL, Orchestrator;
1169
+ var init_orchestrator = __esm({
1170
+ "src/core/dual-agent/orchestrator.ts"() {
1171
+ "use strict";
1172
+ init_client();
1173
+ init_loader();
1174
+ init_conversation();
1175
+ init_permission();
1176
+ init_coder();
1177
+ init_modes();
1178
+ SEND_TO_CODER_TOOL = {
1179
+ type: "function",
1180
+ function: {
1181
+ name: "send-to-coder",
1182
+ description: "\u5C06\u7F16\u7801\u4EFB\u52A1\u53D1\u9001\u7ED9\u7F16\u7801 Agent\u3002\u4F20\u5165\u4EFB\u52A1\u63CF\u8FF0\u548C\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587\uFF0C\u7F16\u7801 Agent \u5C06\u8FD4\u56DE\u4EE3\u7801\u6216\u89E3\u51B3\u65B9\u6848\u3002",
1183
+ parameters: {
1184
+ type: "object",
1185
+ properties: {
1186
+ task: {
1187
+ type: "string",
1188
+ description: "\u8981\u53D1\u9001\u7ED9\u7F16\u7801 Agent \u7684\u4EFB\u52A1\u63CF\u8FF0\uFF0C\u5305\u542B\u76F8\u5173\u4EE3\u7801\u4E0A\u4E0B\u6587"
1189
+ }
1190
+ },
1191
+ required: ["task"]
1192
+ }
1193
+ }
1194
+ };
1195
+ Orchestrator = class {
1196
+ conversation;
1197
+ orchestratorClient;
1198
+ coderClient;
1199
+ registry;
1200
+ config;
1201
+ mode;
1202
+ baseSystemPrompt;
1203
+ memoStore;
1204
+ constructor(registry, config, systemPrompt, memoStore) {
1205
+ this.registry = registry;
1206
+ this.config = config;
1207
+ this.mode = config.collaboration;
1208
+ this.baseSystemPrompt = systemPrompt;
1209
+ this.memoStore = memoStore;
1210
+ const orchConfig = resolveModelConfig(config, "orchestrator");
1211
+ this.orchestratorClient = createLLMClient({
1212
+ apiKey: orchConfig.api_key,
1213
+ baseURL: orchConfig.base_url,
1214
+ model: orchConfig.model,
1215
+ temperature: orchConfig.temperature,
1216
+ maxTokens: orchConfig.max_tokens
1217
+ });
1218
+ const coderConfig = resolveModelConfig(config, "coder");
1219
+ this.coderClient = createLLMClient({
1220
+ apiKey: coderConfig.api_key,
1221
+ baseURL: coderConfig.base_url,
1222
+ model: coderConfig.model,
1223
+ temperature: coderConfig.temperature,
1224
+ maxTokens: coderConfig.max_tokens
1225
+ });
1226
+ this.conversation = new Conversation();
1227
+ this.conversation.setSystemPrompt(this.buildSystemPrompt());
1228
+ }
1229
+ /**
1230
+ * 动态获取调度者的工具列表:注册表工具 + send-to-coder
1231
+ */
1232
+ getTools() {
1233
+ return [
1234
+ ...this.registry.toToolDefinitions(),
1235
+ SEND_TO_CODER_TOOL
1236
+ ];
1237
+ }
1238
+ /**
1239
+ * 构建调度者的系统提示词(包含当前协作模式)
1240
+ */
1241
+ buildSystemPrompt() {
1242
+ const modeInfo = getMode(this.mode);
1243
+ return `${this.baseSystemPrompt}
1244
+
1245
+ # \u4F60\u662F\u8C03\u5EA6\u8005 Agent
1246
+
1247
+ \u534F\u4F5C\u6A21\u5F0F\uFF1A${modeInfo.name} - ${modeInfo.description}
1248
+
1249
+ \u4F60\u662F\u4FA6\u5BDF\u5175 + \u6307\u6325\u5B98\u3002\u4F60\u81EA\u5DF1\u4E0D\u5199\u4EE3\u7801\uFF0C\u4F60\u7684\u804C\u8D23\u662F\uFF1A\u6536\u96C6\u60C5\u62A5 \u2192 \u8BB0\u5165 memo \u2192 \u59D4\u6D3E\u7F16\u7801 Agent\u3002
1250
+
1251
+ ## \u6838\u5FC3\u6D41\u7A0B
1252
+
1253
+ 1. **\u8BC4\u4F30**\uFF1A\u4EFB\u52A1\u662F\u5426\u9700\u8981\u4E86\u89E3\u73B0\u6709\u4EE3\u7801\uFF1F
1254
+ - \u9700\u8981\u4FEE\u6539\u73B0\u6709\u6587\u4EF6\u3001\u9700\u8981\u4E0E\u73B0\u6709\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u3001\u9700\u8981\u7406\u89E3\u4F9D\u8D56\u5173\u7CFB \u2192 \u5148\u6536\u96C6
1255
+ - \u9700\u6C42\u5B8C\u5168\u81EA\u5305\u542B\u3001\u76EE\u6807\u8DEF\u5F84\u5DF2\u660E\u786E \u2192 \u8DF3\u5230\u7B2C 3 \u6B65
1256
+
1257
+ 2. **\u6536\u96C6\u4E0A\u4E0B\u6587**\uFF08\u9AD8\u6548\uFF0C\u4E0D\u91CD\u590D\uFF09
1258
+ - \u7528 glob/grep \u5B9A\u4F4D\uFF08\u4E00\u6B21\u5C31\u591F\uFF0C\u4E0D\u8981\u540C\u4E00\u76EE\u6807\u67E5\u591A\u6B21\uFF09
1259
+ - \u7528 read-file \u6216 spawn-agents \u5E76\u884C\u8BFB\u53D6\u5173\u952E\u6587\u4EF6
1260
+ - **\u5FC5\u987B memo write \u8BB0\u5F55\u53D1\u73B0**\uFF0Ckey \u7528\u6709\u610F\u4E49\u7684\u540D\u79F0
1261
+ \u4F8B\uFF1Amemo write key="demo-structure" value="demo/ \u4E0B\u6709 weather.html\uFF08\u5929\u6C14\u7EC4\u4EF6\u793A\u4F8B\uFF09\uFF0C\u4F7F\u7528\u7EAF HTML+CSS+JS \u5355\u6587\u4EF6\u7ED3\u6784"
1262
+ - \u7CBE\u70BC\u6458\u8981\uFF0C\u4E0D\u8981\u590D\u5236\u6574\u4E2A\u6587\u4EF6\u5185\u5BB9
1263
+
1264
+ 3. **\u59D4\u6D3E\u7F16\u7801**\uFF1Asend-to-coder
1265
+ - task \u4E2D\u5199\u6E05\uFF1A\u505A\u4EC0\u4E48 + \u76EE\u6807\u8DEF\u5F84 + \u5F15\u7528 memo key
1266
+ \u4F8B\uFF1Atask="\u5728 demo/newyear.html \u521B\u5EFA\u9A6C\u5E74\u6625\u8282\u795D\u798F\u7F51\u9875\u3002\u9879\u76EE\u73B0\u6709\u7ED3\u6784\u89C1 memo key 'demo-structure'\u3002\u8981\u6C42\uFF1A\u5355\u6587\u4EF6 HTML\uFF0C\u5305\u542B\u5185\u8054 CSS \u548C JS\uFF0C\u4E2D\u6587\u5185\u5BB9\u3002"
1267
+ - \u6BCF\u6B21\u53EA\u53D1\u4E00\u4E2A\u5177\u4F53\u6B65\u9AA4
1268
+
1269
+ 4. **\u8FED\u4EE3/\u9A8C\u8BC1**\uFF1A\u9700\u8981\u65F6\u7EE7\u7EED\u59D4\u6D3E\u6216\u7528 bash \u8FD0\u884C\u9A8C\u8BC1
1270
+
1271
+ ## \u91CD\u8981
1272
+
1273
+ - memo \u662F\u4F60\u4E0E\u7F16\u7801 Agent \u7684\u5171\u4EAB\u8BB0\u5FC6\uFF0C\u662F\u534F\u4F5C\u7684\u6838\u5FC3\u6865\u6881
1274
+ - \u4E0D\u8981\u91CD\u590D\u63A2\u7D22\uFF08glob \u67E5\u8FC7\u5C31\u4E0D\u8981\u518D bash ls \u540C\u4E00\u76EE\u5F55\uFF09
1275
+ - bash \u7528\u4E8E\u6267\u884C\u547D\u4EE4\uFF08\u6784\u5EFA\u3001\u6D4B\u8BD5\uFF09\uFF0C\u4E0D\u9700\u8981\u59D4\u6D3E
1276
+ - \u5B8C\u6210\u6240\u6709\u6B65\u9AA4\u540E\u7B80\u8981\u544A\u77E5\u7528\u6237\u7ED3\u679C`;
1277
+ }
1278
+ /**
1279
+ * 切换协作模式
1280
+ */
1281
+ setMode(mode) {
1282
+ this.mode = mode;
1283
+ this.conversation.setSystemPrompt(this.buildSystemPrompt());
1284
+ }
1285
+ /**
1286
+ * 执行用户请求
1287
+ */
1288
+ async run(userMessage, callbacks = {}) {
1289
+ callbacks.onModeInfo?.(this.mode);
1290
+ this.conversation.addUserMessage(userMessage);
1291
+ let lastContent = "";
1292
+ while (true) {
1293
+ const tools = this.getTools();
1294
+ const assistantMsg = await this.orchestratorClient.chatStream(
1295
+ this.conversation.getMessages(),
1296
+ tools,
1297
+ callbacks
1298
+ );
1299
+ this.conversation.addAssistantMessage(assistantMsg);
1300
+ if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
1301
+ lastContent = assistantMsg.content || "";
1302
+ break;
1303
+ }
1304
+ for (const toolCall of assistantMsg.tool_calls) {
1305
+ const toolName = toolCall.function.name;
1306
+ let params;
1307
+ try {
1308
+ params = JSON.parse(toolCall.function.arguments);
1309
+ } catch {
1310
+ this.conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
1311
+ continue;
1312
+ }
1313
+ try {
1314
+ if (toolName === "send-to-coder") {
1315
+ const task = params["task"];
1316
+ const coderResponse = await this.invokeCoder(task, callbacks);
1317
+ this.conversation.addToolResult(toolCall.id, coderResponse);
1318
+ continue;
1319
+ }
1320
+ const permLevel = this.registry.getPermissionLevel(toolName);
1321
+ if (permLevel === "deny") {
1322
+ callbacks.onDenied?.(toolName);
1323
+ this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177 "${toolName}" \u5DF2\u88AB\u7981\u6B62\u6267\u884C`);
1324
+ continue;
1325
+ }
1326
+ if (permLevel === "auto") {
1327
+ callbacks.onToolExecuting?.(toolName, params);
1328
+ }
1329
+ if (permLevel === "confirm") {
1330
+ const confirmResult = await confirmExecution(toolName, params);
1331
+ if (!confirmResult.approved) {
1332
+ callbacks.onDenied?.(toolName, confirmResult.feedback);
1333
+ const denyMsg = confirmResult.feedback ? `\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C\uFF0C\u7528\u6237\u53CD\u9988: ${confirmResult.feedback}` : "\u7528\u6237\u62D2\u7EDD\u4E86\u6B64\u64CD\u4F5C";
1334
+ this.conversation.addToolResult(toolCall.id, denyMsg);
1335
+ continue;
1336
+ }
1337
+ }
1338
+ const result = await this.registry.execute(toolName, params, this.config.max_tool_output);
1339
+ callbacks.onToolResult?.(toolName, result.content, result.truncated ?? false);
1340
+ this.conversation.addToolResult(toolCall.id, result.content);
1341
+ } catch (err) {
1342
+ const msg = err instanceof Error ? err.message : String(err);
1343
+ this.conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
1344
+ }
1345
+ }
1346
+ if (assistantMsg.content) {
1347
+ lastContent = assistantMsg.content;
1348
+ }
1349
+ }
1350
+ return lastContent;
1351
+ }
1352
+ /**
1353
+ * 调用编码者 Agent
1354
+ */
1355
+ async invokeCoder(task, callbacks) {
1356
+ callbacks.onCoderStart?.();
1357
+ const modeInfo = getMode(this.mode);
1358
+ let coderTools = [];
1359
+ if (modeInfo.coderHasTools && modeInfo.coderToolNames) {
1360
+ coderTools = this.registry.toToolDefinitions(modeInfo.coderToolNames);
1361
+ }
1362
+ let taskWithMemo = task;
1363
+ if (this.memoStore) {
1364
+ const index = this.memoStore.buildIndex();
1365
+ if (index) {
1366
+ taskWithMemo += `
1367
+
1368
+ [\u5171\u4EAB\u5907\u5FD8\u5F55]
1369
+ ${index}`;
1370
+ }
1371
+ }
1372
+ taskWithMemo += "\n\n[\u91CD\u8981\uFF1A\u7ACB\u5373\u5F00\u59CB\u7F16\u7801\uFF0C\u4E0D\u8981\u63A2\u7D22\uFF0C\u4E0D\u8981\u8F93\u51FA\u5206\u6790]";
1373
+ const coder = new Coder(
1374
+ this.coderClient,
1375
+ this.registry,
1376
+ this.config,
1377
+ modeInfo.coderSystemPrompt,
1378
+ coderTools
1379
+ );
1380
+ const response = await coder.execute(taskWithMemo, {
1381
+ onContent: callbacks.onContent,
1382
+ onToolCallStreaming: callbacks.onToolCallStreaming,
1383
+ onToolExecuting: callbacks.onToolExecuting,
1384
+ onToolResult: callbacks.onToolResult,
1385
+ onDenied: callbacks.onDenied
1386
+ });
1387
+ callbacks.onCoderEnd?.(response);
1388
+ return response;
1389
+ }
1390
+ /**
1391
+ * 获取对话历史
1392
+ */
1393
+ getConversation() {
1394
+ return this.conversation;
1395
+ }
1396
+ };
1397
+ }
1398
+ });
1399
+
1400
+ // src/core/prompt/layers/core.ts
1401
+ import * as os2 from "os";
1402
+ function buildCorePrompt() {
1403
+ return `\u4F60\u662F ZenCode\uFF0C\u4E00\u4E2A CLI \u73AF\u5883\u4E0B\u7684 AI \u7F16\u7A0B\u52A9\u624B\u3002\u4F60\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u8F6F\u4EF6\u5DE5\u7A0B\u4EFB\u52A1\uFF1A\u4FEEbug\u3001\u52A0\u529F\u80FD\u3001\u91CD\u6784\u4EE3\u7801\u3001\u89E3\u91CA\u4EE3\u7801\u7B49\u3002
1404
+
1405
+ \u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}
1406
+ \u7CFB\u7EDF\uFF1A${os2.platform()} ${os2.arch()}
1407
+
1408
+ # \u5DE5\u5177\u4F7F\u7528\u539F\u5219
1409
+
1410
+ \u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177\u53EF\u7528\uFF0C\u8BF7\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u5408\u9002\u7684\u5DE5\u5177\uFF1A
1411
+
1412
+ - **read-file**\uFF1A\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002\u4FEE\u6539\u4EE3\u7801\u524D\u5FC5\u987B\u5148\u8BFB\u53D6\u76EE\u6807\u6587\u4EF6\u3002\u652F\u6301 offset/limit \u8BFB\u53D6\u5927\u6587\u4EF6\u7684\u7279\u5B9A\u90E8\u5206\u3002
1413
+ - **edit-file**\uFF1A\u901A\u8FC7\u5B57\u7B26\u4E32\u66FF\u6362\u7F16\u8F91\u6587\u4EF6\u3002\u4F18\u5148\u4F7F\u7528 edit-file \u800C\u975E write-file \u4FEE\u6539\u5DF2\u6709\u6587\u4EF6\u2014\u2014\u5B83\u66F4\u7CBE\u786E\u3001\u66F4\u5B89\u5168\u3002old_string \u5FC5\u987B\u552F\u4E00\u5339\u914D\uFF0C\u5339\u914D\u5931\u8D25\u65F6\u63D0\u4F9B\u66F4\u591A\u4E0A\u4E0B\u6587\u4F7F\u5176\u552F\u4E00\u3002
1414
+ - **write-file**\uFF1A\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u5B8C\u6574\u91CD\u5199\u6587\u4EF6\u3002\u4EC5\u5728\u521B\u5EFA\u65B0\u6587\u4EF6\u6216\u9700\u8981\u5927\u5E45\u91CD\u5199\u65F6\u4F7F\u7528\u3002
1415
+ - **glob**\uFF1A\u6309\u6A21\u5F0F\u641C\u7D22\u6587\u4EF6\u8DEF\u5F84\u3002\u7528\u4E8E\u67E5\u627E\u6587\u4EF6\u4F4D\u7F6E\uFF08\u5982 \`**/*.ts\`\u3001\`src/**/config.*\`\uFF09\u3002
1416
+ - **grep**\uFF1A\u5728\u6587\u4EF6\u5185\u5BB9\u4E2D\u641C\u7D22\u6B63\u5219\u8868\u8FBE\u5F0F\u3002\u7528\u4E8E\u67E5\u627E\u51FD\u6570\u5B9A\u4E49\u3001\u5F15\u7528\u3001\u7279\u5B9A\u4EE3\u7801\u6A21\u5F0F\u3002
1417
+ - **bash**\uFF1A\u6267\u884C shell \u547D\u4EE4\u3002\u7528\u4E8E\u8FD0\u884C\u6784\u5EFA\u3001\u6D4B\u8BD5\u3001git \u64CD\u4F5C\u7B49\u3002\u4E0D\u8981\u7528 bash \u505A\u80FD\u7528\u4E0A\u8FF0\u5DE5\u5177\u5B8C\u6210\u7684\u4E8B\uFF08\u5982\u4E0D\u8981\u7528 cat \u8BFB\u6587\u4EF6\u3001\u4E0D\u8981\u7528 sed \u7F16\u8F91\u6587\u4EF6\u3001\u4E0D\u8981\u7528 find \u641C\u7D22\u6587\u4EF6\uFF09\u3002
1418
+
1419
+ \u5173\u952E\u89C4\u5219\uFF1A
1420
+ - \u4FEE\u6539\u4EE3\u7801\u524D\u5148\u7528 read-file \u9605\u8BFB\u76F8\u5173\u6587\u4EF6\uFF0C\u7406\u89E3\u73B0\u6709\u903B\u8F91
1421
+ - \u4F18\u5148 edit-file \u7F16\u8F91\u5DF2\u6709\u6587\u4EF6\uFF0C\u800C\u975E write-file \u91CD\u5199
1422
+ - \u4E0D\u8981\u521B\u5EFA\u4E0D\u5FC5\u8981\u7684\u65B0\u6587\u4EF6\uFF0C\u4F18\u5148\u5728\u73B0\u6709\u6587\u4EF6\u4E2D\u4FEE\u6539
1423
+ - \u53EA\u505A\u5FC5\u8981\u7684\u6700\u5C0F\u6539\u52A8\uFF0C\u4E0D\u505A\u989D\u5916"\u6539\u8FDB"
1424
+ - \u4E0D\u8981\u6DFB\u52A0\u7528\u6237\u672A\u8981\u6C42\u7684\u6CE8\u91CA\u3001\u6587\u6863\u3001\u7C7B\u578B\u6CE8\u89E3
1425
+ - \u4E0D\u8981\u5F15\u5165\u5B89\u5168\u6F0F\u6D1E\uFF08\u6CE8\u5165\u3001XSS\u3001SQL \u6CE8\u5165\u7B49 OWASP Top 10\uFF09
1426
+ - \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
1427
+
1428
+ # \u4EA4\u4E92\u98CE\u683C
1429
+
1430
+ - \u4FDD\u6301\u6280\u672F\u5BA2\u89C2\u6027\uFF0C\u57FA\u4E8E\u4E8B\u5B9E\u56DE\u7B54\uFF0C\u4E0D\u8FC7\u5EA6\u8D5E\u540C\u6216\u606D\u7EF4\u7528\u6237\uFF0C\u5FC5\u8981\u65F6\u76F4\u63A5\u6307\u51FA\u95EE\u9898
1431
+ - \u4E0D\u786E\u5B9A\u65F6\u5148\u8C03\u67E5\u9A8C\u8BC1\uFF0C\u800C\u975E\u76F4\u89C9\u6027\u5730\u786E\u8BA4\u7528\u6237\u7684\u5047\u8BBE
1432
+ - \u4E0D\u8981\u7ED9\u51FA\u65F6\u95F4\u9884\u4F30\uFF08"\u5927\u6982\u9700\u8981\u51E0\u5206\u949F"\u4E4B\u7C7B\uFF09
1433
+ - \u56DE\u590D\u7B80\u6D01\uFF0C\u76F4\u63A5\u7ED9\u7ED3\u679C`;
1434
+ }
1435
+ var init_core = __esm({
1436
+ "src/core/prompt/layers/core.ts"() {
1437
+ "use strict";
1438
+ }
1439
+ });
1440
+
1441
+ // src/core/prompt/layers/planning.ts
1442
+ function buildPlanningPrompt() {
1443
+ return `# \u5DE5\u4F5C\u65B9\u6CD5
1444
+
1445
+ \u5904\u7406\u7F16\u7A0B\u4EFB\u52A1\u65F6\uFF1A
1446
+ 1. \u5148\u7528 read-file / grep / glob \u9605\u8BFB\u76F8\u5173\u4EE3\u7801\uFF0C\u7406\u89E3\u73B0\u6709\u903B\u8F91\u548C\u4E0A\u4E0B\u6587
1447
+ 2. \u786E\u5B9A\u65B9\u6848\u540E\u76F4\u63A5\u5B9E\u65BD\uFF0C\u4E0D\u8981\u957F\u7BC7\u89E3\u91CA\u8BA1\u5212
1448
+ 3. \u6539\u5B8C\u5373\u6B62\uFF0C\u4E0D\u8981\u81EA\u884C"\u9A8C\u8BC1"\u6216"\u6D4B\u8BD5"
1449
+
1450
+ \u591A\u6B65\u4EFB\u52A1\u7BA1\u7406\uFF1A
1451
+ - \u5BF9\u4E8E 3 \u4E2A\u4EE5\u4E0A\u6B65\u9AA4\u7684\u4EFB\u52A1\uFF0C\u4F7F\u7528 todo \u5DE5\u5177\u521B\u5EFA\u8BA1\u5212\u518D\u9010\u6B65\u6267\u884C
1452
+ - \u5F00\u59CB\u6B65\u9AA4\u524D\u6807\u8BB0 in-progress\uFF0C\u5B8C\u6210\u540E\u6807\u8BB0 completed
1453
+ - \u6BCF\u6B65\u5B8C\u6210\u540E\u68C0\u67E5\u8BA1\u5212\uFF0C\u51B3\u5B9A\u4E0B\u4E00\u6B65
1454
+
1455
+ \u4EE3\u7801\u8D28\u91CF\uFF1A
1456
+ - \u4E0D\u8981\u5199\u4E0D\u5FC5\u8981\u7684\u6D4B\u8BD5\uFF1B\u5982\u679C\u8981\u5199\u6D4B\u8BD5\uFF0C\u5148\u786E\u8BA4\u9879\u76EE\u7684\u6D4B\u8BD5\u6846\u67B6\u548C\u4F9D\u8D56\uFF0C\u786E\u4FDD\u80FD\u5B9E\u9645\u8FD0\u884C
1457
+ - \u4E0D\u8981\u8FC7\u5EA6\u5DE5\u7A0B\u5316\uFF1A\u4E0D\u8981\u4E3A\u4E00\u6B21\u6027\u64CD\u4F5C\u521B\u5EFA\u62BD\u8C61\u3001\u4E0D\u8981\u4E3A\u5047\u8BBE\u7684\u672A\u6765\u9700\u6C42\u8BBE\u8BA1
1458
+ - \u4E0D\u8981\u4E3A\u4E0D\u53EF\u80FD\u53D1\u751F\u7684\u573A\u666F\u6DFB\u52A0\u9519\u8BEF\u5904\u7406\uFF1B\u53EA\u5728\u7CFB\u7EDF\u8FB9\u754C\uFF08\u7528\u6237\u8F93\u5165\u3001\u5916\u90E8 API\uFF09\u505A\u6821\u9A8C
1459
+ - \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
1460
+ - \u4E09\u884C\u76F8\u4F3C\u4EE3\u7801\u4F18\u4E8E\u4E00\u4E2A\u8FC7\u65E9\u7684\u62BD\u8C61`;
1461
+ }
1462
+ var init_planning = __esm({
1463
+ "src/core/prompt/layers/planning.ts"() {
1464
+ "use strict";
1465
+ }
1466
+ });
1467
+
1468
+ // src/core/prompt/layers/parallel.ts
1469
+ function buildParallelPrompt() {
1470
+ return `# \u5E76\u884C\u6267\u884C\uFF08\u91CD\u8981\uFF09
1471
+
1472
+ \u5F53\u9700\u8981\u8BFB\u53D6\u3001\u641C\u7D22\u6216\u5206\u6790 2 \u4E2A\u4EE5\u4E0A\u72EC\u7ACB\u76EE\u6807\u65F6\uFF0C\u5FC5\u987B\u4F7F\u7528 spawn-agents \u5E76\u884C\u6267\u884C\uFF0C\u4E0D\u8981\u9010\u4E2A\u4E32\u884C\u8C03\u7528\u3002
1473
+
1474
+ \u89C4\u5219\uFF1A
1475
+ - \u9700\u8981\u8BFB 2+ \u4E2A\u6587\u4EF6 \u2192 spawn-agents \u5E76\u884C\u8BFB\u53D6
1476
+ - \u9700\u8981\u641C\u7D22 2+ \u4E2A\u6A21\u5F0F \u2192 spawn-agents \u5E76\u884C\u641C\u7D22
1477
+ - \u9700\u8981\u4E86\u89E3 2+ \u4E2A\u6A21\u5757 \u2192 spawn-agents \u5E76\u884C\u5206\u6790
1478
+ - \u53EA\u6709 1 \u4E2A\u76EE\u6807 \u2192 \u76F4\u63A5\u7528\u5DE5\u5177\uFF0C\u65E0\u9700 spawn-agents
1479
+
1480
+ \u793A\u4F8B - \u7528\u6237\u8BF4"\u5E2E\u6211\u7406\u89E3\u8BA4\u8BC1\u6A21\u5757"\uFF1A
1481
+ \u6B63\u786E\uFF1Aspawn-agents \u540C\u65F6\u8BFB auth controller\u3001auth service\u3001auth middleware\u3001auth types
1482
+ \u9519\u8BEF\uFF1A\u5148 read-file controller\uFF0C\u518D read-file service\uFF0C\u518D read-file middleware...
1483
+
1484
+ \u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EF\u7528 read-file\u3001glob\u3001grep\u3001memo\u3002`;
1485
+ }
1486
+ var init_parallel = __esm({
1487
+ "src/core/prompt/layers/parallel.ts"() {
1488
+ "use strict";
1489
+ }
1490
+ });
1491
+
1492
+ // src/core/prompt/layers/git.ts
1493
+ function buildGitPrompt() {
1494
+ return `# Git \u64CD\u4F5C
1495
+
1496
+ \u5F53\u524D\u9879\u76EE\u4F7F\u7528 git \u7BA1\u7406\u3002
1497
+
1498
+ \u63D0\u4EA4\u89C4\u8303\uFF1A
1499
+ - \u53EA\u5728\u7528\u6237\u660E\u786E\u8981\u6C42\u65F6\u624D\u521B\u5EFA commit
1500
+ - \u7528 git diff \u67E5\u770B\u53D8\u66F4\uFF0C\u518D\u5199 commit message
1501
+ - commit message \u63CF\u8FF0"\u4E3A\u4EC0\u4E48"\u800C\u975E"\u6539\u4E86\u4EC0\u4E48"
1502
+
1503
+ \u5B89\u5168\u89C4\u5219\uFF1A
1504
+ - \u4E0D\u8981 force push\u3001reset --hard\u3001checkout .\u3001clean -f \u7B49\u7834\u574F\u6027\u64CD\u4F5C\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42
1505
+ - \u4E0D\u8981 --no-verify \u8DF3\u8FC7 hook\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42
1506
+ - \u4F18\u5148\u521B\u5EFA\u65B0 commit\uFF0C\u4E0D\u8981 --amend \u4FEE\u6539\u5DF2\u6709 commit\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\uFF08hook \u5931\u8D25\u540E amend \u4F1A\u7834\u574F\u4E0A\u4E00\u4E2A commit\uFF09
1507
+ - git add \u65F6\u6307\u5B9A\u5177\u4F53\u6587\u4EF6\uFF0C\u907F\u514D git add -A \u610F\u5916\u6682\u5B58\u654F\u611F\u6587\u4EF6\uFF08.env\u3001credentials \u7B49\uFF09
1508
+ - \u4E0D\u8981\u4F7F\u7528\u4EA4\u4E92\u5F0F\u6807\u5FD7\uFF08git rebase -i\u3001git add -i\uFF09\uFF0CCLI \u73AF\u5883\u4E0D\u652F\u6301
1509
+ - \u4E0D\u8981\u4FEE\u6539 git config`;
1510
+ }
1511
+ var init_git = __esm({
1512
+ "src/core/prompt/layers/git.ts"() {
1513
+ "use strict";
1514
+ }
1515
+ });
1516
+
1517
+ // src/core/prompt/layers/project.ts
1518
+ import * as fs6 from "fs/promises";
1519
+ import * as path6 from "path";
1520
+ async function buildProjectPrompt() {
1521
+ try {
1522
+ const content = await fs6.readFile(path6.resolve("ZENCODE.md"), "utf-8");
1523
+ return content.trim() || null;
1524
+ } catch {
1525
+ return null;
1526
+ }
1527
+ }
1528
+ async function loadUserPrompts(paths) {
1529
+ const prompts = [];
1530
+ for (const p of paths) {
1531
+ try {
1532
+ const resolved = p.startsWith("~") ? path6.join(process.env["HOME"] || process.env["USERPROFILE"] || "", p.slice(1)) : path6.resolve(p);
1533
+ const content = await fs6.readFile(resolved, "utf-8");
1534
+ if (content.trim()) {
1535
+ prompts.push(content.trim());
1536
+ }
1537
+ } catch {
1538
+ }
1539
+ }
1540
+ return prompts;
1541
+ }
1542
+ var init_project = __esm({
1543
+ "src/core/prompt/layers/project.ts"() {
1544
+ "use strict";
1545
+ }
1546
+ });
1547
+
1548
+ // src/core/prompt/builder.ts
1549
+ import * as fs7 from "fs";
1550
+ function isGitRepo() {
1551
+ try {
1552
+ fs7.statSync(".git");
1553
+ return true;
1554
+ } catch {
1555
+ return false;
1556
+ }
1557
+ }
1558
+ async function buildPrompt(config) {
1559
+ const layers = [];
1560
+ layers.push(buildCorePrompt());
1561
+ if (config.features.planning_layer === "on") {
1562
+ layers.push(buildPlanningPrompt());
1563
+ }
1564
+ const gitEnabled = config.features.git === "on" || config.features.git === "auto" && isGitRepo();
1565
+ if (gitEnabled) {
1566
+ layers.push(buildGitPrompt());
1567
+ }
1568
+ if (config.features.parallel_agents === "on") {
1569
+ layers.push(buildParallelPrompt());
1570
+ }
1571
+ const projectPrompt = await buildProjectPrompt();
1572
+ if (projectPrompt) {
1573
+ layers.push(projectPrompt);
1574
+ }
1575
+ if (config.prompts.length > 0) {
1576
+ const userPrompts = await loadUserPrompts(config.prompts);
1577
+ layers.push(...userPrompts);
1578
+ }
1579
+ const systemPrompt = layers.join("\n\n");
1580
+ return { systemPrompt, layers };
1581
+ }
1582
+ var init_builder = __esm({
1583
+ "src/core/prompt/builder.ts"() {
1584
+ "use strict";
1585
+ init_core();
1586
+ init_planning();
1587
+ init_parallel();
1588
+ init_git();
1589
+ init_project();
1590
+ }
1591
+ });
1592
+
1593
+ // src/core/todo-store.ts
1594
+ var TodoStore;
1595
+ var init_todo_store = __esm({
1596
+ "src/core/todo-store.ts"() {
1597
+ "use strict";
1598
+ TodoStore = class {
1599
+ plan = null;
1600
+ listeners = /* @__PURE__ */ new Set();
1601
+ create(items) {
1602
+ this.plan = {
1603
+ items: items.map((item) => ({
1604
+ id: item.id,
1605
+ title: item.title,
1606
+ status: "pending"
1607
+ }))
1608
+ };
1609
+ this.notify();
1610
+ return this.plan;
1611
+ }
1612
+ update(id, status) {
1613
+ if (!this.plan) return null;
1614
+ const item = this.plan.items.find((i) => i.id === id);
1615
+ if (!item) return null;
1616
+ item.status = status;
1617
+ this.notify();
1618
+ return { ...item };
1619
+ }
1620
+ list() {
1621
+ return this.plan;
1622
+ }
1623
+ clear() {
1624
+ this.plan = null;
1625
+ this.notify();
1626
+ }
1627
+ subscribe(listener) {
1628
+ this.listeners.add(listener);
1629
+ return () => {
1630
+ this.listeners.delete(listener);
1631
+ };
1632
+ }
1633
+ notify() {
1634
+ const snapshot = this.plan ? { items: this.plan.items.map((i) => ({ ...i })) } : null;
1635
+ for (const listener of this.listeners) {
1636
+ listener(snapshot);
1637
+ }
1638
+ }
1639
+ };
1640
+ }
1641
+ });
1642
+
1643
+ // src/core/memo-store.ts
1644
+ var MAX_ENTRIES, MAX_CONTENT_LENGTH, MemoStore;
1645
+ var init_memo_store = __esm({
1646
+ "src/core/memo-store.ts"() {
1647
+ "use strict";
1648
+ MAX_ENTRIES = 30;
1649
+ MAX_CONTENT_LENGTH = 3e3;
1650
+ MemoStore = class {
1651
+ entries = /* @__PURE__ */ new Map();
1652
+ write(key, content, author = "agent") {
1653
+ const trimmed = content.slice(0, MAX_CONTENT_LENGTH);
1654
+ const entry = { key, content: trimmed, author, updatedAt: Date.now() };
1655
+ if (!this.entries.has(key) && this.entries.size >= MAX_ENTRIES) {
1656
+ let oldest = null;
1657
+ let oldestTime = Infinity;
1658
+ for (const [k, v] of this.entries) {
1659
+ if (v.updatedAt < oldestTime) {
1660
+ oldestTime = v.updatedAt;
1661
+ oldest = k;
1662
+ }
1663
+ }
1664
+ if (oldest) this.entries.delete(oldest);
1665
+ }
1666
+ this.entries.set(key, entry);
1667
+ return entry;
1668
+ }
1669
+ read(key) {
1670
+ return this.entries.get(key) ?? null;
1671
+ }
1672
+ list() {
1673
+ return [...this.entries.values()].map((e) => ({
1674
+ key: e.key,
1675
+ author: e.author,
1676
+ preview: e.content.slice(0, 80)
1677
+ }));
1678
+ }
1679
+ delete(key) {
1680
+ return this.entries.delete(key);
1681
+ }
1682
+ clear() {
1683
+ this.entries.clear();
1684
+ }
1685
+ /**
1686
+ * 生成简短的备忘录索引(用于注入 Agent 系统提示词)
1687
+ * 仅包含 key 列表和简短预览,占用极少 token
1688
+ */
1689
+ buildIndex() {
1690
+ if (this.entries.size === 0) return null;
1691
+ const lines = [...this.entries.values()].map(
1692
+ (e) => `- ${e.key}: ${e.content.slice(0, 50)}`
1693
+ );
1694
+ return lines.join("\n");
1695
+ }
1696
+ };
1697
+ }
1698
+ });
1699
+
1700
+ // src/core/sub-agent.ts
1701
+ var DEFAULT_TIMEOUT_MS, SubAgent;
1702
+ var init_sub_agent = __esm({
1703
+ "src/core/sub-agent.ts"() {
1704
+ "use strict";
1705
+ init_conversation();
1706
+ DEFAULT_TIMEOUT_MS = 12e4;
1707
+ SubAgent = class {
1708
+ client;
1709
+ registry;
1710
+ config;
1711
+ task;
1712
+ allowedTools;
1713
+ maxTurns;
1714
+ timeoutMs;
1715
+ memoStore;
1716
+ constructor(client, registry, config, task, allowedTools = ["read-file", "glob", "grep", "memo"], maxTurns = 10, timeoutMs = DEFAULT_TIMEOUT_MS, memoStore) {
1717
+ this.client = client;
1718
+ this.registry = registry;
1719
+ this.config = config;
1720
+ this.task = task;
1721
+ this.allowedTools = allowedTools.filter((t) => t !== "spawn-agents" && t !== "todo");
1722
+ this.maxTurns = Math.min(maxTurns, 15);
1723
+ this.timeoutMs = timeoutMs;
1724
+ this.memoStore = memoStore;
1725
+ }
1726
+ async run() {
1727
+ return Promise.race([
1728
+ this.execute(),
1729
+ this.timeout()
1730
+ ]);
1731
+ }
1732
+ timeout() {
1733
+ return new Promise((_, reject) => {
1734
+ setTimeout(
1735
+ () => reject(new Error(`\u5B50 Agent \u8D85\u65F6\uFF08${this.timeoutMs / 1e3}s\uFF09`)),
1736
+ this.timeoutMs
1737
+ );
1738
+ });
1739
+ }
1740
+ async execute() {
1741
+ const conversation = new Conversation();
1742
+ let systemPrompt = `\u4F60\u662F ZenCode \u5B50 Agent\u3002\u4F60\u7684\u4EFB\u52A1\uFF1A${this.task}
1743
+ \u5B8C\u6210\u540E\u76F4\u63A5\u8FD4\u56DE\u7ED3\u679C\uFF0C\u4E0D\u8981\u591A\u4F59\u89E3\u91CA\u3002`;
1744
+ if (this.memoStore) {
1745
+ const index = this.memoStore.buildIndex();
1746
+ if (index) {
1747
+ systemPrompt += `
1748
+
1749
+ [\u5171\u4EAB\u5907\u5FD8\u5F55 - \u53EF\u7528 memo read \u8BFB\u53D6\u8BE6\u60C5]
1750
+ ${index}`;
1751
+ }
1752
+ }
1753
+ conversation.setSystemPrompt(systemPrompt);
1754
+ conversation.addUserMessage(this.task);
1755
+ const tools = this.registry.toToolDefinitions(this.allowedTools);
1756
+ let lastContent = "";
1757
+ for (let turn = 0; turn < this.maxTurns; turn++) {
1758
+ const assistantMsg = await this.client.chat(
1759
+ conversation.getMessages(),
1760
+ tools.length > 0 ? tools : void 0
1761
+ );
1762
+ conversation.addAssistantMessage(assistantMsg);
1763
+ if (!assistantMsg.tool_calls || assistantMsg.tool_calls.length === 0) {
1764
+ lastContent = assistantMsg.content || "";
1765
+ break;
1766
+ }
1767
+ for (const toolCall of assistantMsg.tool_calls) {
1768
+ const toolName = toolCall.function.name;
1769
+ if (!this.allowedTools.includes(toolName)) {
1770
+ conversation.addToolResult(
1771
+ toolCall.id,
1772
+ `\u5B50 Agent \u4E0D\u5141\u8BB8\u4F7F\u7528\u5DE5\u5177 "${toolName}"`
1773
+ );
1774
+ continue;
1775
+ }
1776
+ let params;
1777
+ try {
1778
+ params = JSON.parse(toolCall.function.arguments);
1779
+ } catch {
1780
+ conversation.addToolResult(toolCall.id, "\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u65E0\u6548\u7684 JSON");
1781
+ continue;
1782
+ }
1783
+ try {
1784
+ const result = await this.registry.execute(
1785
+ toolName,
1786
+ params,
1787
+ this.config.max_tool_output
1788
+ );
1789
+ conversation.addToolResult(toolCall.id, result.content);
1790
+ } catch (err) {
1791
+ const msg = err instanceof Error ? err.message : String(err);
1792
+ conversation.addToolResult(toolCall.id, `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`);
1793
+ }
1794
+ }
1795
+ if (assistantMsg.content) {
1796
+ lastContent = assistantMsg.content;
1797
+ }
1798
+ }
1799
+ return lastContent;
1800
+ }
1801
+ };
1802
+ }
1803
+ });
1804
+
1805
+ // src/tools/spawn-agents.ts
1806
+ function createSpawnAgentsTool(client, registry, config, tracker, memoStore) {
1807
+ return {
1808
+ name: "spawn-agents",
1809
+ description: "\u5E76\u884C\u542F\u52A8\u591A\u4E2A\u5B50 Agent \u6267\u884C\u4EFB\u52A1\u3002\u6BCF\u4E2A\u5B50 Agent \u6709\u72EC\u7ACB\u5BF9\u8BDD\uFF0C\u9ED8\u8BA4\u53EA\u80FD\u7528\u53EA\u8BFB\u5DE5\u5177 (read-file, glob, grep)\u3002\u9002\u7528\u4E8E\u540C\u65F6\u8BFB\u53D6\u548C\u5206\u6790\u591A\u4E2A\u6587\u4EF6\u3001\u641C\u7D22\u591A\u4E2A\u6A21\u5F0F\u7B49\u573A\u666F\u3002",
1810
+ parameters: {
1811
+ type: "object",
1812
+ properties: {
1813
+ tasks: {
1814
+ type: "array",
1815
+ description: "\u8981\u5E76\u884C\u6267\u884C\u7684\u4EFB\u52A1\u5217\u8868",
1816
+ items: {
1817
+ type: "object",
1818
+ properties: {
1819
+ description: {
1820
+ type: "string",
1821
+ description: "\u4EFB\u52A1\u63CF\u8FF0"
1822
+ },
1823
+ tools: {
1824
+ type: "array",
1825
+ description: "\u5141\u8BB8\u4F7F\u7528\u7684\u5DE5\u5177\u5217\u8868\uFF08\u9ED8\u8BA4 read-file, glob, grep\uFF09\uFF0C\u4E0D\u53EF\u5305\u542B spawn-agents",
1826
+ items: { type: "string" }
1827
+ }
1828
+ },
1829
+ required: ["description"]
1830
+ }
1831
+ },
1832
+ max_turns: {
1833
+ type: "number",
1834
+ description: "\u6BCF\u4E2A\u5B50 Agent \u7684\u6700\u5927\u8F6E\u6570\uFF08\u9ED8\u8BA4 10\uFF0C\u4E0A\u9650 15\uFF09"
1835
+ }
1836
+ },
1837
+ required: ["tasks"]
1838
+ },
1839
+ permissionLevel: "auto",
1840
+ async execute(params) {
1841
+ const tasks = params["tasks"];
1842
+ const maxTurns = Math.min(
1843
+ params["max_turns"] || 10,
1844
+ MAX_TURNS_LIMIT
1845
+ );
1846
+ if (!tasks || tasks.length === 0) {
1847
+ return { content: "\u9519\u8BEF\uFF1A\u672A\u63D0\u4F9B\u4EFB\u52A1" };
1848
+ }
1849
+ if (tasks.length > MAX_CONCURRENT) {
1850
+ return { content: `\u9519\u8BEF\uFF1A\u6700\u591A\u652F\u6301 ${MAX_CONCURRENT} \u4E2A\u5E76\u53D1\u4EFB\u52A1` };
1851
+ }
1852
+ const autoTools = registry.listTools().filter((t) => registry.getPermissionLevel(t) === "auto" && t !== "spawn-agents");
1853
+ const descriptions = tasks.map((t) => t.description);
1854
+ tracker?.start(descriptions);
1855
+ const agents = tasks.map((task) => {
1856
+ let tools = task.tools ? task.tools.filter((t) => t !== "spawn-agents").filter((t) => autoTools.includes(t)) : DEFAULT_TOOLS.filter((t) => autoTools.includes(t));
1857
+ if (tools.length === 0) {
1858
+ tools = DEFAULT_TOOLS.filter((t) => autoTools.includes(t));
1859
+ }
1860
+ return new SubAgent(client, registry, config, task.description, tools, maxTurns, void 0, memoStore);
1861
+ });
1862
+ const wrappedRuns = agents.map(
1863
+ (agent) => agent.run().then(
1864
+ (result) => {
1865
+ tracker?.markCompleted();
1866
+ return result;
1867
+ },
1868
+ (err) => {
1869
+ tracker?.markFailed();
1870
+ throw err;
1871
+ }
1872
+ )
1873
+ );
1874
+ const results = await Promise.allSettled(wrappedRuns);
1875
+ tracker?.finish();
1876
+ const succeeded = results.filter((r) => r.status === "fulfilled").length;
1877
+ const failed = results.filter((r) => r.status === "rejected").length;
1878
+ const output = results.map((result, i) => {
1879
+ const task = tasks[i];
1880
+ const status = result.status === "fulfilled" ? "\u2713" : "\u2717";
1881
+ const header = `=== ${status} \u4EFB\u52A1 ${i + 1}: ${task.description} ===`;
1882
+ if (result.status === "fulfilled") {
1883
+ return `${header}
1884
+ ${result.value}`;
1885
+ }
1886
+ return `${header}
1887
+ \u9519\u8BEF: ${result.reason}`;
1888
+ }).join("\n\n");
1889
+ const summary = `[${succeeded} \u6210\u529F${failed > 0 ? `, ${failed} \u5931\u8D25` : ""}]`;
1890
+ return { content: `${summary}
1891
+
1892
+ ${output}` };
1893
+ }
1894
+ };
1895
+ }
1896
+ var DEFAULT_TOOLS, MAX_CONCURRENT, MAX_TURNS_LIMIT;
1897
+ var init_spawn_agents = __esm({
1898
+ "src/tools/spawn-agents.ts"() {
1899
+ "use strict";
1900
+ init_sub_agent();
1901
+ DEFAULT_TOOLS = ["read-file", "glob", "grep", "memo"];
1902
+ MAX_CONCURRENT = 10;
1903
+ MAX_TURNS_LIMIT = 15;
1904
+ }
1905
+ });
1906
+
1907
+ // src/tools/todo.ts
1908
+ function createTodoTool(store) {
1909
+ return {
1910
+ name: "todo",
1911
+ description: "\u7BA1\u7406\u4EFB\u52A1\u8BA1\u5212\u3002\u652F\u6301\u521B\u5EFA\u8BA1\u5212\u3001\u66F4\u65B0\u6761\u76EE\u72B6\u6001\u3001\u67E5\u770B\u8BA1\u5212\u548C\u6E05\u7A7A\u8BA1\u5212\u3002\u5BF9\u4E8E\u5305\u542B\u591A\u4E2A\u6B65\u9AA4\u7684\u4EFB\u52A1\uFF0C\u5148\u521B\u5EFA\u8BA1\u5212\u518D\u9010\u6B65\u6267\u884C\u3002",
1912
+ parameters: {
1913
+ type: "object",
1914
+ properties: {
1915
+ action: {
1916
+ type: "string",
1917
+ description: "\u64CD\u4F5C\u7C7B\u578B",
1918
+ enum: ["create", "update", "list", "clear"]
1919
+ },
1920
+ items: {
1921
+ type: "array",
1922
+ description: "create \u65F6\u5FC5\u586B\uFF1A\u8BA1\u5212\u6761\u76EE\u5217\u8868",
1923
+ items: {
1924
+ type: "object",
1925
+ properties: {
1926
+ id: { type: "string", description: "\u6761\u76EE ID" },
1927
+ title: { type: "string", description: "\u6761\u76EE\u6807\u9898" }
1928
+ },
1929
+ required: ["id", "title"]
1930
+ }
1931
+ },
1932
+ id: {
1933
+ type: "string",
1934
+ description: "update \u65F6\u5FC5\u586B\uFF1A\u8981\u66F4\u65B0\u7684\u6761\u76EE ID"
1935
+ },
1936
+ status: {
1937
+ type: "string",
1938
+ description: "update \u65F6\u5FC5\u586B\uFF1A\u65B0\u72B6\u6001",
1939
+ enum: ["pending", "in-progress", "completed"]
1940
+ }
1941
+ },
1942
+ required: ["action"]
1943
+ },
1944
+ permissionLevel: "auto",
1945
+ async execute(params) {
1946
+ const action = params["action"];
1947
+ switch (action) {
1948
+ case "create": {
1949
+ const items = params["items"];
1950
+ if (!items || items.length === 0) {
1951
+ return { content: "\u9519\u8BEF\uFF1Acreate \u9700\u8981\u63D0\u4F9B items" };
1952
+ }
1953
+ const plan = store.create(items);
1954
+ const lines = plan.items.map(
1955
+ (item) => `\u25CB [${item.id}] ${item.title}`
1956
+ );
1957
+ return {
1958
+ content: `\u8BA1\u5212\u5DF2\u521B\u5EFA\uFF08${plan.items.length} \u4E2A\u6761\u76EE\uFF09\uFF1A
1959
+ ${lines.join("\n")}`
1960
+ };
1961
+ }
1962
+ case "update": {
1963
+ const id = params["id"];
1964
+ const status = params["status"];
1965
+ if (!id || !status) {
1966
+ return { content: "\u9519\u8BEF\uFF1Aupdate \u9700\u8981\u63D0\u4F9B id \u548C status" };
1967
+ }
1968
+ const item = store.update(
1969
+ id,
1970
+ status
1971
+ );
1972
+ if (!item) {
1973
+ return { content: `\u9519\u8BEF\uFF1A\u672A\u627E\u5230\u6761\u76EE "${id}"` };
1974
+ }
1975
+ const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
1976
+ return {
1977
+ content: `\u5DF2\u66F4\u65B0\uFF1A${icon} [${item.id}] ${item.title} \u2192 ${item.status}`
1978
+ };
1979
+ }
1980
+ case "list": {
1981
+ const plan = store.list();
1982
+ if (!plan) {
1983
+ return { content: "\u5F53\u524D\u6CA1\u6709\u8BA1\u5212" };
1984
+ }
1985
+ const completed = plan.items.filter(
1986
+ (i) => i.status === "completed"
1987
+ ).length;
1988
+ const lines = plan.items.map((item) => {
1989
+ const icon = item.status === "completed" ? "\u25CF" : item.status === "in-progress" ? "\u25D0" : "\u25CB";
1990
+ return `${icon} [${item.id}] ${item.title}`;
1991
+ });
1992
+ return {
1993
+ content: `\u8BA1\u5212\u8FDB\u5EA6 ${completed}/${plan.items.length}\uFF1A
1994
+ ${lines.join("\n")}`
1995
+ };
1996
+ }
1997
+ case "clear": {
1998
+ store.clear();
1999
+ return { content: "\u8BA1\u5212\u5DF2\u6E05\u7A7A" };
2000
+ }
2001
+ default:
2002
+ return {
2003
+ content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: create, update, list, clear`
2004
+ };
2005
+ }
2006
+ }
2007
+ };
2008
+ }
2009
+ var init_todo = __esm({
2010
+ "src/tools/todo.ts"() {
2011
+ "use strict";
2012
+ }
2013
+ });
2014
+
2015
+ // src/tools/memo.ts
2016
+ function createMemoTool(store) {
2017
+ return {
2018
+ name: "memo",
2019
+ description: "\u5171\u4EAB\u5907\u5FD8\u5F55\uFF1A\u5728\u591A\u4E2A Agent \u4E4B\u95F4\u5171\u4EAB\u4FE1\u606F\u3002write \u5199\u5165\u53D1\u73B0/\u51B3\u7B56/\u6458\u8981\u4F9B\u5176\u4ED6 Agent \u8BFB\u53D6\uFF0Cread \u6309 key \u8BFB\u53D6\uFF0Clist \u67E5\u770B\u6240\u6709\u53EF\u7528\u6761\u76EE\u3002",
2020
+ parameters: {
2021
+ type: "object",
2022
+ properties: {
2023
+ action: {
2024
+ type: "string",
2025
+ description: "\u64CD\u4F5C\u7C7B\u578B",
2026
+ enum: ["write", "read", "list", "delete", "clear"]
2027
+ },
2028
+ key: {
2029
+ type: "string",
2030
+ description: "write/read/delete \u65F6\u5FC5\u586B\uFF1A\u5907\u5FD8\u5F55 key"
2031
+ },
2032
+ content: {
2033
+ type: "string",
2034
+ description: "write \u65F6\u5FC5\u586B\uFF1A\u8981\u5199\u5165\u7684\u5185\u5BB9\uFF08\u5EFA\u8BAE\u7B80\u6D01\u6458\u8981\uFF0C\u6700\u591A 3000 \u5B57\u7B26\uFF09"
2035
+ }
2036
+ },
2037
+ required: ["action"]
2038
+ },
2039
+ permissionLevel: "auto",
2040
+ async execute(params) {
2041
+ const action = params["action"];
2042
+ switch (action) {
2043
+ case "write": {
2044
+ const key = params["key"];
2045
+ const content = params["content"];
2046
+ if (!key || !content) {
2047
+ return { content: "\u9519\u8BEF\uFF1Awrite \u9700\u8981\u63D0\u4F9B key \u548C content" };
2048
+ }
2049
+ const entry = store.write(key, content);
2050
+ return { content: `\u5DF2\u5199\u5165 memo [${entry.key}]\uFF08${entry.content.length} \u5B57\u7B26\uFF09` };
2051
+ }
2052
+ case "read": {
2053
+ const key = params["key"];
2054
+ if (!key) {
2055
+ return { content: "\u9519\u8BEF\uFF1Aread \u9700\u8981\u63D0\u4F9B key" };
2056
+ }
2057
+ const entry = store.read(key);
2058
+ if (!entry) {
2059
+ return { content: `memo [${key}] \u4E0D\u5B58\u5728` };
2060
+ }
2061
+ return { content: `[${entry.key}] by ${entry.author}:
2062
+ ${entry.content}` };
2063
+ }
2064
+ case "list": {
2065
+ const items = store.list();
2066
+ if (items.length === 0) {
2067
+ return { content: "\u5907\u5FD8\u5F55\u4E3A\u7A7A" };
2068
+ }
2069
+ const lines = items.map(
2070
+ (item) => `[${item.key}] (${item.author}) ${item.preview}`
2071
+ );
2072
+ return { content: `\u5171 ${items.length} \u6761\u5907\u5FD8\u5F55\uFF1A
2073
+ ${lines.join("\n")}` };
2074
+ }
2075
+ case "delete": {
2076
+ const key = params["key"];
2077
+ if (!key) {
2078
+ return { content: "\u9519\u8BEF\uFF1Adelete \u9700\u8981\u63D0\u4F9B key" };
2079
+ }
2080
+ const ok = store.delete(key);
2081
+ return { content: ok ? `\u5DF2\u5220\u9664 memo [${key}]` : `memo [${key}] \u4E0D\u5B58\u5728` };
2082
+ }
2083
+ case "clear": {
2084
+ store.clear();
2085
+ return { content: "\u5907\u5FD8\u5F55\u5DF2\u6E05\u7A7A" };
2086
+ }
2087
+ default:
2088
+ return { content: `\u9519\u8BEF\uFF1A\u672A\u77E5\u64CD\u4F5C "${action}"\u3002\u652F\u6301: write, read, list, delete, clear` };
2089
+ }
2090
+ }
2091
+ };
2092
+ }
2093
+ var init_memo = __esm({
2094
+ "src/tools/memo.ts"() {
2095
+ "use strict";
2096
+ }
2097
+ });
2098
+
2099
+ // src/cli/tui/bridge.ts
2100
+ function createThinkFilter() {
2101
+ let inThink = false;
2102
+ let tagBuffer = "";
2103
+ let lineStart = true;
2104
+ let postThink = false;
2105
+ return function filter(text) {
2106
+ let result = "";
2107
+ for (let i = 0; i < text.length; i++) {
2108
+ const ch = text[i];
2109
+ if (tagBuffer.length > 0) {
2110
+ tagBuffer += ch;
2111
+ if (tagBuffer === "<think>") {
2112
+ inThink = true;
2113
+ lineStart = true;
2114
+ tagBuffer = "";
2115
+ result += `\u256D\u2500 \u{1F4AD} ${THINK_BORDER}
2116
+ `;
2117
+ } else if (tagBuffer === "</think>") {
2118
+ inThink = false;
2119
+ postThink = true;
2120
+ tagBuffer = "";
2121
+ result += `
2122
+ \u2570${THINK_BORDER}\u2500\u2500\u2500
2123
+
2124
+ `;
2125
+ } else if (!"<think>".startsWith(tagBuffer) && !"</think>".startsWith(tagBuffer)) {
2126
+ if (inThink && lineStart) {
2127
+ result += "\u2502 ";
2128
+ lineStart = false;
2129
+ }
2130
+ result += tagBuffer;
2131
+ tagBuffer = "";
2132
+ }
2133
+ continue;
2134
+ }
2135
+ if (ch === "<") {
2136
+ tagBuffer = "<";
2137
+ continue;
2138
+ }
2139
+ if (inThink) {
2140
+ if (lineStart) {
2141
+ result += "\u2502 ";
2142
+ lineStart = false;
2143
+ }
2144
+ result += ch;
2145
+ if (ch === "\n") {
2146
+ lineStart = true;
2147
+ }
2148
+ } else {
2149
+ if (postThink) {
2150
+ if (ch === "\n" || ch === "\r" || ch === " " || ch === " ") continue;
2151
+ postThink = false;
2152
+ }
2153
+ result += ch;
2154
+ }
2155
+ }
2156
+ return result;
2157
+ };
2158
+ }
2159
+ function createTokenBatcher(dispatch) {
2160
+ let buffer = "";
2161
+ let timer = null;
2162
+ function flush() {
2163
+ if (buffer.length > 0) {
2164
+ const text = buffer;
2165
+ buffer = "";
2166
+ dispatch({ type: "APPEND_CONTENT", text });
2167
+ }
2168
+ }
2169
+ function start() {
2170
+ if (!timer) {
2171
+ timer = setInterval(flush, BATCH_INTERVAL_MS);
2172
+ }
2173
+ }
2174
+ function stop() {
2175
+ flush();
2176
+ if (timer) {
2177
+ clearInterval(timer);
2178
+ timer = null;
2179
+ }
2180
+ }
2181
+ function append(text) {
2182
+ buffer += text;
2183
+ start();
2184
+ }
2185
+ function pause() {
2186
+ if (timer) {
2187
+ clearInterval(timer);
2188
+ timer = null;
2189
+ }
2190
+ }
2191
+ return { append, stop, flush, pause };
2192
+ }
2193
+ function extractCodeFromArgs(name, args) {
2194
+ const field = name === "write-file" ? "content" : "new_string";
2195
+ const patterns = [`"${field}": "`, `"${field}":"`];
2196
+ for (const pattern of patterns) {
2197
+ const idx = args.indexOf(pattern);
2198
+ if (idx >= 0) {
2199
+ let raw = args.slice(idx + pattern.length);
2200
+ if (raw.endsWith('"}')) raw = raw.slice(0, -2);
2201
+ else if (raw.endsWith('"')) raw = raw.slice(0, -1);
2202
+ return raw.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
2203
+ }
2204
+ }
2205
+ return null;
2206
+ }
2207
+ function registerConfirmToolId(toolName, id) {
2208
+ activeToolIds.set(toolName, id);
2209
+ }
2210
+ function createBridgeCallbacks(dispatch) {
2211
+ const batcher = createTokenBatcher(dispatch);
2212
+ const thinkFilter = createThinkFilter();
2213
+ activeToolIds = /* @__PURE__ */ new Map();
2214
+ streamingToolIds = /* @__PURE__ */ new Map();
2215
+ lastStreamingArgs = /* @__PURE__ */ new Map();
2216
+ toolCallCounter = 0;
2217
+ let streamingThrottleTimer = null;
2218
+ let pendingStreamingUpdate = null;
2219
+ function flushStreamingUpdate() {
2220
+ if (pendingStreamingUpdate) {
2221
+ pendingStreamingUpdate();
2222
+ pendingStreamingUpdate = null;
2223
+ }
2224
+ if (streamingThrottleTimer) {
2225
+ clearTimeout(streamingThrottleTimer);
2226
+ streamingThrottleTimer = null;
2227
+ }
2228
+ }
2229
+ return {
2230
+ onContent: (text) => {
2231
+ const filtered = thinkFilter(text);
2232
+ if (filtered) batcher.append(filtered);
2233
+ },
2234
+ onToolCallStreaming: (index, name, accumulatedArgs) => {
2235
+ if (!streamingToolIds.has(index)) {
2236
+ batcher.flush();
2237
+ const id2 = `tool-${++toolCallCounter}`;
2238
+ streamingToolIds.set(index, id2);
2239
+ activeToolIds.set(name, id2);
2240
+ dispatch({ type: "TOOL_STREAMING", id: id2, name, streamingContent: "0" });
2241
+ }
2242
+ const id = streamingToolIds.get(index);
2243
+ lastStreamingArgs.set(id, accumulatedArgs);
2244
+ const lineCount = (accumulatedArgs.match(/\\n/g) || []).length;
2245
+ pendingStreamingUpdate = () => {
2246
+ dispatch({ type: "TOOL_STREAMING", id, name, streamingContent: String(lineCount) });
2247
+ };
2248
+ if (!streamingThrottleTimer) {
2249
+ streamingThrottleTimer = setTimeout(() => {
2250
+ flushStreamingUpdate();
2251
+ }, 80);
2252
+ }
2253
+ },
2254
+ onToolExecuting: (name, params) => {
2255
+ flushStreamingUpdate();
2256
+ batcher.flush();
2257
+ batcher.pause();
2258
+ const existingId = activeToolIds.get(name);
2259
+ const id = existingId || `tool-${++toolCallCounter}`;
2260
+ activeToolIds.set(name, id);
2261
+ dispatch({ type: "TOOL_EXECUTING", id, name, params });
2262
+ },
2263
+ onToolResult: (name, result, truncated) => {
2264
+ const id = activeToolIds.get(name) || `tool-${++toolCallCounter}`;
2265
+ const lines = result.split("\n");
2266
+ const isWriteTool = name === "write-file" || name === "edit-file";
2267
+ let summary;
2268
+ if (isWriteTool) {
2269
+ const code = extractCodeFromArgs(name, lastStreamingArgs.get(id) || "");
2270
+ const codeLines = code ? code.split("\n").length : 0;
2271
+ summary = codeLines > 0 ? `${codeLines} \u884C` : truncated ? "\u8F93\u51FA\u5DF2\u622A\u65AD" : `${lines.length} \u884C`;
2272
+ } else {
2273
+ summary = truncated ? `\u8F93\u51FA\u5DF2\u622A\u65AD` : `${lines.length} \u884C`;
2274
+ }
2275
+ const preview = lines.slice(0, 5).join("\n").slice(0, 200);
2276
+ const resultContent = lines.length > 5 || preview.length >= 200 ? preview + "..." : preview;
2277
+ dispatch({ type: "TOOL_RESULT", id, resultSummary: summary, resultContent });
2278
+ },
2279
+ onCoderStart: () => {
2280
+ batcher.flush();
2281
+ dispatch({ type: "CODER_START" });
2282
+ },
2283
+ onCoderEnd: () => {
2284
+ dispatch({ type: "CODER_END" });
2285
+ },
2286
+ onDenied: (toolName, feedback) => {
2287
+ const id = activeToolIds.get(toolName) || `tool-${++toolCallCounter}`;
2288
+ dispatch({ type: "TOOL_DENIED", id, feedback });
2289
+ },
2290
+ onError: (err) => {
2291
+ batcher.stop();
2292
+ dispatch({ type: "SET_ERROR", error: err.message });
2293
+ },
2294
+ // Called internally when streaming is complete
2295
+ _stopBatcher: () => {
2296
+ batcher.stop();
2297
+ }
2298
+ };
2299
+ }
2300
+ var BATCH_INTERVAL_MS, THINK_BORDER, toolCallCounter, activeToolIds, streamingToolIds, lastStreamingArgs;
2301
+ var init_bridge = __esm({
2302
+ "src/cli/tui/bridge.ts"() {
2303
+ "use strict";
2304
+ BATCH_INTERVAL_MS = 64;
2305
+ THINK_BORDER = "\u2500".repeat(40);
2306
+ toolCallCounter = 0;
2307
+ activeToolIds = /* @__PURE__ */ new Map();
2308
+ streamingToolIds = /* @__PURE__ */ new Map();
2309
+ lastStreamingArgs = /* @__PURE__ */ new Map();
2310
+ }
2311
+ });
2312
+
2313
+ // src/core/sub-agent-tracker.ts
2314
+ var SubAgentTracker;
2315
+ var init_sub_agent_tracker = __esm({
2316
+ "src/core/sub-agent-tracker.ts"() {
2317
+ "use strict";
2318
+ SubAgentTracker = class {
2319
+ progress = null;
2320
+ listeners = /* @__PURE__ */ new Set();
2321
+ start(descriptions) {
2322
+ this.progress = {
2323
+ total: descriptions.length,
2324
+ completed: 0,
2325
+ failed: 0,
2326
+ descriptions
2327
+ };
2328
+ this.notify();
2329
+ }
2330
+ markCompleted() {
2331
+ if (!this.progress) return;
2332
+ this.progress = { ...this.progress, completed: this.progress.completed + 1 };
2333
+ this.notify();
2334
+ }
2335
+ markFailed() {
2336
+ if (!this.progress) return;
2337
+ this.progress = { ...this.progress, failed: this.progress.failed + 1 };
2338
+ this.notify();
2339
+ }
2340
+ finish() {
2341
+ this.progress = null;
2342
+ this.notify();
2343
+ }
2344
+ get current() {
2345
+ return this.progress;
2346
+ }
2347
+ subscribe(listener) {
2348
+ this.listeners.add(listener);
2349
+ return () => {
2350
+ this.listeners.delete(listener);
2351
+ };
2352
+ }
2353
+ notify() {
2354
+ const snapshot = this.progress ? { ...this.progress } : null;
2355
+ for (const listener of this.listeners) {
2356
+ listener(snapshot);
2357
+ }
2358
+ }
2359
+ };
2360
+ }
2361
+ });
2362
+
2363
+ // src/cli/tui/state.ts
2364
+ function createInitialState(modelName, agentMode, collaboration) {
2365
+ return {
2366
+ messages: [],
2367
+ isRunning: false,
2368
+ error: void 0,
2369
+ coderWorking: false,
2370
+ modelName,
2371
+ agentMode,
2372
+ collaboration,
2373
+ todoPlan: null,
2374
+ subAgentProgress: null
2375
+ };
2376
+ }
2377
+ function nextId() {
2378
+ return `msg-${++messageCounter}`;
2379
+ }
2380
+ function updateLastAssistant(messages, updater) {
2381
+ const result = [...messages];
2382
+ for (let i = result.length - 1; i >= 0; i--) {
2383
+ if (result[i].role === "assistant") {
2384
+ result[i] = updater(result[i]);
2385
+ return result;
2386
+ }
2387
+ }
2388
+ return result;
2389
+ }
2390
+ function updateToolInBlocks(blocks, toolId, updater) {
2391
+ return blocks.map((b) => {
2392
+ if (b.type === "tool" && b.toolCall.id === toolId) {
2393
+ return { type: "tool", toolCall: updater(b.toolCall) };
2394
+ }
2395
+ return b;
2396
+ });
2397
+ }
2398
+ function tuiReducer(state, action) {
2399
+ switch (action.type) {
2400
+ case "ADD_USER_MESSAGE":
2401
+ return {
2402
+ ...state,
2403
+ messages: [
2404
+ ...state.messages,
2405
+ {
2406
+ id: nextId(),
2407
+ role: "user",
2408
+ blocks: [{ type: "text", text: action.text }],
2409
+ isStreaming: false
2410
+ }
2411
+ ]
2412
+ };
2413
+ case "START_ASSISTANT":
2414
+ return {
2415
+ ...state,
2416
+ error: void 0,
2417
+ messages: [
2418
+ ...state.messages,
2419
+ {
2420
+ id: nextId(),
2421
+ role: "assistant",
2422
+ blocks: [],
2423
+ isStreaming: true
2424
+ }
2425
+ ]
2426
+ };
2427
+ case "APPEND_CONTENT": {
2428
+ return {
2429
+ ...state,
2430
+ messages: updateLastAssistant(state.messages, (msg) => {
2431
+ const blocks = [...msg.blocks];
2432
+ const last = blocks[blocks.length - 1];
2433
+ if (last && last.type === "text") {
2434
+ blocks[blocks.length - 1] = { type: "text", text: last.text + action.text };
2435
+ } else {
2436
+ blocks.push({ type: "text", text: action.text });
2437
+ }
2438
+ return { ...msg, blocks };
2439
+ })
2440
+ };
2441
+ }
2442
+ case "TOOL_EXECUTING": {
2443
+ return {
2444
+ ...state,
2445
+ messages: updateLastAssistant(state.messages, (msg) => {
2446
+ const existingIdx = msg.blocks.findIndex(
2447
+ (b) => b.type === "tool" && b.toolCall.id === action.id
2448
+ );
2449
+ if (existingIdx >= 0) {
2450
+ return {
2451
+ ...msg,
2452
+ blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
2453
+ ...tc,
2454
+ status: "running",
2455
+ params: action.params
2456
+ }))
2457
+ };
2458
+ }
2459
+ return {
2460
+ ...msg,
2461
+ blocks: [
2462
+ ...msg.blocks,
2463
+ {
2464
+ type: "tool",
2465
+ toolCall: {
2466
+ id: action.id,
2467
+ name: action.name,
2468
+ params: action.params,
2469
+ status: "running"
2470
+ }
2471
+ }
2472
+ ]
2473
+ };
2474
+ })
2475
+ };
2476
+ }
2477
+ case "TOOL_STREAMING": {
2478
+ return {
2479
+ ...state,
2480
+ messages: updateLastAssistant(state.messages, (msg) => {
2481
+ const existingIdx = msg.blocks.findIndex(
2482
+ (b) => b.type === "tool" && b.toolCall.id === action.id
2483
+ );
2484
+ if (existingIdx >= 0) {
2485
+ return {
2486
+ ...msg,
2487
+ blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
2488
+ ...tc,
2489
+ streamingContent: action.streamingContent
2490
+ }))
2491
+ };
2492
+ }
2493
+ return {
2494
+ ...msg,
2495
+ blocks: [
2496
+ ...msg.blocks,
2497
+ {
2498
+ type: "tool",
2499
+ toolCall: {
2500
+ id: action.id,
2501
+ name: action.name,
2502
+ params: {},
2503
+ status: "running",
2504
+ streamingContent: action.streamingContent
2505
+ }
2506
+ }
2507
+ ]
2508
+ };
2509
+ })
2510
+ };
2511
+ }
2512
+ case "TOOL_RESULT": {
2513
+ return {
2514
+ ...state,
2515
+ messages: updateLastAssistant(state.messages, (msg) => ({
2516
+ ...msg,
2517
+ blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
2518
+ ...tc,
2519
+ status: "done",
2520
+ resultSummary: action.resultSummary,
2521
+ resultContent: action.resultContent
2522
+ }))
2523
+ }))
2524
+ };
2525
+ }
2526
+ case "TOOL_DENIED": {
2527
+ return {
2528
+ ...state,
2529
+ messages: updateLastAssistant(state.messages, (msg) => ({
2530
+ ...msg,
2531
+ blocks: updateToolInBlocks(msg.blocks, action.id, (tc) => ({
2532
+ ...tc,
2533
+ status: "denied",
2534
+ denyFeedback: action.feedback
2535
+ }))
2536
+ }))
2537
+ };
2538
+ }
2539
+ case "TOOL_CONFIRMING": {
2540
+ return {
2541
+ ...state,
2542
+ messages: updateLastAssistant(state.messages, (msg) => ({
2543
+ ...msg,
2544
+ blocks: [
2545
+ ...msg.blocks,
2546
+ {
2547
+ type: "tool",
2548
+ toolCall: {
2549
+ id: action.id,
2550
+ name: action.name,
2551
+ params: action.params,
2552
+ status: "confirming"
2553
+ }
2554
+ }
2555
+ ],
2556
+ confirmPending: {
2557
+ toolName: action.name,
2558
+ params: action.params,
2559
+ resolve: action.resolve
2560
+ }
2561
+ }))
2562
+ };
2563
+ }
2564
+ case "CONFIRM_RESPONDED": {
2565
+ return {
2566
+ ...state,
2567
+ messages: updateLastAssistant(state.messages, (msg) => ({
2568
+ ...msg,
2569
+ confirmPending: void 0
2570
+ }))
2571
+ };
2572
+ }
2573
+ case "FINISH_STREAMING":
2574
+ return {
2575
+ ...state,
2576
+ messages: updateLastAssistant(state.messages, (msg) => ({
2577
+ ...msg,
2578
+ isStreaming: false
2579
+ }))
2580
+ };
2581
+ case "SET_RUNNING":
2582
+ return { ...state, isRunning: action.running };
2583
+ case "SET_ERROR":
2584
+ return { ...state, error: action.error, isRunning: false };
2585
+ case "CLEAR_ERROR":
2586
+ return { ...state, error: void 0 };
2587
+ case "CODER_START":
2588
+ return { ...state, coderWorking: true };
2589
+ case "CODER_END":
2590
+ return { ...state, coderWorking: false };
2591
+ case "CLEAR_MESSAGES":
2592
+ return { ...state, messages: [] };
2593
+ case "SET_MODE":
2594
+ return { ...state, agentMode: action.agentMode, collaboration: action.collaboration };
2595
+ case "SET_TODO_PLAN":
2596
+ return { ...state, todoPlan: action.plan };
2597
+ case "SET_SUB_AGENT_PROGRESS":
2598
+ return { ...state, subAgentProgress: action.progress };
2599
+ default:
2600
+ return state;
2601
+ }
2602
+ }
2603
+ var messageCounter;
2604
+ var init_state = __esm({
2605
+ "src/cli/tui/state.ts"() {
2606
+ "use strict";
2607
+ messageCounter = 0;
2608
+ }
2609
+ });
2610
+
2611
+ // src/cli/tui/components/ToolCallLine.tsx
2612
+ import { Box, Text } from "ink";
2613
+ import { jsx, jsxs } from "react/jsx-runtime";
2614
+ function getToolParamSummary(name, params) {
2615
+ switch (name) {
2616
+ case "bash":
2617
+ return String(params["command"] || "").slice(0, 60);
2618
+ case "read-file":
2619
+ case "write-file":
2620
+ case "edit-file":
2621
+ return String(params["path"] || "");
2622
+ case "glob":
2623
+ return String(params["pattern"] || "");
2624
+ case "grep":
2625
+ return String(params["pattern"] || "");
2626
+ case "send-to-coder":
2627
+ return String(params["task"] || "").slice(0, 40);
2628
+ case "spawn-agents": {
2629
+ const tasks = params["tasks"];
2630
+ if (!tasks) return "";
2631
+ if (tasks.length <= 2) {
2632
+ return tasks.map((t) => t.description.slice(0, 30)).join(", ");
2633
+ }
2634
+ return `${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`;
2635
+ }
2636
+ case "todo": {
2637
+ const action = String(params["action"] || "");
2638
+ const id = params["id"] ? ` [${params["id"]}]` : "";
2639
+ return `${action}${id}`;
2640
+ }
2641
+ case "memo": {
2642
+ const action = String(params["action"] || "");
2643
+ const key = params["key"] ? ` [${params["key"]}]` : "";
2644
+ return `${action}${key}`;
2645
+ }
2646
+ default: {
2647
+ const keys = Object.keys(params);
2648
+ if (keys.length > 0 && keys[0]) {
2649
+ return String(params[keys[0]] || "").slice(0, 40);
2650
+ }
2651
+ return "";
2652
+ }
2653
+ }
2654
+ }
2655
+ function getToolIcon(name) {
2656
+ switch (name) {
2657
+ case "bash":
2658
+ return "$";
2659
+ case "write-file":
2660
+ return "+";
2661
+ case "edit-file":
2662
+ return "\xB1";
2663
+ case "read-file":
2664
+ return "\u{1F4C4}";
2665
+ case "glob":
2666
+ return "\u{1F50D}";
2667
+ case "grep":
2668
+ return "\u{1F50D}";
2669
+ case "spawn-agents":
2670
+ return "\u26A1";
2671
+ case "todo":
2672
+ return "\u{1F4CB}";
2673
+ case "memo":
2674
+ return "\u{1F4DD}";
2675
+ default:
2676
+ return "\u2699";
2677
+ }
2678
+ }
2679
+ function getCodeContent(name, params) {
2680
+ if (name === "write-file") {
2681
+ return params["content"] || null;
2682
+ }
2683
+ if (name === "edit-file") {
2684
+ return params["new_string"] || null;
2685
+ }
2686
+ return null;
2687
+ }
2688
+ function truncateCode(code, maxLines) {
2689
+ const lines = code.split("\n");
2690
+ if (lines.length <= maxLines) return code;
2691
+ return lines.slice(0, maxLines).join("\n") + `
2692
+ ... (\u5171 ${lines.length} \u884C)`;
2693
+ }
2694
+ function ToolCallLine({ toolCall }) {
2695
+ const { name, params, status, resultSummary, resultContent, denyFeedback } = toolCall;
2696
+ const summary = getToolParamSummary(name, params);
2697
+ const icon = getToolIcon(name);
2698
+ const isWriteTool = name === "write-file" || name === "edit-file";
2699
+ const rawCode = isWriteTool && status === "done" ? getCodeContent(name, params) : null;
2700
+ let statusNode;
2701
+ let statusText = "";
2702
+ switch (status) {
2703
+ case "running":
2704
+ statusNode = /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u23F3" });
2705
+ break;
2706
+ case "done":
2707
+ statusNode = /* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" });
2708
+ statusText = resultSummary || "";
2709
+ break;
2710
+ case "denied":
2711
+ statusNode = /* @__PURE__ */ jsx(Text, { color: "red", children: "\u2717" });
2712
+ statusText = "denied";
2713
+ break;
2714
+ case "confirming":
2715
+ statusNode = /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u26A0" });
2716
+ statusText = "[y/N]";
2717
+ break;
2718
+ }
2719
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
2720
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
2721
+ statusNode,
2722
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", bold: true, children: [
2723
+ icon,
2724
+ " ",
2725
+ name
2726
+ ] }),
2727
+ summary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: summary }) : null,
2728
+ statusText ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: statusText }) : null
2729
+ ] }),
2730
+ status === "done" && rawCode && /* @__PURE__ */ jsx(Box, { marginLeft: 3, marginTop: 0, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateCode(rawCode, 5) }) }),
2731
+ status === "done" && !isWriteTool && resultContent && /* @__PURE__ */ jsx(
2732
+ Box,
2733
+ {
2734
+ marginLeft: 3,
2735
+ marginTop: 0,
2736
+ borderStyle: "single",
2737
+ borderColor: "gray",
2738
+ paddingX: 1,
2739
+ children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: resultContent })
2740
+ }
2741
+ ),
2742
+ status === "denied" && denyFeedback && /* @__PURE__ */ jsxs(Box, { marginLeft: 3, gap: 1, children: [
2743
+ /* @__PURE__ */ jsx(Text, { color: "red", children: "\u53CD\u9988:" }),
2744
+ /* @__PURE__ */ jsx(Text, { children: denyFeedback })
2745
+ ] })
2746
+ ] });
2747
+ }
2748
+ var init_ToolCallLine = __esm({
2749
+ "src/cli/tui/components/ToolCallLine.tsx"() {
2750
+ "use strict";
2751
+ }
2752
+ });
2753
+
2754
+ // src/cli/tui/components/MessageBubble.tsx
2755
+ import React from "react";
2756
+ import { Box as Box2, Text as Text2 } from "ink";
2757
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2758
+ var MessageBubble;
2759
+ var init_MessageBubble = __esm({
2760
+ "src/cli/tui/components/MessageBubble.tsx"() {
2761
+ "use strict";
2762
+ init_ToolCallLine();
2763
+ MessageBubble = React.memo(function MessageBubble2({ message }) {
2764
+ const { role, blocks, isStreaming } = message;
2765
+ const color = role === "user" ? "green" : role === "assistant" ? "cyan" : "gray";
2766
+ const icon = role === "user" ? ">" : role === "assistant" ? "\u25C6" : "\u2022";
2767
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2768
+ blocks.map((block, i) => {
2769
+ if (block.type === "text") {
2770
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
2771
+ i === 0 ? /* @__PURE__ */ jsxs2(Text2, { color, bold: true, children: [
2772
+ icon,
2773
+ " "
2774
+ ] }) : /* @__PURE__ */ jsx2(Text2, { children: " " }),
2775
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx2(Text2, { children: block.text }) })
2776
+ ] }, `text-${i}`);
2777
+ } else {
2778
+ return /* @__PURE__ */ jsx2(ToolCallLine, { toolCall: block.toolCall }, block.toolCall.id);
2779
+ }
2780
+ }),
2781
+ blocks.length === 0 && /* @__PURE__ */ jsxs2(Box2, { children: [
2782
+ /* @__PURE__ */ jsxs2(Text2, { color, bold: true, children: [
2783
+ icon,
2784
+ " "
2785
+ ] }),
2786
+ isStreaming && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "..." })
2787
+ ] })
2788
+ ] });
2789
+ });
2790
+ }
2791
+ });
2792
+
2793
+ // src/cli/tui/components/ChatArea.tsx
2794
+ import React2, { useRef } from "react";
2795
+ import { Static, Box as Box3, Text as Text3 } from "ink";
2796
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2797
+ function splitMessages(messages) {
2798
+ let staticEnd = 0;
2799
+ for (let i = 0; i < messages.length; i++) {
2800
+ if (messages[i].isStreaming || messages[i].confirmPending) break;
2801
+ staticEnd = i + 1;
2802
+ }
2803
+ const dynamicMsgs = messages.slice(staticEnd);
2804
+ const streamingMsg = dynamicMsgs.find((m) => m.isStreaming) || null;
2805
+ const otherDynamic = dynamicMsgs.filter((m) => m !== streamingMsg);
2806
+ return {
2807
+ staticMsgs: messages.slice(0, staticEnd),
2808
+ streamingMsg,
2809
+ otherDynamic
2810
+ };
2811
+ }
2812
+ function extractFromBlocks(msg, staticItems, dynamicNodes, isStreaming) {
2813
+ let lineIdx = 0;
2814
+ for (let bi = 0; bi < msg.blocks.length; bi++) {
2815
+ const block = msg.blocks[bi];
2816
+ const isLastBlock = bi === msg.blocks.length - 1;
2817
+ if (block.type === "text") {
2818
+ const lines = block.text.split("\n");
2819
+ if (isStreaming && isLastBlock) {
2820
+ const partial = lines.pop() || "";
2821
+ for (const line of lines) {
2822
+ staticItems.push({
2823
+ id: `${msg.id}-L${lineIdx}`,
2824
+ type: "line",
2825
+ text: line,
2826
+ isFirstLine: lineIdx === 0
2827
+ });
2828
+ lineIdx++;
2829
+ }
2830
+ if (partial || lineIdx === 0) {
2831
+ dynamicNodes.push(
2832
+ /* @__PURE__ */ jsxs3(Box3, { children: [
2833
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: lineIdx === 0 ? "\u25C6 " : " " }),
2834
+ /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text3, { children: partial }) })
2835
+ ] }, "partial")
2836
+ );
2837
+ }
2838
+ } else {
2839
+ for (const line of lines) {
2840
+ staticItems.push({
2841
+ id: `${msg.id}-L${lineIdx}`,
2842
+ type: "line",
2843
+ text: line,
2844
+ isFirstLine: lineIdx === 0
2845
+ });
2846
+ lineIdx++;
2847
+ }
2848
+ }
2849
+ } else if (block.type === "tool") {
2850
+ const tc = block.toolCall;
2851
+ const isWriteTool = tc.name === "write-file" || tc.name === "edit-file";
2852
+ staticItems.push({
2853
+ id: `${msg.id}-TH${tc.id}`,
2854
+ type: "tool-header",
2855
+ toolCall: tc
2856
+ });
2857
+ if (isWriteTool && tc.status === "running" && isStreaming) {
2858
+ const lineCount = parseInt(tc.streamingContent || "0", 10);
2859
+ dynamicNodes.push(
2860
+ /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 4, children: /* @__PURE__ */ jsxs3(Text3, { color: "cyan", children: [
2861
+ "\u270E \u751F\u6210\u4E2D...",
2862
+ lineCount > 0 ? ` ${lineCount} \u884C` : ""
2863
+ ] }) }, `tool-progress-${tc.id}`)
2864
+ );
2865
+ }
2866
+ if (tc.status === "done" || tc.status === "denied") {
2867
+ staticItems.push({
2868
+ id: `${msg.id}-TD${tc.id}`,
2869
+ type: "tool-done",
2870
+ toolCall: tc
2871
+ });
2872
+ }
2873
+ }
2874
+ }
2875
+ }
2876
+ function renderStaticItem(item) {
2877
+ if (item.type === "message") {
2878
+ return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: item.message }) }, item.id);
2879
+ }
2880
+ if (item.type === "line") {
2881
+ return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
2882
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: item.isFirstLine ? "\u25C6 " : " " }),
2883
+ /* @__PURE__ */ jsx3(Text3, { children: item.text })
2884
+ ] }, item.id);
2885
+ }
2886
+ if (item.type === "tool-header") {
2887
+ const tc = item.toolCall;
2888
+ const icon = getToolIcon(tc.name);
2889
+ const summary = getToolParamSummary(tc.name, tc.params);
2890
+ return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
2891
+ /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u23F3" }),
2892
+ /* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
2893
+ icon,
2894
+ " ",
2895
+ tc.name
2896
+ ] }),
2897
+ summary ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: summary }) : null
2898
+ ] }) }, item.id);
2899
+ }
2900
+ if (item.type === "tool-done") {
2901
+ return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, marginLeft: 0, children: /* @__PURE__ */ jsx3(ToolCallLine, { toolCall: item.toolCall }) }, item.id);
2902
+ }
2903
+ return null;
2904
+ }
2905
+ var ChatArea;
2906
+ var init_ChatArea = __esm({
2907
+ "src/cli/tui/components/ChatArea.tsx"() {
2908
+ "use strict";
2909
+ init_MessageBubble();
2910
+ init_ToolCallLine();
2911
+ ChatArea = React2.memo(function ChatArea2({ messages }) {
2912
+ const { staticMsgs, streamingMsg, otherDynamic } = splitMessages(messages);
2913
+ const streamedIds = useRef(/* @__PURE__ */ new Set());
2914
+ if (streamingMsg) {
2915
+ streamedIds.current.add(streamingMsg.id);
2916
+ }
2917
+ const currentItems = [];
2918
+ for (const msg of staticMsgs) {
2919
+ if (streamedIds.current.has(msg.id)) {
2920
+ const ignore = [];
2921
+ extractFromBlocks(msg, currentItems, ignore, false);
2922
+ } else {
2923
+ currentItems.push({ id: msg.id, type: "message", message: msg });
2924
+ }
2925
+ }
2926
+ const dynamicNodes = [];
2927
+ if (streamingMsg) {
2928
+ extractFromBlocks(streamingMsg, currentItems, dynamicNodes, true);
2929
+ }
2930
+ const seenIds = useRef(/* @__PURE__ */ new Set());
2931
+ const accumulated = useRef([]);
2932
+ let hasNew = false;
2933
+ for (const item of currentItems) {
2934
+ if (!seenIds.current.has(item.id)) {
2935
+ seenIds.current.add(item.id);
2936
+ accumulated.current.push(item);
2937
+ hasNew = true;
2938
+ }
2939
+ }
2940
+ if (hasNew) {
2941
+ accumulated.current = [...accumulated.current];
2942
+ }
2943
+ const staticItems = accumulated.current;
2944
+ const showPlaceholder = streamingMsg && streamingMsg.blocks.length === 0;
2945
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
2946
+ staticItems.length > 0 && /* @__PURE__ */ jsx3(Static, { items: staticItems, children: renderStaticItem }),
2947
+ (dynamicNodes.length > 0 || showPlaceholder) && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
2948
+ showPlaceholder && /* @__PURE__ */ jsxs3(Box3, { children: [
2949
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "\u25C6 " }),
2950
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "..." })
2951
+ ] }),
2952
+ dynamicNodes
2953
+ ] }),
2954
+ otherDynamic.map((msg) => /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(MessageBubble, { message: msg }) }, msg.id))
2955
+ ] });
2956
+ });
2957
+ }
2958
+ });
2959
+
2960
+ // src/cli/tui/components/InputArea.tsx
2961
+ import { useState } from "react";
2962
+ import { Box as Box4, Text as Text4, useInput } from "ink";
2963
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2964
+ function CleanTextInput({ onSubmit, placeholder }) {
2965
+ const [value, setValue] = useState("");
2966
+ const [cursor, setCursor] = useState(0);
2967
+ useInput((input, key) => {
2968
+ if (key.return) {
2969
+ const trimmed = value.trim();
2970
+ if (trimmed) {
2971
+ onSubmit(trimmed);
2972
+ }
2973
+ setValue("");
2974
+ setCursor(0);
2975
+ return;
2976
+ }
2977
+ if (key.backspace || key.delete) {
2978
+ if (cursor > 0) {
2979
+ setValue((prev) => prev.slice(0, cursor - 1) + prev.slice(cursor));
2980
+ setCursor((prev) => prev - 1);
2981
+ }
2982
+ return;
2983
+ }
2984
+ if (key.leftArrow) {
2985
+ setCursor((prev) => Math.max(0, prev - 1));
2986
+ return;
2987
+ }
2988
+ if (key.rightArrow) {
2989
+ setCursor((prev) => Math.min(value.length, prev + 1));
2990
+ return;
2991
+ }
2992
+ if (input === "a" && key.ctrl) {
2993
+ setCursor(0);
2994
+ return;
2995
+ }
2996
+ if (input === "e" && key.ctrl) {
2997
+ setCursor(value.length);
2998
+ return;
2999
+ }
3000
+ if (input === "u" && key.ctrl) {
3001
+ setValue((prev) => prev.slice(cursor));
3002
+ setCursor(0);
3003
+ return;
3004
+ }
3005
+ if (key.ctrl || key.meta || key.escape) return;
3006
+ if (!input || input.length === 0) return;
3007
+ if (input.includes("\x1B") || input.includes("\0")) return;
3008
+ if (/^(?:\[<\d+;\d+;\d+[Mm])+$/.test(input)) return;
3009
+ if (/^\[<[0-9;Mm]*$/.test(input)) return;
3010
+ if (input === "[5~" || input === "[6~") return;
3011
+ for (let i = 0; i < input.length; i++) {
3012
+ const code = input.charCodeAt(i);
3013
+ if (code < 32 && code !== 9) return;
3014
+ if (code === 127 || code >= 128 && code <= 159) return;
3015
+ }
3016
+ setValue((prev) => prev.slice(0, cursor) + input + prev.slice(cursor));
3017
+ setCursor((prev) => prev + input.length);
3018
+ });
3019
+ if (value.length === 0) {
3020
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
3021
+ /* @__PURE__ */ jsx4(Text4, { inverse: true, children: " " }),
3022
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: placeholder || "" })
3023
+ ] });
3024
+ }
3025
+ const before = value.slice(0, cursor);
3026
+ const at = cursor < value.length ? value[cursor] : " ";
3027
+ const after = cursor < value.length ? value.slice(cursor + 1) : "";
3028
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
3029
+ /* @__PURE__ */ jsx4(Text4, { children: before }),
3030
+ /* @__PURE__ */ jsx4(Text4, { inverse: true, children: at }),
3031
+ /* @__PURE__ */ jsx4(Text4, { children: after })
3032
+ ] });
3033
+ }
3034
+ function InputArea({ onSubmit, isRunning }) {
3035
+ return /* @__PURE__ */ jsxs4(Box4, { paddingX: 1, children: [
3036
+ /* @__PURE__ */ jsx4(Text4, { color: isRunning ? "gray" : "green", bold: true, children: "> " }),
3037
+ isRunning ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "\u7B49\u5F85\u56DE\u590D\u4E2D..." }) : /* @__PURE__ */ jsx4(
3038
+ CleanTextInput,
3039
+ {
3040
+ onSubmit,
3041
+ placeholder: "\u8F93\u5165\u6D88\u606F\u6216 /\u547D\u4EE4..."
3042
+ }
3043
+ )
3044
+ ] });
3045
+ }
3046
+ var init_InputArea = __esm({
3047
+ "src/cli/tui/components/InputArea.tsx"() {
3048
+ "use strict";
3049
+ }
3050
+ });
3051
+
3052
+ // src/cli/tui/components/StatusBar.tsx
3053
+ import { Box as Box5, Text as Text5 } from "ink";
3054
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3055
+ function StatusBar({ agentMode, collaboration, coderWorking, isRunning, modelName, todoPlan, subAgentProgress }) {
3056
+ const modeLabel = agentMode === "dual" ? `dual(${collaboration})` : "single";
3057
+ const todoProgress = todoPlan ? `${todoPlan.items.filter((i) => i.status === "completed").length}/${todoPlan.items.length}` : null;
3058
+ return /* @__PURE__ */ jsxs5(Box5, { paddingX: 1, gap: 1, children: [
3059
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2500\u2500" }),
3060
+ /* @__PURE__ */ jsx5(Text5, { bold: true, children: modeLabel }),
3061
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u25B6" }),
3062
+ /* @__PURE__ */ jsx5(Text5, { bold: true, children: modelName }),
3063
+ coderWorking && /* @__PURE__ */ jsxs5(Fragment2, { children: [
3064
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
3065
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2699 coder" })
3066
+ ] }),
3067
+ isRunning && !coderWorking && !subAgentProgress && /* @__PURE__ */ jsxs5(Fragment2, { children: [
3068
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
3069
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "thinking..." })
3070
+ ] }),
3071
+ subAgentProgress && /* @__PURE__ */ jsxs5(Fragment2, { children: [
3072
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
3073
+ /* @__PURE__ */ jsxs5(Text5, { color: "magenta", children: [
3074
+ "\u26A1 ",
3075
+ subAgentProgress.completed + subAgentProgress.failed,
3076
+ "/",
3077
+ subAgentProgress.total,
3078
+ " agents"
3079
+ ] })
3080
+ ] }),
3081
+ todoProgress && /* @__PURE__ */ jsxs5(Fragment2, { children: [
3082
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
3083
+ /* @__PURE__ */ jsxs5(Text5, { color: "cyan", children: [
3084
+ "plan ",
3085
+ todoProgress
3086
+ ] })
3087
+ ] }),
3088
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2502" }),
3089
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "/help" })
3090
+ ] });
3091
+ }
3092
+ var init_StatusBar = __esm({
3093
+ "src/cli/tui/components/StatusBar.tsx"() {
3094
+ "use strict";
3095
+ }
3096
+ });
3097
+
3098
+ // src/cli/tui/components/ConfirmPrompt.tsx
3099
+ import { useState as useState2 } from "react";
3100
+ import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
3101
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3102
+ function getToolDetails(toolName, params) {
3103
+ const lines = [];
3104
+ let label = toolName;
3105
+ switch (toolName) {
3106
+ case "bash":
3107
+ label = "Bash";
3108
+ lines.push(`\u547D\u4EE4: ${String(params["command"] || "")}`);
3109
+ break;
3110
+ case "write-file":
3111
+ label = "Write";
3112
+ lines.push(`\u6587\u4EF6: ${String(params["path"] || "")}`);
3113
+ if (params["content"]) {
3114
+ const content = String(params["content"]);
3115
+ const preview = content.length > 120 ? content.slice(0, 120) + "..." : content;
3116
+ lines.push(`\u5185\u5BB9: ${preview.split("\n").slice(0, 3).join("\n ")}`);
3117
+ }
3118
+ break;
3119
+ case "edit-file":
3120
+ label = "Edit";
3121
+ lines.push(`\u6587\u4EF6: ${String(params["path"] || "")}`);
3122
+ if (params["old_string"]) {
3123
+ const old = String(params["old_string"]);
3124
+ const preview = old.length > 80 ? old.slice(0, 80) + "..." : old;
3125
+ lines.push(`\u66FF\u6362: ${preview}`);
3126
+ }
3127
+ if (params["new_string"]) {
3128
+ const neu = String(params["new_string"]);
3129
+ const preview = neu.length > 80 ? neu.slice(0, 80) + "..." : neu;
3130
+ lines.push(`\u4E3A: ${preview}`);
3131
+ }
3132
+ break;
3133
+ case "git":
3134
+ label = "Git";
3135
+ lines.push(`\u547D\u4EE4: git ${String(params["command"] || "")}`);
3136
+ break;
3137
+ default:
3138
+ for (const [key, value] of Object.entries(params)) {
3139
+ const str = typeof value === "string" ? value : JSON.stringify(value);
3140
+ lines.push(`${key}: ${str.slice(0, 80)}`);
3141
+ }
3142
+ break;
3143
+ }
3144
+ return { lines, label };
3145
+ }
3146
+ function FeedbackInput({ onSubmit }) {
3147
+ const [value, setValue] = useState2("");
3148
+ const [cursor, setCursor] = useState2(0);
3149
+ useInput2((input, key) => {
3150
+ if (key.return) {
3151
+ onSubmit(value);
3152
+ setValue("");
3153
+ setCursor(0);
3154
+ return;
3155
+ }
3156
+ if (key.backspace || key.delete) {
3157
+ if (cursor > 0) {
3158
+ setValue((prev) => prev.slice(0, cursor - 1) + prev.slice(cursor));
3159
+ setCursor((prev) => prev - 1);
3160
+ }
3161
+ return;
3162
+ }
3163
+ if (key.leftArrow) {
3164
+ setCursor((prev) => Math.max(0, prev - 1));
3165
+ return;
3166
+ }
3167
+ if (key.rightArrow) {
3168
+ setCursor((prev) => Math.min(value.length, prev + 1));
3169
+ return;
3170
+ }
3171
+ if (key.escape) {
3172
+ onSubmit("");
3173
+ return;
3174
+ }
3175
+ if (key.ctrl || key.meta) return;
3176
+ if (!input || input.length === 0) return;
3177
+ if (input.includes("\x1B") || input.includes("\0")) return;
3178
+ for (let i = 0; i < input.length; i++) {
3179
+ const code = input.charCodeAt(i);
3180
+ if (code < 32) return;
3181
+ if (code === 127 || code >= 128 && code <= 159) return;
3182
+ }
3183
+ setValue((prev) => prev.slice(0, cursor) + input + prev.slice(cursor));
3184
+ setCursor((prev) => prev + input.length);
3185
+ });
3186
+ if (value.length === 0) {
3187
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
3188
+ /* @__PURE__ */ jsx6(Text6, { inverse: true, children: " " }),
3189
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u8F93\u5165\u53CD\u9988\u6307\u4EE4\u7ED9 AI..." })
3190
+ ] });
3191
+ }
3192
+ const before = value.slice(0, cursor);
3193
+ const at = cursor < value.length ? value[cursor] : " ";
3194
+ const after = cursor < value.length ? value.slice(cursor + 1) : "";
3195
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
3196
+ /* @__PURE__ */ jsx6(Text6, { children: before }),
3197
+ /* @__PURE__ */ jsx6(Text6, { inverse: true, children: at }),
3198
+ /* @__PURE__ */ jsx6(Text6, { children: after })
3199
+ ] });
3200
+ }
3201
+ function ConfirmPrompt({ confirm, onRespond }) {
3202
+ const [selected, setSelected] = useState2(0);
3203
+ const [feedbackMode, setFeedbackMode] = useState2(false);
3204
+ useInput2((input, key) => {
3205
+ if (feedbackMode) return;
3206
+ if (key.leftArrow) {
3207
+ setSelected((prev) => (prev - 1 + OPTIONS.length) % OPTIONS.length);
3208
+ } else if (key.rightArrow) {
3209
+ setSelected((prev) => (prev + 1) % OPTIONS.length);
3210
+ } else if (key.return) {
3211
+ onRespond(OPTIONS[selected].key);
3212
+ } else if (input === "y" || input === "Y") {
3213
+ onRespond("allow");
3214
+ } else if (input === "n" || input === "N") {
3215
+ onRespond("deny");
3216
+ } else if (input === "a" || input === "A") {
3217
+ onRespond("always");
3218
+ } else if (key.tab) {
3219
+ setFeedbackMode(true);
3220
+ } else if (key.escape) {
3221
+ onRespond("deny");
3222
+ }
3223
+ }, { isActive: !feedbackMode });
3224
+ const { lines, label } = getToolDetails(confirm.toolName, confirm.params);
3225
+ if (feedbackMode) {
3226
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
3227
+ /* @__PURE__ */ jsxs6(
3228
+ Box6,
3229
+ {
3230
+ flexDirection: "column",
3231
+ borderStyle: "round",
3232
+ borderColor: "yellow",
3233
+ paddingX: 1,
3234
+ children: [
3235
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "yellow", children: label }),
3236
+ lines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: line }, i))
3237
+ ]
3238
+ }
3239
+ ),
3240
+ /* @__PURE__ */ jsxs6(Box6, { paddingX: 1, gap: 1, children: [
3241
+ /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u53CD\u9988: " }),
3242
+ /* @__PURE__ */ jsx6(
3243
+ FeedbackInput,
3244
+ {
3245
+ onSubmit: (text) => {
3246
+ const trimmed = text.trim();
3247
+ if (trimmed) {
3248
+ onRespond({ feedback: trimmed });
3249
+ } else {
3250
+ setFeedbackMode(false);
3251
+ }
3252
+ }
3253
+ }
3254
+ )
3255
+ ] }),
3256
+ /* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter \u53D1\u9001 Esc \u8FD4\u56DE\u9009\u62E9" }) })
3257
+ ] });
3258
+ }
3259
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
3260
+ /* @__PURE__ */ jsxs6(
3261
+ Box6,
3262
+ {
3263
+ flexDirection: "column",
3264
+ borderStyle: "round",
3265
+ borderColor: "yellow",
3266
+ paddingX: 1,
3267
+ children: [
3268
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "yellow", children: label }),
3269
+ lines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: line }, i))
3270
+ ]
3271
+ }
3272
+ ),
3273
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 0, paddingX: 1, gap: 2, children: OPTIONS.map((opt, i) => {
3274
+ const isSelected = i === selected;
3275
+ return /* @__PURE__ */ jsx6(Box6, { children: isSelected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", inverse: true, children: [
3276
+ " ",
3277
+ opt.label,
3278
+ " "
3279
+ ] }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
3280
+ " ",
3281
+ opt.label,
3282
+ " "
3283
+ ] }) }, opt.key);
3284
+ }) }),
3285
+ /* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2190 \u2192 \u9009\u62E9 Enter \u786E\u8BA4 y \u5141\u8BB8 n \u62D2\u7EDD a \u59CB\u7EC8 Tab \u53CD\u9988" }) })
3286
+ ] });
3287
+ }
3288
+ var OPTIONS;
3289
+ var init_ConfirmPrompt = __esm({
3290
+ "src/cli/tui/components/ConfirmPrompt.tsx"() {
3291
+ "use strict";
3292
+ OPTIONS = [
3293
+ { key: "allow", label: "\u5141\u8BB8" },
3294
+ { key: "always", label: "\u59CB\u7EC8\u5141\u8BB8" },
3295
+ { key: "deny", label: "\u62D2\u7EDD" }
3296
+ ];
3297
+ }
3298
+ });
3299
+
3300
+ // src/cli/tui/components/TodoPanel.tsx
3301
+ import { Box as Box7, Text as Text7 } from "ink";
3302
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3303
+ function getStatusIcon(status) {
3304
+ switch (status) {
3305
+ case "completed":
3306
+ return "\u25CF";
3307
+ case "in-progress":
3308
+ return "\u25D0";
3309
+ default:
3310
+ return "\u25CB";
3311
+ }
3312
+ }
3313
+ function getStatusColor(status) {
3314
+ switch (status) {
3315
+ case "completed":
3316
+ return "green";
3317
+ case "in-progress":
3318
+ return "yellow";
3319
+ default:
3320
+ return "gray";
3321
+ }
3322
+ }
3323
+ function TodoPanel({ plan }) {
3324
+ const completed = plan.items.filter((i) => i.status === "completed").length;
3325
+ const total = plan.items.length;
3326
+ return /* @__PURE__ */ jsxs7(
3327
+ Box7,
3328
+ {
3329
+ flexDirection: "column",
3330
+ borderStyle: "round",
3331
+ borderColor: "cyan",
3332
+ paddingX: 1,
3333
+ marginBottom: 0,
3334
+ children: [
3335
+ /* @__PURE__ */ jsxs7(Box7, { justifyContent: "space-between", children: [
3336
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "Plan" }),
3337
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
3338
+ completed,
3339
+ "/",
3340
+ total
3341
+ ] })
3342
+ ] }),
3343
+ plan.items.map((item) => /* @__PURE__ */ jsxs7(Box7, { gap: 1, children: [
3344
+ /* @__PURE__ */ jsx7(Text7, { color: getStatusColor(item.status), children: getStatusIcon(item.status) }),
3345
+ /* @__PURE__ */ jsx7(
3346
+ Text7,
3347
+ {
3348
+ color: item.status === "completed" ? "green" : void 0,
3349
+ dimColor: item.status === "pending",
3350
+ children: item.title
3351
+ }
3352
+ )
3353
+ ] }, item.id))
3354
+ ]
3355
+ }
3356
+ );
3357
+ }
3358
+ var init_TodoPanel = __esm({
3359
+ "src/cli/tui/components/TodoPanel.tsx"() {
3360
+ "use strict";
3361
+ }
3362
+ });
3363
+
3364
+ // src/cli/tui/App.tsx
3365
+ import { useReducer, useCallback, useEffect, useRef as useRef2, useState as useState3 } from "react";
3366
+ import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
3367
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3368
+ function App({ config, client, agent, orchestrator, registry, todoStore, memoStore, subAgentTracker }) {
3369
+ const [state, dispatch] = useReducer(
3370
+ tuiReducer,
3371
+ createInitialState(
3372
+ config.model,
3373
+ config.agent_mode,
3374
+ config.collaboration
3375
+ )
3376
+ );
3377
+ const [resetKey, setResetKey] = useState3(0);
3378
+ const agentRef = useRef2(agent);
3379
+ const orchestratorRef = useRef2(orchestrator);
3380
+ const todoStoreRef = useRef2(todoStore);
3381
+ const subAgentTrackerRef = useRef2(subAgentTracker);
3382
+ agentRef.current = agent;
3383
+ orchestratorRef.current = orchestrator;
3384
+ todoStoreRef.current = todoStore;
3385
+ subAgentTrackerRef.current = subAgentTracker;
3386
+ useEffect(() => {
3387
+ return todoStoreRef.current.subscribe((plan) => {
3388
+ dispatch({ type: "SET_TODO_PLAN", plan });
3389
+ });
3390
+ }, []);
3391
+ useEffect(() => {
3392
+ let timer = null;
3393
+ let latest = null;
3394
+ const unsub = subAgentTrackerRef.current.subscribe((progress) => {
3395
+ latest = progress;
3396
+ if (progress === null) {
3397
+ if (timer) {
3398
+ clearTimeout(timer);
3399
+ timer = null;
3400
+ }
3401
+ dispatch({ type: "SET_SUB_AGENT_PROGRESS", progress: null });
3402
+ return;
3403
+ }
3404
+ if (!timer) {
3405
+ dispatch({ type: "SET_SUB_AGENT_PROGRESS", progress });
3406
+ timer = setTimeout(() => {
3407
+ timer = null;
3408
+ if (latest) dispatch({ type: "SET_SUB_AGENT_PROGRESS", progress: latest });
3409
+ }, 2e3);
3410
+ }
3411
+ });
3412
+ return () => {
3413
+ unsub();
3414
+ if (timer) clearTimeout(timer);
3415
+ };
3416
+ }, []);
3417
+ const confirmPending = state.messages.reduce(
3418
+ (found, msg) => found || msg.confirmPending,
3419
+ void 0
3420
+ );
3421
+ useEffect(() => {
3422
+ setStructuredConfirmHandler((toolName, params) => {
3423
+ return new Promise((resolve7) => {
3424
+ const id = `confirm-${Date.now()}`;
3425
+ registerConfirmToolId(toolName, id);
3426
+ dispatch({
3427
+ type: "TOOL_CONFIRMING",
3428
+ id,
3429
+ name: toolName,
3430
+ params,
3431
+ resolve: (result) => {
3432
+ if (result === "always") {
3433
+ registry.addAutoApprove(toolName);
3434
+ resolve7({ approved: true });
3435
+ } else if (result === "allow") {
3436
+ resolve7({ approved: true });
3437
+ } else if (result === "deny") {
3438
+ resolve7({ approved: false });
3439
+ } else {
3440
+ resolve7({ approved: false, feedback: result.feedback });
3441
+ }
3442
+ }
3443
+ });
3444
+ });
3445
+ });
3446
+ return () => {
3447
+ setStructuredConfirmHandler(null);
3448
+ };
3449
+ }, [registry]);
3450
+ const handleSubmit = useCallback(async (text) => {
3451
+ if (text.startsWith("/")) {
3452
+ handleSlashCommand2(text, {
3453
+ config,
3454
+ orchestrator: orchestratorRef.current,
3455
+ registry,
3456
+ dispatch,
3457
+ setResetKey,
3458
+ client,
3459
+ todoStore,
3460
+ memoStore,
3461
+ subAgentTracker
3462
+ });
3463
+ return;
3464
+ }
3465
+ dispatch({ type: "ADD_USER_MESSAGE", text });
3466
+ dispatch({ type: "START_ASSISTANT" });
3467
+ dispatch({ type: "SET_RUNNING", running: true });
3468
+ const callbacks = createBridgeCallbacks(dispatch);
3469
+ try {
3470
+ if (config.agent_mode === "single") {
3471
+ await agentRef.current.run(text, callbacks);
3472
+ } else {
3473
+ await orchestratorRef.current.run(text, callbacks);
3474
+ }
3475
+ } catch (err) {
3476
+ const msg = err instanceof Error ? err.message : String(err);
3477
+ dispatch({ type: "SET_ERROR", error: msg });
3478
+ }
3479
+ callbacks._stopBatcher?.();
3480
+ dispatch({ type: "FINISH_STREAMING" });
3481
+ dispatch({ type: "SET_RUNNING", running: false });
3482
+ }, [config]);
3483
+ const handleConfirmResponse = useCallback((result) => {
3484
+ if (confirmPending) {
3485
+ confirmPending.resolve(result);
3486
+ dispatch({ type: "CONFIRM_RESPONDED", id: "" });
3487
+ const approved = result === "allow" || result === "always";
3488
+ if (approved) {
3489
+ for (const msg of state.messages) {
3490
+ for (const block of msg.blocks) {
3491
+ if (block.type === "tool" && block.toolCall.status === "confirming") {
3492
+ dispatch({ type: "TOOL_EXECUTING", id: block.toolCall.id, name: block.toolCall.name, params: block.toolCall.params });
3493
+ }
3494
+ }
3495
+ }
3496
+ }
3497
+ }
3498
+ }, [confirmPending, state.messages]);
3499
+ useInput3((input, key) => {
3500
+ if (input === "c" && key.ctrl) {
3501
+ if (state.isRunning) {
3502
+ dispatch({ type: "SET_RUNNING", running: false });
3503
+ dispatch({ type: "FINISH_STREAMING" });
3504
+ } else {
3505
+ process.exit(0);
3506
+ }
3507
+ return;
3508
+ }
3509
+ if (input === "d" && key.ctrl) {
3510
+ process.exit(0);
3511
+ }
3512
+ });
3513
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3514
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ jsx8(ChatArea, { messages: state.messages }) }),
3515
+ state.error && /* @__PURE__ */ jsxs8(
3516
+ Box8,
3517
+ {
3518
+ borderStyle: "round",
3519
+ borderColor: "red",
3520
+ paddingX: 1,
3521
+ marginBottom: 1,
3522
+ children: [
3523
+ /* @__PURE__ */ jsx8(Text8, { color: "red", bold: true, children: "\u2716 Error: " }),
3524
+ /* @__PURE__ */ jsx8(Text8, { color: "red", children: state.error })
3525
+ ]
3526
+ }
3527
+ ),
3528
+ confirmPending && /* @__PURE__ */ jsx8(
3529
+ ConfirmPrompt,
3530
+ {
3531
+ confirm: confirmPending,
3532
+ onRespond: handleConfirmResponse
3533
+ }
3534
+ ),
3535
+ state.todoPlan && /* @__PURE__ */ jsx8(TodoPanel, { plan: state.todoPlan }),
3536
+ /* @__PURE__ */ jsx8(
3537
+ InputArea,
3538
+ {
3539
+ onSubmit: handleSubmit,
3540
+ isRunning: state.isRunning || !!confirmPending
3541
+ }
3542
+ ),
3543
+ /* @__PURE__ */ jsx8(
3544
+ StatusBar,
3545
+ {
3546
+ agentMode: state.agentMode,
3547
+ collaboration: state.collaboration,
3548
+ coderWorking: state.coderWorking,
3549
+ isRunning: state.isRunning,
3550
+ modelName: state.modelName,
3551
+ todoPlan: state.todoPlan,
3552
+ subAgentProgress: state.subAgentProgress
3553
+ }
3554
+ )
3555
+ ] }, resetKey);
3556
+ }
3557
+ function handleSlashCommand2(input, ctx) {
3558
+ const { config, orchestrator, registry, dispatch, setResetKey, client, todoStore, memoStore, subAgentTracker } = ctx;
3559
+ const parts = input.trim().split(/\s+/);
3560
+ const command = parts[0];
3561
+ switch (command) {
3562
+ case "/help":
3563
+ dispatch({ type: "ADD_USER_MESSAGE", text: input });
3564
+ dispatch({ type: "START_ASSISTANT" });
3565
+ dispatch({
3566
+ type: "APPEND_CONTENT",
3567
+ text: `\u53EF\u7528\u547D\u4EE4:
3568
+ /help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
3569
+ /mode [\u6A21\u5F0F] \u5207\u6362\u534F\u4F5C\u6A21\u5F0F (delegated/autonomous/controlled)
3570
+ /single \u5207\u6362\u5230\u5355 Agent \u6A21\u5F0F
3571
+ /dual \u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F
3572
+ /parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
3573
+ /todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
3574
+ /clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
3575
+ /info \u663E\u793A\u5F53\u524D\u914D\u7F6E
3576
+ Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42 / \u9000\u51FA
3577
+ Ctrl+D \u9000\u51FA`
3578
+ });
3579
+ dispatch({ type: "FINISH_STREAMING" });
3580
+ break;
3581
+ case "/mode": {
3582
+ const mode = parts[1];
3583
+ if (!mode) {
3584
+ dispatch({ type: "ADD_USER_MESSAGE", text: input });
3585
+ dispatch({ type: "START_ASSISTANT" });
3586
+ dispatch({
3587
+ type: "APPEND_CONTENT",
3588
+ text: `\u5F53\u524D\u534F\u4F5C\u6A21\u5F0F: ${config.collaboration}
3589
+ \u53EF\u9009: delegated, autonomous, controlled`
3590
+ });
3591
+ dispatch({ type: "FINISH_STREAMING" });
3592
+ } else if (["delegated", "autonomous", "controlled"].includes(mode)) {
3593
+ config.collaboration = mode;
3594
+ orchestrator?.setMode(mode);
3595
+ dispatch({ type: "SET_MODE", agentMode: config.agent_mode, collaboration: mode });
3596
+ }
3597
+ break;
3598
+ }
3599
+ case "/single":
3600
+ config.agent_mode = "single";
3601
+ dispatch({ type: "SET_MODE", agentMode: "single", collaboration: config.collaboration });
3602
+ break;
3603
+ case "/dual":
3604
+ config.agent_mode = "dual";
3605
+ dispatch({ type: "SET_MODE", agentMode: "dual", collaboration: config.collaboration });
3606
+ break;
3607
+ case "/clear":
3608
+ dispatch({ type: "CLEAR_MESSAGES" });
3609
+ setResetKey((prev) => prev + 1);
3610
+ break;
3611
+ case "/parallel": {
3612
+ const current = config.features.parallel_agents;
3613
+ const next = current === "on" ? "off" : "on";
3614
+ config.features.parallel_agents = next;
3615
+ if (next === "off") {
3616
+ registry.unregister("spawn-agents");
3617
+ } else if (!registry.has("spawn-agents")) {
3618
+ registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker, memoStore));
3619
+ }
3620
+ dispatch({ type: "ADD_USER_MESSAGE", text: input });
3621
+ dispatch({ type: "START_ASSISTANT" });
3622
+ dispatch({ type: "APPEND_CONTENT", text: `\u5E76\u884C\u5B50 Agent \u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}` });
3623
+ dispatch({ type: "FINISH_STREAMING" });
3624
+ break;
3625
+ }
3626
+ case "/todo": {
3627
+ const current = config.features.todo;
3628
+ const next = current === "on" ? "off" : "on";
3629
+ config.features.todo = next;
3630
+ if (next === "off") {
3631
+ registry.unregister("todo");
3632
+ } else if (!registry.has("todo")) {
3633
+ registry.register(createTodoTool(todoStore));
3634
+ }
3635
+ dispatch({ type: "ADD_USER_MESSAGE", text: input });
3636
+ dispatch({ type: "START_ASSISTANT" });
3637
+ dispatch({ type: "APPEND_CONTENT", text: `Todo \u8BA1\u5212\u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}` });
3638
+ dispatch({ type: "FINISH_STREAMING" });
3639
+ break;
3640
+ }
3641
+ case "/info":
3642
+ dispatch({ type: "ADD_USER_MESSAGE", text: input });
3643
+ dispatch({ type: "START_ASSISTANT" });
3644
+ dispatch({
3645
+ type: "APPEND_CONTENT",
3646
+ text: `\u6A21\u578B: ${config.model}
3647
+ Agent \u6A21\u5F0F: ${config.agent_mode}
3648
+ \u534F\u4F5C\u6A21\u5F0F: ${config.collaboration}
3649
+ \u57FA\u7840 URL: ${config.base_url}`
3650
+ });
3651
+ dispatch({ type: "FINISH_STREAMING" });
3652
+ break;
3653
+ default:
3654
+ dispatch({ type: "ADD_USER_MESSAGE", text: input });
3655
+ dispatch({ type: "START_ASSISTANT" });
3656
+ dispatch({
3657
+ type: "APPEND_CONTENT",
3658
+ text: `\u672A\u77E5\u547D\u4EE4: ${command}\u3002\u8F93\u5165 /help \u67E5\u770B\u5E2E\u52A9\u3002`
3659
+ });
3660
+ dispatch({ type: "FINISH_STREAMING" });
3661
+ break;
3662
+ }
3663
+ }
3664
+ var init_App = __esm({
3665
+ "src/cli/tui/App.tsx"() {
3666
+ "use strict";
3667
+ init_permission();
3668
+ init_state();
3669
+ init_bridge();
3670
+ init_spawn_agents();
3671
+ init_todo();
3672
+ init_ChatArea();
3673
+ init_InputArea();
3674
+ init_StatusBar();
3675
+ init_ConfirmPrompt();
3676
+ init_TodoPanel();
3677
+ }
3678
+ });
3679
+
3680
+ // src/cli/tui/index.tsx
3681
+ var tui_exports = {};
3682
+ __export(tui_exports, {
3683
+ startTui: () => startTui
3684
+ });
3685
+ import { render } from "ink";
3686
+ import { jsx as jsx9 } from "react/jsx-runtime";
3687
+ async function startTui(options) {
3688
+ const { config } = options;
3689
+ const { systemPrompt } = await buildPrompt(config);
3690
+ const registry = new ToolRegistry();
3691
+ registerCoreTools(registry);
3692
+ registry.setPermissions(config.permissions);
3693
+ const client = createLLMClient({
3694
+ apiKey: config.api_key,
3695
+ baseURL: config.base_url,
3696
+ model: config.model,
3697
+ temperature: config.temperature,
3698
+ maxTokens: config.max_tokens
3699
+ });
3700
+ const todoStore = new TodoStore();
3701
+ const memoStore = new MemoStore();
3702
+ const subAgentTracker = new SubAgentTracker();
3703
+ if (config.features.parallel_agents === "on") {
3704
+ registry.register(createSpawnAgentsTool(client, registry, config, subAgentTracker, memoStore));
3705
+ }
3706
+ if (config.features.todo === "on") {
3707
+ registry.register(createTodoTool(todoStore));
3708
+ }
3709
+ registry.register(createMemoTool(memoStore));
3710
+ const agent = new Agent(client, registry, config, systemPrompt);
3711
+ const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
3712
+ const { waitUntilExit } = render(
3713
+ /* @__PURE__ */ jsx9(
3714
+ App,
3715
+ {
3716
+ config,
3717
+ client,
3718
+ agent,
3719
+ orchestrator,
3720
+ registry,
3721
+ todoStore,
3722
+ memoStore,
3723
+ subAgentTracker
3724
+ }
3725
+ ),
3726
+ { patchConsole: true }
3727
+ );
3728
+ await waitUntilExit();
3729
+ }
3730
+ var init_tui = __esm({
3731
+ "src/cli/tui/index.tsx"() {
3732
+ "use strict";
3733
+ init_client();
3734
+ init_registry();
3735
+ init_register();
3736
+ init_agent();
3737
+ init_orchestrator();
3738
+ init_builder();
3739
+ init_todo_store();
3740
+ init_memo_store();
3741
+ init_sub_agent_tracker();
3742
+ init_spawn_agents();
3743
+ init_todo();
3744
+ init_memo();
3745
+ init_App();
3746
+ }
3747
+ });
3748
+
3749
+ // src/cli/index.ts
3750
+ init_loader();
3751
+ init_client();
3752
+ init_registry();
3753
+ init_register();
3754
+ init_agent();
3755
+ init_orchestrator();
3756
+ init_builder();
3757
+ import { Command } from "commander";
3758
+
3759
+ // src/cli/repl.ts
3760
+ init_client();
3761
+ init_registry();
3762
+ init_agent();
3763
+ init_orchestrator();
3764
+ init_builder();
3765
+ init_register();
3766
+ init_permission();
3767
+ init_todo_store();
3768
+ init_memo_store();
3769
+ init_spawn_agents();
3770
+ init_todo();
3771
+ init_memo();
3772
+ init_bridge();
3773
+ import * as readline from "readline";
3774
+
3775
+ // src/cli/ui.ts
3776
+ import chalk2 from "chalk";
3777
+ import ora from "ora";
3778
+ import { marked } from "marked";
3779
+ import * as _markedTerminal from "marked-terminal";
3780
+ import { createTwoFilesPatch } from "diff";
3781
+ var markedTerminal2 = _markedTerminal.markedTerminal;
3782
+ marked.use(markedTerminal2());
3783
+ function printStream(text) {
3784
+ process.stdout.write(text);
3785
+ }
3786
+ function printToolCall(toolName, params) {
3787
+ let detail = "";
3788
+ if (toolName === "bash" && params["command"]) {
3789
+ detail = ` ${chalk2.dim(String(params["command"]).slice(0, 80))}`;
3790
+ } else if ((toolName === "read-file" || toolName === "write-file" || toolName === "edit-file") && params["path"]) {
3791
+ detail = ` ${chalk2.dim(String(params["path"]))}`;
3792
+ } else if (toolName === "glob" && params["pattern"]) {
3793
+ detail = ` ${chalk2.dim(String(params["pattern"]))}`;
3794
+ } else if (toolName === "grep" && params["pattern"]) {
3795
+ detail = ` ${chalk2.dim(String(params["pattern"]))}`;
3796
+ } else if (toolName === "spawn-agents" && params["tasks"]) {
3797
+ const tasks = params["tasks"];
3798
+ detail = ` ${chalk2.dim(`${tasks.length} \u4E2A\u5E76\u884C\u4EFB\u52A1`)}`;
3799
+ } else if (toolName === "todo" && params["action"]) {
3800
+ const action = String(params["action"]);
3801
+ const id = params["id"] ? ` [${params["id"]}]` : "";
3802
+ detail = ` ${chalk2.dim(`${action}${id}`)}`;
3803
+ } else if (toolName === "memo" && params["action"]) {
3804
+ const action = String(params["action"]);
3805
+ const key = params["key"] ? ` [${params["key"]}]` : "";
3806
+ detail = ` ${chalk2.dim(`${action}${key}`)}`;
3807
+ }
3808
+ const icon = toolName === "spawn-agents" ? "\u26A1" : toolName === "todo" ? "\u{1F4CB}" : toolName === "memo" ? "\u{1F4DD}" : "\u2699";
3809
+ console.log(chalk2.yellow(` ${icon} ${toolName}`) + detail);
3810
+ }
3811
+ function printToolResult(toolName, result, truncated) {
3812
+ if (truncated) {
3813
+ console.log(chalk2.dim(` \u2713 ${toolName} (\u8F93\u51FA\u5DF2\u622A\u65AD)`));
3814
+ } else {
3815
+ const lines = result.split("\n").length;
3816
+ console.log(chalk2.dim(` \u2713 ${toolName} (${lines} \u884C)`));
3817
+ }
3818
+ }
3819
+ function printError(message) {
3820
+ console.error(chalk2.red(`\u2717 ${message}`));
3821
+ }
3822
+ function printInfo(message) {
3823
+ console.log(chalk2.cyan(`\u2139 ${message}`));
3824
+ }
3825
+ function printWarning(message) {
3826
+ console.log(chalk2.yellow(`\u26A0 ${message}`));
3827
+ }
3828
+ function printSuccess(message) {
3829
+ console.log(chalk2.green(`\u2713 ${message}`));
3830
+ }
3831
+ function printWelcome(modelName, mode) {
3832
+ console.log(chalk2.bold.cyan("\n ZenCode") + chalk2.dim(" - \u6781\u7B80 AI \u7F16\u7A0B\u52A9\u624B\n"));
3833
+ console.log(chalk2.dim(` \u6A21\u578B: ${modelName}`));
3834
+ console.log(chalk2.dim(` \u6A21\u5F0F: ${mode}`));
3835
+ console.log(chalk2.dim(` \u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA
3836
+ `));
3837
+ }
3838
+
3839
+ // src/cli/repl.ts
3840
+ function handleSlashCommand(input, context) {
3841
+ const parts = input.trim().split(/\s+/);
3842
+ const command = parts[0];
3843
+ switch (command) {
3844
+ case "/help":
3845
+ console.log(`
3846
+ \u53EF\u7528\u547D\u4EE4:
3847
+ /help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F
3848
+ /mode [\u6A21\u5F0F] \u5207\u6362\u534F\u4F5C\u6A21\u5F0F (delegated/autonomous/controlled)
3849
+ /single \u5207\u6362\u5230\u5355 Agent \u6A21\u5F0F
3850
+ /dual \u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F
3851
+ /parallel \u5207\u6362\u5E76\u884C\u5B50 Agent \u529F\u80FD on/off
3852
+ /todo \u5207\u6362 todo \u8BA1\u5212\u529F\u80FD on/off
3853
+ /clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2
3854
+ /info \u663E\u793A\u5F53\u524D\u914D\u7F6E
3855
+ /exit \u9000\u51FA
3856
+ `);
3857
+ return true;
3858
+ case "/mode": {
3859
+ const mode = parts[1];
3860
+ if (!mode) {
3861
+ printInfo(`\u5F53\u524D\u534F\u4F5C\u6A21\u5F0F: ${context.config.collaboration}`);
3862
+ printInfo("\u53EF\u9009: delegated, autonomous, controlled");
3863
+ return true;
3864
+ }
3865
+ if (["delegated", "autonomous", "controlled"].includes(mode)) {
3866
+ context.config.collaboration = mode;
3867
+ context.setMode?.(mode);
3868
+ printSuccess(`\u5DF2\u5207\u6362\u5230 ${mode} \u6A21\u5F0F`);
3869
+ } else {
3870
+ printError(`\u65E0\u6548\u6A21\u5F0F: ${mode}\u3002\u53EF\u9009: delegated, autonomous, controlled`);
3871
+ }
3872
+ return true;
3873
+ }
3874
+ case "/single":
3875
+ context.config.agent_mode = "single";
3876
+ printSuccess("\u5DF2\u5207\u6362\u5230\u5355 Agent \u6A21\u5F0F");
3877
+ return true;
3878
+ case "/dual":
3879
+ context.config.agent_mode = "dual";
3880
+ printSuccess("\u5DF2\u5207\u6362\u5230\u53CC Agent \u6A21\u5F0F");
3881
+ return true;
3882
+ case "/clear":
3883
+ printSuccess("\u5BF9\u8BDD\u5386\u53F2\u5DF2\u6E05\u7A7A\uFF08\u5C06\u5728\u4E0B\u6B21\u5BF9\u8BDD\u65F6\u751F\u6548\uFF09");
3884
+ return true;
3885
+ case "/parallel": {
3886
+ const current = context.config.features.parallel_agents;
3887
+ const next = current === "on" ? "off" : "on";
3888
+ context.config.features.parallel_agents = next;
3889
+ if (next === "off") {
3890
+ context.registry.unregister("spawn-agents");
3891
+ } else if (!context.registry.has("spawn-agents")) {
3892
+ context.registry.register(
3893
+ createSpawnAgentsTool(context.client, context.registry, context.config, void 0, context.memoStore)
3894
+ );
3895
+ }
3896
+ printSuccess(`\u5E76\u884C\u5B50 Agent \u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}`);
3897
+ return true;
3898
+ }
3899
+ case "/todo": {
3900
+ const current = context.config.features.todo;
3901
+ const next = current === "on" ? "off" : "on";
3902
+ context.config.features.todo = next;
3903
+ if (next === "off") {
3904
+ context.registry.unregister("todo");
3905
+ } else if (!context.registry.has("todo")) {
3906
+ context.registry.register(createTodoTool(context.todoStore));
3907
+ }
3908
+ printSuccess(`Todo \u8BA1\u5212\u529F\u80FD\u5DF2${next === "on" ? "\u5F00\u542F" : "\u5173\u95ED"}`);
3909
+ return true;
3910
+ }
3911
+ case "/info":
3912
+ printInfo(`\u6A21\u578B: ${context.config.model}`);
3913
+ printInfo(`Agent \u6A21\u5F0F: ${context.config.agent_mode}`);
3914
+ printInfo(`\u534F\u4F5C\u6A21\u5F0F: ${context.config.collaboration}`);
3915
+ printInfo(`\u57FA\u7840 URL: ${context.config.base_url}`);
3916
+ return true;
3917
+ case "/exit":
3918
+ process.exit(0);
3919
+ default:
3920
+ printWarning(`\u672A\u77E5\u547D\u4EE4: ${command}\u3002\u8F93\u5165 /help \u67E5\u770B\u5E2E\u52A9\u3002`);
3921
+ return true;
3922
+ }
3923
+ }
3924
+ async function startRepl(options) {
3925
+ const { config } = options;
3926
+ const { systemPrompt } = await buildPrompt(config);
3927
+ const registry = new ToolRegistry();
3928
+ registerCoreTools(registry);
3929
+ registry.setPermissions(config.permissions);
3930
+ const client = createLLMClient({
3931
+ apiKey: config.api_key,
3932
+ baseURL: config.base_url,
3933
+ model: config.model,
3934
+ temperature: config.temperature,
3935
+ maxTokens: config.max_tokens
3936
+ });
3937
+ const todoStore = new TodoStore();
3938
+ const memoStore = new MemoStore();
3939
+ if (config.features.parallel_agents === "on") {
3940
+ registry.register(createSpawnAgentsTool(client, registry, config, void 0, memoStore));
3941
+ }
3942
+ if (config.features.todo === "on") {
3943
+ registry.register(createTodoTool(todoStore));
3944
+ }
3945
+ registry.register(createMemoTool(memoStore));
3946
+ let singleAgent;
3947
+ let orchestrator;
3948
+ if (config.agent_mode === "single") {
3949
+ singleAgent = new Agent(client, registry, config, systemPrompt);
3950
+ } else {
3951
+ orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
3952
+ }
3953
+ const modeLabel = config.agent_mode === "dual" ? `\u53CCAgent (${config.collaboration})` : "\u5355Agent";
3954
+ printWelcome(config.model, modeLabel);
3955
+ const rl = readline.createInterface({
3956
+ input: process.stdin,
3957
+ output: process.stdout,
3958
+ prompt: "> ",
3959
+ historySize: 100
3960
+ });
3961
+ setConfirmHandler(async (promptText) => {
3962
+ return new Promise((resolve7) => {
3963
+ process.stdout.write(promptText);
3964
+ const onData = (data) => {
3965
+ process.stdin.removeListener("data", onData);
3966
+ process.stdin.pause();
3967
+ const answer = data.toString().trim().toLowerCase();
3968
+ resolve7(answer === "y");
3969
+ };
3970
+ process.stdin.resume();
3971
+ process.stdin.once("data", onData);
3972
+ });
3973
+ });
3974
+ rl.prompt();
3975
+ rl.on("line", async (line) => {
3976
+ const input = line.trim();
3977
+ if (!input) {
3978
+ rl.prompt();
3979
+ return;
3980
+ }
3981
+ if (input.startsWith("/")) {
3982
+ const handled = handleSlashCommand(input, {
3983
+ config,
3984
+ orchestrator,
3985
+ registry,
3986
+ client,
3987
+ todoStore,
3988
+ memoStore,
3989
+ setMode: (mode) => {
3990
+ orchestrator?.setMode(mode);
3991
+ }
3992
+ });
3993
+ if (handled) {
3994
+ rl.prompt();
3995
+ return;
3996
+ }
3997
+ }
3998
+ if (config.agent_mode === "single" && !singleAgent) {
3999
+ singleAgent = new Agent(client, registry, config, systemPrompt);
4000
+ orchestrator = void 0;
4001
+ } else if (config.agent_mode === "dual" && !orchestrator) {
4002
+ orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
4003
+ singleAgent = void 0;
4004
+ }
4005
+ rl.pause();
4006
+ let isStreaming = false;
4007
+ const thinkFilter = createThinkFilter();
4008
+ const callbacks = {
4009
+ onContent: (text) => {
4010
+ const filtered = thinkFilter(text);
4011
+ if (!filtered) return;
4012
+ if (!isStreaming) {
4013
+ isStreaming = true;
4014
+ process.stdout.write("\n");
4015
+ }
4016
+ printStream(filtered);
4017
+ },
4018
+ onToolExecuting: (name, params) => {
4019
+ if (isStreaming) {
4020
+ process.stdout.write("\n");
4021
+ isStreaming = false;
4022
+ }
4023
+ printToolCall(name, params);
4024
+ },
4025
+ onToolResult: (name, result, truncated) => {
4026
+ printToolResult(name, result, truncated);
4027
+ },
4028
+ onCoderStart: () => {
4029
+ if (isStreaming) {
4030
+ process.stdout.write("\n");
4031
+ isStreaming = false;
4032
+ }
4033
+ printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
4034
+ },
4035
+ onCoderEnd: () => {
4036
+ printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
4037
+ },
4038
+ onError: (err) => {
4039
+ if (isStreaming) {
4040
+ process.stdout.write("\n");
4041
+ isStreaming = false;
4042
+ }
4043
+ printError(err.message);
4044
+ }
4045
+ };
4046
+ try {
4047
+ if (config.agent_mode === "single" && singleAgent) {
4048
+ await singleAgent.run(input, callbacks);
4049
+ } else if (orchestrator) {
4050
+ await orchestrator.run(input, callbacks);
4051
+ }
4052
+ } catch (err) {
4053
+ const msg = err instanceof Error ? err.message : String(err);
4054
+ printError(msg);
4055
+ }
4056
+ if (isStreaming) {
4057
+ process.stdout.write("\n");
4058
+ }
4059
+ console.log();
4060
+ rl.resume();
4061
+ rl.prompt();
4062
+ });
4063
+ rl.on("close", () => {
4064
+ console.log("\n\u518D\u89C1\uFF01");
4065
+ process.exit(0);
4066
+ });
4067
+ }
4068
+
4069
+ // src/cli/index.ts
4070
+ init_todo_store();
4071
+ init_memo_store();
4072
+ init_spawn_agents();
4073
+ init_todo();
4074
+ init_memo();
4075
+ init_bridge();
4076
+ async function runOnce(prompt, config) {
4077
+ const { systemPrompt } = await buildPrompt(config);
4078
+ const registry = new ToolRegistry();
4079
+ registerCoreTools(registry);
4080
+ registry.setPermissions(config.permissions);
4081
+ const client = createLLMClient({
4082
+ apiKey: config.api_key,
4083
+ baseURL: config.base_url,
4084
+ model: config.model,
4085
+ temperature: config.temperature,
4086
+ maxTokens: config.max_tokens
4087
+ });
4088
+ const todoStore = new TodoStore();
4089
+ const memoStore = new MemoStore();
4090
+ if (config.features.parallel_agents === "on") {
4091
+ registry.register(createSpawnAgentsTool(client, registry, config, void 0, memoStore));
4092
+ }
4093
+ if (config.features.todo === "on") {
4094
+ registry.register(createTodoTool(todoStore));
4095
+ }
4096
+ registry.register(createMemoTool(memoStore));
4097
+ let isStreaming = false;
4098
+ const thinkFilter = createThinkFilter();
4099
+ const callbacks = {
4100
+ onContent: (text) => {
4101
+ const filtered = thinkFilter(text);
4102
+ if (!filtered) return;
4103
+ if (!isStreaming) {
4104
+ isStreaming = true;
4105
+ }
4106
+ printStream(filtered);
4107
+ },
4108
+ onToolExecuting: (name, params) => {
4109
+ if (isStreaming) {
4110
+ process.stdout.write("\n");
4111
+ isStreaming = false;
4112
+ }
4113
+ printToolCall(name, params);
4114
+ },
4115
+ onToolResult: (name, result, truncated) => {
4116
+ printToolResult(name, result, truncated);
4117
+ },
4118
+ onCoderStart: () => {
4119
+ if (isStreaming) {
4120
+ process.stdout.write("\n");
4121
+ isStreaming = false;
4122
+ }
4123
+ printInfo("\u7F16\u7801 Agent \u5F00\u59CB\u5DE5\u4F5C...");
4124
+ },
4125
+ onCoderEnd: () => {
4126
+ printInfo("\u7F16\u7801 Agent \u5B8C\u6210");
4127
+ },
4128
+ onError: (err) => {
4129
+ printError(err.message);
4130
+ }
4131
+ };
4132
+ try {
4133
+ if (config.agent_mode === "single") {
4134
+ const agent = new Agent(client, registry, config, systemPrompt);
4135
+ await agent.run(prompt, callbacks);
4136
+ } else {
4137
+ const orchestrator = new Orchestrator(registry, config, systemPrompt, memoStore);
4138
+ await orchestrator.run(prompt, callbacks);
4139
+ }
4140
+ } catch (err) {
4141
+ const msg = err instanceof Error ? err.message : String(err);
4142
+ printError(msg);
4143
+ process.exit(1);
4144
+ }
4145
+ if (isStreaming) {
4146
+ process.stdout.write("\n");
4147
+ }
4148
+ }
4149
+ function createCli() {
4150
+ const program2 = new Command();
4151
+ program2.name("zencode").description("\u6781\u7B80 CLI AI \u7F16\u7A0B\u5DE5\u5177").version("0.1.0").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("--single", "\u4F7F\u7528\u5355 Agent \u6A21\u5F0F").option("--dual", "\u4F7F\u7528\u53CC Agent \u6A21\u5F0F").option("--mode <mode>", "\u534F\u4F5C\u6A21\u5F0F (delegated/autonomous/controlled)").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) => {
4152
+ const config = loadConfig(opts);
4153
+ if (!config.api_key) {
4154
+ printError("\u672A\u8BBE\u7F6E API \u5BC6\u94A5\u3002\u8BF7\u901A\u8FC7\u4EE5\u4E0B\u65B9\u5F0F\u4E4B\u4E00\u8BBE\u7F6E\uFF1A");
4155
+ printError(" 1. \u73AF\u5883\u53D8\u91CF ZENCODE_API_KEY");
4156
+ printError(" 2. \u914D\u7F6E\u6587\u4EF6 ~/.zencode/config.yaml \u4E2D\u8BBE\u7F6E api_key");
4157
+ printError(" 3. CLI \u53C2\u6570 --api-key");
4158
+ process.exit(1);
4159
+ }
4160
+ const prompt = promptParts.join(" ");
4161
+ if (prompt) {
4162
+ await runOnce(prompt, config);
4163
+ } else if (opts.simple) {
4164
+ await startRepl({ config });
4165
+ } else {
4166
+ const { startTui: startTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
4167
+ await startTui2({ config });
4168
+ }
4169
+ });
4170
+ return program2;
4171
+ }
4172
+
4173
+ // bin/zencode.ts
4174
+ var program = createCli();
4175
+ program.parse();
4176
+ //# sourceMappingURL=zencode.js.map