zencode-cli 0.2.1 → 0.2.3

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 DELETED
@@ -1,613 +0,0 @@
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
- ```