zencode-cli 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +613 -0
- package/README.md +400 -0
- package/dist/bin/zencode.js +309 -57
- package/dist/bin/zencode.js.map +1 -1
- package/package.json +4 -2
package/DESIGN.md
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
# ZenCode 设计文档
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
ZenCode 是一个极简的 CLI AI 编程工具,核心理念是「用最少的提示词,让模型把全部能力集中在编程本身」。
|
|
6
|
+
|
|
7
|
+
### 设计目标
|
|
8
|
+
|
|
9
|
+
1. **最小认知负担** — 用户只需告诉 AI 要做什么,AI 自动完成收集上下文、编写代码、验证结果
|
|
10
|
+
2. **高效的 Agent 协作** — 通过 memo 作为共享记忆,实现 Orchestrator 与 Coder 的高效配合
|
|
11
|
+
3. **优秀的终端体验** — 全屏 TUI,支持流式输出、实时进度、工具确认
|
|
12
|
+
|
|
13
|
+
### 核心架构
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ 用户 (User) │
|
|
18
|
+
└────────────────────────────┬────────────────────────────────────┘
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ CLI 入口 (bin/zencode.ts) │
|
|
23
|
+
│ createCli() → program.parse() │
|
|
24
|
+
└────────────────────────────┬────────────────────────────────────┘
|
|
25
|
+
│
|
|
26
|
+
┌──────────────┼──────────────┐
|
|
27
|
+
▼ ▼ ▼
|
|
28
|
+
┌─────────┐ ┌──────────┐ ┌──────────┐
|
|
29
|
+
│ TUI 模式 │ │ 单次执行 │ │ 简单REPL │
|
|
30
|
+
│(默认) │ │ 模式 │ │ 模式 │
|
|
31
|
+
└────┬────┘ └────┬─────┘ └────┬─────┘
|
|
32
|
+
│ │ │
|
|
33
|
+
└─────────────┼──────────────┘
|
|
34
|
+
▼
|
|
35
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ Agent 层 (src/core/) │
|
|
37
|
+
│ ┌───────────────┐ ┌────────────────┐ │
|
|
38
|
+
│ │ Agent │◄──────►│ Orchestrator │ │
|
|
39
|
+
│ │ (单Agent) │ │ (双Agent调度者) │ │
|
|
40
|
+
│ └───────────────┘ └───────┬────────┘ │
|
|
41
|
+
│ │ │
|
|
42
|
+
│ ▼ │
|
|
43
|
+
│ ┌────────────────┐ │
|
|
44
|
+
│ │ Coder │ │
|
|
45
|
+
│ │ (双Agent执行者) │ │
|
|
46
|
+
│ └────────────────┘ │
|
|
47
|
+
└────────────────────────────┬────────────────────────────────────┘
|
|
48
|
+
│
|
|
49
|
+
▼
|
|
50
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
51
|
+
│ LLM 层 (src/llm/) │
|
|
52
|
+
│ LLMClient │
|
|
53
|
+
│ chatStream() / chat() │
|
|
54
|
+
└────────────────────────────┬────────────────────────────────────┘
|
|
55
|
+
│
|
|
56
|
+
▼
|
|
57
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
58
|
+
│ 工具层 (src/tools/) │
|
|
59
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
|
|
60
|
+
│ │ read-file│ │write-file│ │ edit-file│ │ spawn-agents │ │
|
|
61
|
+
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
|
|
62
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
63
|
+
│ │ bash │ │ memo │ │ todo │ │
|
|
64
|
+
│ └──────────┘ └──────────┘ └──────────┘ │
|
|
65
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 核心组件
|
|
69
|
+
|
|
70
|
+
### 1. CLI 入口
|
|
71
|
+
|
|
72
|
+
**文件**: `src/cli/index.ts`
|
|
73
|
+
|
|
74
|
+
负责解析命令行参数、加载配置、选择运行模式。
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// 三种运行模式
|
|
78
|
+
if (prompt) {
|
|
79
|
+
await runOnce(prompt, config); // 单次执行
|
|
80
|
+
} else if (opts.simple) {
|
|
81
|
+
await startRepl({ config }); // 简单 REPL
|
|
82
|
+
} else {
|
|
83
|
+
await startTui({ config }); // 全屏 TUI (默认)
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Agent 层
|
|
88
|
+
|
|
89
|
+
#### 单 Agent 模式
|
|
90
|
+
|
|
91
|
+
**文件**: `src/core/agent.ts`
|
|
92
|
+
|
|
93
|
+
适用于简单任务,一个 Agent 完成所有工作。
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
用户输入 → LLM → 工具调用 → 执行 → 结果 → LLM → ...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
class Agent {
|
|
101
|
+
async run(prompt: string, callbacks: AgentCallbacks) {
|
|
102
|
+
while (true) {
|
|
103
|
+
const msg = await this.client.chatStream(prompt, tools, callbacks);
|
|
104
|
+
if (!msg.tool_calls) break;
|
|
105
|
+
// 执行工具循环
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### 双 Agent 模式
|
|
112
|
+
|
|
113
|
+
**文件**: `src/core/dual-agent/orchestrator.ts`
|
|
114
|
+
|
|
115
|
+
调度者(Orchestrator)+ 编码者(Coder),通过 memo 共享上下文。
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
Orchestrator Coder
|
|
119
|
+
│ │
|
|
120
|
+
├── glob/read-file 收集 │
|
|
121
|
+
├── memo write 记录 │
|
|
122
|
+
│ │
|
|
123
|
+
├── send-to-coder ──────────►│
|
|
124
|
+
│ ├── memo read
|
|
125
|
+
│ ├── write-file/edit-file
|
|
126
|
+
│ ├── memo write
|
|
127
|
+
◄─────────────────────────────┤
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
##### Orchestrator 系统提示词
|
|
131
|
+
|
|
132
|
+
```markdown
|
|
133
|
+
你是侦察兵 + 指挥官。你自己不写代码,你的职责是:
|
|
134
|
+
收集情报 → 记入 memo → 委派编码 Agent。
|
|
135
|
+
|
|
136
|
+
1. 评估:任务是否需要了解现有代码?
|
|
137
|
+
2. 收集上下文:glob/read-file
|
|
138
|
+
3. memo write:记录关键发现
|
|
139
|
+
4. send-to-coder:任务 + 引用 memo key
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
##### Coder 身份
|
|
143
|
+
|
|
144
|
+
```markdown
|
|
145
|
+
1. 读 memo:如果任务提到 memo key,先 memo read
|
|
146
|
+
2. 编码:新建 → write-file,修改 → read-file → edit-file
|
|
147
|
+
3. 写 memo:记录改动摘要
|
|
148
|
+
4. 回复:一句话说明结果
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
##### 协作模式
|
|
152
|
+
|
|
153
|
+
| 模式 | Coder 工具 | 说明 |
|
|
154
|
+
|------|-----------|------|
|
|
155
|
+
| `delegated` | 有 | 调度者委派,Coder 独立执行 |
|
|
156
|
+
| `autonomous` | 有 | Coder 自主决策 |
|
|
157
|
+
| `controlled` | 无 | Coder 只返回代码 |
|
|
158
|
+
|
|
159
|
+
### 3. 工具层
|
|
160
|
+
|
|
161
|
+
#### 工具注册表
|
|
162
|
+
|
|
163
|
+
**文件**: `src/tools/registry.ts`
|
|
164
|
+
|
|
165
|
+
管理所有可用工具及其权限级别。
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
class ToolRegistry {
|
|
169
|
+
private tools: Map<string, Tool> = new Map();
|
|
170
|
+
private permissions: Map<string, PermissionLevel> = new Map();
|
|
171
|
+
|
|
172
|
+
register(tool: Tool) { ... }
|
|
173
|
+
execute(name: string, params: Record<string, unknown>): Promise<ToolResult> { ... }
|
|
174
|
+
toToolDefinitions(filter?: string[]): ToolDefinition[] { ... }
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### 权限级别
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
type PermissionLevel = 'auto' | 'confirm' | 'deny';
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- `auto` — 自动执行(如 read-file、glob)
|
|
185
|
+
- `confirm` — 需要用户确认(如 write-file、bash)
|
|
186
|
+
- `deny` — 禁止执行
|
|
187
|
+
|
|
188
|
+
#### 内置工具
|
|
189
|
+
|
|
190
|
+
| 工具 | 功能 | 权限 |
|
|
191
|
+
|------|------|------|
|
|
192
|
+
| `read-file` | 读取文件 | auto |
|
|
193
|
+
| `write-file` | 创建/覆盖文件 | confirm |
|
|
194
|
+
| `edit-file` | 编辑文件 | confirm |
|
|
195
|
+
| `glob` | 文件搜索 | auto |
|
|
196
|
+
| `grep` | 代码搜索 | auto |
|
|
197
|
+
| `bash` | 执行命令 | confirm |
|
|
198
|
+
| `spawn-agents` | 并行子 Agent | confirm |
|
|
199
|
+
| `memo` | 共享备忘录 | auto |
|
|
200
|
+
| `todo` | 任务计划 | auto |
|
|
201
|
+
|
|
202
|
+
#### 自定义工具
|
|
203
|
+
|
|
204
|
+
工具接口定义:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface Tool {
|
|
208
|
+
name: string;
|
|
209
|
+
description: string;
|
|
210
|
+
parameters: JSONSchema;
|
|
211
|
+
permissionLevel: PermissionLevel;
|
|
212
|
+
execute(params: Record<string, unknown>): Promise<ToolResult>;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 4. 记忆系统
|
|
217
|
+
|
|
218
|
+
#### Memo 共享备忘录
|
|
219
|
+
|
|
220
|
+
**文件**: `src/core/memo-store.ts`
|
|
221
|
+
|
|
222
|
+
双 Agent 协作的核心桥梁。
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
class MemoStore {
|
|
226
|
+
private memos: Map<string, string> = new Map();
|
|
227
|
+
|
|
228
|
+
write(key: string, value: string): void
|
|
229
|
+
read(key: string): string | undefined
|
|
230
|
+
delete(key: string): boolean
|
|
231
|
+
list(): Map<string, string>
|
|
232
|
+
buildIndex(): string // 生成可嵌入任务的索引
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**协作流程**:
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
Orchestrator Coder
|
|
240
|
+
│ │
|
|
241
|
+
├── glob/read-file 收集 │
|
|
242
|
+
├── memo write("demo-structure", │
|
|
243
|
+
│ "demo/ 下有 weather.html...") │
|
|
244
|
+
│ │
|
|
245
|
+
├── send-to-coder( │
|
|
246
|
+
│ "在 demo/newyear.html 创建..." │
|
|
247
|
+
│ + "参考 memo key 'demo-structure'") │
|
|
248
|
+
│ │
|
|
249
|
+
│ memo read("demo-structure")
|
|
250
|
+
│ write-file(...)
|
|
251
|
+
│ memo write("done-newyear", "创建了...")
|
|
252
|
+
◄───────────────────────────────┤
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### Todo 计划
|
|
256
|
+
|
|
257
|
+
**文件**: `src/core/todo-store.ts`
|
|
258
|
+
|
|
259
|
+
多步骤任务管理。
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
interface TodoItem {
|
|
263
|
+
id: string;
|
|
264
|
+
content: string;
|
|
265
|
+
status: 'pending' | 'completed' | 'failed';
|
|
266
|
+
createdAt: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
class TodoStore {
|
|
270
|
+
private items: TodoItem[] = [];
|
|
271
|
+
|
|
272
|
+
add(content: string): TodoItem
|
|
273
|
+
complete(id: string): void
|
|
274
|
+
delete(id: string): void
|
|
275
|
+
list(): TodoItem[]
|
|
276
|
+
subscribe(callback: (plan: TodoPlan) => void): () => void
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 5. TUI 层
|
|
281
|
+
|
|
282
|
+
**文件**: `src/cli/tui/`
|
|
283
|
+
|
|
284
|
+
基于 Ink + React 的全屏终端界面。
|
|
285
|
+
|
|
286
|
+
#### 架构
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
┌─────────────────────────────────────────────────────┐
|
|
290
|
+
│ App.tsx │
|
|
291
|
+
│ ┌───────────────────────────────────────────────┐ │
|
|
292
|
+
│ │ ChatArea.tsx │ │
|
|
293
|
+
│ │ Static (已完成内容) │ Dynamic (流式内容) │ │
|
|
294
|
+
│ └───────────────────────────────────────────────┘ │
|
|
295
|
+
│ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
|
|
296
|
+
│ │ InputArea│ │StatusBar │ │ ConfirmPrompt │ │
|
|
297
|
+
│ └──────────┘ └──────────┘ └────────────────┘ │
|
|
298
|
+
└─────────────────────────────────────────────────────┘
|
|
299
|
+
│ │
|
|
300
|
+
▼ ▼
|
|
301
|
+
┌─────────────────────────────────────────────────────┐
|
|
302
|
+
│ bridge.ts │
|
|
303
|
+
│ createBridgeCallbacks(dispatch) → TuiAction │
|
|
304
|
+
└─────────────────────────────────────────────────────┘
|
|
305
|
+
│
|
|
306
|
+
▼
|
|
307
|
+
┌─────────────────────────────────────────────────────┐
|
|
308
|
+
│ state.ts │
|
|
309
|
+
│ tuiReducer(state, action) │
|
|
310
|
+
└─────────────────────────────────────────────────────┘
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### 渲染策略
|
|
314
|
+
|
|
315
|
+
采用 **Log-Style** 渲染,面向 Windows 终端兼容性:
|
|
316
|
+
|
|
317
|
+
1. **Static 区域** — 已完成的内容,推入 Ink `<Static>` 后写入 scrollback,永不重绘
|
|
318
|
+
2. **Dynamic 区域** — 最小重绘面积(最多 1 行),用于流式输出
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// 渲染流程
|
|
322
|
+
function extractFromBlocks(msg, staticItems, dynamicNodes, isStreaming) {
|
|
323
|
+
// 完整行 → Static
|
|
324
|
+
// 末尾部分行 → Dynamic (1 行)
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
#### 工具调用显示
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
⏳ + write-file demo/index.html ← Static (tool-header)
|
|
332
|
+
✎ 生成中... 37 行 ← Dynamic (progress)
|
|
333
|
+
✓ + write-file demo/index.html 203 行 ← Static (tool-done)
|
|
334
|
+
<!DOCTYPE html> ← Static (代码预览)
|
|
335
|
+
...
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### Windows 终端兼容性
|
|
339
|
+
|
|
340
|
+
- 移除 `<Spinner>` 动画(定时器重绘导致残影)
|
|
341
|
+
- 移除边框组件(borderStyle 会产生 3 行残影)
|
|
342
|
+
- 实时进度用 Dynamic 区域(Static 在 Windows 上不实时刷新)
|
|
343
|
+
- 使用 `seenIds` + `accumulated` ref 防止 Static 重复渲染
|
|
344
|
+
|
|
345
|
+
### 6. LLM 层
|
|
346
|
+
|
|
347
|
+
**文件**: `src/llm/client.ts`
|
|
348
|
+
|
|
349
|
+
OpenAI 兼容 API 客户端。
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
class LLMClient {
|
|
353
|
+
// 流式调用
|
|
354
|
+
async chatStream(
|
|
355
|
+
messages: Message[],
|
|
356
|
+
tools: ToolDefinition[] | undefined,
|
|
357
|
+
callbacks: StreamCallbacks
|
|
358
|
+
): Promise<Message> { ... }
|
|
359
|
+
|
|
360
|
+
// 非流式调用
|
|
361
|
+
async chat(
|
|
362
|
+
messages: Message[],
|
|
363
|
+
tools?: ToolDefinition[]
|
|
364
|
+
): Promise<Message> { ... }
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### 流式回调
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
interface StreamCallbacks {
|
|
372
|
+
onContent?: (text: string) => void; // 文本内容
|
|
373
|
+
onToolCall?: (toolCall: ToolCall) => void; // 完整工具调用
|
|
374
|
+
onToolCallStreaming?: (index, name, args) => void; // 工具参数流式
|
|
375
|
+
onFinish?: (message: Message) => void; // 完成
|
|
376
|
+
onError?: (error: Error) => void; // 错误
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 7. 配置系统
|
|
381
|
+
|
|
382
|
+
**文件**: `src/config/loader.ts`
|
|
383
|
+
|
|
384
|
+
支持多种配置方式(优先级从高到低):
|
|
385
|
+
|
|
386
|
+
1. CLI 参数 (`--api-key`)
|
|
387
|
+
2. 环境变量 (`ZENCODE_API_KEY`)
|
|
388
|
+
3. 配置文件 (`~/.zencode/config.yaml`)
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
interface ZenCodeConfig {
|
|
392
|
+
model: string;
|
|
393
|
+
api_key: string;
|
|
394
|
+
base_url: string;
|
|
395
|
+
agent_mode: 'single' | 'dual';
|
|
396
|
+
collaboration: 'delegated' | 'autonomous' | 'controlled';
|
|
397
|
+
features: {
|
|
398
|
+
parallel_agents: 'on' | 'off';
|
|
399
|
+
todo: 'on' | 'off';
|
|
400
|
+
};
|
|
401
|
+
permissions: Record<string, PermissionLevel>;
|
|
402
|
+
max_tokens: number;
|
|
403
|
+
temperature: number;
|
|
404
|
+
max_tool_output: number;
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## 数据流
|
|
409
|
+
|
|
410
|
+
### 1. 用户输入 → LLM
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
用户输入
|
|
414
|
+
│
|
|
415
|
+
▼
|
|
416
|
+
App.tsx: handleSubmit()
|
|
417
|
+
│
|
|
418
|
+
▼
|
|
419
|
+
Agent.run() / Orchestrator.run()
|
|
420
|
+
│
|
|
421
|
+
▼
|
|
422
|
+
LLMClient.chatStream()
|
|
423
|
+
│
|
|
424
|
+
▼
|
|
425
|
+
StreamCallbacks.onContent() → TUI 显示
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### 2. LLM 工具调用
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
LLM 返回 tool_calls
|
|
432
|
+
│
|
|
433
|
+
├── onToolCallStreaming() ──→ TUI: 工具参数流式更新
|
|
434
|
+
│
|
|
435
|
+
▼
|
|
436
|
+
检查权限 (registry.getPermissionLevel())
|
|
437
|
+
│
|
|
438
|
+
├── auto → 直接执行
|
|
439
|
+
├── confirm → 等待用户确认
|
|
440
|
+
└── deny → 拒绝
|
|
441
|
+
│
|
|
442
|
+
▼
|
|
443
|
+
registry.execute()
|
|
444
|
+
│
|
|
445
|
+
▼
|
|
446
|
+
onToolResult() ──→ TUI: 工具结果
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 3. 双 Agent 协作
|
|
450
|
+
|
|
451
|
+
```
|
|
452
|
+
Orchestrator LLM
|
|
453
|
+
│
|
|
454
|
+
├── send-to-coder
|
|
455
|
+
│
|
|
456
|
+
▼
|
|
457
|
+
invokeCoder()
|
|
458
|
+
│ 构造任务 + 注入 memo
|
|
459
|
+
▼
|
|
460
|
+
Coder LLM
|
|
461
|
+
│
|
|
462
|
+
├── memo read (获取上下文)
|
|
463
|
+
├── write-file / edit-file
|
|
464
|
+
├── memo write (记录改动)
|
|
465
|
+
│
|
|
466
|
+
▼
|
|
467
|
+
返回结果给 Orchestrator
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## 关键设计决策
|
|
471
|
+
|
|
472
|
+
### 1. 为什么用双 Agent?
|
|
473
|
+
|
|
474
|
+
**问题**: 单 Agent 既要收集上下文,又要写代码,容易「既要又要」,效率低下。
|
|
475
|
+
|
|
476
|
+
**解决**: 分离关注点
|
|
477
|
+
- Orchestrator: 侦察兵 + 指挥官,不写代码,只负责收集信息和委派
|
|
478
|
+
- Coder: 执行者,收到的任务已包含所有上下文,直接动手
|
|
479
|
+
|
|
480
|
+
### 2. 为什么用 memo 作为协作桥梁?
|
|
481
|
+
|
|
482
|
+
**问题**: Orchestrator 收集的上下文无法传递给 Coder,导致 Coder 重复探索。
|
|
483
|
+
|
|
484
|
+
**解决**: memo 是共享记忆
|
|
485
|
+
- Orchestrator 收集 → `memo write`
|
|
486
|
+
- Coder 编码前 → `memo read`
|
|
487
|
+
- Coder 完成 → `memo write` 记录改动
|
|
488
|
+
|
|
489
|
+
### 3. 为什么用 TUI 而非纯文本?
|
|
490
|
+
|
|
491
|
+
**问题**: 纯文本模式难以表达复杂信息(工具调用、确认、进度)。
|
|
492
|
+
|
|
493
|
+
**解决**: 全屏 TUI
|
|
494
|
+
- 流式输出,自然分段
|
|
495
|
+
- 工具调用可视化
|
|
496
|
+
- 用户确认交互
|
|
497
|
+
- 实时进度显示
|
|
498
|
+
|
|
499
|
+
### 4. 为什么 Log-Style 渲染?
|
|
500
|
+
|
|
501
|
+
**问题**: Ink 的动态重绘机制在 Windows 终端上产生残影(ANSI 清除不生效)。
|
|
502
|
+
|
|
503
|
+
**解决**: 只增不减的 Static
|
|
504
|
+
- 已完成内容全部推入 `<Static>`,永不重绘
|
|
505
|
+
- Dynamic 区域最小化(1 行)
|
|
506
|
+
- 特殊处理:实时进度用 Dynamic
|
|
507
|
+
|
|
508
|
+
## 扩展开发
|
|
509
|
+
|
|
510
|
+
### 添加新工具
|
|
511
|
+
|
|
512
|
+
1. 在 `src/tools/` 目录创建工具文件
|
|
513
|
+
2. 实现 `Tool` 接口
|
|
514
|
+
3. 在 `src/tools/register.ts` 注册
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
// src/tools/my-tool.ts
|
|
518
|
+
export const myTool: Tool = {
|
|
519
|
+
name: 'my-tool',
|
|
520
|
+
description: '我的工具描述',
|
|
521
|
+
parameters: {
|
|
522
|
+
type: 'object',
|
|
523
|
+
properties: {
|
|
524
|
+
input: { type: 'string' }
|
|
525
|
+
},
|
|
526
|
+
required: ['input']
|
|
527
|
+
},
|
|
528
|
+
permissionLevel: 'confirm',
|
|
529
|
+
async execute(params) {
|
|
530
|
+
// 实现逻辑
|
|
531
|
+
return { content: '结果' };
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### 添加新模型
|
|
537
|
+
|
|
538
|
+
修改 `src/llm/client.ts`,确保兼容 OpenAI API 格式:
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
// 只需 base_url 指向兼容端点
|
|
542
|
+
const client = new OpenAI({
|
|
543
|
+
apiKey: 'your-key',
|
|
544
|
+
baseURL: 'https://your-model-api.com/v1'
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### 添加新功能
|
|
549
|
+
|
|
550
|
+
1. 在对应模块添加逻辑
|
|
551
|
+
2. 如需 TUI 支持,在 `src/cli/tui/state.ts` 添加 action
|
|
552
|
+
3. 在对应组件处理渲染
|
|
553
|
+
|
|
554
|
+
## 文件结构
|
|
555
|
+
|
|
556
|
+
```
|
|
557
|
+
zencode/
|
|
558
|
+
├── bin/
|
|
559
|
+
│ └── zencode.ts # CLI 入口
|
|
560
|
+
├── src/
|
|
561
|
+
│ ├── cli/
|
|
562
|
+
│ │ ├── index.ts # CLI 主程序
|
|
563
|
+
│ │ ├── repl.ts # 简单 REPL
|
|
564
|
+
│ │ ├── ui.ts # 非 TUI 输出
|
|
565
|
+
│ │ └── tui/
|
|
566
|
+
│ │ ├── index.tsx # TUI 入口
|
|
567
|
+
│ │ ├── App.tsx # 主组件
|
|
568
|
+
│ │ ├── state.ts # 状态管理
|
|
569
|
+
│ │ ├── bridge.ts # 回调桥接
|
|
570
|
+
│ │ └── components/ # UI 组件
|
|
571
|
+
│ │ ├── ChatArea.tsx
|
|
572
|
+
│ │ ├── InputArea.tsx
|
|
573
|
+
│ │ ├── StatusBar.tsx
|
|
574
|
+
│ │ └── ...
|
|
575
|
+
│ ├── config/
|
|
576
|
+
│ │ ├── loader.ts # 配置加载
|
|
577
|
+
│ │ └── types.ts # 类型定义
|
|
578
|
+
│ ├── core/
|
|
579
|
+
│ │ ├── agent.ts # 单 Agent
|
|
580
|
+
│ │ ├── conversation.ts # 对话管理
|
|
581
|
+
│ │ ├── todo-store.ts # Todo 存储
|
|
582
|
+
│ │ ├── memo-store.ts # Memo 存储
|
|
583
|
+
│ │ ├── sub-agent-tracker.ts # 子 Agent 追踪
|
|
584
|
+
│ │ └── dual-agent/
|
|
585
|
+
│ │ ├── orchestrator.ts # 调度者
|
|
586
|
+
│ │ ├── coder.ts # 编码者
|
|
587
|
+
│ │ └── modes.ts # 协作模式定义
|
|
588
|
+
│ ├── llm/
|
|
589
|
+
│ │ ├── client.ts # LLM 客户端
|
|
590
|
+
│ │ └── types.ts # 类型定义
|
|
591
|
+
│ ├── tools/
|
|
592
|
+
│ │ ├── registry.ts # 工具注册表
|
|
593
|
+
│ │ ├── permission.ts # 权限检查
|
|
594
|
+
│ │ ├── register.ts # 内置工具注册
|
|
595
|
+
│ │ ├── types.ts # 工具类型
|
|
596
|
+
│ │ ├── read-file.ts
|
|
597
|
+
│ │ ├── write-file.ts
|
|
598
|
+
│ │ ├── edit-file.ts
|
|
599
|
+
│ │ ├── bash.ts
|
|
600
|
+
│ │ ├── glob.ts
|
|
601
|
+
│ │ ├── grep.ts
|
|
602
|
+
│ │ ├── memo.ts
|
|
603
|
+
│ │ ├── todo.ts
|
|
604
|
+
│ │ └── spawn-agents.ts
|
|
605
|
+
│ └── prompt/
|
|
606
|
+
│ ├── builder.ts # 提示词构建
|
|
607
|
+
│ └── layers/ # 提示词分层
|
|
608
|
+
├── dist/ # 构建输出
|
|
609
|
+
├── package.json
|
|
610
|
+
├── tsconfig.json
|
|
611
|
+
├── tsup.config.ts
|
|
612
|
+
└── README.md
|
|
613
|
+
```
|