agent-Specialization/docs/cli_ui_display_spec.md
JOJO cc9df7959a feat(cli): 新增 React/Ink CLI 端,修复全屏布局与光标定位
## 新增:基于 React/Ink 的 CLI 终端(cli/)

使用 Ink 框架构建完整的终端 UI,取代原有的纯 print 交互方式:

- App.tsx:主应用组件,管理对话状态、任务轮询、键盘输入、slash 命令
- components.tsx:Timeline、Composer、StatusLine、Picker、SlashMenu 等全部 UI 组件
- eventMapper.ts:将后端 SSE 事件流映射为 Timeline 条目,支持 thinking/tool/assistant/sub_agent 等类型
- api.ts:封装所有后端 REST 接口(任务、对话、模型、权限、工作区等)
- commands.ts:slash 命令定义(/new /resume /model /permission /execution /workspace /compact /tools 等)
- workspaces.ts:本地工作区目录的持久化管理
- types.ts:所有共享类型定义
- format.ts:context 格式化、路径缩写等工具函数

## 重大修复:去除固定高度全屏模式,恢复滚轮滚动

**问题根因**:根 Box 设置了 `height={stdout.rows}` 使 Ink 进入原地重绘模式
(类似 vim/htop),所有内容在同一块屏幕区域反复擦写,永远不会真正
"滚出"屏幕顶部,导致 terminal 的 scrollback buffer 始终为空,鼠标
滚轮无任何响应。

**修复方案**:
- 移除根 Box 的 `height` 约束和 `overflow="hidden"`
- 移除内层 Box 的 `flexGrow`/`justifyContent="flex-end"` 全屏布局
- Timeline 不再做行数截断,直接渲染全部条目
- 内容自然流入 terminal scrollback buffer,鼠标滚轮恢复正常

**同步清理**:删除 `getTimelineMaxRows`、`estimateTimelineRows`、
`scrollOffset` 状态及方向键滚动逻辑(这些都是全屏模式下的补丁,
去除固定高度后不再需要)

## 修复:Composer 光标位置偏移一行

`setCursorPosition` 中 y 轴使用 `lines.length` 导致单行时偏移 +1。
改为 `lines.length - 1`,与实际最后一行渲染位置对齐。

## 其他

- AGENTS.md / README.md:补充 CLI 启动方式与架构说明
- server/chat.py:补充任务轮询接口所需字段
- package.json:新增 cli 相关脚本入口
- docs/cli_slash_commands_spec.md、docs/cli_ui_display_spec.md:CLI 设计文档

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:06:51 +08:00

524 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLI UI 显示规范(草案)
> 目标:把 Web 端已有的信息表达方式,压缩成一个低噪声、可扫描、适合终端的 CLI 交互层。
> 适用范围CLI 前端展示层,不定义 Agent Runtime 本身。
## 1. 设计原则
1. **事件流优先**CLI 只消费结构化事件,不直接消费原始 API 响应。
2. **极简优先**:正文少、状态清楚、动作明确。
3. **固定符号**:统一用少量状态符号表达进度与结果。
4. **完整展示变更**:文件编辑类内容不折叠,直接完整显示。
5. **思考可见但不冗长**:只保留滚动中的短窗口,不保留完整历史。
6. **交互独立**审批、选择、Slash 命令都走独立输入态。
---
## 2. 总体布局
```text
[历史事件区]
[当前思考 / 工具 / 子智能体输出]
[最终结果]
──
[输入栏]
[状态栏]
```
### 底部状态栏建议
```text
5 后台智能体 3 后台指令 版本控制: 开 自动审批 沙箱 24k/200k
```
可扩展字段:
- 模型名
- 运行模式thinking / fast 先不做切换也可保留显示位)
- Git 开关
- 自动审批状态
- 沙箱/直连状态
- 上下文剩余量
---
## 3. 状态符号
| 状态 | 符号 | 颜色 | 说明 |
|---|---|---|---|
| 运行中 | `◦` / `•` 交替 | 黄/灰 | 闪烁 |
| 完成 | `•` | 绿 | 静态 |
| 失败 | `•` | 红 | 静态 |
| 用户输入 | `` | 白/青 | 静态 |
| 审批中 | `◦` | 黄 | 慢闪 |
| 已取消 | `•` | 灰 | 静态 |
---
## 4. 思考块显示
思考块只保留一种样式:**5 行滚动窗口**。
### 4.1 运行中
```text
◦ 思考中
└ 第一行
第二行
第三行
第四行
第五行
```
当加入第 6 行时,自动滚动:
```text
◦ 思考中
└ 第二行
第三行
第四行
第五行
第六行
```
### 4.2 完成后
```text
• 思考完成 4.2s
```
不做折叠展开,不保留完整思考历史。
### 4.3 规则
- 只显示最新 5 行
- 只显示当前思考内容
- 完成后只留完成状态
- 不显示“思考全文”
---
## 5. 子智能体前台显示
子智能体前台展示方式与思考块一致,也是 5 行滚动窗口。
```text
◦ 子智能体 review-agent...
└ 运行 ls -la && pwd
搜索 python 新版本
第三行
第四行
第五行
```
完成后:
```text
• 子智能体完成 8.1s
```
### 规则
- 子智能体只显示前台进度,不显示全部历史
- 只保留最新 5 行
- 完成态只显示耗时和结果摘要
---
## 6. 工具块通用格式
统一使用:
```text
• 动作摘要
└ 结果摘要
```
### 6.1 运行中
```text
◦ 运行 npm run build
└ 正在执行...
```
### 6.2 完成
```text
• 运行 npm run build
└ 成功,用时 8.2s
```
### 6.3 失败
```text
• 运行 npm run build
└ 失败exit code 2
```
---
## 7. 工具显示规范
下面定义 `└` 后面的内容包含哪些字段、如何显示。
### 7.1 运行命令类
#### 适用工具
- `run_command`
- `terminal_input`
- `terminal_session`
- `terminal_snapshot`
#### 显示字段
- `command` / `input`
- `timeout`
- `exit_code`
- `stdout` / `output`
- `stderr`(若可分离)
#### 显示规则
```text
• 运行 printf '' > test.txt && ls -l test.txt
└ -rw-r--r-- 1 jojo staff 0 May 14 14:48 test.txt
```
长输出:
```text
• 运行 ls -la && pwd
└ 第 1 行内容
第 2 行内容
… +6 lines
第 9 行内容
第 10 行内容
```
#### 约束
- 第一行优先显示结果中最有信息量的一行
- 长文本可局部截断,但不要丢掉关键错误
- 如果有 exit code必须显示
---
### 7.2 文件编辑类
#### 适用工具
- `write_file`
- `edit_file`
#### 原则
**必须完整显示,不折叠。**
因为:
- 这是文件内容本身,不是工具摘要
- 单次替换通常不会太大
- 终端里保留完整 + / - 最有价值
#### 7.2.1 Write
显示字段:
- `path`
- `mode`append / overwrite
- `status`
- `content`
显示模板:
```text
• Write test.txt (+3 -0)
1 +这是第一行测试内容。
2 +这是第二行测试内容。
3 +这是第三行测试内容。
```
#### 7.2.2 Edit
显示字段:
- `path`
- `replace_all`
- `found`
- `replacements`
- 每个 replacement 的 `old` / `new`
显示模板:
```text
• Edited main.py (+2 -2)
14 -old line 1
15 -old line 2
14 +new line 1
15 +new line 2
```
#### 7.2.3 规则
- 不折叠
- 不显示上下文行
- 只显示参数里已有的变更信息
- 若存在多段替换,按替换顺序逐段输出
---
### 7.3 阅读类
#### 适用工具
- `read_file`
- `read_skill`
#### 显示字段
- `path`
- `status`
- `size`
- `read_type`
- `content`
- `search` / `extract` 子模式信息
#### 显示模板
普通读取:
```text
• 阅读 test.txt
└ 读取 3 行
```
提取模式:
```text
• 阅读 test.txt
└ 提取第 12-28 行
```
搜索模式(如果未来恢复):
```text
• 搜索 handle_task
└ 找到 12 处,位于 5 个文件
```
#### 规则
- 正文尽量压缩成“读取了多少 / 提取了什么 / 找到了什么”
- 需要看全文时再走展开命令
---
### 7.4 搜索类
当前 CLI v1 **不做搜索工具**
如果未来恢复,显示方式参考阅读类:
```text
• 搜索 "xxx"
└ 找到 N 处,位于 M 个文件
```
---
### 7.5 文件创建 / 删除 / 重命名
当前 CLI v1 **不做这些工具块**,可直接忽略。
---
### 7.6 子智能体 / 后台任务类
#### 显示字段
- `name`
- `status`
- `latest_progress_lines`
- `elapsed`
#### 显示模板
```text
◦ 子智能体 review-agent...
└ 运行 ls -la && pwd
搜索 python 新版本
第三行
第四行
第五行
```
完成后:
```text
• 子智能体完成 8.1s
```
#### 规则
- 只显示最新 5 行
- 运行中用 `◦`
- 完成后用 `•`
---
### 7.7 审批 / 交互类
#### 适用场景
- 权限审批
- 需要用户确认的危险操作
- 自定义选择
- Slash 命令补全
#### 显示方式
使用**输入栏扩展 + 选项列表**,不是普通消息流。
示例:
```text
┌────────────────────────────────────┐
│ 需要批准:运行 rm -rf dist │
│ 原因:删除构建目录 │
│ │
1. Allow once │
│ 2. Always allow │
│ 3. Deny │
└────────────────────────────────────┘
```
#### 规则
- 上下键切换
- 当前行高亮
- Enter 确认
- Esc 或拒绝关闭
---
## 8. Slash 命令规范
所有操作都通过 `/` 输入。
### 8.1 触发方式
输入 `/``/a` 后,打开命令建议列表。
### 8.2 显示方式
```text
/approve 简要说明
/allow 简要说明
/assist 简要说明
```
### 8.3 规则
- 支持模糊过滤
- 上下键选择
- 当前项变色
- Enter 执行
---
## 9. 最终输出区
任务完成后,使用固定分隔结构:
```text
──
• 已创建 test.txt 文件。
1. 修改文件列表test.txt
2. 核心变更点:在 /Users/jojo/Desktop/test 下创建了一个空文本文件
3. 验证结果:已执行 ls -l test.txt文件存在。
──
```
### 最终结果字段
建议统一输出:
1. **修改文件列表**
2. **核心变更点**
3. **验证结果**
如果有失败:
- 失败原因
- 已做的尝试
- 下一步建议
---
## 10. 信息优先级
从高到低:
1. 当前交互态(审批 / 命令选择)
2. 当前思考 / 当前子智能体进度
3. 当前工具输出
4. 最终结论
5. 低优先级元信息
---
## 11. 参考的 Web 端表现
Web 端已经存在的对应参考:
- `static/src/components/chat/actions/ThinkingAction.vue`
- 思考块
- 运行中 / 完成两态
- 可折叠
- `static/src/components/chat/actions/ToolAction.vue`
- 工具块
- `renderEnhancedToolResult()`
- 文件编辑 / 读取 / 命令 / 图片等不同分支
- `static/src/components/chat/StackedBlocks.vue`
- 思考 + 工具的堆叠展示
- `static/src/components/chat/MinimalBlocks.vue`
- 摘要组展示
- 最终只保留当前步骤预览
- `static/src/components/panels/ToolApprovalPanel.vue`
- 审批面板
- 编辑 diff、写入内容、命令预览
CLI 的目标不是复制这些视觉细节,而是抽取其中最有信息价值的字段。
---
## 12. 推荐的 CLI 默认策略
### 默认开启
- 5 行思考滚动
- 5 行子智能体滚动
- 文件编辑完整展示
- 命令输出摘要展示
- Slash 命令补全
- 审批选择弹层
### 默认关闭
- 完整思考历史
- 文件创建 / 删除 / 重命名显示
- 搜索工具
- 折叠展开复杂交互
---
## 13. 一句话总结
CLI 的目标是:
> 用最少的符号,表达最多的有效信息;用固定的状态和结构,让用户能一眼看懂 Agent 在做什么。